thinkphp 的 es 日志驱动必须实现 thinklogdriver 接口并重写 save() 方法,通过官方 sdk 批量写入、错误静默处理、本地失败日志记录,配合 psr-4 自动加载和索引模板预置,避免请求阻塞与日志丢失。

为什么不能直接用 thinklogdriverFile 写入 ES
ES 不是文件系统,File 驱动的写入逻辑硬编码了 fopen / fwrite / fclose,没法塞进 HTTP 请求。强行改写会导致日志丢失、阻塞请求、无重试 —— 尤其在 ES 临时不可用时,error_log 都可能被压垮。
ThinkPHP 日志体系真正可扩展的入口是 thinklogDriver 接口,所有驱动必须实现 save() 方法;自定义驱动只要返回 true,框架就认为“写成功”,后续由你控制发请求、重试、异步落盘等逻辑。
怎么写一个继承 thinklogDriver 的 ES 驱动
核心就三件事:构造时初始化 ES 客户端、重写 save() 把日志转成 bulk 格式、处理失败不抛异常(否则整个请求崩)。
-
save()接收的是二维数组:$log['level']、$log['msg']、$log['time']、$log['file']等,不是原始字符串 - 别用
curl_exec()手写 HTTP —— 用elasticsearch/elasticsearch官方 SDK,它自带连接池和简单重试 - bulk 写入必须按
index+document成对出现,顺序错一条整批失败;建议每 50 条打包一次 - 超时设为
1s,失败后记录到本地runtime/log/es-fail.log,避免雪崩
use thinklogDriver;
use ElasticsearchClientBuilder;
class EsDriver extends Driver
{
protected $client;
public function __construct(array $config = [])
{
$this->client = ClientBuilder::create()
->setHosts([$config['host'] ?? '127.0.0.1:9200'])
->setRetries(1)
->build();
}
public function save(array $log): bool
{
try {
$body = [];
foreach ($log as $record) {
$body[] = ['index' => ['_index' => 'tp-log-' . date('Y-m-d')]];
$body[] = [
'level' => $record['level'],
'msg' => $record['msg'],
'time' => $record['time'],
'file' => $record['file'] ?? '',
'line' => $record['line'] ?? 0,
'trace' => $record['trace'] ?? null,
];
}
$this->client->bulk(['body' => $body]);
return true;
} catch (Exception $e) {
error_log('[ES Log Fail] ' . $e->getMessage() . "
", 3, RUNTIME_PATH . 'log/es-fail.log');
return true; // 关键:不能 throw,否则 TP 中断请求
}
}
}
如何注册这个驱动并避免 ClassNotFoundException
TP6 默认只 autoload thinklogdriver* 下的类,你的 EsDriver 如果放在 appcommonlogEsDriver.php,不配自动加载就会报错。
- 在
composer.json的"autoload"→"psr-4"里加一行:"app\common\log\": "app/common/log/",然后运行composer dump-autoload - 配置文件
config/log.php中,'default'改成'es',再加'es'驱动配置:'es' => [ 'type' => 'appcommonlogEsDriver', 'host' => '192.168.1.100:9200', ], - 确保
appcommonlogEsDriver类名与文件路径严格一致,大小写敏感,Windows 下容易忽略这点
ES 写入慢或丢日志?重点检查这三点
ES 驱动不像文件写入那么“稳”,网络抖动、mapping 冲突、bulk 大小都会导致静默失败。
- ES 索引没提前建好?
tp-log-2024-06-15这种日期索引如果靠自动创建,首次写入可能因 mapping 不匹配被丢弃 —— 建议用 index template 预置level(keyword)、msg(text)、time(date) - bulk 超过 10MB 或 1000 条?ES 默认有
http.max_content_length: 100mb,但大 bulk 容易触发 circuit breaker,建议限制单次body不超过 50 条或 5MB - 日志里出现
"type": "circuit_breaking_exception"?说明 JVM heap 不够,不是代码问题,得调 ES 配置,不是换驱动能解决的
最常被跳过的环节是:没确认 ES 的 index.refresh_interval,默认 30 秒,查日志时以为没写进去,其实是还没刷到检索里 —— 开发期可以临时设成 1s,但上线必须改回去。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










