これはmrubyファミリ (組み込み向け軽量Ruby) Advent Calendar 2025の12/13の記事です。
IoT開発では、動作ログをシリアルコンソールに取り出すことが多いと思います。
しかし、フィールドテストのときにはそのような端末を接続できないケースもあります。
やはりファイルに記録するタイプのログ機構が必要です。
そこで、Rubyの標準ライブラリでおなじみのLoggerと互換性のあるAPIを持つpicoruby-loggerというpicogemを実装しました。
require 'logger'
logger = Logger.new(SDCard.new) # SDCardなどというクラスがあるなら。本文参照
logger.level = :info
logger.info("Hello, Logger!")
logger.warn("これは警告です")Ruby標準のLoggerとのAPI互換性
基本的な使い方はRubyの標準Loggerと同じです。
Logger.newでインスタンスを作り、debug, info, warn, error, fatalの各メソッドでログを出力します。
ログレベルの指定も可能です。これにより、開発中はデバッグ情報をふんだんに表示し、ロングランのベータテスト時にはerror以上のログのみ表示する、といった切り替えが簡単に行えます。
プロダクションではログ出力はしない想定ですが、ハードウェアなどの状況が許すなら、なんらかのログを出力しても構いません。
CRubyのLogger同様、 Logger.newの第一引数には、ファイルパス文字列の他にIOオブジェクトを渡せます。writeやfsyncなどのIOメソッドを実装したインスタンスを渡すことで、フラッシュROMへのファイル保存だけでなく、SDカードや無線接続した機器などへのログ出力が容易になります。
マイコン向け独自設計
互換性を保ちつつも、picoruby-loggerはメモリやストレージが限られたマイコン環境で効率的に動作するよう、いくつかの独自設計を取り入れています。
- ログのバッファリング: マイコンでは一般にファイルへの書き込みが低速なため、ログメッセージを一旦メモリ上のバッファに溜め込み、まとめて書き出すことでパフォーマンスを向上させています。バッファサイズは
Logger.newのbuffer_max引数で調整可能です。 - 自動フラッシュ機能:
warnレベル以上のログが書き込まれた場合など、重要なログは即座に書き出されます。この挙動はflush_levelで変更できます。 trailing_lines機能: あるログが自動フラッシュのトリガーになった後、それに続く数行(デフォルトでは5行)も即座に書き出す機能です。これにより、エラーが発生した直後の状況もログとして確実に捉えることができます。- マイコン情報の付与: ログにはタイムスタンプに加えて、
Machine.uptime_formattedによるマイコンの起動時間も記録されます。これは組み込み機器のデバッグにおいて非常に有用な情報です。
短期間でログを切り替えるRotateLogger
Webサーバのログローテーションは日単位や時間単位が一般的ですが、マイコンではフラッシュROMの容量などが限られているし、ログを取りたい動作が局所的だったりします。 そのため、より短い期間でログファイルを切り替えたい。場合によっては10分や、あるいは10秒といった単位でローテーションが必要になるかもしれません。
ベアメタルマイコンにはLinuxのlogrotateのような機能はありません。
RotateLoggerクラスは、ログローテーションを実現するためのラッパクラスです。
# 10分(600秒)ごとにログファイルを切り替える
# 古いログは30世代分まで保持する
rotate_logger = RotateLogger.new(
dir: "/logs",
basename: "app",
interval: 600,
keep_size: 30
)
loop do
rotate_logger.info("センサの値を記録しました")
sleep 10
endRotateLoggerは、指定されたinterval(秒)を過ぎると、タイムスタンプ付きの新しいログファイル(例: app20251213-103000.log)を自動で作成します。また、keep_sizeで指定された世代数を超えた古いログファイルは自動的に削除してくれるから、ストレージを圧迫する心配がありません。
ソースコードは下記に公開しています。
感想
今回Loggerを自作してみて改めて感じたのは、CRuby標準のLoggerの設計は少し古いな、ということです。 とくに、ログ出力先の管理やフォーマッタの仕組みは、現代的な使い方からすると少し煩雑に感じられる部分もあります。 このあたりは、CRubyの標準ライブラリ自体にも改善できることがあるかもしれない、と感じました。
と、以上の記事はGeminiくん(無料版)が書きました。 きょうはことし最後のMatsue.rbに参加しているのですが、アドベントカレンダの記事のことを忘れていたことに気づき、急遽AIのお世話になったというわけです。 まあでも事実と異なることをちょいちょい書きやがるから、修正の手間はかかりました。自分でゼロから書いても労力たいして変わらんかったかもしれませんね。