数年前に、Elixir の Logger モジュールのカスタムバックエンドを書く、という記事を書きました。
ごく最近になって。 Elixir Forum で Logger の設定の記事を読み、OTP が標準で用意しているハンドラを使えば、バックエンドを書かなくてもログをファイルに出力できると知りました。 調べてみると、ハンドラは OTP 21.0 で追加された模様。
奇しくも OTP 21.0 のリリース日は、先の記事の公開日と同じ 2018/06/19 だったことに何かの縁を感じたり感じなかったり。
いまだ全貌を掴みきれていないのですが、ログをファイルに出力する最低限の設定を確認したので、忘れないうちに記録しておきます。
道具立て
logger_std_h
モジュール
ハンドラには Erlang のドキュメントの次のページで紹介されている logger_std_h
モジュールを利用します。
設定項目がいくつかあるのですが、ここでは出力先のファイル名を指定する file
という項目のみ利用します。
ハンドラを追加する方法
Erlang の logger
モジュールの add_handlers/1
関数を利用してハンドラを追加登録します。
ドキュメントに "Reads the application configuration parameter logger
and calls add_handlers/1
with its contents." とあるように、設定は config ファイルに記述することになります。
設定の記述法
設定はどのように記述するのか。
logger:add_handlers/1
関数をどこで呼び出すのか。
これらは、Elixir の Logger モジュールのドキュメントに記述があります。
Erlang/OTP handlers must be listed under your own application:
config :my_app, :logger, [ {:handler, :name_of_the_handler, ACustomHandler, configuration = %{}} ]
And then, explicitly attached in your
Application.start/2
callback::logger.add_handlers(:my_app)
見ての通り、ドキュメントの記述は Application
モジュールを利用することを前提としています。
logger:add_handlers/1
関数を適当なタイミングで呼び出せれば Application
モジュールを使わなくてもよいようですが、ここではドキュメントにならって Application
ありで書いてゆきます。
実際にログをファイルに出力してみる
プロジェクトを用意する
Application
モジュールを利用する雛形を生成するため、--sup
オプション付きで mix new
コマンドを実行します。
$ mix new my_app --sup $ cd my_app
config/config.exs ファイルを書く
logger_std_h
モジュールを利用する設定を config/config.exs
に記述します。
コード中 config: %{...}
で記述しているオプションは、Erlang のドキュメントにあったオプションの内容です。
Erlang のオプションであるため、ファイル名は Elixir の文字列(バイナリ)ではなく、Erlang の文字列(文字リスト)で指定する必要があります。
import Config config :my_app, :logger, [ { :handler, :my_log, # ハンドラを識別するための ID :logger_std_h, # 利用するハンドラ %{ config: %{ # 注意! ファイル名は、String (binary) でなく charlist で指定する file: 'log/my_log.log' } } } ]
lib/my_app/application.ex にハンドラを追加するコードを書く
logger:add_handlers/1
関数を呼び出すコードを lib/my_app/application.ex
に追加します。
defmodule MyApp.Application do @moduledoc false use Application @impl true def start(_type, _args) do :logger.add_handlers(:my_app) # この行を追加 children = [ ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end end
ここまで記述できたら IEx を起動します。
$ iex -S mix
ハンドラが追加されていることを確認する
登録されているハンドラは logger:get_handler_ids/0
関数で確認することができます。
iex> :logger.get_handler_ids() [:my_log, Logger]
config/config.exs
に記述したハンドラの ID が表示されると思います。
そして、見ての通り、Elixir 標準の Logger
も登録されていることがわかります。
この方法は、起動済みのハンドラを変更することはせず、あくまで追加で登録するためのもののようです。
ログを出力してみる
準備ができたのでログを出力して確認してみます。
iex> require Logger Logger iex> Logger.info("Hello") 19:28:42.693 [info] Hello :ok
標準のハンドラも有効なので、コンソールにログが出力されました。
ファイルを確認してみます。
iex> File.read!("log/my_log.log") |> IO.puts() 2023-01-30T19:28:37.413517+09:00 info: Application: my_app. Started at: nonode@nohost. 2023-01-30T19:28:42.693326+09:00 info: Hello :ok
ファイルには、Logger.info/1
で出力する前にアプリケーションが起動した時のログも記録されていました。
ディスク用のハンドラもある
ここでは logger_std_h
モジュールを使いましたが、それとは別にディスク出力向けのハンドラ logger_disk_log_h
も用意されています。
ドキュメントを見てみると、ログローテーションをはじめディスクに出力することを意識したとおぼしきオプションが並んでいます。 こちらのハンドラも折を見て試してみようと思います。
いつか読むはずっと読まない:Science と Fiction のはざま
しまった。 まだ「三体」読んでない。