日志配置详解

日志系统的全部配置集中在 config/log.php。本篇给出完整配置项说明、内置 File 驱动的参数与存储规则,以及自定义日志驱动的扩展方式。

默认配置全览

框架自带的 config/log.php

php
use Viswoole\Log\Drives\File;

return [
  // 默认通道
  'default' => 'file',
  // 按日志级别指定写入通道,例如:['error' => 'email']
  'type_channel' => [],
  // 是否跟踪日志来源
  'trace_source' => true,
  // 是否同时将日志输出到控制台(只建议在开发环境中使用)
  'console' => false,
  // 日志驱动通道,可自行实现日志驱动:需继承 \Viswoole\Log\Drive 类
  // 或实现 \Viswoole\Log\Contract\DriveInterface 接口
  'channels' => [
    'file' => File::class,
  ],
];

配置项总表

配置键类型源码默认值说明
defaultstring首个通道名默认通道名称,内部按小写索引
type_channelarray[]级别 → 通道映射,键为级别名,值为通道名或通道名数组;引用不存在的通道启动时抛 LogException
trace_sourceboolfalse是否记录调用来源(文件:行号);框架默认配置中显式设为 true
consolebooltrue是否同步输出到控制台;框架默认配置中显式设为 false
channelsarray['default' => File 实例]通道列表,键为通道名,值为驱动定义(三种形式见下)

默认值差异

「源码默认值」指完全未提供该配置项时 LogManager 的兜底行为;框架自带的 config/log.php 覆盖了 trace_sourceconsole 两项。通道列表完全未配置时,框架会注册一个名为 default 的 File 通道。

通道定义

channels 中每个通道支持三种形式:

php
use App\Log\ElasticsearchDrive;
use Viswoole\Log\Drives\File;

'channels' => [
  // 形式一:驱动类名(容器无参实例化)
  'file' => File::class,

  // 形式二:配置数组,options 为驱动构造参数
  'error' => [
    'driver' => File::class,
    'options' => [
      'log_dir' => BASE_PATH . '/runtime/error_logs',
      'storageDays' => 90,
    ],
  ],

  // 形式三:驱动实例
  'es' => new ElasticsearchDrive(['hosts' => ['http://127.0.0.1:9200']]),
],

驱动类不存在、options 格式错误或未实现 DriveInterface 时抛出 LogException。通道名内部统一转小写。

File 驱动

内置驱动 Viswoole\Log\Drives\File 将日志写入本地文件,按「日期 / 级别」两级目录组织,支持按大小切割与每日自动清理。

构造参数

参数类型默认值说明
storageDaysint7日志保留天数,超期日期目录被自动清理
maxFilesint30单个级别下最大文件数量,超出后删除最早文件
fileSizeint10485760单文件最大字节数(10MB),超出自动切割
dateFormatstringc时间戳格式(date() 格式字符)
logFormatstring见下文文本格式规则,json 为 false 时生效
jsonbooltrue是否以 JSON 格式存储
json_flagsintJSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHESJSON 编码标志位
log_dirstringBASE_PATH.'/runtime/logs'日志根目录

默认文本格式为 [%timestamp][%level]: %message %context in %source

存储结构

text
runtime/logs/
├── 20260911/
│   ├── info/
│   │   ├── info_0.log
│   │   └── info_1.log        # 超过 fileSize 后切割出的新文件
│   ├── error/
│   │   └── error_0.log
│   └── sql/
└── 20260912/
    └── ...

写入与清理规则:

  • 切割:单个文件达到 fileSize 后,新日志写入编号 +1 的新文件(info_1.loginfo_2.log
  • 数量上限:单级别文件数超过 maxFiles 时删除最早的文件
  • 自动清理:构造时注册 Swoole 定时器,每日凌晨清理目录名超过 storageDays 的日期目录;日期目录名必须是 8 位 Ymd 数字格式,避免误删其他目录

JSON 与文本格式

json: true(默认)时每条日志一行 JSON,字段固定为 timestamplevelmessagecontextsource

json
{
  "timestamp": "2026-01-01T12:00:00+08:00",
  "level": "info",
  "message": "用户登录",
  "context": { "uid": 1 },
  "source": "/app/Controller/AuthController.php:25"
}

json: false 时按 logFormat 渲染,支持占位符:

占位符说明
%timestampdateFormat 格式化的时间
%level日志级别
%message日志消息
%context上下文数据(JSON 编码,空时显示 {}
%source调用来源
php
'options' => [
  'json' => false,
  'logFormat' => '%timestamp | [%level] %message %context in %source',
],

格式规则兼容 sprintf

logFormat 中不含 %占位符 时,规则将按 vsprintf 语义处理,日志字段按 timestamp、level、message、context、source 顺序填充。

自定义日志驱动

继承 Drive 基类(推荐)

Viswoole\Log\Drive 已实现级别快捷方法、协程聚合缓存(record)与直写(write)逻辑,子类只需实现 save() 定义持久化策略,协程结束时由 Recorder 自动批量调用:

php
namespace App\Log;

use Viswoole\Log\Drive;

class DatabaseDrive extends Drive
{
  public function __construct(protected string $table = 'system_logs')
  {
  }

  /**
   * 批量持久化日志(协程结束时自动调用)
   *
   * @param array<int, array{
   *   timestamp: int, level: string, message: string,
   *   context: array, source: string
   * }> $logRecords
   */
  public function save(array $logRecords): void
  {
    foreach ($logRecords as $record) {
      // 写入数据库 / 推送到外部服务等
    }
  }
}

驱动内的协程安全

基类把聚合缓存放在每个协程独立的 Recorder(存于协程上下文)中,自定义 save() 无需关心协程隔离,只需专注批量持久化本身。

接口契约

不继承基类时,需实现 Viswoole\Log\Contract\DriveInterface 的全部方法,它由两部分组成:

  • CollectorInterface(收集器接口):error / warning / info / debug / alert / sql / task 七个级别方法与 mixed($level, $message, $context)
  • 驱动扩展:record()(协程缓存写入)、write()(直写)、save(array $logRecords)(批量持久化)、getRecord() / clearRecord()(协程缓存读取与清理)

因此自行实现时需同时处理「级别分发 + 协程聚合 + 持久化」三层逻辑,没有特殊理由建议直接继承 Drive

注册自定义通道

php
// 方式一:config/log.php(推荐)
'channels' => [
  'database' => [
    'driver' => \App\Log\DatabaseDrive::class,
    'options' => ['table' => 'app_logs'],
  ],
],

// 方式二:服务启动前动态注册
Log::addChannel('database', \App\Log\DatabaseDrive::class);

结合 type_channel 可把指定级别导入自定义通道,实现错误告警、审计入库等策略,见 级别与通道

环境变量集成

推荐通过环境变量区分环境差异,读取方式见 环境变量

php
use Viswoole\Log\Drives\File;

return [
  'default' => env('LOG_CHANNEL', 'file'),
  'console' => env('APP_DEBUG', false),
  'channels' => [
    'file' => [
      'driver' => File::class,
      'options' => [
        'storageDays' => (int)env('LOG_STORAGE_DAYS', 7),
        'log_dir' => env('LOG_DIR', BASE_PATH . '/runtime/logs'),
      ],
    ],
  ],
];
ini
LOG_CHANNEL=file
LOG_STORAGE_DAYS=30
LOG_DIR=/var/log/viswoole

下一步