Logrus ist ein strukturierter Logger für Go (Golang), der vollständig API-kompatibel mit dem Standard-Bibliotheks-Logger ist.
Logrus befindet sich im Wartungsmodus. Wir werden keine neuen Funktionen einführen. Es ist einfach zu schwierig, dies auf eine Art und Weise zu tun, die nicht die Projekte vieler Leute zum Scheitern bringt, und das ist das Letzte, was Sie von Ihrer Logging-Bibliothek erwarten (wieder...).
Das bedeutet nicht, dass Logrus tot ist. Logrus wird aus Sicherheitsgründen, (abwärtskompatiblen) Fehlerbehebungen und Leistung (wo wir durch die Schnittstelle eingeschränkt sind) weiterhin gepflegt.
Ich glaube, dass der größte Beitrag von Logrus darin besteht, dass er zum weitverbreiteten Einsatz der strukturierten Protokollierung in Golang beigetragen hat. Es scheint keinen Grund zu geben, eine größere Breaking-Iteration in Logrus V2 durchzuführen, da die fantastische Go-Community diese unabhängig erstellt hat. Es sind viele fantastische Alternativen entstanden. Logrus würde so aussehen, wenn es mit dem, was wir heute über strukturierte Protokollierung in Go wissen, neu gestaltet worden wäre. Schauen Sie sich zum Beispiel Zerolog, Zap und Apex an.
Sehen Sie seltsame Probleme bei der Unterscheidung zwischen Groß- und Kleinschreibung? In der Vergangenheit war es möglich, Logrus sowohl in Groß- als auch in Kleinbuchstaben zu importieren. Aufgrund der Go-Paketumgebung verursachte dies Probleme in der Community und wir brauchten einen Standard. In einigen Umgebungen gab es Probleme mit der Großbuchstabenvariante, daher wurde die Kleinschreibung gewählt. Alles, was logrus
verwendet, muss Kleinbuchstaben verwenden: github.com/sirupsen/logrus
. Jedes Paket, bei dem dies nicht der Fall ist, sollte geändert werden.
Informationen zum Beheben von Glide finden Sie in diesen Kommentaren. Eine ausführliche Erläuterung des Gehäuseproblems finden Sie in diesem Kommentar.
In der Entwicklung schön farbcodiert (wenn ein TTY angehängt ist, sonst nur einfacher Text):
Mit log.SetFormatter(&log.JSONFormatter{})
zum einfachen Parsen durch Logstash oder Splunk:
{"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"}
Mit dem Standardwert log.SetFormatter(&log.TextFormatter{})
ist die Ausgabe mit dem logfmt-Format kompatibel, wenn kein TTY angehängt ist:
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
Um dieses Verhalten auch dann sicherzustellen, wenn ein TTY angeschlossen ist, stellen Sie Ihren Formatierer wie folgt ein:
log . SetFormatter ( & log. TextFormatter {
DisableColors : true ,
FullTimestamp : true ,
})
Wenn Sie die aufrufende Methode als Feld hinzufügen möchten, weisen Sie den Logger an über:
log . SetReportCaller ( true )
Dadurch wird der Aufrufer wie folgt als „Methode“ hinzugefügt:
{ "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
Beachten Sie, dass dadurch ein messbarer Mehraufwand entsteht – die Kosten hängen von der Go-Version ab, liegen in aktuellen Tests mit 1.6 und 1.7 jedoch zwischen 20 und 40 %. Sie können dies in Ihrer Umgebung über Benchmarks validieren:
go test -bench=.*CallerTracing
Der Name der Organisation wurde in Kleinbuchstaben geändert – und dieser Name wird nicht wieder geändert. Wenn aufgrund der Groß-/Kleinschreibung Importkonflikte auftreten, verwenden Sie bitte den Kleinbuchstaben-Import: github.com/sirupsen/logrus
.
Der einfachste Weg, Logrus zu verwenden, ist einfach der exportierte Logger auf Paketebene:
package main
import (
log "github.com/sirupsen/logrus"
)
func main () {
log . WithFields (log. Fields {
"animal" : "walrus" ,
}). Info ( "A walrus appears" )
}
Beachten Sie, dass es vollständig API-kompatibel mit dem stdlib-Logger ist, sodass Sie Ihre log
überall durch log "github.com/sirupsen/logrus"
ersetzen können und jetzt über die Flexibilität von Logrus verfügen. Sie können es nach Ihren Wünschen anpassen:
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" )
}
Für eine erweiterte Nutzung, wie z. B. die Protokollierung an mehreren Standorten aus derselben Anwendung, können Sie auch eine Instanz des logrus
-Loggers erstellen:
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 fördert eine sorgfältige, strukturierte Protokollierung durch Protokollierungsfelder anstelle langer, nicht analysierbarer Fehlermeldungen. Anstelle von: log.Fatalf("Failed to send event %s to topic %s with key %d")
sollten Sie beispielsweise Folgendes protokollieren, was viel besser erkennbar ist:
log . WithFields (log. Fields {
"event" : event ,
"topic" : topic ,
"key" : key ,
}). Fatal ( "Failed to send event" )
Wir haben festgestellt, dass diese API Sie dazu zwingt, über die Protokollierung auf eine Art und Weise nachzudenken, die viel nützlichere Protokollierungsmeldungen erzeugt. Wir waren in unzähligen Situationen, in denen uns nur ein einziges hinzugefügtes Feld zu einer bereits vorhandenen Protokollanweisung Stunden gespart hätte. Der WithFields
-Aufruf ist optional.
Im Allgemeinen sollte die Verwendung einer der Funktionen der printf
-Familie bei Logrus als Hinweis gesehen werden, dass Sie ein Feld hinzufügen sollten. Sie können die Funktionen der printf
-Familie jedoch weiterhin mit Logrus verwenden.
Oft ist es hilfreich, Felder immer an Protokollanweisungen in einer Anwendung oder Teilen davon anzuhängen. Beispielsweise möchten Sie möglicherweise immer request_id
und user_ip
im Kontext einer Anfrage protokollieren. Anstatt log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
in jede Zeile zu schreiben, können Sie stattdessen einen logrus.Entry
erstellen, der weitergegeben wird:
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" )
Sie können Hooks für Protokollierungsstufen hinzufügen. Zum Beispiel, um Fehler an einen Ausnahmeverfolgungsdienst für Error
, Fatal
und Panic
zu senden, Informationen an StatsD zu senden oder an mehreren Orten gleichzeitig zu protokollieren, z. B. Syslog.
Logrus wird mit integrierten Haken geliefert. Fügen Sie diese oder Ihren benutzerdefinierten Hook in init
hinzu:
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 )
}
}
Hinweis: Der Syslog-Hook unterstützt auch die Verbindung zum lokalen Syslog (z. B. „/dev/log“ oder „/var/run/syslog“ oder „/var/run/log“). Einzelheiten finden Sie in der README-Datei zum Syslog-Hook.
Eine Liste der derzeit bekannten Service-Hooks finden Sie auf dieser Wiki-Seite
Logrus verfügt über sieben Protokollierungsstufen: Trace, Debug, Info, Warning, Error, Fatal und Panic.
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." )
Sie können die Protokollierungsstufe für einen Logger
festlegen, dann werden nur Einträge mit diesem oder einem darüber liegenden Schweregrad protokolliert:
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log . SetLevel ( log . InfoLevel )
Es kann nützlich sein log.Level = logrus.DebugLevel
in einer Debug- oder ausführlichen Umgebung festzulegen, wenn Ihre Anwendung dies verfügt.
Hinweis: Wenn Sie unterschiedliche Protokollebenen für die globale Protokollierung ( log.SetLevel(...)
) und die Syslog-Protokollierung wünschen, lesen Sie bitte die README-Datei zum Syslog-Hook.
Neben den mit WithField
oder WithFields
hinzugefügten Feldern werden einige Felder automatisch zu allen Protokollierungsereignissen hinzugefügt:
time
. Der Zeitstempel, als der Eintrag erstellt wurde.msg
. Die Protokollierungsnachricht, die nach dem AddFields
-Aufruf an {Info,Warn,Error,Fatal,Panic}
übergeben wird. Failed to send event.
level
. Die Protokollierungsebene. ZB info
. Logrus hat keine Ahnung von Umwelt.
Wenn Sie möchten, dass Hooks und Formatter nur in bestimmten Umgebungen verwendet werden, sollten Sie dies selbst erledigen. Wenn Ihre Anwendung beispielsweise über eine globale Variable Environment
verfügt, bei der es sich um eine Zeichenfolgendarstellung der Umgebung handelt, könnten Sie Folgendes tun:
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 {})
}
}
Mit dieser Konfiguration sollte logrus
verwendet werden, aber JSON in der Produktion ist meist nur dann nützlich, wenn Sie die Protokollaggregation mit Tools wie Splunk oder Logstash durchführen.
Die integrierten Protokollierungsformatierer sind:
logrus.TextFormatter
. Protokolliert das Ereignis in Farben, wenn stdout ein TTY ist, andernfalls ohne Farben.ForceColors
auf true
. Um keine farbige Ausgabe zu erzwingen, selbst wenn ein TTY vorhanden ist, setzen Sie das Feld DisableColors
auf true
. Für Windows siehe github.com/mattn/go-colorable.DisableLevelTruncation
auf true
.PadLevelText
auf true
setzen, wird dieses Verhalten aktiviert, indem dem Ebenentext Auffüllungen hinzugefügt werden.logrus.JSONFormatter
. Protokolliert Felder als JSON.Protokollierungsformatierer von Drittanbietern:
FluentdFormatter
. Formatiert Einträge, die von Kubernetes und Google Container Engine analysiert werden können.GELF
. Formatiert Einträge so, dass sie der GELF 1.1-Spezifikation von Graylog entsprechen.logstash
. Protokolliert Felder als Logstash-Ereignisse.prefixed
. Zeigt die Quelle des Protokolleintrags zusammen mit dem alternativen Layout an.zalgo
. Die Macht von Zalgo beschwören.nested-logrus-formatter
. Konvertiert Logrus-Felder in eine verschachtelte Struktur.powerful-logrus-formatter
. beim Drucken des Protokolls den Dateinamen, die Zeilennummer des Protokolls und den Namen der letzten Funktion abrufen; Protokoll in Dateien speichern.caption-json-formatter
. Der Nachrichten-JSON-Formatierer von Logrus wurde mit einer für Menschen lesbaren Beschriftung hinzugefügt. Sie können Ihren Formatierer definieren, indem Sie die Formatter
-Schnittstelle implementieren, die eine Format
Methode erfordert. Format
erfordert einen *Entry
. entry.Data
ist ein Fields
( map[string]interface{}
) mit allen Ihren Feldern sowie den Standardfeldern (siehe Abschnitt „Einträge“ oben):
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 kann in einen io.Writer
umgewandelt werden. Dieser Writer ist das Ende einer io.Pipe
und es liegt in Ihrer Verantwortung, sie zu schließen.
w := logger . Writer ()
defer w . Close ()
srv := http. Server {
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog : log . New ( w , "" , 0 ),
}
Jede an diesen Writer geschriebene Zeile wird auf die übliche Weise mit Formatierern und Hooks gedruckt. Die Ebene für diese Einträge ist info
.
Das bedeutet, dass wir den Standard-Bibliothekslogger einfach überschreiben können:
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 ())
Log-Rotation ist bei Logrus nicht möglich. Die Protokollrotation sollte von einem externen Programm (wie logrotate(8)
) durchgeführt werden, das alte Protokolleinträge komprimieren und löschen kann. Es sollte keine Funktion des Loggers auf Anwendungsebene sein.
Werkzeug | Beschreibung |
---|---|
Logrus Mate | Logrus Mate ist ein Tool für Logrus zum Verwalten von Loggern. Sie können die Logger-Ebene, den Hook und den Formatierer anhand der Konfigurationsdatei initialisieren. Der Logger wird mit unterschiedlichen Konfigurationen in verschiedenen Umgebungen generiert. |
Logrus-Viper-Helfer | Ein Helfer um Logrus mit spf13/Viper zu umwickeln, um die Konfiguration mit Fangs zu laden! Und um die Logrus-Konfiguration zu vereinfachen, verwenden Sie einige Verhaltensweisen von Logrus Mate. Probe |
Logrus verfügt über eine integrierte Funktion zur Bestätigung des Vorhandenseins von Protokollmeldungen. Dies wird über den test
-Hook implementiert und bietet:
test.NewLocal
und test.NewGlobal
), die im Grunde nur den test
Hook hinzufügentest.NewNullLogger
), der nur Protokollmeldungen aufzeichnet (und keine ausgibt): 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 kann eine oder mehrere Funktionen registrieren, die aufgerufen werden, wenn eine fatal
Meldung protokolliert wird. Die registrierten Handler werden ausgeführt, bevor Logrus einen os.Exit(1)
ausführt. Dieses Verhalten kann hilfreich sein, wenn Anrufer ordnungsgemäß herunterfahren müssen. Im Gegensatz zu einem panic("Something went wrong...")
der mit einer verzögerten recover
abgefangen werden kann, kann ein Aufruf von os.Exit(1)
nicht abgefangen werden.
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...
Standardmäßig ist Logger durch einen Mutex für gleichzeitige Schreibvorgänge geschützt. Der Mutex wird beim Aufrufen von Hooks und beim Schreiben von Protokollen gehalten. Wenn Sie sicher sind, dass eine solche Sperre nicht erforderlich ist, können Sie logger.SetNoLock() aufrufen, um die Sperre zu deaktivieren.
Zu den Situationen, in denen eine Sperre nicht erforderlich ist, gehören:
Sie haben keine Hooks registriert oder der Hook-Aufruf ist bereits threadsicher.
Das Schreiben in logger.Out ist bereits threadsicher, zum Beispiel:
logger.Out ist durch Sperren geschützt.
logger.Out ist ein os.File-Handler, der mit dem Flag O_APPEND
geöffnet wird und jeder Schreibvorgang kleiner als 4 KB ist. (Dies ermöglicht Multi-Thread-/Multi-Prozess-Schreiben)
(Siehe http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)