【灰色魔術】Rails.loggerと独自Loggerのログレベルを同じにさせたかった

お魚といえば安いものから高いものまでピンきりですが、上はどこまでいくんでしょうか。魚ですよ!値段順で調べるで見ると、マグロなどが高いのはやはり用意に想像がつくところですが、キログラム単価で見た時には特にウニやかずのこなど量が取れないものがぶっちぎってるんですね。

さて、RailsのLoggerでは、ログレベル、すなわちどのレベルのログまでがログファイルに保存されるかを指定することができます。

Rails.logger.level = :info

この辺りは既に十分いろんなわかりやすい説明があるのでそのあたりをご参照いただくとして。
Debugging Rails Applications — Ruby on Rails Guides

一方で、独自ライブラリ等でRailsと独立のロガーを持っている場合、すなわちRails.loggerと別にLoggerのインスタンスを持っていて、そいつのログレベルを設定するときには、小文字シンボル:infoや:warnでは設定できません。
Loggerクラスに定義されている定数Logger::INFOやLogger::WARNを指定する必要があります。

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

では、Railsでアプリを書いていてなおかつそのログレベルを環境変数等で指定可能にしたいが、その際にそういった独自Loggerのログレベルも一致させたい場合はどうすれば。
ちょっと何を言っているかわかりにくいでしょうが、下記のコードのようなことです。

log_level = (ENV['LOG_LEVEL'] || :info).to_sym
Rails.logger.level = log_level.downcase
$logger = Logger.new
$logger.level = Logger.const_get(log_level.upcase)

Rails.logger.levelにつっこむシンボルを使って強引にLoggerクラスの定数をRubyのClassに実装されているconst_getを使って動的にひっぱってくる…というような方法をとっています。
黒魔術的というほどではないですが少し灰色なかんじ。

このようにすれば、Railsのloggerのログレベルと独自Loggerのログレベルを一度で同時に指定できるようになります。
例えば

% LOG_LEVEL=Debug rails c
> Rails.logger.level
:debug
> $logger.level
0
# ↑ Logger::DEBUGの値

そもそもRails.logger.levelも:infoなどのシンボルではなくLogger::DEBUG(=0)など数値で指定することもできるのですが、
その場合もこの技法を活用すればユーザ指定時(シェルから%LOG_LEVEL=WARN rails cする場合)などでは文字列で書けるので便利と言えそうです。

指定されたLOG_LEVELの文字列自体が正しくない(例えば%LOG_LEVEL=DEBUUUG rails cなどと書いてしまった)場合も、const_getが例外を吐いてくれるというチェック機構が自動で働くことになるので、直接数値を指定可能にする(で数値の範囲を独自でチェックするロジックを書く)よりもラクで安全という面もあるかもしれません。

ログの収集や活用・分析などなど(fluentdとか)についてはまた備忘録がてらにまとめたいところもありますが、またの機会に。

コメントを残す

メールアドレスが公開されることはありません。