Ruby 日志实战:标准库 Logger 从入门到生产配置

Ruby 标准库 Logger 中文实战:Logger.new 与六个日志级别、formatter 自定义格式、progname 与 datetime_format、shift_age/shift_size 日志轮转、多输出与 JSON 结构化技巧,以及观测云 DataKit 采集落地方案。

最佳实践
Ruby 日志实战:标准库 Logger 从入门到生产配置技术指南封面

Ruby 标准库内置的 Logger 类覆盖了日志的基础需求:级别、格式化、轮转一应俱全,无需任何 gem 即可使用。本文系统讲解 Logger 的核心用法、生产化配置技巧(JSON 输出、轮转、多目的地),以及如何把 Ruby 应用日志接入观测云。

核心要点速览

  • Logger 是 Ruby 标准库require "logger" 即用,提供 DEBUG/INFO/WARN/ERROR/FATAL/公开资料未说明 六个级别。
  • formatter 是定制输出的关键:默认格式简陋,生产环境应自定义为带时间戳的键值对或 JSON。
  • 轮转内建Logger.new("app.log", "daily")shift_age/shift_size 参数防止磁盘写满。
  • 生产落地:JSON 输出 stdout 或文件,观测云 DataKit 采集解析,监控器告警。

快速上手:创建 logger 与日志级别

require "logger"

logger = Logger.new($stdout)
logger.level = Logger::INFO

logger.debug("调试细节")          # 不输出(级别为 INFO)
logger.info("服务启动 port=3000")
logger.warn("配置项缺失,使用默认值")
logger.error("数据库连接失败")
logger.fatal("无法恢复,进程退出")

输出(默认格式):

I, [2026-08-25T14:40:11.552301 #1294858]  INFO -- : 服务启动 port=3000
W, [2026-08-25T14:40:11.553120 #1294858]  WARN -- : 配置项缺失,使用默认值

六个级别按严重程度递增:DEBUG < INFO < WARN < ERROR < FATAL < 公开资料未说明level 控制最低输出级别;也可以用 logger.info? 判断当前级别是否启用。

块写法在级别不够时不执行,可避免无谓的字符串拼接:

logger.debug { "昂贵的调试信息 #{expensive_compute}" }

默认格式不够用?自定义 formatter

Logger 的 formatter 是一个接收 (severity, datetime, progname, msg) 的 proc:

logger.formatter = proc do |severity, datetime, progname, msg|
  "[#{datetime.strftime('%Y-%m-%dT%H:%M:%S%z')}] #{severity} #{msg}\n"
end

输出:[2026-08-25T14:40:11+0800] INFO 服务启动 port=3000

progname 可用于标记子系统:logger = Logger.new($stdout); logger.progname = "OrderService"

生产化:JSON 结构化输出

纯文本日志难以被平台解析,生产建议直接输出 JSON:

require "logger"
require "json"

logger = Logger.new($stdout)
logger.formatter = proc do |severity, datetime, progname, msg|
  payload = {
    timestamp: datetime.strftime("%Y-%m-%dT%H:%M:%S.%L%z"),
    level: severity,
    progname: progname,
    message: msg.is_a?(String) ? msg : msg.inspect,
  }
  "#{JSON.generate(payload)}\n"
end

logger.info("用户登录")
{"timestamp":"2026-08-25T14:41:05.123+0800","level":"INFO","progname":null,"message":"用户登录"}

JSON 输出后,观测云 DataKit 采集时自动解析为字段,检索、聚合、告警全部基于字段进行。如果需要带字段的结构化记录(user_id=123),可以在 msg 里传 Hash 并在 formatter 中合并进 payload。

日志轮转:别让磁盘写满

Logger 内建轮转,两种模式:

# 按时间:daily/weekly/monthly
Logger.new("logs/app.log", "daily")

# 按大小:保留 7 个 100MB 的历史文件
Logger.new("logs/app.log", 7, 100 * 1024 * 1024)

第二种签名 Logger.new(path, shift_age, shift_size):文件超过 shift_size 后轮转,最多保留 shift_age 个历史文件。

记录异常

begin
  process_payment(order)
rescue => e
  logger.error("扣款失败 order_id=#{order.id} error=#{e.class}: #{e.message}\n#{e.backtrace.first(10).join("\n")}")
end

Ruby 的 Logger 不会自动附加堆栈——必须显式记录 e.backtrace,否则错误日志只剩一行消息,排查时缺少最关键的上下文。formatter 里也可以判断 msg.is_a?(Exception) 统一处理堆栈输出。

多目的地输出

标准 Logger 只支持单一目的地。要多路输出,常见做法是包一层:

class MultiLogger
  def initialize(*loggers) = @loggers = loggers
  Logger::Severity.constants.each do |level|
    define_method(level.downcase) { |*args, &blk| @loggers.each { |l| l.send(level.downcase, *args, &blk) } }
  end
end

更省事的选择是换支持多 appender 的库(如 Semantic Logger,见本系列专篇)。容器化部署时最推荐的做法是只写 stdout——多目的地分发交给采集层。

观测云落地:Ruby 日志采集与分析

  1. 应用侧:JSON formatter + stdout 输出(容器)或轮转文件(主机)。
  2. DataKit 采集:容器 stdout 自动采集;主机在 conf.d/log/logging.conf 配置 logfiles 指向日志文件,source 设为 ruby-appservice 标识服务。JSON 自动解析为字段。
  3. Pipeline 标准化level(INFO/ERROR 等)映射标准 status 字段,timestamp 解析为事件时间 time
  4. 检索告警:日志查看器按 status/service 过滤与聚类分析;监控 > 监控器 > 新建日志检测监控器,对 error 日志设阈值,告警策略绑定钉钉/企业微信/飞书通知对象。
  5. 链路关联:接入观测云 APM(Ruby 探针)后 trace_id 注入日志,错误日志一键跳转调用链。
  6. 成本控制:多索引按级别拆分保留策略,历史数据数据转发归档对象存储。

常见问题(FAQ)

Ruby 项目必须用标准库 Logger 吗?

不是必须,但它是零依赖的稳妥起点。需要多 appender、异步写入、payload 结构化字段时,Semantic Logger 是 Ruby 生态的主流选择;Rails 项目还可以用 Lograge 优化请求日志。

Logger 是线程安全的吗?

是。标准库 Logger 内部有锁保护,多线程共享一个 logger 实例是安全的,不会写交错。

日志时间格式怎么统一?

在 formatter 中用 datetime.strftimedatetime.iso8601(3) 输出 ISO-8601 带毫秒格式,生产环境建议统一时区(如东八区或 UTC),避免多机日志对不上时间。

老代码里散落着 puts 怎么办?

puts 没有级别、没有时间戳、不进日志文件,生产环境应全部替换为 logger 调用。可以短期用一个 $stdout 重定向的临时方案兜底采集,但长期必须改代码。

系列阅读


获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

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

立即开始

选择观测云版本

代码托管平台