unifreak/qlog

可配置的记录器,可以将日志记录到Redis队列以持久化或记录到stashed进行调试

v1.0.0 2019-06-20 10:03 UTC

This package is auto-updated.

Last update: 2024-09-20 22:23:06 UTC


README

中文用户请见 https://github.com/UniFreak/QLog/blob/master/README.cn.md

QLog 包装了 Monolog 包,并添加了一些自定义的处理器/处理器/格式化器,以提供以下功能

  • 根据配置将日志记录到stash区域(用于后续输出和调试)或Redis队列
  • 提供一些预定义的日志通道和相应的日志方法
  • 所有记录都有额外的字段,提供关于耗时和内存使用的详细信息。请参阅一些概念
  • 为每条记录自动生成一个会话键。请参阅一些概念
  • 使用 _id 标识符标记记录
  • 搜索stashed记录

目前,QLog 的两个主要用例是

  • 在API响应中打印stashed记录,用于实时调试
  • 异步处理Redis队列中的记录,将它们聚合到一个地方(如ES)进行搜索或可视化

安装

只需运行 composer require unifreak/qlog

用法

一些概念

QLog 以预定义的结构将日志记录作为数组,如下所示

[
    "message" => "api call to api.example.com",
    "context" => [
        "method" => "GET",
        "params" => [
            "id" => 12345,
            "name" => "John",
        ],
    ],
    "level" => 200,
    "level_name" => "INFO",
    "channel" => "app",
    "datetime" => "2019:03:28 18:53:19.587777",
    "time" => "15 ms",
    "time_total" => "15 ms",
    "session" => "15a1c8220475f41990cc389ad5f6b495",
    "mem" => "3 MB",
]
  • message:日志消息

  • context:日志上下文

  • level & level_name

    日志级别,支持的级别有 DEBUGINFONOTICEWARNINGERRORCRITICALALERTEMERGENCY。您可以通过调用相应的日志方法来指定级别。请参阅日志

  • channel

    日志通道。每条日志都位于一个指定的通道中,以指示此日志可能与哪个应用程序事件相关。

    QLog有几个预定义的通道

    QLogger::CHANNEL_APP = 'app'; // Applicatoin
    QLogger::CHANNEL_SQL = 'sql'; // SQL
    QLogger::CHANNEL_API = 'api'; // Api call
    QLogger::CHANNEL_REQ = 'req'; // Request
    QLogger::CHANNEL_RESP = 'resp'; // Response

    您可以通过调用不同的 in 方法将记录记录到预定义的通道或自定义通道。请参阅指定通道

  • datetime:日志日期时间

  • time / time_total / mem:自上次日志记录以来消耗的时间 / 自首次日志记录以来消耗的时间 / 消耗的内存

  • session:

    日志记录的会话键。此会话键可以用于将不同请求中的日志链接在一起,这些请求可能跨越多个应用程序。例如,对应用程序A的API调用 A:api/a

    A:api/a --> B:api/b --> C:api/c

    如果A、B和C中的所有日志都共享相同的会话键,则我们可以搜索整个跨应用程序日志链。

    QLogger的会话键是随机字符串。QLogger会从 QLOG_SESSION cookie中读取以初始化会话键,如果没有 QLOG_SESSION cookie,则将生成一个新的会话键。之后,所有日志记录都将具有相同的会话键。

    但是请注意:您必须手动维护 QLOG_SESSION cookie以链接记录。

  • _id:id

    如果我们只有会话键,这将是一个问题:因为会话键是随机的,我们在搜索记录链之前不知道它,所以我们如何知道这个会话键?

    这就是 id 字段的作用。

    默认日志记录结构中没有 _id 字段,但您可以通过调用 idBy() 方法指定多个 _id 字段。请参阅指定ID

    id 的主要目的是首先进行搜索以定位一个感兴趣的记录,然后使用此记录的会话密钥来搜索整个日志记录链。

初始化

QLog 构造函数需要两个参数:一个 \Predis\Client/\Reids 实例和一个配置数组。如下所示

use Unifreak\QLog\QLogger;

$redis = new \Predis\Client(['host' => '127.0.0.1', 'port' => 6379]);
$config = [
    'default_channel' => QLogger::CHANNEL_APP,
    'queue_name' => 'qlog:example.com',
    'size' => 3000,
    'log_to' => QLogger::LOG_REDIS,
];
$log = new QLogger($redis, $config);

