Logging
Logging
モジュールは、計算の履歴と進捗をイベントのログとして記録する方法を提供します。イベントは、ソースコードにログ記録ステートメントを挿入することによって作成されます。例えば:
@warn "Abandon printf debugging, all ye who enter here!"
┌ Warning: Abandon printf debugging, all ye who enter here!
└ @ Main REPL[1]:1
システムは、ソースコードにprintln()
の呼び出しを散りばめることに対していくつかの利点を提供します。まず、ソースコードを編集することなく、メッセージの可視性と表示を制御できる点です。たとえば、上記の@warn
とは対照的に
@debug "The sum of some values $(sum(rand(100)))"
デフォルトでは出力を生成しません。さらに、このようなデバッグステートメントをソースコードに残しておくのは非常に安価です。なぜなら、システムはメッセージが後で無視される場合、評価を回避するからです。この場合、sum(rand(100))
と関連する文字列処理は、デバッグログが有効になっていない限り、決して実行されることはありません。
第二に、ログツールを使用すると、各イベントに任意のデータをキーと値のペアのセットとして添付できます。これにより、後で分析するためにローカル変数や他のプログラムの状態をキャプチャできます。たとえば、ローカル配列変数 A
とベクトル v
の合計をキー s
として添付するには、次のようにします。
A = ones(Int, 4, 4)
v = ones(100)
@info "Some variables" A s=sum(v)
# output
┌ Info: Some variables
│ A =
│ 4×4 Matrix{Int64}:
│ 1 1 1 1
│ 1 1 1 1
│ 1 1 1 1
│ 1 1 1 1
└ s = 100.0
すべてのロギングマクロ @debug
、@info
、@warn
および @error
は、より一般的なマクロ @logmsg
のドキュメントで詳細に説明されている共通の機能を共有しています。
Log event structure
各イベントは、ユーザーによって提供されたデータと自動的に抽出されたデータのいくつかのデータを生成します。まず、ユーザー定義のデータを見てみましょう:
ログレベルは、早期フィルタリングに使用されるメッセージの広範なカテゴリです。標準のレベルはいくつかあり、タイプ
LogLevel
があります。ユーザー定義のレベルも可能です。それぞれの目的は異なります:Logging.Debug
(ログレベル -1000) はプログラムの開発者向けの情報です。これらのイベントはデフォルトで無効になっています。Logging.Info
(ログレベル 0) は、ユーザーへの一般的な情報のためのものです。これは、println
を直接使用する代替手段と考えてください。Logging.Warn
(ログレベル1000)は、何か問題が発生しており、対処が必要な可能性が高いことを意味しますが、現時点ではプログラムはまだ動作しています。Logging.Error
(ログレベル2000)は、何かが間違っていることを意味し、このコードの部分では回復する可能性が低いです。しばしば、このログレベルは不要であり、例外をスローすることで必要な情報をすべて伝えることができます。
メッセージ はイベントを説明するオブジェクトです。慣例として、メッセージとして渡される
AbstractString
はマークダウン形式であると見なされます。他のタイプは、テキストベースの出力にはprint(io, obj)
またはstring(obj)
を使用して表示され、インストールされたロガーで使用される他のマルチメディア表示にはおそらくshow(io,mime,obj)
が使用されます。オプショナルな キー–バリュー ペア は、各イベントに任意のデータを添付することを可能にします。一部のキーには、イベントの解釈に影響を与える慣習的な意味があります(
@logmsg
を参照)。
システムは各イベントに対していくつかの標準情報も生成します:
- ログマクロが展開された
module
。 - ソースコード内でログマクロが発生する
file
とline
。 - メッセージ
id
は、ログマクロが出現する ソースコードステートメント のユニークで固定された識別子です。この識別子は、ファイルのソースコードが変更されても比較的安定しているように設計されており、ログステートメント自体が同じである限り維持されます。 - イベントのための
group
は、デフォルトで拡張子なしのファイルの基本名に設定されています。これは、ログレベルよりも細かくメッセージをカテゴリにグループ化するために使用できます(例えば、すべての非推奨警告はグループ:depwarn
を持ちます)、またはモジュール内またはモジュール間で論理的なグループにまとめるために使用できます。
注意すべきは、イベントの時間などの有用な情報がデフォルトでは含まれていないことです。これは、そのような情報を抽出するのが高コストであり、現在のロガーに動的に利用可能であるためです。必要に応じて、時間、バックトレース、グローバル変数の値、およびその他の有用な情報でイベントデータを補強するために、custom loggerを定義するのは簡単です。
Processing log events
例に示されているように、ログ記録のステートメントは、ログイベントがどこに行くのか、またはどのように処理されるのかについて言及していません。これは、システムを構成可能で同時使用に自然なものにする重要な設計機能です。これは、2つの異なる関心事を分離することによって実現されています:
- ログ イベントの作成は、イベントがトリガーされる場所と含める情報を決定する必要があるモジュールの著者の関心事です。
- ログイベントの処理 — つまり、表示、フィルタリング、集約、記録 — は、複数のモジュールを協力するアプリケーションに統合する必要があるアプリケーション作成者の関心事です。
Loggers
イベントの処理は logger によって行われ、これはイベントを見る最初のユーザー設定可能なコードです。すべてのロガーは AbstractLogger
のサブタイプでなければなりません。
イベントがトリガーされると、グローバルロガーをフォールバックとして使用し、タスクローカルロガーを探すことで適切なロガーが見つかります。ここでの考え方は、アプリケーションコードがログイベントをどのように処理すべきかを知っており、コールスタックの上部のどこかに存在するということです。したがって、ロガーを発見するためにコールスタックを上に向かって探る必要があります。つまり、ロガーは動的スコープであるべきです。(これは、ロガーが字句的スコープであるロギングフレームワークとの対比点です。モジュールの著者によって明示的に提供されるか、単純なグローバル変数として提供されます。そのようなシステムでは、複数のモジュールから機能を構成しながらロギングを制御するのが難しいです。)
グローバルロガーは global_logger
で設定でき、タスクローカルロガーは with_logger
を使用して制御されます。新しく生成されたタスクは親タスクのロガーを継承します。
ライブラリによって提供されるロガータイプは3つあります。 ConsoleLogger
は、REPLを起動したときに表示されるデフォルトのロガーです。これは、イベントを読みやすいテキスト形式で表示し、フォーマットやフィルタリングに対してシンプルでユーザーフレンドリーな制御を提供しようとします。 NullLogger
は、必要に応じてすべてのメッセージをドロップする便利な方法です。これは、 devnull
ストリームのロギングに相当します。 SimpleLogger
は、主にロギングシステム自体のデバッグに役立つ非常にシンプルなテキストフォーマットロガーです。
カスタムロガーは、reference sectionで説明されている関数のオーバーロードを備えている必要があります。
Early filtering and message handling
イベントが発生すると、メッセージが破棄されるのを避けるために、いくつかの初期フィルタリングステップが行われます:
- メッセージログレベルは、グローバル最小レベル(
disable_logging
を介して設定)に対してチェックされます。これは粗雑ですが、非常に安価なグローバル設定です。 - 現在のロガーの状態が調べられ、メッセージレベルがロガーのキャッシュされた最小レベルと照合されます。これは
Logging.min_enabled_level
を呼び出すことで見つけられます。この動作は環境変数を介してオーバーライドすることができます(詳細は後述します)。 Logging.shouldlog
関数は、現在のロガーを使用して呼び出され、いくつかの最小限の情報(レベル、モジュール、グループ、ID)が静的に計算されます。最も便利なのは、shouldlog
にイベントid
が渡され、キャッシュされた述語に基づいて早期にイベントを破棄するために使用できることです。
すべてのチェックが通過した場合、メッセージとキー–バリューのペアは完全に評価され、Logging.handle_message
関数を介して現在のロガーに渡されます。 handle_message()
は、必要に応じて追加のフィルタリングを行い、イベントを画面に表示したり、ファイルに保存したりします。
ログイベントを生成する際に発生する例外は、デフォルトでキャプチャされ、ログに記録されます。これにより、個々の壊れたイベントがアプリケーションをクラッシュさせるのを防ぎ、運用システムであまり使用されないデバッグイベントを有効にする際に役立ちます。この動作は、Logging.catch_exceptions
を拡張することで、ロガータイプごとにカスタマイズできます。
Testing log events
ログイベントは通常のコードを実行する副作用ですが、特定の情報メッセージや警告をテストしたい場合があるかもしれません。Test
モジュールは、ログイベントストリームに対してパターンマッチングを行うために使用できる@test_logs
マクロを提供します。
Environment variables
メッセージフィルタリングは、JULIA_DEBUG
環境変数を通じて影響を受けることができ、ファイルやモジュールのデバッグログを有効にする簡単な方法として機能します。JULIA_DEBUG=loading
でジュリアをロードすると、loading.jl
内の @debug
ログメッセージがアクティブになります。例えば、Linuxシェルでは:
$ JULIA_DEBUG=loading julia -e 'using OhMyREPL'
┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an incompatible cache header
└ @ Base loading.jl:1328
[ Info: Recompiling stale cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji for module OhMyREPL
┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an incompatible cache header
└ @ Base loading.jl:1328
...
Windowsでは、まずset JULIA_DEBUG="loading"
を実行することでCMD
で同じことを達成でき、Powershell
では$env:JULIA_DEBUG="loading"
を使用します。
同様に、環境変数を使用して、Pkg
のようなモジュールやモジュールのルートのデバッグログを有効にすることができます(Base.moduleroot
を参照)。すべてのデバッグログを有効にするには、特別な値all
を使用してください。
REPLからデバッグログをオンにするには、ENV["JULIA_DEBUG"]
を興味のあるモジュールの名前に設定します。REPLで定義された関数はモジュールMain
に属しており、それらのログは次のように有効にできます:
julia> foo() = @debug "foo"
foo (generic function with 1 method)
julia> foo()
julia> ENV["JULIA_DEBUG"] = Main
Main
julia> foo()
┌ Debug: foo
└ @ Main REPL[1]:1
複数のモジュールのデバッグを有効にするには、カンマ区切りを使用します: JULIA_DEBUG=loading,Main
。
Examples
Example: Writing log events to a file
時には、ログイベントをファイルに書き込むことが有用です。以下は、タスクローカルおよびグローバルロガーを使用して情報をテキストファイルに書き込む方法の例です:
# Load the logging module
julia> using Logging
# Open a textfile for writing
julia> io = open("log.txt", "w+")
IOStream(<file log.txt>)
# Create a simple logger
julia> logger = SimpleLogger(io)
SimpleLogger(IOStream(<file log.txt>), Info, Dict{Any,Int64}())
# Log a task-specific message
julia> with_logger(logger) do
@info("a context specific log message")
end
# Write all buffered messages to the file
julia> flush(io)
# Set the global logger to logger
julia> global_logger(logger)
SimpleLogger(IOStream(<file log.txt>), Info, Dict{Any,Int64}())
# This message will now also be written to the file
julia> @info("a global log message")
# Close the file
julia> close(io)
Example: Enable debug-level messages
ここに、ConsoleLogger
を作成する例があります。これは、ログレベルがLogging.Debug
以上または等しいメッセージを通過させます。
julia> using Logging
# Create a ConsoleLogger that prints any log messages with level >= Debug to stderr
julia> debuglogger = ConsoleLogger(stderr, Logging.Debug)
# Enable debuglogger for a task
julia> with_logger(debuglogger) do
@debug "a context specific log message"
end
# Set the global logger
julia> global_logger(debuglogger)
Reference
Logging module
Logging.Logging
— Moduleログイベントのストリームをキャプチャ、フィルタリング、表示するためのユーティリティ。通常、ログイベントを作成するために Logging
をインポートする必要はありません。これには、標準のロギングマクロ(例えば @info
)がすでに Base
によってエクスポートされ、デフォルトで利用可能です。
Creating events
Logging.@logmsg
— Macro@debug メッセージ [key=value | value ...]
@info メッセージ [key=value | value ...]
@warn メッセージ [key=value | value ...]
@error メッセージ [key=value | value ...]
@logmsg レベル メッセージ [key=value | value ...]
情報のある `メッセージ` でログレコードを作成します。便利のために、標準の重大度レベル `Debug`、`Info`、`Warn`、`Error` でログを記録するための4つのロギングマクロ `@debug`、`@info`、`@warn`、`@error` が定義されています。 `@logmsg` は、`level` をプログラム的に任意の `LogLevel` またはカスタムログレベルタイプに設定することを可能にします。
`メッセージ` は、ログイベントの人間が読み取れる説明である文字列に評価される式である必要があります。慣例として、この文字列は提示されるときにマークダウンとしてフォーマットされます。
オプションの `key=value` ペアのリストは、ログレコードの一部としてログバックエンドに渡される任意のユーザー定義メタデータをサポートします。もし `value` 式のみが提供される場合、式を表すキーが [`Symbol`](@ref) を使用して生成されます。例えば、`x` は `x=x` になり、`foo(10)` は `Symbol("foo(10)")=foo(10)` になります。キーと値のペアのリストをスプラットするには、通常のスプラット構文 `@info "blah" kws...` を使用します。
自動生成されたログデータをオーバーライドするためのいくつかのキーがあります:
* `_module=mod` は、メッセージのソース位置から異なる発信モジュールを指定するために使用できます。
* `_group=symbol` は、メッセージグループをオーバーライドするために使用できます(これは通常、ソースファイルのベース名から派生します)。
* `_id=symbol` は、自動生成されたユニークなメッセージ識別子をオーバーライドするために使用できます。これは、異なるソース行で生成されたメッセージを非常に密接に関連付ける必要がある場合に便利です。
* `_file=string` と `_line=integer` は、ログメッセージの明示的なソース位置をオーバーライドするために使用できます。
慣習的な意味を持ついくつかのキーと値のペアもあります:
* `maxlog=integer` は、メッセージが `maxlog` 回以上表示されないようにバックエンドにヒントを与えるために使用されるべきです。
* `exception=ex` は、ログメッセージと共に例外を輸送するために使用されるべきで、通常は `@error` と共に使用されます。関連するバックトレース `bt` は、タプル `exception=(ex,bt)` を使用して添付できます。
# 例
julia @debug "詳細なデバッグ情報。デフォルトでは表示されません" @info "情報メッセージ" @warn "何かが奇妙でした。注意を払うべきです" @error "非致命的なエラーが発生しました"
x = 10 @info "メッセージに添付された変数" x a=42.0
@debug begin sA = sum(A) "sum(A) = :sA は高価な操作であり、shouldlog
が true を返すときのみ評価されます" end
for i=1:10000 @info "デフォルトのバックエンドでは、(i = :i) が10回だけ表示されます" maxlog=10 @debug "Algorithm1" i progress=i/10000 end ```
Logging.LogLevel
— TypeLogLevel(level)
ログレコードの重大度/冗長性。
ログレベルは、潜在的なログレコードがフィルタリングされる基準を提供します。これは、ログレコードデータ構造自体を構築するための他の作業が行われる前に行われます。
例
julia> Logging.LogLevel(0) == Logging.Info
true
Logging.Debug
— Constantデバッグ
LogLevel(-1000)
のエイリアスです。
Logging.Info
— Constant情報
LogLevel(0)
のエイリアスです。
Logging.Warn
— Constant警告
LogLevel(1000)
のエイリアスです。
Logging.Error
— Constantエラー
LogLevel(2000)
のエイリアスです。
Logging.BelowMinLevel
— ConstantBelowMinLevel
LogLevel(-1_000_001)
のエイリアスです。
Logging.AboveMaxLevel
— ConstantAboveMaxLevel
LogLevel(1_000_001)
のエイリアスです。
Processing events with AbstractLogger
イベント処理は、AbstractLogger
に関連付けられた関数をオーバーライドすることで制御されます:
Methods to implement | Brief description | |
---|---|---|
Logging.handle_message | Handle a log event | |
Logging.shouldlog | Early filtering of events | |
Logging.min_enabled_level | Lower bound for log level of accepted events | |
Optional methods | Default definition | Brief description |
Logging.catch_exceptions | true | Catch exceptions during event evaluation |
Logging.AbstractLogger
— Typeロガーは、ログレコードがどのようにフィルタリングされ、配信されるかを制御します。ログレコードが生成されると、ロガーはレコードを検査し、それに対して何をするかを決定する最初のユーザー設定可能なコードです。
Logging.handle_message
— Functionhandle_message(logger, level, message, _module, group, id, file, line; key1=val1, ...)
logger
にlevel
でメッセージをログします。メッセージが生成された論理的な場所はモジュール_module
とgroup
によって示され、ソースの場所はfile
とline
によって示されます。id
はフィルタリング時にログステートメントを識別するためのキーとして使用される任意の一意の値(通常はSymbol
)です。
Logging.shouldlog
— Functionshouldlog(logger, level, _module, group, id)
logger
がlevel
でメッセージを受け入れ、_module
、group
のために生成され、ユニークなログ識別子id
を持つ場合、true
を返します。
Logging.min_enabled_level
— Functionmin_enabled_level(logger)
logger
の最小有効レベルを返します。つまり、すべてのメッセージがフィルタリングされるレベル以下または等しいログレベルです。
Logging.catch_exceptions
— Functioncatch_exceptions(logger)
ログ記録の構築中に発生する例外をロガーがキャッチすべきかどうかを示す true
を返します。デフォルトでは、メッセージはキャッチされます。
デフォルトでは、すべての例外がキャッチされ、ログメッセージの生成がプログラムをクラッシュさせるのを防ぎます。これにより、ユーザーは本番システムでデバッグログなどのあまり使用されない機能を自信を持って切り替えることができます。
監査証跡としてロギングを使用したい場合は、ロガータイプに対してこれを無効にする必要があります。
Logging.disable_logging
— Functiondisable_logging(level)
level
以下のログレベルのすべてのログメッセージを無効にします。これはグローバル設定であり、無効にするとデバッグログが非常に安価になります。
例
Logging.disable_logging(Logging.Info) # デバッグと情報を無効にする
Using Loggers
ロガーのインストールと検査:
Logging.global_logger
— Functionglobal_logger()
現在のタスクに特定のロガーが存在しない場合にメッセージを受信するために使用されるグローバルロガーを返します。
global_logger(logger)
グローバルロガーを logger
に設定し、以前のグローバルロガーを返します。
Logging.with_logger
— Functionwith_logger(function, logger)
function
を実行し、すべてのログメッセージをlogger
に送ります。
例
function test(x)
@info "x = $x"
end
with_logger(logger) do
test(1)
test([1,2])
end
Logging.current_logger
— Functioncurrent_logger()
現在のタスクのロガーを返します。タスクにロガーが添付されていない場合は、グローバルロガーが返されます。
システムに供給されるロガー:
Logging.NullLogger
— TypeNullLogger()
すべてのメッセージを無効にし、出力を生成しないロガー - /dev/null のロガーに相当します。
Base.CoreLogging.ConsoleLogger
— TypeConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt,
show_limited=true, right_justify=0)
テキストコンソールでの可読性を最適化したフォーマットのロガーで、例えばJulia REPLでのインタラクティブな作業に適しています。
min_level
未満のログレベルはフィルタリングされます。
メッセージのフォーマットはキーワード引数を設定することで制御できます:
meta_formatter
は、ログイベントのメタデータ(level, _module, group, id, file, line)
を受け取り、ログメッセージのための色(printstyledに渡されるもの)とプレフィックスおよびサフィックスを返す関数です。デフォルトは、ログレベルでプレフィックスを付け、モジュール、ファイル、行位置を含むサフィックスを付けることです。show_limited
は、大きなデータ構造の印刷を画面に収まるように制限し、フォーマット中に:limit
IOContext
キーを設定します。right_justify
は、ログメタデータが右揃えされる整数列です。デフォルトはゼロ(メタデータは独自の行に表示されます)です。
Logging.SimpleLogger
— TypeSimpleLogger([stream,] min_level=Info)
すべてのメッセージを min_level
以上のレベルで stream
にログするためのシンプルなロガーです。ストリームが閉じている場合、ログレベルが Warn
以上のメッセージは stderr
に記録され、それ以下は stdout
に記録されます。