想要为ThinkPHP应用配置JSON格式的日志输出,以便无缝对接ELK、Loki等现代化日志分析平台吗?实现结构化日志记录是提升系统可观测性与运维效率的关键一步。本文将为您提供一份从基础配置到高级定制的完整指南。

一、启用JSON格式日志(File通道)
最直接的配置方式是从基础的File日志通道入手。该通道原生支持通过一个参数开启结构化输出。启用后,每条日志都将被格式化为标准的JSON对象,包含时间戳、日志级别、消息体等规范化字段,极大地方便了后续的自动化采集与解析。
具体配置步骤如下:
首先,定位到您项目配置目录下的config/log.php文件。
接着,在channels['file']配置项中,将'json'参数的值设置为true。
同时,请确认'type'已正确设置为'File',并确保'path'所指向的目录具备写入权限。
保存配置后,重启应用使其生效。您可以通过触发一条测试日志(例如记录一个警告信息),然后检查日志文件内容,验证其是否已转换为规整的JSON字符串。
二、配置自定义JSON日志通道(Console通道)
当应用部署在Docker或Kubernetes等容器化环境中时,通常需要将日志直接输出至标准输出(stdout),以便由Promtail等日志采集器抓取。此时,仅配置File通道无法满足需求,需要专门为控制台输出创建一个独立的JSON日志通道。
配置方法如下:
在config/log.php文件的'channels'数组中,新增一个通道,可命名为'console_json'。
关键步骤:将'type'设置为一个自定义的驱动类,例如'app\service\JsonConsole'。
接下来,需要实现这个驱动类。在app/service/JsonConsole.php文件中,创建一个继承自think\log\driver\File或直接实现think\log\DriverInterface接口的类。核心任务是重写其write方法,在写入前,使用json_encode函数将日志记录数组转换为JSON字符串。
最后,在此write方法中,不再写入文件,而是改用file_put_contents('php://stdout', $json_line . "", FILE_APPEND),将格式化后的JSON行直接输出到控制台。
配置完成后,您可以将'default'默认通道修改为此通道,或在代码中通过Log::channel('console_json')->info(...)的方式指定使用。
三、配置全局JSON日志处理器
为了在所有日志记录中自动附加统一的上下文信息(如请求ID、用户标识、模块名称),从而形成信息更丰富、结构更统一的日志,我们可以利用ThinkPHP的processor(处理器)机制。
配置流程如下:
在config/log.php的根级别配置中,添加一个'processor'项,其值为一个闭包函数。
该闭包函数接收$record参数(即原始的日志记录数组)。您可以在其中向$record['context']或$record['extra']追加自定义字段,例如从think\Request对象获取请求ID,或从Session、Token中解析用户ID。
处理完成后,返回修改后的$record数组,它将被后续的日志驱动编码为JSON。
请注意,需确保各个独立的通道配置中没有设置自身的'processor',以免覆盖此全局配置。配置后,请记录一条日志进行验证,确认新增字段已正确出现在JSON结构中。
四、配置JSON日志时间格式与字段映射
默认生成的JSON日志,其时间字段通常采用ISO 8601格式。然而,下游的日志收集系统(如特定版本的ELK Stack)可能要求特定的字段名(如@timestamp)或时间格式(如毫秒级时间戳)。为此,我们需要对输出格式进行定制化映射。
首先,在File通道的配置中,将'format'设置为null或空字符串,以禁用默认的文本模板格式化,确保由JSON驱动完全控制输出。
然后,在自定义驱动(如前述的JsonConsole)的write方法中(若File通道也需定制,可创建另一个自定义驱动),手动构建最终的JSON数组。您可以在此进行灵活定制:将$record['time']转换为毫秒时间戳并赋值给@timestamp字段;将'level'字段重命名为'severity',将'msg'重命名为'message',以符合OpenTelemetry等通用日志规范。
在输出前,建议使用json_last_error()检查JSON编码是否成功。若编码失败,应有回退机制,例如记录原始数组并同时记录一条错误日志,避免日志数据丢失。
最终,确保生成的每行日志,其字段名称和值类型均符合目标日志平台的数据模式要求。
五、配置JSON日志敏感字段过滤
JSON日志在提升可读性的同时,也带来了敏感信息泄露的风险。若将用户密码、API密钥、手机号等敏感数据原样记录,将构成严重的安全隐患。因此,在日志被序列化为JSON之前,实施敏感信息过滤或脱敏处理,是一项至关重要的安全实践。
此过滤逻辑最适合放置在全局的'processor'闭包函数中执行。
您可以在闭包中,递归地遍历$record['context']和$record['extra']数组,识别键名包含'password'、'token'、'auth'、'id_card'等敏感词汇的字段。
识别后,将其值替换为掩码字符串(如******)或统一标记(如'[FILTERED]')。
需要特别关注$record['context']['data']这类字段,它常包含完整的API请求或响应数据,需进行深度扫描。若发现其中含有敏感键,可考虑将整层数据替换为一个标记,例如['filtered' => true]。
为提供双重保障,也可以在自定义JsonConsole驱动的write方法入口处,再次对整个$record数组执行脱敏逻辑。
全部配置完成后,务必进行严格验证:检查生成的日志文件,确保所有敏感信息已被妥善处理,同时保证其他正常日志字段的完整性与JSON结构的正确性。
