容器日志默认输出到/var/lib/docker/containers//-json.log;docker logs查不到新日志主因是应用重定向日志、json日志轮转或容器重启导致文件丢失。

容器日志默认输出到哪里?docker logs 为什么查不到新日志?
Docker 容器默认把 stdout 和 stderr 直接写入本地 JSON 文件(路径类似 /var/lib/docker/containers/<container-id>/<container-id>-json.log</container-id></container-id>),docker logs 就是读这个文件。但问题常出在:
- 应用自己重定向了日志(比如写了
/var/log/app.log),那docker logs根本看不到 - 日志轮转后旧文件被删,而
docker logs --since只能查 JSON 日志的元数据时间,不保证文件还存在 - 容器重启过,旧日志文件被清理(取决于
log-driver配置)
实操建议:
- 先确认应用是否真的往
stdout输出:进容器执行ls -l /proc/1/fd/{1,2},看是否指向pipe:或socket:,而不是文件 - 查看当前日志驱动:
docker inspect <container-id> | jq '.HostConfig.LogConfig.Type'</container-id>,默认是json-file,但生产环境建议切到syslog或fluentd - 不要依赖
docker logs做长期归档——它没压缩、没索引、不支持按字段过滤
怎么让容器日志自动发到 ELK 或 Loki?fluentd 和 filebeat 选哪个?
直接挂载宿主机日志目录再用 filebeat 采集,看似简单,但容易漏日志(容器启动/销毁瞬间)、权限错乱、路径硬编码。更稳的做法是让 Docker 自己把日志转发出去。
fluentd 适合已有 Ruby 生态或需要复杂过滤(比如动态提取 level 字段、改写 message);filebeat 更轻量、资源占用低、原生支持 docker 模块自动识别容器元数据(container.id, image 等)。
实操建议:
- 给 Docker daemon 配
log-driver: "fluentd"或"filebeat"(需先启对应服务) - fluentd 配置里必须设
@id docker-logs并启用buffer,否则容器高频打日志时会丢 - filebeat 的
docker.input模块默认只读json-file,如果用了journald驱动,得换用journald输入类型 - 所有转发链路都要加 TLS 和重试:filebeat 到 Logstash、Logstash 到 ES/Loki,任何一环断开都可能丢日志
docker logs --tail 100 很慢?JSON 日志文件太大怎么优化?
docker logs --tail N 是从 JSON 日志文件末尾往前逐行扫描,不是随机读。文件超 1GB 后,哪怕只 tail 10 行也可能卡几秒——因为 Docker 要解析每行 JSON 的 timestamp 字段来倒序定位。
根本原因有两个:
- 日志没分级(所有 info/warn/error 混在一起),导致体积膨胀
- JSON 日志没压缩,且 Docker 默认不轮转(
max-size和max-file都是 10M/3 个,远不够)
实操建议:
- 启动容器时强制限制日志大小:
--log-opt max-size=50m --log-opt max-file=5 - 如果用
json-file驱动,加--log-opt labels=env,service把标签写进日志,方便后续过滤 - 禁用日志时间戳(
--log-opt tag="{{.ImageName}}/{{.Name}}"),避免重复记录时间——应用自己打的时间更准 - 不要用
docker logs -f在生产环境实时盯屏,它会持续 hold 文件句柄,影响 logrotate
Loki 查询慢、rate() 计算不准?容器日志的 label 设计很关键
Loki 不存日志内容,只存 label + 时间戳 + 流(stream)。查询性能和聚合准确度,几乎全靠 label 是否合理。常见坑是:
- 所有容器共用一个
job="docker",导致{job="docker"}匹配几百万流,查rate({job="docker"}[5m])直接 OOM - 用容器 ID 当
containerlabel,每次重建就变,无法关联历史 - 忘记加
namespace或pod(K8s 场景),导致跨环境日志混在一起
实操建议:
- Docker 场景下,label 至少包含:
job(按业务域分,如api,worker)、env(prod/staging)、service(来自--label service=auth-api) - K8s 场景下,用 Promtail 的
kubernetes插件自动注入namespace,pod_name,container_name,别手动写死 - 避免用高基数 label:比如
request_id或user_id,Loki 会为每个唯一值建索引,吃光内存
日志监控不是“通了就行”,label 结构、日志源头控制、转发链路容错——这三块没对齐,后面查问题永远慢半拍。










