Loguru ist eine Bibliothek, die darauf abzielt, in Python eine angenehme Protokollierung zu bringen.
Haben Sie sich jemals faul gefühlt, einen Logger zu konfigurieren und stattdessen print()
zu benutzen? Mit Loguru haben Sie keine Entschuldigung, von Anfang an keine Protokollierung zu verwenden. Dies ist so einfach wie from loguru import logger
.
Außerdem soll diese Bibliothek die Python -Protokollierung weniger schmerzhaft machen, indem eine Reihe nützlicher Funktionen hinzugefügt werden, die Einschränkungen der Standard -Logger lösen. Die Verwendung von Protokollen in Ihrer Anwendung sollte ein Automatismus sein. Loguru versucht, es sowohl angenehm als auch mächtig zu machen.
pip install loguru
Das Hauptkonzept von Loguru ist, dass es nur einen logger
gibt.
Aus Gründen der Bequemlichkeit ist es vorkonfiguriert und gibt zunächst an stderr
aus (aber das ist völlig konfigurierbar).
from loguru import logger
logger . debug ( "That's it, beautiful and simple logging!" )
Der logger
ist nur eine Schnittstelle, die Protokollnachrichten in konfigurierte Handler sendet. Einfach, richtig?
Wie füge ich einen Handler hinzu? Wie richte ich Protokollformatierung ein? Wie filtere ich Nachrichten? Wie setze ich Level?
Eine Antwort: Die Funktion add()
.
logger . add ( sys . stderr , format = "{time} {level} {message}" , filter = "my_module" , level = "INFO" )
Diese Funktion sollte verwendet werden, um Süwen zu registrieren, die für die Verwaltung von Protokollnachrichten verantwortlich sind, die mit einem Datensatzdikte kontextualisiert wurden. Eine Spüle kann viele Formen annehmen: eine einfache Funktion, einen String-Pfad, ein Datei-ähnliches Objekt, eine Korutinefunktion oder ein integrierter Handler.
Beachten Sie, dass Sie auch einen zuvor hinzugefügten Handler remove()
können, indem Sie die Kennung verwenden, die beim Hinzufügen zurückgegeben wurde. Dies ist besonders nützlich, wenn Sie den Standard stderr
-Handler ersetzen möchten: Rufen Sie einfach logger.remove()
um einen Neuanfang zu erstellen.
Wenn Sie protokollierte Nachrichten an eine Datei senden möchten, müssen Sie nur einen String -Pfad als Waschbecken verwenden. Es kann auch automatisch für den Einfachheit halber festgelegt werden:
logger . add ( "file_{time}.log" )
Es ist auch leicht zu konfigurierbar, wenn Sie rotierende Logger benötigen, wenn Sie ältere Protokolle entfernen möchten oder wenn Sie Ihre Dateien beim Schließen komprimieren möchten.
logger . add ( "file_1.log" , rotation = "500 MB" ) # Automatically rotate too big file
logger . add ( "file_2.log" , rotation = "12:00" ) # New file is created each day at noon
logger . add ( "file_3.log" , rotation = "1 week" ) # Once the file is too old, it's rotated
logger . add ( "file_X.log" , retention = "10 days" ) # Cleanup after some time
logger . add ( "file_Y.log" , compression = "zip" ) # Save some loved space
Loguru bevorzugt die viel elegantere und {}
Formatierung über %
, die Protokollierungsfunktionen entsprechen str.format()
.
logger . info ( "If you're using Python {}, prefer {feature} of course!" , 3.6 , feature = "f-strings" )
Haben Sie jemals gesehen, wie Ihr Programm unerwartet abstürzt, ohne etwas in der Protokolldatei zu sehen? Haben Sie jemals bemerkt, dass Ausnahmen in Threads nicht protokolliert wurden? Dies kann mit dem Dekorateur / Kontext -Manager catch()
gelöst werden, der sicherstellt, dass ein Fehler korrekt an den logger
ausgegeben wird.
@ logger . catch
def my_function ( x , y , z ):
# An error? It's caught anyway!
return 1 / ( x + y + z )
Loguru fügt Ihren Protokollen automatisch Farben hinzu, wenn Ihr Terminal kompatibel ist. Sie können Ihren Lieblingsstil definieren, indem Sie Markup -Tags im Sink -Format verwenden.
logger . add ( sys . stdout , colorize = True , format = "<green>{time}</green> <level>{message}</level>" )
Alle dem logger
hinzugefügten Waschbecken sind standardmäßig mit Thread-sicher. Sie sind nicht multiprozesssicher, aber Sie können die Nachrichten enqueue
um die Integrität der Protokolle sicherzustellen. Das gleiche Argument kann auch verwendet werden, wenn Sie eine asynchronisierte Protokollierung wünschen.
logger . add ( "somefile.log" , enqueue = True )
Coroutine -Funktionen, die als Waschbecken verwendet werden, werden ebenfalls unterstützt und sollten mit complete()
erwartet werden.
Protokollierung von Ausnahmen, die in Ihrem Code auftreten, ist wichtig, um Fehler zu verfolgen, aber es ist ziemlich nutzlos, wenn Sie nicht wissen, warum es fehlgeschlagen ist. Loguru hilft Ihnen dabei, Probleme zu identifizieren, indem die gesamte Stapelverfolgung angezeigt werden kann, einschließlich der Werte von Variablen (danke better_exceptions
dafür!).
Der Code:
# Caution, "diagnose=True" is the default and may leak sensitive data in prod
logger . add ( "out.log" , backtrace = True , diagnose = True )
def func ( a , b ):
return a / b
def nested ( c ):
try :
func ( 5 , c )
except ZeroDivisionError :
logger . exception ( "What?!" )
nested ( 0 )
Würde zu:
2018-07-17 01: 38: 43,975 | Fehler | __main __: verschachtelt: 10 - was ?!
Traceback (letzte Anruflast):
Datei "test.py", Zeile 12, in <Modul>
verschachtelt (0)
└ <Funktion unter 0x7f5c755322f0>
> Datei "test.py", Zeile 8, in verschachtelten
Func (5, c)
│ └ 0
└ <Funktion func unter 0x7f5c79fc2e18>
Datei "test.py", Zeile 4, in Func
Return a / b
│ └ 0
└ 5
NulodivisionError: Division von Null
Beachten Sie, dass diese Funktion aufgrund nicht verfügbarer Frame -Daten nicht auf der Standardpython -Reply funktioniert.
Siehe auch: Sicherheitsüberlegungen bei Verwendung von Loguru.
Möchten Sie, dass Ihre Protokolle serialisiert werden, um sie einfacher zu analysieren oder sie weiterzugeben? Unter Verwendung des serialize
-Arguments wird jede Protokollnachricht in eine JSON -Zeichenfolge konvertiert, bevor sie an die konfigurierte Spüle gesendet wird.
logger . add ( custom_sink_function , serialize = True )
Mit bind()
können Sie Ihre Logger -Nachrichten kontextualisieren, indem Sie das zusätzliche Datensatzattribut ändern.
logger . add ( "file.log" , format = "{extra[ip]} {extra[user]} {message}" )
context_logger = logger . bind ( ip = "192.168.0.1" , user = "someone" )
context_logger . info ( "Contextualize your logger easily" )
context_logger . bind ( user = "someone_else" ). info ( "Inline binding of extra attribute" )
context_logger . info ( "Use kwargs to add context during formatting: {user}" , user = "anybody" )
Es ist möglich, einen kontextlokalen Zustand vorübergehend mit contextualize()
zu ändern:
with logger . contextualize ( task = task_id ):
do_something ()
logger . info ( "End of task" )
Sie können auch eine feinkörnige Kontrolle über Ihre Protokolle haben, indem Sie bind()
und filter
kombinieren:
logger . add ( "special.log" , filter = lambda record : "special" in record [ "extra" ])
logger . debug ( "This message is not logged to the file" )
logger . bind ( special = True ). info ( "This message, though, is logged to the file!" )
Mit der patch()
-Methode können dynamische Werte an den Datensatzdikt der neuen Nachricht angehängt werden:
logger . add ( sys . stderr , format = "{extra[utc]} {message}" )
logger = logger . patch ( lambda record : record [ "extra" ]. update ( utc = datetime . utcnow ()))
Irgendwann möchten Sie ausführliche Informationen ohne Leistungsstrafe in der Produktion protokollieren. Sie können die opt()
-Methode verwenden, um dies zu erreichen.
logger . opt ( lazy = True ). debug ( "If sink level <= DEBUG: {x}" , x = lambda : expensive_function ( 2 ** 64 ))
# By the way, "opt()" serves many usages
logger . opt ( exception = True ). info ( "Error stacktrace added to the log message (tuple accepted too)" )
logger . opt ( colors = True ). info ( "Per message <blue>colors</blue>" )
logger . opt ( record = True ). info ( "Display values from the record (eg. {record[thread]})" )
logger . opt ( raw = True ). info ( "Bypass sink formatting n " )
logger . opt ( depth = 1 ). info ( "Use parent stack context (useful within wrapped functions)" )
logger . opt ( capture = False ). info ( "Keyword arguments not added to {dest} dict" , dest = "extra" )
Loguru verfügt über alle Standard -Protokollierungsstufen, zu denen trace()
und success()
hinzugefügt werden. Benötigen Sie mehr? Erstellen Sie es dann einfach mit der Funktion level()
.
new_level = logger . level ( "SNAKY" , no = 38 , color = "<yellow>" , icon = "?" )
logger . log ( "SNAKY" , "Here we go!" )
Die Standardprotokollierung ist mit Argumenten wie datefmt
oder msecs
, %(asctime)s
und %(created)s
, naive DateTimes ohne Zeitzone -Informationen, nicht intuitive Formatierung usw. Loguru behebt: Naive DateTimes:
logger . add ( "file.log" , format = "{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}" )
Die Verwendung des Protokolls in Ihren Skripten ist einfach, und Sie können configure()
. Um Loguru aus einer Bibliothek aus zu verwenden, denken Sie daran, niemals add()
aufzurufen, sondern disable()
stattdessen, damit die Protokollierungsfunktionen zu keinem Op werden. Wenn ein Entwickler die Protokolle Ihrer Bibliothek sehen möchte, können er es erneut enable()
.
# For scripts
config = {
"handlers" : [
{ "sink" : sys . stdout , "format" : "{time} - {message}" },
{ "sink" : "file.log" , "serialize" : True },
],
"extra" : { "user" : "someone" }
}
logger . configure ( ** config )
# For libraries, should be your library's `__name__`
logger . disable ( "my_library" )
logger . info ( "No matter added sinks, this message is not displayed" )
# In your application, enable the logger in the library
logger . enable ( "my_library" )
logger . info ( "This message however is propagated to the sinks" )
Für zusätzliche Bequemlichkeit können Sie auch die loguru-config
Bibliothek verwenden, um den logger
direkt aus einer Konfigurationsdatei einzurichten.
Möchten Sie einen integrierten Handler
als Loguru-Waschbecken verwenden?
handler = logging . handlers . SysLogHandler ( address = ( 'localhost' , 514 ))
logger . add ( handler )
Müssen Sie Loguru -Nachrichten in Standardprotokollierung verbreiten?
class PropagateHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
logging . getLogger ( record . name ). handle ( record )
logger . add ( PropagateHandler (), format = "{message}" )
Möchten Sie Standard -Protokollierungsnachrichten in Ihre Loguru -Waschbecken abfangen?
class InterceptHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
# Get corresponding Loguru level if it exists.
level : str | int
try :
level = logger . level ( record . levelname ). name
except ValueError :
level = record . levelno
# Find caller from where originated the logged message.
frame , depth = inspect . currentframe (), 0
while frame and ( depth == 0 or frame . f_code . co_filename == logging . __file__ ):
frame = frame . f_back
depth += 1
logger . opt ( depth = depth , exception = record . exc_info ). log ( level , record . getMessage ())
logging . basicConfig ( handlers = [ InterceptHandler ()], level = 0 , force = True )
Magst du die Standard -Logger -Formatierung nicht? Würde es eine weitere DEBUG
-Farbe bevorzugen? Kein Problem:
# Linux / OSX
export LOGURU_FORMAT = "{time} | <lvl>{message}</lvl>"
# Windows
setx LOGURU_DEBUG_COLOR "<green>"
Es ist häufig nützlich, bestimmte Informationen aus generierten Protokollen zu extrahieren. Aus diesem Grund bietet Loguru eine parse()
-Methode, mit der Protokolle und Regexes umgehen können.
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups
caster_dict = dict ( time = dateutil . parser . parse , level = int ) # Transform matching groups
for groups in logger . parse ( "file.log" , pattern , cast = caster_dict ):
print ( "Parsed:" , groups )
# {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
Loguru kann problemlos mit der Great notifiers
Library (separat installiert werden) kombiniert werden, um eine E-Mail zu erhalten, wenn Ihr Programm unerwartet ausfällt oder um viele andere Art von Benachrichtigungen zu senden.
import notifiers
params = {
"username" : "[email protected]" ,
"password" : "abc123" ,
"to" : "[email protected]"
}
# Send a single notification
notifier = notifiers . get_notifier ( "gmail" )
notifier . notify ( message = "The application is running!" , ** params )
# Be alerted on each error message
from notifiers . logging import NotificationHandler
handler = NotificationHandler ( "gmail" , defaults = params )
logger . add ( handler , level = "ERROR" )
Obwohl die Auswirkungen auf die Aufführungen in den meisten Fällen vernachlässigbar sind, würde ein Logger mit Null kostenlos ohne große Sorge überall verwenden. In einer bevorstehenden Veröffentlichung werden die kritischen Funktionen von Loguru für maximale Geschwindigkeit in C implementiert.