配置选项:$config

  • queue_name:

    必需,指定 QLog 要记录的 Redis 队列

  • default_channel:

    指定默认通道。默认为 app

  • size:Redis 队列的最大大小。默认为 3000

  • log_to:记录记录的位置。支持三个值

    • 0:仅记录到存储区
    • 1:默认值。仅记录到 Redis 队列
    • 2:记录到存储区和 Redis 队列

日志

您可以通过调用不同的日志方法来记录不同级别的记录,传递日志消息(必需)和日志上下文(可选)

$message = 'log message';
$context = ['some' => 'context'];

$log->info($message, $context);
$log->notice($message, $context);
$log->warn($message, $context);
$log->error($message, $context);
$log->critical($message, $context);
$log->alert($message, $context);
$log->emergency($message, $context);

指定通道

您可以通过调用这些方法来指定预定义的通道

$log->inApp()->info($message, $context); // Specify channel: app
$log->inSql()->info($message, $context); // Specify channel: sql
$log->inApi()->info($message, $context); // Specify channel: api
$log->inReq()->info($message, $context); // Specify channel: req
$log->inResp()->info($message, $context); // Specify channel: resp

此外,您还可以通过调用 in() 方法来指定自定义通道

$log->in('custom_channel')->info($message, $context); // Specify channel: custom_channel

指定 ID

您可以通过调用 idBy() 方法来指定多个 id 名称/值对。**注意**:id 名称必须以 _id 结尾。

$log->idBy('car_id', 123)->idBy('user_id', 321)->info($message, $context);

然后记录将具有额外的两个 id 字段

[
    'car_id' => 123,
    'user_id' => 321
]

**注意**:

  • 如果您多次使用相同的名称调用 idBy(),则后者将覆盖前者
  • Id 是 sticky,这意味着所有后续日志都将自动保留指定的 id 名称/值。

过滤存储的日志

您可以通过调用这些方法来过滤存储的日志

$log->shift(); // get the first record
$log->pop(); // get the last record
$log->stashed(); // get all records
$log->stashed(function($record) { // filter for specific records
    // like: filter for records that have car_id and level greater than warning
    return !empty($record['car_id']) && $record['level'] > QLogger::WARN;
});
$log->clean(); // clear all records

**注意**:如果 QLog 配置为不记录到存储区(log_to 配置选项),则存储区将为空,因此上述所有方法将返回空数组

Laravel & Lumen

QLog 为 laravel/lumen 提供了一个外观类和服务提供者类

  • 外观:Unifreak\QLog\QLogFacade
  • 服务提供者:Unifreak\QLog\QLogServiceProvider

在注册外观和服务提供者之后,您可以使用 QLog 来访问 QLogger,如下所示

use QLog;

QLog::in('my_channel')->idBy('car_id', 123)->warning('somthing went wrong');
dump(QLog::stashed());

QLogServiceProvider 还启用了自动记录 SQL 查询和 GuzzleHttp 请求。您可以使用以下查询参数来控制 QLog 的日志行为

  1. qlog_debug:

    非零值等同于传递 qlog_autolog=2qlog_log_to=0,启用自动记录 GuzzleHttp 请求和 SQL 查询,并且仅记录到存储区。见下文

  2. qlog_autolog:控制自动记录行为

    • 1:默认值。自动记录 GuzzleHttp 请求
    • 2:自动记录 GuzzleHttp 请求和 SQL 查询
  3. qlog_log_to:控制日志区域

    • 0:仅记录到存储区
    • 1:仅记录到 Redis 队列
    • 2:同时记录到存储区和 Redis 队列

注册外观和服务提供者

  1. 添加一个新的配置文件 config/qlog.php
return [
    // Disable QLog:
    // - If disabled, all log methods call will simply be ignored
    // - But if there is `qlog_debug` query parameter present, QLog will be auto-enabled
    'disable' => false,
    // redis connection config
    'redis' => [
        'host'     => $redisHost,
        'port'     => $redisPort
    ],
    // qlog config
    'queue_name' => 'qlog:example.com',
    'size' => 2000,
];
  1. 将以下代码添加到 bootstrap/app.php
if (!class_exists('QLog')) {
    class_alias(Unifreak\QLog\QLogFacade::class, 'QLog');
}

$app->configure('qlog');
$app->register(Unifreak\QLog\QLogServiceProvider::class);

**注意**:确保在 $app->withEloquent() 之后注册服务提供者,否则 SQL 查询自动记录功能将无法正常工作

有关更多信息,请参阅 laravel/lumen 的官方文档

TODO

  • QLogServiceProvider 中自动记录异常
  • 当记录达到配置的级别时,自动发送邮件