Elixir Loggerをラップしないでください


Elixirは、強力で便利な伐採施設がLogger モジュールです.それはエリクサーのプロジェクトで普遍的に使用されます.そして、それはロギング話がはるかにより簡単でない大部分の他の言語でよりはるかにElixirで接近できるようになります.
の基本的な使い方Logger あなたのために十分かもしれない、ほとんどの時間が、時には、それのいくつかの側面をカスタマイズする必要が発生します.
これを実現する一つの方法は、下位レベルのログ記録モジュールをラップすることです.これは完全に自然なことです.プロジェクトが成長するにつれて、コンベンションと抽象的な繰り返しコードを強制するために、より低いレベルの機能をラップします.
それで、あなたはエリクサーのものを包むのが完全に妥当であると思いますLogger モジュールを参照してください.
ここで問題があります.次のラッパーモジュールを作成することで、このソリューションを実装し始めます.
defmodule MyLogger do
  require Logger
  def info(message, metadata \\ []) do
    Logger.info("[myprefix] " <> message, metadata)
  end
end
初めはすべてうまくいっているようです.この新しいラッパー関数を別のモジュールから呼び出し、
defmodule Example do
  def work() do
    MyLogger.info("working...")
  end
end
iex(2)> Example.work

11:38:16.428 [info]  [myprefix] working ...
問題は、通常のログメッセージと一緒にソースの位置情報を含めることを決定するときに発見されます.
import Config

config :logger, :console,
  metadata: [:file, :function, :module]
iex(1)> Example.work

14:00:03.912 file=lib/my_logger.ex function=info/2 module=MyLogger [info]  [myprefix] working ...
ご覧のように、すべてのログ行は、ログイベントのソースを新しいラッピングコードとして報告します.これはあなたが欲しかったものではありません.

何が問題ですか。


問題はログAPI関数などですLogger.info and Logger.debug , 関数ではなくマクロであり、この文脈では、あなたがどこで呼ぶのかが問題です.
マクロはどんなファイルを知っていますか__CALLER__ 特別な形.実際、それは正確にどのようにLogger この情報をキャプチャします.
配置することによってLogger.info つの場所で呼び出し(そして、関数でそれを呼ぶ)、誤ってログ記録システムにすべてのログイベントがその1つの場所から発信することを通知しました.

解決策は?


ログマクロを全くラップしないでください.あなたはおそらくいくつかの他の手段によって欲しいものを達成することができます.ドキュメントを見てください Custom Formatting ログイベントをログラインにシリアル化する方法をカスタマイズします.エリクシールLoggerがあなたのために十分に柔軟でないならば、より低いレベルを見て検討してくださいErlang Logger オプションです.

ログインログを本当にラップしたい


あなたがElixirロガーに呼び出しをラップすることを主張するならば、それをマクロで包みなさい.
defmodule MyLogger do
  # I don't recommend doing this
  defmacro info(message, metadata \\ []) do
    quote do
      require Logger
      Logger.info("[myprefix] " <> unquote(message), unquote(metadata))
    end
  end
end

iex(1)> Example.work

13:09:48.324 file=lib/example.ex function=work/0 module=Example [info]  [myprefix] working ...
この理由はマクロへの呼び出しであるLogger.info/2 今すぐ呼び出しのコンテキストで展開MyLogger.info/2 , したがって、ソースの位置情報が保存されます.
マクロとして利用可能な情報の種類のデモンストレーションとして、ここで代替マクロです
defmodule MyLogger do
  defmacro info(message, metadata \\ []) do
    env = __CALLER__

    quote bind_quoted: [
            message: message,
            metadata: metadata,
            file: env.file,
            line: env.line
          ] do
      require Logger

      Logger.info("source=#{Path.basename(file)}:#{line} #{message}", metadata)
    end
  end
end

iex(3)> Example.work

13:20:14.575 [info]  source=example.ex:5 working ...