创建一个yii2的应用
在@app/Controllers目录新建一个TestController, 在控制器里新建一个actionIndex方法
class TestController extends Controller{
public function actionIndex() {
}
}
注:@app是yii2中的路径别名语法,表示应用的基础目录
在actionIndex方法中写日志
...
public function actionIndex() {
//记录一个错误级别的日志
Yii::error('人艰不拆!');
}
...
进入yii2应用的日志目录@runtime/logs, 看到生成了一个名为app.log的文件, 内容如下:
//日志产生时间 访问者ip地址 用户id SESSIONID 日志级别 日志类别 日志内容
1 2014-12-04 12:27:26 [10.18.60.111][-][-][error][application] 人艰不拆!
//trace记录
2 in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:18
警告级别 WARNING
public function actionIndex() {
//记录一个警告级别的日志(日志内容可以为任何数据类型)
Yii::warning(['source'=>'关电总局','content'=>'不能看美剧了']);
}
日志输入结果
3 2014-12-04 14:23:32 [10.18.60.111][-][-][warning][application] [
4 'source' => '关电总局',
5 'content' => '不能看美剧了',
6 ]
7 in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:18
提示级别 INFO
//记录一个提示级别的日志
Yii::info('喝酸奶只舔盖,我就是这么任性!');
代码追踪级别 TRACE
//记录一个trace级别的日志
Yii::trace('我这个级别,一般是在开发环境使用');
trace级别的日志,只会在dev开发环境下才会触发, 所以Yii::trace方法中做了如下判断
... public static function trace($message, $category = 'application') { if (YII_DEBUG) { //只有开发环境(YII_DEBUG为true)才会触发 static::getLogger()->log($message, Logger::LEVEL_TRACE, $category); } } ...
打开应用的配置文件@app/config/web.php, 在log组件的targets属性数组中添加一个"target"对象
...
'components' => [
...
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning','info','trace','profile'],
'logVars'=>[],
//除了except对应的分类之外,其他的都写入到
'except'=>['yii\db\*','app\models\*']
],
//在原配置的基础上,增加以下配置(新增一个target)
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning','info','trace','profile'],
'logVars'=>[],
//表示以yii\db\或者app\models\开头的分类都会写入这个文件
'categories'=>['yii\db\*','app\models\*'],
//表示写入到文件sql.log中
'logFile'=>'@runtime/logs/sql.log',
],
],
],
...
],
...
调用的时候,只需要加上第二个参数,填写对应的分类即可
public function actionIndex() {
//日志的第二个参数就是分类category
Yii::info('select * from table', 'yii\db\Query');
}
进入@runtime/logs下,可以看到生成了一个sql.log的文件
└── logs
├── app.log
└── sql.log
//sql.log内容如下
2014-12-05 18:58:51 [10.18.60.111][-][-][info][yii\db\Query] select * from table
2 in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:16
...
'components' => [
...
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning','info','trace','profile'],
'logVars'=>[],
//表示以yii\db\或者app\models\开头的分类都会写入这个文件
'categories'=>['yii\db\*','app\models\*'],
//表示写入到文件sql.log.2014xxxx
'logFile'=>'@runtime/logs/sql.log.'.date('Ymd'),
],
],
],
...
],
...
2.再次使用Yii::info('select * from table', 'yii\db\Query');
, 你会看到日志目录里logs
├── app.log
├── sql.log
└── sql.log.20141208
CREATE TABLE log (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
level INTEGER,
category VARCHAR(255),
log_time INTEGER,
prefix TEXT,
message TEXT,
INDEX idx_log_level (level),
INDEX idx_log_category (category)
)
...
'components' => [
...
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
...
[
'class' => 'yii\log\DbTarget', //DbTaget类表示将日志记录到数据库中
'levels' => ['error', 'warning','info'],
'logVars'=>[],
'logTable'=>'log',//logTable表示要记录日志的表名,默认为log
],
...
],
],
...
],
...
Yii::info('切克闹');
, 所以符合条件的target都会记录这条信息
在表log中会插入一条数据
+----+-------+-------------+------------+----------------------+-----------+
| id | level | category | log_time | prefix | message |
+----+-------+-------------+------------+----------------------+-----------+
| 11 | 4 | application | 1418010724 | [10.18.60.111][-][-] | 切克闹 |
+----+-------+-------------+------------+----------------------+-----------+
1 row in set (0.00 sec)
在app.log文件中会追加一条数据
2014-12-08 11:52:03 [10.18.60.111][-][-][info][application] 切克闹 180 in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:16
yii\log\EmailTarget
,记录到syslog yii\log\SyslogTarget
,此处不再细述, 更重要的是,你可以自定义target类来记录日志Yii2的日志主要由
yii\log\Logger
,yii\log\Dispatcher
,yii\log\Target
三类来完成,其中Logger在内存中记录日志信息,当日志信息数达到一定量或者是脚本结束时,
Logger就把日志信息交给了Dispatcher, Dispatcher把日志根据不同的配置分发到不同的Target子类,最终Target的子类来完成对日志的具体处理:
1.Yii类中yii\db\Logger的实例化,,当调用Yii::info('反正我信了')时,Yii类(BaseYii)是如何反应的
代码yii\BaseYii#353:
//私有的静态属性$_logger用来储存Logger对象
private static $_logger;
//公有方法返回Logger的单例对象
public static function getLogger()
{
if (self::$_logger !== null) {
return self::$_logger;
} else {
return self::$_logger = static::createObject('yii\log\Logger');
}
}
//调用info等写日志方法时,实际调用的是Logger的log方法,并传递了写入的信息$message,日志等级,日志分类$category
public static function info($message, $category = 'application')
{
static::getLogger()->log($message, Logger::LEVEL_INFO, $category);
}
2.yii\log\Logger类的log方法
代码yii\log\Logger#135
//将日志信息写入到$this->messages属性中
public function log($message, $level, $category = 'application')
{
$time = microtime(true);
$traces = [];
//如果在应用初始化时配置log组件的traceLevel大于0,则将debug_backtrace信息也记录到日志中
if ($this->traceLevel > 0) {
$count = 0;
$ts = debug_backtrace();
array_pop($ts); // remove the last trace since it would be the entry script, not very useful
foreach ($ts as $trace) {
if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII2_PATH) !== 0) {
unset($trace['object'], $trace['args']);
$traces[] = $trace;
if (++$count >= $this->traceLevel) {
break;
}
}
}
}
//将信息保存到messages数组
$this->messages[] = [$message, $level, $category, $time, $traces];
//当保存的信息数量达到$this->flushInterval属性值时,执行flush方法清空日志,默认值为1000
if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
$this->flush();
}
}
//清空日志信息,其实是调用yii\db\Dispatcher的dispatch方法,并传递信息数组$this->messages,和$final参数,$final为true表示脚本已执行结束,不管messages已经多少条都会写入
public function flush($final = false)
{
if ($this->dispatcher instanceof Dispatcher) {
$this->dispatcher->dispatch($this->messages, $final);
}
$this->messages = [];
}
3.yii\log\Dispatcher类的dispatch方法, 此类即应用的log组件
代码yii\log\Dispatcher#177
public $targets = []; //targets来自于应用的配置,参见代码@app/configs/web.php
public function dispatch($messages, $final)
{
$targetErrors = [];
//遍历所有的targets,如果target可用(enabled属性为true),执行target的collect方法(collect方法在父类yii\log\Target中实现)
foreach ($this->targets as $target) {
if ($target->enabled) {
try {
//调用yii\log\Target的collect方法
$target->collect($messages, $final);
} catch (\Exception $e) {
$target->enabled = false;
$targetErrors[] = [
'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToString($e),
Logger::LEVEL_WARNING,
__METHOD__,
microtime(true),
[],
];
}
}
}
if (!empty($targetErrors)) {
$this->dispatch($targetErrors, true);
}
}
```
代码@app/configs/web.php
...
'components' => [
...
//此处配置yii\log\Dispatcher的属性
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0, //traceLevel大于0,则会记录debug_backtrace信息
//此处配置yii\log\Dispatcher的targets属性,每个子元素都是yii\log\Target的子类
'targets' => [
[
'class' => 'yii\log\DbTarget', //DbTaget类表示将日志记录到数据库中
'levels' => ['error', 'warning','info'],
'logVars'=>[],
'logTable'=>'log',//logTable表示要记录日志的表名,默认为log
],
...
],
],
...
],
...
4.yii\log\Target类的collect方法
代码yii\log\Target#100
//执行对$messages的收集
public function collect($messages, $final)
{
//对$messages进行过滤,过滤掉不属于当前调用的target的信息
$this->messages = array_merge($this->messages, $this->filterMessages($messages, $this->getLevels(), $this->categories, $this->except));
$count = count($this->messages);
//当日志信息数量达到执行数量$this->exportInterval时执行,默认值为1000
if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
//记录$_GET,$_POST,$_SERVER等系统访问信息
if (($context = $this->getContextMessage()) !== '') {
$this->messages[] = [$context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME];
}
// set exportInterval to 0 to avoid triggering export again while exporting
$oldExportInterval = $this->exportInterval;
$this->exportInterval = 0;
//调用export方法,此方法最终实现对message的处理,参见代码yii\log\FileTarget
$this->export();
$this->exportInterval = $oldExportInterval;
$this->messages = [];
}
}
5.以日志写入到文件为例,yii\log\FileTarget类的export方法
代码yii\log\FileTarget#97
//将messages写入到文件$this->logFile中
public function export()
{
//对messages进行格式化处理
$text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n";
if (($fp = @fopen($this->logFile, 'a')) === false) {
throw new InvalidConfigException("Unable to append to log file: {$this->logFile}");
}
@flock($fp, LOCK_EX);
// clear stat cache to ensure getting the real current file size and not a cached one
// this may result in rotating twice when cached file size is used on subsequent calls
clearstatcache();
if (@filesize($this->logFile) > $this->maxFileSize * 1024) {
$this->rotateFiles();
@flock($fp, LOCK_UN);
@fclose($fp);
//执行写入
@file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
} else {
//执行写入
@fwrite($fp, $text);
@flock($fp, LOCK_UN);
@fclose($fp);
}
if ($this->fileMode !== null) {
@chmod($this->logFile, $this->fileMode);
}
}