Logrus は Go (golang) 用の構造化ロガーで、標準ライブラリ ロガーと完全に API 互換性があります。
Logrus はメンテナンス モードです。新しい機能は導入しません。多くの人のプロジェクトを壊さない方法でこれを実行するのは単純に難しすぎます。これは、Logging ライブラリに最も望んでいないことです (繰り返しになります...)。
これはログルスが死んだという意味ではありません。 Logrus は、セキュリティ、(下位互換性のある) バグ修正、および (インターフェースによって制限されている場合) パフォーマンスのために維持され続けます。
Logrus の最大の貢献は、今日の Golang における構造化ログの普及に一役買ったことだと思います。素晴らしい Go コミュニティが独立してそれらを構築しているため、Logrus V2 に大規模な分割イテレーションを行う理由はないようです。多くの素晴らしい代替案が登場しました。 Logrus は、現在の Go の構造化ロギングについてわかっていることを基に再設計されていれば、それらのように見えるでしょう。たとえば、Zerolog、Zap、Apex をチェックしてください。
大文字と小文字を区別する奇妙な問題が発生しましたか?以前は、Logrus を大文字と小文字の両方でインポートすることが可能でした。 Go パッケージ環境が原因で、コミュニティで問題が発生したため、標準が必要でした。一部の環境では大文字のバリアントで問題が発生したため、小文字が決定されました。 logrus
使用するものはすべて小文字を使用する必要があります: github.com/sirupsen/logrus
。そうでないパッケージは変更する必要があります。
Glide を修正するには、これらのコメントを参照してください。ケーシングの問題の詳細な説明については、このコメントを参照してください。
開発中にうまく色分けされています (TTY が添付されている場合、それ以外の場合は単なるプレーンテキスト):
logstash または Splunk による簡単な解析のために、 log.SetFormatter(&log.JSONFormatter{})
を使用します。
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
TTY が接続されていない場合のデフォルトのlog.SetFormatter(&log.TextFormatter{})
では、出力は logfmt 形式と互換性があります。
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
TTY が接続されている場合でもこの動作を保証するには、フォーマッタを次のように設定します。
log . SetFormatter ( & log. TextFormatter {
DisableColors : true ,
FullTimestamp : true ,
})
呼び出しメソッドをフィールドとして追加したい場合は、次のようにロガーに指示します。
log . SetReportCaller ( true )
これにより、次のように呼び出し元が「メソッド」として追加されます。
{ "animal" : " penguin " , "level" : " fatal " , "method" : " github.com/sirupsen/arcticcreatures.migrate " , "msg" : " a penguin swims by " ,
"time" : " 2014-03-10 19:57:38.562543129 -0400 EDT " }
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
これにより、測定可能なオーバーヘッドが追加されることに注意してください。コストは Go のバージョンによって異なりますが、1.6 と 1.7 での最近のテストでは 20 ~ 40% です。ベンチマークを使用して、環境内でこれを検証できます。
go test -bench=.*CallerTracing
組織の名前は小文字に変更されましたが、これは元に戻されません。大文字と小文字の区別が原因でインポートの競合が発生する場合は、小文字のインポート ( github.com/sirupsen/logrus
を使用してください。
Logrus を使用する最も簡単な方法は、パッケージ レベルでエクスポートされたロガーを使用することです。
package main
import (
log "github.com/sirupsen/logrus"
)
func main () {
log . WithFields (log. Fields {
"animal" : "walrus" ,
}). Info ( "A walrus appears" )
}
これは stdlib ロガーと完全に API 互換であるため、どこでもlog
インポートをlog "github.com/sirupsen/logrus"
に置き換えることができ、Logrus の柔軟性が得られることに注意してください。必要に応じてカスタマイズできます。
package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func init () {
// Log as JSON instead of the default ASCII formatter.
log . SetFormatter ( & log. JSONFormatter {})
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log . SetOutput ( os . Stdout )
// Only log the warning severity or above.
log . SetLevel ( log . WarnLevel )
}
func main () {
log . WithFields (log. Fields {
"animal" : "walrus" ,
"size" : 10 ,
}). Info ( "A group of walrus emerges from the ocean" )
log . WithFields (log. Fields {
"omg" : true ,
"number" : 122 ,
}). Warn ( "The group's number increased tremendously!" )
log . WithFields (log. Fields {
"omg" : true ,
"number" : 100 ,
}). Fatal ( "The ice breaks!" )
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log . WithFields (log. Fields {
"common" : "this is a common field" ,
"other" : "I also should be logged always" ,
})
contextLogger . Info ( "I'll be logged with common and other field" )
contextLogger . Info ( "Me too" )
}
同じアプリケーションから複数の場所にログを記録するなど、より高度な使用法を行うには、 logrus
Logger のインスタンスを作成することもできます。
package main
import (
"os"
"github.com/sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus . New ()
func main () {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log . Out = os . Stdout
// You could set this to any `io.Writer` such as a file
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
log . WithFields (logrus. Fields {
"animal" : "walrus" ,
"size" : 10 ,
}). Info ( "A group of walrus emerges from the ocean" )
}
Logrus は、長くて解析不可能なエラー メッセージの代わりに、ロギング フィールドを通じて慎重で構造化されたロギングを奨励します。たとえば、 log.Fatalf("Failed to send event %s to topic %s with key %d")
の代わりに、より発見しやすいものをログに記録する必要があります。
log . WithFields (log. Fields {
"event" : event ,
"topic" : topic ,
"key" : key ,
}). Fatal ( "Failed to send event" )
この API を使用すると、より有用なログ メッセージを生成する方法でログを記録することを考える必要があることがわかりました。すでに存在するログ ステートメントに 1 つのフィールドを追加するだけで数時間の節約ができるという状況に、私たちは数え切れないほど遭遇してきました。 WithFields
呼び出しはオプションです。
一般に、Logrus でprintf
-family 関数のいずれかを使用することは、フィールドを追加する必要があるというヒントと見なされますが、Logrus では引き続きprintf
-family 関数を使用できます。
多くの場合、アプリケーションまたはその一部のログ ステートメントにフィールドを常に添付すると便利です。たとえば、リクエストのコンテキストでrequest_id
とuser_ip
常にログに記録したい場合があります。各行にlog.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
記述する代わりに、 logrus.Entry
作成して渡すことができます。
requestLogger := log . WithFields (log. Fields { "request_id" : request_id , "user_ip" : user_ip })
requestLogger. Info ( "something happened on that request" ) # will log request_id and user_ip
requestLogger . Warn ( "something not great happened" )
ロギングレベルのフックを追加できます。たとえば、 Error
、 Fatal
、 Panic
に関する例外追跡サービスにエラーを送信したり、 StatsD に情報を送信したり、複数の場所 (syslog など) に同時にログを送信したりします。
Logrus にはフックが組み込まれています。これら、またはカスタム フックをinit
に追加します。
import (
log "github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init () {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log . AddHook ( airbrake . NewHook ( 123 , "xyz" , "production" ))
hook , err := logrus_syslog . NewSyslogHook ( "udp" , "localhost:514" , syslog . LOG_INFO , "" )
if err != nil {
log . Error ( "Unable to connect to local syslog daemon" )
} else {
log . AddHook ( hook )
}
}
注: Syslog フックは、ローカル syslog (例: "/dev/log"、"/var/run/syslog"、または "/var/run/log") への接続もサポートします。詳細はsyslogフックのREADMEをご確認ください。
現在知られているサービスフックのリストは、この Wiki ページにあります。
Logrus には、トレース、デバッグ、情報、警告、エラー、致命的、パニックの 7 つのログ レベルがあります。
log . Trace ( "Something very low level." )
log . Debug ( "Useful debugging information." )
log . Info ( "Something noteworthy happened!" )
log . Warn ( "You should probably take a look at this." )
log . Error ( "Something failed but I'm not quitting." )
// Calls os.Exit(1) after logging
log . Fatal ( "Bye." )
// Calls panic() after logging
log . Panic ( "I'm bailing." )
Logger
でログ レベルを設定すると、その重大度またはそれ以上の重大度を持つエントリのみがログに記録されます。
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log . SetLevel ( log . InfoLevel )
アプリケーションにデバッグ環境または詳細環境がある場合は、 log.Level = logrus.DebugLevel
を設定すると便利な場合があります。
注: グローバル ( log.SetLevel(...)
) と syslog ログに異なるログ レベルが必要な場合は、syslog フックの README を確認してください。
WithField
またはWithFields
で追加されるフィールドに加えて、いくつかのフィールドがすべてのログ イベントに自動的に追加されます。
time
。エントリが作成されたときのタイムスタンプ。msg
。 AddFields
呼び出し後に、ログ メッセージが{Info,Warn,Error,Fatal,Panic}
に渡されました。例: Failed to send event.
level
。ロギングレベル。たとえば、 info
。 ログラスには環境という概念がありません。
フックとフォーマッタを特定の環境でのみ使用したい場合は、自分で処理する必要があります。たとえば、アプリケーションに環境の文字列表現であるグローバル変数Environment
ある場合、次のようにすることができます。
import (
log "github.com/sirupsen/logrus"
)
func init () {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log . SetFormatter ( & log. JSONFormatter {})
} else {
// The TextFormatter is default, you don't actually have to do this.
log . SetFormatter ( & log. TextFormatter {})
}
}
この構成はlogrus
が使用されることを意図した方法ですが、本番環境の JSON はほとんどの場合、Splunk や Logstash などのツールを使用してログ集計を行う場合にのみ役に立ちます。
組み込みのロギング フォーマッタは次のとおりです。
logrus.TextFormatter
。標準出力が tty の場合はイベントを色で記録し、それ以外の場合は色なしで記録します。ForceColors
フィールドをtrue
に設定します。 TTY がある場合でもカラー出力を強制的に行わないようにするには、 DisableColors
フィールドをtrue
に設定します。 Windows の場合は、github.com/mattn/go-colorable を参照してください。DisableLevelTruncation
フィールドをtrue
に設定します。PadLevelText
フィールドをtrue
に設定すると、レベル テキストにパディングが追加され、この動作が有効になります。logrus.JSONFormatter
。フィールドを JSON としてログに記録します。サードパーティのログフォーマッタ:
FluentdFormatter
。 Kubernetes と Google Container Engine で解析できるエントリをフォーマットします。GELF
。 Graylog の GELF 1.1 仕様に準拠するようにエントリをフォーマットします。logstash
。フィールドを Logstash イベントとしてログに記録します。prefixed
.ログエントリのソースを代替レイアウトとともに表示します。zalgo
。ザルゴの力を発動する。nested-logrus-formatter
。 logrus フィールドを入れ子構造に変換します。powerful-logrus-formatter
。ログを出力するときに、ファイル名、ログの行番号、および最新の関数の名前を取得します。ログをファイルに保存します。caption-json-formatter
。 logrus のメッセージ JSON フォーマッタに人間が読めるキャプションが追加されました。フォーマッタを定義するには、 Formatter
インターフェイスを実装し、 Format
メソッドを必要とします。 Format
*Entry
を受け取ります。 entry.Data
、すべてのフィールドとデフォルトのフィールドを含むFields
タイプ ( map[string]interface{}
) です (上記の「エントリ」セクションを参照)。
type MyJSONFormatter struct {
}
log . SetFormatter ( new ( MyJSONFormatter ))
func ( f * MyJSONFormatter ) Format ( entry * Entry ) ([] byte , error ) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized , err := json . Marshal ( entry . Data )
if err != nil {
return nil , fmt . Errorf ( "Failed to marshal fields to JSON, %w" , err )
}
return append ( serialized , 'n' ), nil
}
io.Writer
としてのロガーLogrus はio.Writer
に変換できます。そのライターはio.Pipe
の終端であり、それを閉じるのはあなたの責任です。
w := logger . Writer ()
defer w . Close ()
srv := http. Server {
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog : log . New ( w , "" , 0 ),
}
そのライターに書き込まれた各行は、フォーマッタとフックを使用して通常の方法で出力されます。これらのエントリのレベルはinfo
です。
これは、標準ライブラリ ロガーを簡単にオーバーライドできることを意味します。
logger := logrus . New ()
logger . Formatter = & logrus. JSONFormatter {}
// Use logrus for standard log output
// Note that `log` here references stdlib's log
// Not logrus imported under the name `log`.
log . SetOutput ( logger . Writer ())
Logrus ではログのローテーションは提供されません。ログのローテーションは、古いログ エントリを圧縮して削除できる外部プログラム ( logrotate(8)
など) によって実行する必要があります。これはアプリケーションレベルのロガーの機能であってはなりません。
道具 | 説明 |
---|---|
ログラスメイト | Logrus mate は、Logrus がロガーを管理するためのツールです。設定ファイルによって初期ロガーのレベル、フック、フォーマッタを設定できます。ロガーは、異なる環境では異なる設定で生成されます。 |
ログラス バイパー ヘルパー | spf13/Viper でラップして牙で構成をロードする Logrus の周りのヘルパー! Logrus の構成を簡素化するには、Logrus Mate のいくつかの動作を使用します。サンプル |
Logrus には、ログ メッセージの存在を確認するための機能が組み込まれています。これはtest
フックを通じて実装され、次の機能を提供します。
test.NewLocal
およびtest.NewGlobal
) のデコレータ。基本的にはtest
フックを追加するだけです。test.NewNullLogger
): import (
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSomething ( t * testing. T ){
logger , hook := test . NewNullLogger ()
logger . Error ( "Helloerror" )
assert . Equal ( t , 1 , len ( hook . Entries ))
assert . Equal ( t , logrus . ErrorLevel , hook . LastEntry (). Level )
assert . Equal ( t , "Helloerror" , hook . LastEntry (). Message )
hook . Reset ()
assert . Nil ( t , hook . LastEntry ())
}
Logrus は、 fatal
レベルのメッセージが記録されたときに呼び出される 1 つ以上の関数を登録できます。登録されたハンドラーは、logrus がos.Exit(1)
実行する前に実行されます。この動作は、呼び出し元を正常にシャットダウンする必要がある場合に役立つ場合があります。遅延recover
でインターセプトできるpanic("Something went wrong...")
呼び出しとは異なり、 os.Exit(1)
への呼び出しはインターセプトできません。
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...
デフォルトでは、Logger は同時書き込みのミューテックスによって保護されています。ミューテックスは、フックの呼び出し時とログの書き込み時に保持されます。このようなロックが必要ないと確信できる場合は、logger.SetNoLock() を呼び出してロックを無効にすることができます。
ロックが必要ない状況には次のようなものがあります。
フックが登録されていないか、フック呼び出しはすでにスレッドセーフになっています。
logger.Out への書き込みはすでにスレッドセーフです。次に例を示します。
logger.Out はロックによって保護されています。
logger.Out は、 O_APPEND
フラグで開かれた os.File ハンドラーであり、すべての書き込みは 4k 未満です。 (これによりマルチスレッド/マルチプロセスの書き込みが可能になります)
(http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/ を参照)