mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-09-05 07:15:31 +08:00
feat(log): 新增 DebugMysqlAsync 异步日志驱动与 debug_mysql_async 通道
This commit is contained in:
@@ -19,6 +19,7 @@ PREFIX=ul_
|
|||||||
FIELDS_CACHE=false
|
FIELDS_CACHE=false
|
||||||
|
|
||||||
[LOG]
|
[LOG]
|
||||||
|
# 日志通道三选一:file(本地文件)/ debug_mysql(直写数据库)/ debug_mysql_async(异步:本地 JSONL + 定时导入,写日志零 DB 依赖)
|
||||||
CHANNEL=file
|
CHANNEL=file
|
||||||
|
|
||||||
[TIMER]
|
[TIMER]
|
||||||
|
|||||||
@@ -72,7 +72,11 @@ return [
|
|||||||
'charset' => Env::get('database.charset', 'utf8'),
|
'charset' => Env::get('database.charset', 'utf8'),
|
||||||
// 数据库表前缀
|
// 数据库表前缀
|
||||||
'prefix' => Env::get('database.prefix', 'ul_'),
|
'prefix' => Env::get('database.prefix', 'ul_'),
|
||||||
]
|
],
|
||||||
|
// 异步模式:本地 JSONL + 定时导入,写日志零 DB 依赖
|
||||||
|
'debug_mysql_async' => [
|
||||||
|
'type' => 'DebugMysqlAsync',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
86
extend/think/log/driver/DebugMysqlAsync.php
Normal file
86
extend/think/log/driver/DebugMysqlAsync.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace think\log\driver;
|
||||||
|
|
||||||
|
use think\App;
|
||||||
|
use think\contract\LogHandlerInterface;
|
||||||
|
use think\log\DebugLogToolkit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步调试日志驱动:本地 JSONL 落盘 + 定时导入,写日志零 DB 依赖.
|
||||||
|
*
|
||||||
|
* 与 DebugMysql 的分工:DebugMysql 写日志时直连 DB(含重连/CSV 文件降级逻辑);
|
||||||
|
* 本驱动根本不连 DB——save() 只把行追加进 {runtimePath}log/{Ymd}.jsonl,
|
||||||
|
* DB 导入由定时任务异步完成。写日志的动作从物理上不可能成为业务故障点:
|
||||||
|
* 零 DB 依赖、绝不抛异常、绝不返回 false。
|
||||||
|
*
|
||||||
|
* 三条铁律:
|
||||||
|
* 1. 禁止缓存文件句柄或日期——DebugLogToolkit 每次现算(长驻 CLI 跨天滚动);
|
||||||
|
* 2. 禁止跨 save() 静态攒批——每次调用独立落盘;
|
||||||
|
* 3. 禁止任何 DB 访问/重连/降级逻辑——本驱动根本不连 DB。
|
||||||
|
*
|
||||||
|
* 行格式契约:DebugLogToolkit::FIELDS(与 ul_debug_log 列一一对应)。
|
||||||
|
*/
|
||||||
|
class DebugMysqlAsync implements LogHandlerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 驱动配置(当前无可用项,保留以兼容通道配置注入).
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $config = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造器签名与 DebugMysql 一致(框架 Log Manager 经容器 invokeClass 注入 App 实例与通道配置).
|
||||||
|
*/
|
||||||
|
public function __construct(App $app, $config = [])
|
||||||
|
{
|
||||||
|
if (is_array($config)) {
|
||||||
|
$this->config = array_merge($this->config, $config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 落盘一批日志(扁平 array<LogRecord>).
|
||||||
|
*
|
||||||
|
* 镜像 DebugMysql.save 的遍历方式(type 作 level、message 作 content、
|
||||||
|
* 非字符串 message 转 print_r),但落盘动作换为 DebugLogToolkit:
|
||||||
|
* 全部行拼接成一个多行字符串后只调一次 appendLine(单次 flock 批量写入);
|
||||||
|
* 行间以 \n 连接、行尾 \n 由 appendLine 统一补齐,避免双换行。
|
||||||
|
* 整体 catch \Throwable 后仍返回 true——日志失败绝不反噬业务。
|
||||||
|
*
|
||||||
|
* @param array $log Channel save() 传入的扁平 LogRecord 数组
|
||||||
|
*/
|
||||||
|
public function save(array $log): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$lines = [];
|
||||||
|
|
||||||
|
foreach ($log as $log_item) {
|
||||||
|
if (!is_object($log_item) || !isset($log_item->type) || !is_string($log_item->type) || !isset($log_item->message)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = $log_item->message;
|
||||||
|
|
||||||
|
if (!is_string($content)) {
|
||||||
|
$content = print_r($content, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = DebugLogToolkit::buildRow($log_item->type, $content);
|
||||||
|
|
||||||
|
$lines[] = DebugLogToolkit::encodeRow($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($lines)) {
|
||||||
|
DebugLogToolkit::appendLine(implode("\n", $lines));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
// 铁律:绝不抛异常、绝不返回 false(失败即静默丢弃本批日志)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user