Rails 日志实战:配置、请求标记与 Lograge 结构化输出
Rails 日志中文实战:Rails.logger 与 config.log_level、ActiveSupport::TaggedLogging 请求标记、log_tags 注入 request_id、Lograge 单行 JSON 请求日志、生产环境日志配置清单,以及观测云 DataKit 采集与告警落地方案。
Rails 自带完整的日志体系:Rails.logger、按环境的配置文件、请求级标记。但默认的 Rails 请求日志是多行纯文本——开发友好,生产灾难。本文讲解 Rails 日志的配置要点、log_tags 请求关联、Lograge 结构化改造,以及观测云平台落地方案。
核心要点速览
- Rails.logger 开箱即用:框架默认文件位置为
log/<environment>.log,但生成的生产配置可能改用 stdout,级别由config.log_level控制。 - 默认请求日志不适合生产:一次请求几十行多行日志,解析与聚合都困难,Lograge 把它压缩成一行结构化日志。
- log_tags + TaggedLogging:给通过该 logger 写出的日志打上 request_id 等标记,是请求级排障的基础。
- 生产落地:JSON 输出 stdout,观测云 DataKit 采集,监控器告警,APM trace_id 关联。
Rails 日志的默认行为
Rails 应用里随处可用 Rails.logger:
Rails.logger.info("订单创建成功 order_id=#{order.id}")
Rails.logger.error("支付回调验签失败")
默认行为:
- 传统配置输出到 log/production.log;新生成的生产配置可能使用 stdout,应查看实际 production.rb;
- 级别由环境配置控制:开发
debug,生产传统上是info(新版本 Rails 默认从RAILS_LOG_LEVEL环境变量读取); - 每次 HTTP 请求会产生一大坨多行日志:Started、Processing、SQL、Rendered、Completed……
在控制器里还可以用 logger(ActionController 已注入)。自定义子系统日志建议 ActiveSupport::Logger.new 或直接用 Rails.logger.tagged 派生。
调整级别与输出目的地
# config/environments/production.rb
config.log_level = :info
# 输出到 stdout(容器部署推荐)
config.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
临时排障不想重启?Rails 控制台里 Rails.logger.level = :debug 即时生效(仅控制台当前进程,不会改变独立 Web worker)。
用 log_tags 给每个请求打标记
config.log_tags = [:request_id, :remote_ip]
之后通过该 TaggedLogging logger 写出的请求日志会带上 [7f3a9c...] [10.0.0.8] 前缀。配合 grep request_id 或在观测云中按字段过滤,即可还原单次请求的全部日志。
类内使用:logger.tagged("OrderService") { logger.info("...") } 手动追加标记。
Lograge:把请求日志压缩成一行
默认 Rails 请求日志又长又散。Lograge 汇总控制器请求事件,并不会把所有业务日志或后台任务都自动转换为单行结构化日志:
# Gemfile
gem "lograge"
# config/environments/production.rb
require "time"
config.lograge.enabled = true
config.lograge.formatter = Lograge::Formatters::Json.new
config.lograge.logger = ActiveSupport::Logger.new($stdout) # 请求 JSON 不加 TaggedLogging 文本前缀
config.lograge.custom_options = lambda do |event|
{ user_id: event.payload[:current_user_id],
request_id: event.payload[:request_id], timestamp: Time.now.utc.iso8601 }
end
current_user_id/request_id 必须由应用注入事件 payload,它们不是天然存在的字段。在现有 ApplicationController 中保留原有继承和认证逻辑,合并以下方法(已有同名方法时合并字段):
def append_info_to_payload(payload)
super
payload[:request_id] = request.request_id
payload[:current_user_id] = current_user&.id if respond_to?(:current_user, true)
end
用户字段依赖应用已有的 current_user;下面是配置后的示意输出:
{"method":"GET","path":"/api/orders","format":"json","controller":"Api::OrdersController","action":"index","status":200,"duration":45.2,"view":12.3,"db":8.9,"timestamp":"2026-08-25T14:42:10.123+08:00","user_id":42}
method、path、status、duration、db/view 耗时一应俱全——访问日志、慢请求分析、5xx 告警全部建立在这一行之上。custom_options 里按需注入业务字段(用户 ID、租户 ID)。
生产配置清单
一份稳妥的 production.rb 日志配置:
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
config.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout)) # 容器:stdout
config.log_tags = [:request_id]
config.lograge.enabled = true
config.lograge.formatter = Lograge::Formatters::Json.new
config.lograge.logger = ActiveSupport::Logger.new($stdout) # 请求 JSON 不加 TaggedLogging 文本前缀
# 业务 logger 用 JSON formatter(见 Ruby 篇自定义 formatter)
要点:stdout 输出(十二要素)、Lograge 请求 JSON、request_id 标记、级别走环境变量。业务日志仍需单独配置 JSON formatter,不能直接在 JSON 前拼接 TaggedLogging 文本前缀。文件轮转交给容器运行时或系统 logrotate,Rails 自身不用管。
核对请求日志与业务日志
Lograge 与业务 logger 可能输出不同字段。接入观测云时,按 DataKit 日志配置采集各自的实际输出,并用同一个请求检查 request_id 能否在两类日志中查到。
Lograge 的 status 是 HTTP 状态码,应另存为 HTTP 字段,再映射日志级别,避免覆盖后丢失原始响应码。核对 duration 的毫秒值以及各版本字段,再分析慢请求;若要关联 APM,另外注入与已采集链路一致的 trace_id,普通 request ID 不等同于 Trace ID。
常见问题(FAQ)
Rails 7/8 默认日志有什么变化?
Rails 8 新应用模板通常配置 stdout 和 RAILS_LOG_LEVEL,但升级已有应用不会自动改写 production.rb;RAILS_LOG_TO_STDOUT 也不是所有版本通用的开关,应看实际环境配置。
开发环境也想用 Lograge 吗?
不建议。开发环境默认的多行详细日志(含 SQL、渲染细节)正是排障所需;Lograge 是给生产环境的机器可读格式。按环境开关即可。
Sidekiq 等后台任务的日志怎么统一?
Sidekiq 自带 logger,可在 Sidekiq.configure_server 块中设置 config.logger = Rails.logger 统一出口;任务日志建议 tagged(job_id) 标记,平台侧按 job_id 过滤还原任务执行过程。
日志里出现敏感参数怎么办?
Rails 的 config.filter_parameters(如 :password, :token)会在参数日志中打码;Lograge 的 custom_options 也要注意别注入敏感字段。最后检查实际输出的请求和异常样本,确认自定义字段没有绕过过滤规则。
系列阅读
- 上一篇:Ruby 日志实战
- 下一篇:Ruby 日志库六款对比
- 相关阅读:Semantic Logger 实战 | 微服务日志最佳实践
[1]: 观测云文档《DataKit 日志采集综述》https://docs.guance.com/integrations/datakit-logging/
[2]: 观测云文档《日志查看器》https://docs.guance.com/logs/explorer/
[3]: 观测云文档《监控》https://docs.guance.com/monitoring/
[4]: 观测云《通过敏感数据扫描能力保障数据安全合规》https://www.guance.com/learn/articles/sensitive-data-scanning