Skip to content

Instantly share code, notes, and snippets.

@saitouena
Created November 1, 2019 05:43
Show Gist options
  • Save saitouena/3495c94fda08d003c5740dbc1e56dc36 to your computer and use it in GitHub Desktop.
Save saitouena/3495c94fda08d003c5740dbc1e56dc36 to your computer and use it in GitHub Desktop.
(ns clojure-playground.logback
(:import [ch.qos.logback.classic LoggerContext Logger Level]
[ch.qos.logback.core.util StatusPrinter]
[org.slf4j LoggerFactory MDC]))
;; http://logback.qos.ch/manual/index_ja.html
;; project.clj: [ch.qos.logback/logback-classic "1.3.0-alpha5"]
;; http://logback.qos.ch/manual/introduction_ja.html
(def logger ^Logger (LoggerFactory/getLogger "chapters.introduction.HelloWorld1"))
(.debug logger "Hello world.")
;; 上記の例では、logbackのクラスを一つも参照していないことに注意してください。
;; あなたのクラスの大半はSLF4J APIを使用するだけで、logbackの存在には気づかないでしょう。
(def logger ^Logger (LoggerFactory/getLogger "chapters.introduction.HelloWorld2"))
(.debug logger "Hello world.")
(def lc ^LoggerContext (LoggerFactory/getILoggerFactory))
(type lc)
(StatusPrinter/print ^LoggerContext lc)
;; http://logback.qos.ch/manual/architecture_ja.html
;; 特定のロギング式を無効にしつつ、他のロギング式には一切影響を与えない機能です。
;; たとえば、"com.foo"という名前のロガーは、"com.foo.Bar" というロガーの親になります。
;; 同様に、 "java"は"java.util"の親であると同時に、"java.util.Vector" の祖先になります。
;; この命名スキームは、ほとんどの開発者がきちんと理解しなければならないものです。
(LoggerFactory/getLogger org.slf4j.Logger/ROOT_LOGGER_NAME)
;; ロガーのレベルと、ロギング要求のレベルがある
(def logger ^Logger (LoggerFactory/getLogger "com.foo"))
(.setLevel logger Level/INFO)
(def barlogger ^Logger (LoggerFactory/getLogger "com.foo.Bar"))
(.warn logger "Low fuel level")
;; このロギング要求は無効です。DEBUG < INFO.
(.debug logger "Starting search for nearest gas station.")
;; "com.foo.Bar" という名前のロガーは、"com.foo" ロガーからレベルを継承します。
;; したがって、このロギング要求は有効です。INFO >= INFO.
(.warn barlogger "Located nearest gas station.")
(.debug barlogger "Exiting gas station search")
;; ロガーの取得
;; LoggerFactory.getLoggerを呼び出しましょう。 同じ名前なら、常に同じロガーインスタンスへの参照を返します。
(= (LoggerFactory/getLogger "com.foo") (LoggerFactory/getLogger "com.foo"))
;; ロガーをクラスごとにインスタンス化すれば、それぞれのロガーの名前はクラスの完全名になります。
;; これはロガーを定義する簡単かつ便利な方法です。
;; アペンダーとレイアウト
;; アペンダーの加算性
;; ロガーLのログ出力は、Lとその祖先も割り当てられた全てのアペンダーに転送される。
;; これが「アペンダーの加算性」の定義である。
;; パラメータ化ロギング
(def entry (Object.))
(def logger ^Logger (LoggerFactory/getLogger "p.log"))
;; ロギング要求が有効かどうかを判断した後にだけ、そして、それが有効な場合にだけ、ロガーはメッセージを書式化して、
;; '{}' を entry の文字列表現で置き換えます。つまり、ロギング要求が無効な場合、このやり方だとパラメータ構築のコストが発生しません。
(.debug logger "The entry is {}" entry)
;; 二行目のやり方は一行目のやり方に比べて少なくとも30倍は遅くなるでしょう。
;; outperformの誤訳
;; the second variant will outperform the first variant by a factor of at least 30.
(.debug logger "The new entry is {}. It replaces {}." (Object.) (Object.))
;; 引数が三つ以上になる場合、Object[]でラップしなければなりません
(def param-array (object-array 3))
(aset param-array 0 (Object.))
(aset param-array 1 (Object.))
(aset param-array 2 (Object.))
(.debug logger "Value {} was inserted between {} and {}." param-array)
;; 内部実装を覗いてみよう
;; ステップ1. フィルタチェインの決定 はまだでてきてないのでよくわからない
;; ステップ3. LoggingEventオブジェクトの作成 MDCはここで出てくる
;; 性能
;; 問題1. ロギングが完全にオフになっているときの性能
;; ルートロガーのレベルに最高レベルのLevel.OFFを設定すると、完全にロギングをオフにすることができます
;; パラメータ化されたロギングを利用する
;; 狭い範囲のループにロギング式が含まれていると、アプリケーションの動作が緩慢になってしまいます。
;; 問題2. ロギングをオンにした状態で、ロギングするかどうかを判定する場合の性能
;; 親ロガーのレベルを変更すると、全ての子ロガーは変更通知を受け取ります。
;; 問題3. 実際にロギングする(書式化と出力デバイスへの書き込み)
;; フォーマッタなどの実装を頑張っているらしい
;; 第3章 logbackの設定
;; http://logback.qos.ch/manual/configuration_ja.html
;; Fooクラスからlogbackへの依存は、org.slf4j.LoggerFactoryとorg.slf4j.Loggerのimportを介した間接的なものであることに注意しましょう。
;; 何?
;; resources/logback.xmlを置いてみる
;; REPLは立ち上げ直さないと読み込まれない
(comment
(do
(def lc ^LoggerContext (LoggerFactory/getILoggerFactory))
(StatusPrinter/print ^LoggerContext lc)
;; コード中でStatusPrinterをプログラム的に呼び出す代わりに、設定ファイルで内部ステータスを出力するように指定することができます。
;; たとえそうなっていて欲しくても、(この設定をしても)ルートロガーのログレベルはDEBUGになりません。
;; ルートロガーのログレベルを設定すること自体はできる
;; logback-classicモジュールでは、Appenderにフィルターを追加することが出来ます。
;; 第8章 診断コンテキスト
(MDC/put "first" "Dorothy")
(def logger ^Logger (LoggerFactory/getLogger "hoge"))
(MDC/put "last" "Parker")
(.info logger "Check enclosed.")
(.debug logger "The most beautiful two words in English.")
(MDC/put "first" "Richard")
(MDC/put "last" "Nixon")
(.info logger "I am not a crook")
(.info logger "Attributed to the former US president. 17 Nov 1973.")))
;; logback.xmlで、ログのフォーマットに %X{first} などとして、値を参照できる。
;; 診断コンテキストが一番脚光を浴びるのはクライアント・サーバーアーキテクチャだ。
;; 例を眺める
;; リクエストを処理するとき、いつでもMDCに正しい情報が設定されていることを確実にするためにできるのは、
;; 処理の始めにユーザー名を設定して、処理の終わりに削除することです。こういう場合サーブレットFilterを使うとよいでしょう。
;; servletを使っていない気がするから役に立たないけど、同様の機構が用意されていたりしないかな
<configuration debug="true" scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%X{first} %X{last} (%X{nokey}) - %m%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment