laravel5日志设置篇(3/3) – 精确到微秒及日志输出位置记录

想要实现的功能

  • 记录日志输出时的时间精度到微秒
  • 记录日志输出时程序执行位置(方法/行数)
  • 记录进程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.*

具体实现

  1. 继承Illuminate\Log\Writer类,复写getDefaultFormatter方法
  2. 编写Monolog用处理模块
  3. 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;
    }
    ...

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注