🍠

Ruby Ougai で Bunyan format

に公開

内容

Ruby で Bunyan format を出力する gem Ougai を使ってみる。

Bunyan format は Node.js 向けの構造化ログライブラリ Bunyan の出力する JSON のフォーマットである。
構造化されたログは分析などが行いやすかったりフィルタリングをしやすいといった利点がある。

以前 Rust で Bunyan format を出力するといった記事も書いたが、 Ruby でも使いたくなった。

https://zenn.dev/creanciel/articles/cargo-lambda-bunyan

インストール

gem をインストールする。

内部で logger を継承しているようだが Ruby 3.5 からはデフォルトの gem から外れるようでワーニングが出る。
こちらも追加しておく。

bundle init
bundle add logger
bundle add ougai

サンプル

logger インスタンスの作成

Ougai のインスタンスを STDOUT で作成

https://github.com/Creanciel/ZennRubyOugai/blob/929fe53bb54549d2f620c8e41425825e7402dec4/apps/sample/main.rb#L5

シンプルな出力

https://github.com/Creanciel/ZennRubyOugai/blob/929fe53bb54549d2f620c8e41425825e7402dec4/apps/sample/main.rb#L7

コンソールには以下のように出る。(パース済み)

{
  "name": "main",
  "hostname": "Korvatunturi",
  "pid": 3963,
  "level": 50,
  "time": "2025-12-25T00:00:00.000+09:00",
  "v": 0,
  "msg": "エラー"
}

例外付き

例外付きで出力してみる。
第二引数に Exception が入れられるので追加してみる。

https://github.com/Creanciel/ZennRubyOugai/blob/929fe53bb54549d2f620c8e41425825e7402dec4/apps/sample/main.rb#L10-L14

以下のように出力される。
自前で JSON にパースするのもありなのだが、 Exception を分解するのは面倒なのでこういうところがこのライブラリを使うメリットだろう。

{
  "name": "main",
  "hostname": "Korvatunturi",
  "pid": 3963,
  "level": 50,
  "time": "2025-12-25T00:00:00.000+09:00",
  "v": 0,
  "msg": "An error occured",
  "err": {
    "name": "JSON::ParserError",
    "message": "unexpected token at '{'",
    "stack": "/home/users/.local/share/mise/installs/ruby/3.4.7/lib/ruby/3.4.0/json/common.rb:221:in 'JSON::Ext::Parser.parse'\n  /home/users/.local/share/mise/installs/ruby/3.4.7/lib/ruby/3.4.0/json/common.rb:221:in 'JSON.parse'\n  main.rb:11:in '<main>'"
  }
}

カスタムなメッセージ

Rust の tracing 同様、カスタムな文言も追加できる。

https://github.com/Creanciel/ZennRubyOugai/blob/929fe53bb54549d2f620c8e41425825e7402dec4/apps/sample/main.rb#L19-L22

最後に Hash を加えてやることで JSON に展開される。

{
  "name": "rintaro",
  "hostname": "Santa",
  "pid": 3963,
  "level": 50,
  "time": "2025-12-25T00:00:00.000+09:00",
  "v": 0,
  "err": {
    "name": "StandardError",
    "message": "エラー",
    "stack": "main.rb:17:in '<main>'"
  },
  "book": "vita sexualis",
  "msg": "An error occured"
}

Ruby の最後の引数の Hash の {} は省略できるのでさらに簡単に

https://github.com/Creanciel/ZennRubyOugai/blob/929fe53bb54549d2f620c8e41425825e7402dec4/apps/sample/main.rb#L24-L28

でも同様の結果となる。

まとめ

Ruby では雑に puts ぐらいしかやってこなかったのでログの整理にはいい機会である。
まあ軽く組んでみるとかいう目的で Ruby を使うときには不要かもしれないが本番でバリバリ Ruby が動いている場合は CloudWatch なら簡単にフィルタリングできるし、検討しても良いだろう。

ところで本家の Node の Bunyan を使ったことないな。

Discussion