如何用jq将容器服务的stdout/stderr日志转为结构化JSON
分别处理容器服务的stdout/stderr并包装为结构化JSON日志
核心思路
通过修改容器的/entrypoint.sh,利用bash进程替换将服务的stdout和stderr分流到独立处理逻辑中,分别添加severity字段后输出结构化JSON;同时解决缓冲问题避免日志无输出/延迟。
方案1:自定义日志包装函数+进程替换
修改你的/entrypoint.sh,替换原服务启动命令为以下逻辑:
#!/bin/bash # 保留原有entrypoint的前置初始化操作(比如环境变量配置、文件权限设置等) # 定义日志包装函数:接收severity参数,将每行输入转为JSON格式 wrap_json_log() { local severity="$1" # 强制行缓冲读取,避免全缓冲导致无输出 while IFS= read -r line; do # 转义JSON特殊字符:双引号、换行符 local escaped_msg=$(printf "%s" "$line" | sed -e 's/"/\\"/g' -e 's/\n/\\n/g') # 输出结构化JSON,用date生成ISO8601时间戳 printf '{"timestamp": "%s", "severity": "%s", "message": "%s"}\n' \ "$(date -Iseconds)" \ "$severity" \ "$escaped_msg" done } # 替换为你的实际服务启动命令,用进程替换分别处理stdout和stderr # 用stdbuf强制行缓冲,解决管道全缓冲问题 stdbuf -oL -eL your_service \ > >(wrap_json_log "INFO") \ 2> >(wrap_json_log "ERROR")
关键细节
stdbuf -oL -eL:强制服务的stdout/stderr使用行缓冲(默认管道下是全缓冲,会导致日志无法实时输出)- 进程替换
>(...):将stdout/stderr导向独立的函数进程,实现分流处理 sed转义:处理JSON中的双引号和换行,避免格式错误
方案2:用awk一站式处理(更高效)
利用awk内置的时间函数和字符串处理能力,减少外部命令调用,性能更优:
#!/bin/bash # 保留原有entrypoint前置操作 # 定义awk处理脚本,通过进程替换分别处理两个流 log_wrapper() { local severity="$1" awk -v sev="$severity" ' BEGIN { # 用awk内置函数生成时间戳,避免反复调用date timestamp = strftime("%Y-%m-%dT%H:%M:%S%z") } { # 转义双引号和换行 gsub(/"/, "\\\"", $0) gsub(/\n/, "\\n", $0) printf "{\"timestamp\": \"%s\", \"severity\": \"%s\", \"message\": \"%s\"}\n", timestamp, sev, $0 } ' } # 启动服务并分流处理 stdbuf -oL -eL your_service \ > >(log_wrapper "INFO") \ 2> >(log_wrapper "ERROR")
方案3:替换PID1确保信号传递(可选)
如果原entrypoint用exec启动服务(让服务成为PID1),修改为以下形式,保证服务能正确接收信号(比如容器停止时的SIGTERM):
#!/bin/bash # 原有前置操作... wrap_json_log() { # 同方案1的函数定义 } # 用exec启动服务,同时绑定进程替换 exec stdbuf -oL -eL your_service \ > >(wrap_json_log "INFO") \ 2> >(wrap_json_log "ERROR")
常见问题排查
- 无输出:大概率是缓冲问题,必须添加
stdbuf强制行缓冲;另外检查服务是否真的有日志输出(可以先直接运行服务看输出) - JSON格式错误:确保转义了所有特殊字符(双引号、换行、制表符等)
- 信号丢失:用
exec让服务成为PID1,或者在entrypoint中用wait等待服务进程
内容的提问来源于stack exchange,提问作者OscarVanL
相关产品推荐
相关产品推荐

