简介

在实际开发中,我们发现在 Debug 的时候经常需要查询日志。而传统的方式是需要 SSH 到生产环境,然后使用 cattailgrep 等命令查询日志,且无法进行日志的统计和分析,深度挖掘这些日志的价值。

本片文章的侧重点在于优雅的让 Laravel 直接将日志写入 Elasticsearch,当然你也可以选择使用 Logstash 采集 Laravel 的本地日志。

环境搭建可以参考《Elastic Stack 之 Elasticsearch》和《Elastic Stack 之 Kibana》这两篇博文。

所需依赖

  • elasticsearch/elasticsearch
  • betterde/logger(如果需要)

原理分析

monolog

Laravel 使用 monolog/monolog 作为默认日志处理模块。monolog 自带了很多 Handler 其中就有 ElasticsearchHandler,后面我们会进行介绍。

生命周期

  • Illuminate\Foundation\Application 的构造函数中注册基本的服务提供者,其中就有 Illuminate\Log\LogServiceProvider
  • Illuminate\Log\LogServiceProvider 中将 Illuminate\Log\LogManager 注册到容器,此时还没有 Logger。
  • 当我们使用 Illuminate\Support\Facades\Log::info() 的时候,LogManager 会调用内部一系列方法根据配置文件创建 Logger 和其所需的 Handlers
  • 最终调用 Logger 的 info()、error()、debug() 等方法,实现记录日志的功能。

lifecycle

简单适配

只需要通过修改 config/logging.php.env 文件就可以实现,将日志直接写入 Elasticcsearch:

'elastic' => [
    'driver' => 'monolog',
    'level' => 'debug',
    'name' => 'Develop',
    'tap' => [],
    'handler' => ElasticsearchHandler::class,
    'formatter' => \Monolog\Formatter\ElasticsearchFormatter::class,
    'formatter_with' => [
        'index' => 'monolog',
        'type' => '_doc'
    ],
    'handler_with' => [
        'client' => \Elasticsearch\ClientBuilder::create()->setHosts(['http://localhost:9200'])->build(),
    ],
],
LOG_CHANNEL=elastic

轮子

会发现当我们在写入日志的时候,Laravel 是单条同步进行的,对于我们使用原创存储来说这将影响一定的性能。所以我们需要在整个生命周期中临时保存所有的日志信息,在请求结束前触发同步或异步将日志批量写入存储。

为此我周末写了一个扩展包 betterde/logger,如果你在使用的过程中有任何问题可以在 Github 上提 IssuePR

安装

$ composer require betterde/logger
$ php artisan vendor:publish --tag=betterde.logger

配置

<?php

return [
    /*
     * 是否开启批量写入,需要设置中间件
     */
    'batch' => false,

    /*
     * 是否使用队列
     */
    'queue' => false,

    /*
     * 日志级别,值可以参考 Monolog\Logger.php 中的定义
     */
    'level' => 200,

    /*
     * 是否在多个 Handler 中流转日志数据
     */
    'bubble' => false,

    /*
     * Elasticsearch DB
     */
    'elasticsearch' => [
        'hosts' => [
            [
                /*
                 * host 是必填项
                 */
                'host' => env('ELASTICSEARCH_HOST', 'localhost'),
                'port' => env('ELASTICSEARCH_PORT', 9200),
                'scheme' => env('ELASTICSEARCH_SCHEME', 'http'),
                'user' => env('ELASTICSEARCH_USER', null),
                'pass' => env('ELASTICSEARCH_PASS', null)
            ],
        ],
        'retries' => 2,
        /*
         * 证书路径
         */
        'cert' => ''
    ],

    /*
     * Handler 的设置
     */
    'options' => [
        'index' => 'monolog', // Elastic index name
        'type' => '_doc',    // Elastic document type
        'ignore_error' => false,     // Suppress Elasticsearch exceptions
    ],

    /*
     * 对于异常日志是否记录追踪详情
     */
    'exception' => [
        'trace' => false,
    ],

    /*
     * 扩展属性,用于区分多个项目使用同一个 Elasticsearch Index,extra 数组里的 Key 都是可以自定义的,我这里只是举例
     */
    'extra' => [
        'host' => 'example.com',
        'php' => '7.3.5',
        'laravel' => '6.5.2'
    ]
];

好了现在可以尽情的享受写 CURD 的乐趣了。

I hope this is helpful, Happy hacking…