想要实现的功能
- 记录日志输出时的时间精度到微秒
- 记录日志输出时程序执行位置(方法/行数)
- 记录进程ID
例如:
- app.log
[2017-10-22 10:13:57.833504] production.INFO: -- Startup {"method":"GET","uri":"/v1/stocks"} {"pid":7124,"line":"App\\Bootstrap\\ApplicationLog->startupLog:66"}
[2017-10-22 10:13:57.898702] production.INFO: get stocks info {"pid":7124,"line":"App\\Http\\Controllers\\StocksController->index:66"}
[2017-10-22 10:13:57.903274] production.INFO: -- Shutdown {"time":"70.371[ms]","memory":"2048[kb]"} {"pid":7124,"line":"App\\Bootstrap\\ApplicationLog->handleShutdownLog:81"}
- sql.log
[2017-10-22 10:11:19.750913] production.DEBUG: select count(*) as aggregate from `stocks_input` where `stocks_input`.`deleted_at` is null; {"time":0.63} {"pid":7124}
[2017-10-22 10:11:19.752787] production.DEBUG: select * from `stocks_input` where `stocks_input`.`deleted_at` is null order by `created_at` desc limit 10 offset 0; {"time":0.83} {"pid":7124}
[2017-10-22 10:11:19.756438] production.DEBUG: select * from `goods` where `goods`.`id` = 1 limit 1; {"time":0.9} {"pid":7124}
环境
- php 7.0.22
- laravel 5.2.*
具体实现
- 继承
Illuminate\Log\Writer
类,复写getDefaultFormatter
方法 - 编写
Monolog
用处理模块 - 1步骤创建的类替换老Writer及注册
Monolog
用处理模块
ps. 依赖设置
laravel5日志设置篇(1/3) – 记录开始和结束日志
laravel5日志设置篇(2/3) – 记录sql日志
新Write类 (app/Utils/Writer.php)
<?php
namespace App\Utils\Monolog;
use Illuminate\Log\Writer as BaseWriter;
use Monolog\Formatter\LineFormatter;
/**
* 时间精确到微秒
*
* @package app.Utils.Monolog
*/
class Writer extends BaseWriter
{
/**
* Get a default Monolog formatter instance.
*
* @return \Monolog\Formatter\LineFormatter
*/
protected function getDefaultFormatter()
{
return new LineFormatter(null, 'Y-m-d H:i:s.u', true, true);
}
}
Monolog输出处理模块
- 程序执行位置信息 (app/Utils/Monolog/Processor/IntrospectionProcessor.php)
<?php
namespace App\Utils\Monolog\Processor;
use Monolog\Logger;
/**
* Monolog用处理模块
* 标准IntrospectionProcessor的简化版
*
* @package app.Utils.Monolog.Processor
*/
class IntrospectionProcessor
{
private $level;
private $skipClassesPartials;
public function __construct($level = Logger::DEBUG, array $skipClassesPartials = ['Monolog\\', 'Illuminate\\'])
{
$this->level = Logger::toMonologLevel($level);
$this->skipClassesPartials = $skipClassesPartials;
}
/**
* @param array $record
* @return array
*/
public function __invoke(array $record)
{
// return if the level is not high enough
if ($record['level'] < $this->level) {
return $record;
}
$trace = debug_backtrace();
// skip first since it's always the current method
array_shift($trace);
// the call_user_func call is also skipped
array_shift($trace);
$i = 0;
while (isset($trace[$i]['class'])) {
foreach ($this->skipClassesPartials as $part) {
if (strpos($trace[$i]['class'], $part) !== false) {
$i++;
continue 2;
}
}
break;
}
$line = null;
if (isset($trace[$i]['class'])) {
$line = $trace[$i]['class'];
if (isset($trace[$i]['class'])) {
$line = $line . '->' . $trace[$i]['function'];
}
} else if (isset($trace[$i - 1]['file'])) {
$line = $trace[$i - 1]['file'];
}
if (null !== $line && isset($trace[$i - 1]['line'])) {
$line = $line . ':' . $trace[$i - 1]['line'];
}
$record['extra']['line'] = $line;
return $record;
}
}
- 线程ID信息 (app/Utils/Monolog/Processor/ProcessIdProcessor.php)
<?php
namespace App\Utils\Monolog\Processor;
/**
* Monolog用处理模块
* 标准IntrospectionProcessor的简化版, 原输出键值文字长度过长
*
* @package app.Utils.Monolog.Processor
*/
class ProcessIdProcessor
{
/**
* @param array $record
* @return array
*/
public function __invoke(array $record)
{
$record['extra']['pid'] = getmypid();
return $record;
}
}
替换老Writer及注册Monolog
用处理模块 (app/Providers/LogServiceProvider.php)
<?php
namespace App\Providers;
// 引入新创建IntrospectionProcessor, ProcessIdProcessor, Writer类
use App\Utils\Monolog\Processor\IntrospectionProcessor;
use App\Utils\Monolog\Processor\ProcessIdProcessor;
use App\Utils\Monolog\Writer;
use Carbon\Carbon;
use Illuminate\Support\ServiceProvider;
use Monolog\Logger as Monolog;
/**
* 日志记录器服务
*
* @package app.Providers
*/
class LogServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('app-log', function () {
return $this->createAppLogger();
});
$this->app->singleton('sql-log', function () {
return $this->createSqlLogger();
});
}
/**
* Create the app logger.
*
* @return \Illuminate\Log\Writer
*/
public function createAppLogger()
{
//新增
$processors = [
new ProcessIdProcessor(),
new IntrospectionProcessor()
];
$log = new Writer(
new Monolog($this->channel(), [], $processors), $this->app['events']
);
$this->configureHandler($log, 'app');
return $log;
}
/**
* Create the sql logger.
*
* @return \Illuminate\Log\Writer
*/
public function createSqlLogger()
{
//新增
$processors = [
new ProcessIdProcessor(),
];
$log = new Writer(
new Monolog($this->channel(), [], $processors), $this->app['events']
);
$this->configureHandler($log, 'sql');
return $log;
}
...