Rails 日志实战:配置、请求标记与 Lograge 结构化输出

Rails 日志中文实战:Rails.logger 与 config.log_level、ActiveSupport::TaggedLogging 请求标记、log_tags 注入 request_id、Lograge 单行 JSON 请求日志、生产环境日志配置清单,以及观测云 DataKit 采集与告警落地方案。

最佳实践
Rails 日志实战:配置、请求标记与 Lograge 结构化输出技术指南封面

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 也要注意别注入敏感字段。最后检查实际输出的请求和异常样本,确认自定义字段没有绕过过滤规则。

系列阅读


[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

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台