Ruby 日志实战:标准库 Logger 从入门到生产配置
Ruby 标准库 Logger 中文实战:Logger.new 与六个日志级别、formatter 自定义格式、progname 与 datetime_format、shift_age/shift_size 日志轮转、多输出与 JSON 结构化技巧,以及观测云 DataKit 采集落地方案。
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 日志采集与分析
- 应用侧:JSON formatter + stdout 输出(容器)或轮转文件(主机)。
- DataKit 采集:容器 stdout 自动采集;主机在
conf.d/log/logging.conf配置logfiles指向日志文件,source设为ruby-app、service标识服务。JSON 自动解析为字段。 - Pipeline 标准化:
level(INFO/ERROR 等)映射标准status字段,timestamp解析为事件时间time。 - 检索告警:日志查看器按 status/service 过滤与聚类分析;监控 > 监控器 > 新建日志检测监控器,对 error 日志设阈值,告警策略绑定钉钉/企业微信/飞书通知对象。
- 链路关联:接入观测云 APM(Ruby 探针)后 trace_id 注入日志,错误日志一键跳转调用链。
- 成本控制:多索引按级别拆分保留策略,历史数据数据转发归档对象存储。
常见问题(FAQ)
Ruby 项目必须用标准库 Logger 吗?
不是必须,但它是零依赖的稳妥起点。需要多 appender、异步写入、payload 结构化字段时,Semantic Logger 是 Ruby 生态的主流选择;Rails 项目还可以用 Lograge 优化请求日志。
Logger 是线程安全的吗?
是。标准库 Logger 内部有锁保护,多线程共享一个 logger 实例是安全的,不会写交错。
日志时间格式怎么统一?
在 formatter 中用 datetime.strftime 或 datetime.iso8601(3) 输出 ISO-8601 带毫秒格式,生产环境建议统一时区(如东八区或 UTC),避免多机日志对不上时间。
老代码里散落着 puts 怎么办?
puts 没有级别、没有时间戳、不进日志文件,生产环境应全部替换为 logger 调用。可以短期用一个 $stdout 重定向的临时方案兜底采集,但长期必须改代码。
系列阅读
- 上一篇:Go 日志库九款对比
- 下一篇:Rails 日志实战
- 相关阅读:Semantic Logger 实战 | 日志格式化最佳实践