Loggning HOWTO¶
- Författare:
Vinay Sajip <vinay_sajip at red-dove dot com>
Den här sidan innehåller handledningsinformation. För länkar till referensinformation och en kokbok för loggning, se Övriga resurser.
Grundläggande loggningshandledning¶
Loggning är ett sätt att spåra händelser som inträffar när en viss programvara körs. Programvarans utvecklare lägger till loggningsanrop i sin kod för att indikera att vissa händelser har inträffat. En händelse beskrivs med ett beskrivande meddelande som eventuellt kan innehålla variabeldata (dvs. data som potentiellt är olika för varje gång händelsen inträffar). Händelser har också en betydelse som utvecklaren tillskriver händelsen; betydelsen kan också kallas nivå eller alstringsgrad.
När ska man använda loggning¶
Du kan komma åt loggningsfunktionalitet genom att skapa en logger via logger = getLogger(__name__)
och sedan anropa loggerns metoder debug()
, info()
, warning()
, error()
och critical()
. För att avgöra när loggning ska användas och för att se vilka loggermetoder som ska användas när, se tabellen nedan. Den anger, för var och en av en uppsättning vanliga uppgifter, det bästa verktyget att använda för den uppgiften.
Uppgift du vill utföra |
Det bästa verktyget för uppgiften |
---|---|
Visa konsolutmatning för vanlig användning av ett kommandoradsskript eller -program |
|
Rapportera händelser som inträffar under normal drift av ett program (t.ex. för statusövervakning eller felsökning) |
En loggers |
Utfärda en varning för en viss runtime-händelse |
En loggers |
Rapportera ett fel angående en viss runtime-händelse |
Upprätta ett undantag |
Rapportera undertryckande av ett fel utan att skapa ett undantag (t.ex. felhanterare i en serverprocess som körs under lång tid) |
En loggers metod |
Loggningsmetoderna har namn efter nivån eller allvarlighetsgraden på de händelser som de används för att spåra. Standardnivåerna och deras tillämplighet beskrivs nedan (i stigande ordning efter allvarlighetsgrad):
Nivå |
När den används |
---|---|
|
Detaljerad information, vanligtvis av intresse endast vid diagnostisering av problem. |
|
Bekräftelse på att saker och ting fungerar som förväntat. |
”VARNING |
En indikation på att något oväntat har hänt eller en indikation på ett problem inom en snar framtid (t.ex. ”lågt diskutrymme”). Programvaran fungerar fortfarande som förväntat. |
|
På grund av ett mer allvarligt problem har programvaran inte kunnat utföra någon funktion. |
”KRITISK |
Ett allvarligt fel som indikerar att själva programmet kanske inte kan fortsätta att köras. |
Standardnivån är WARNING
, vilket innebär att endast händelser med denna allvarlighetsgrad och högre kommer att spåras, såvida inte loggningspaketet är konfigurerat för att göra något annat.
Händelser som spåras kan hanteras på olika sätt. Det enklaste sättet att hantera spårade händelser är att skriva ut dem till konsolen. Ett annat vanligt sätt är att skriva dem till en diskfil.
Ett enkelt exempel¶
Ett mycket enkelt exempel är:
import logging
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything
Om du skriver in dessa rader i ett skript och kör det, kommer du att se:
VARNING:root:Se upp!
skrivs ut på konsolen. Meddelandet INFO
visas inte eftersom standardnivån är WARNING
. Det utskrivna meddelandet innehåller nivåangivelsen och beskrivningen av händelsen i loggningsanropet, t.ex. ”Se upp!”. Den faktiska utskriften kan formateras ganska flexibelt om du behöver det; formateringsalternativ kommer också att förklaras senare.
Observera att vi i det här exemplet använder funktioner direkt i modulen logging
, t.ex. logging.debug
, i stället för att skapa en logger och anropa funktioner i den. Dessa funktioner arbetar på rotloggern, men kan vara användbara eftersom de kommer att anropa basicConfig()
åt dig om den inte har anropats ännu, som i det här exemplet. I större program vill du dock vanligtvis kontrollera loggningskonfigurationen explicit - så av det skälet, liksom av andra, är det bättre att skapa loggrar och anropa deras metoder.
Loggning till en fil¶
En mycket vanlig situation är att registrera loggningshändelser i en fil, så låt oss titta på det härnäst. Var noga med att prova följande i en nystartad Python-tolk, och fortsätt inte bara från den session som beskrivs ovan:
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logger.debug('This message should go to the log file')
logger.info('So should this')
logger.warning('And this, too')
logger.error('And non-ASCII stuff, too, like Øresund and Malmö')
Ändrad i version 3.9: Argumentet encoding har lagts till. I tidigare Python-versioner, eller om det inte anges, är den kodning som används det standardvärde som används av open()
. Även om det inte visas i exemplet ovan, kan ett errors-argument nu också skickas, vilket avgör hur kodningsfel hanteras. För tillgängliga värden och standardvärdet, se dokumentationen för open()
.
Och om vi nu öppnar filen och tittar på vad vi har, bör vi hitta loggmeddelandena:
DEBUG:__main__:Detta meddelande ska gå till loggfilen
INFO:__main__:Det ska även det här
WARNING:__main__:Och det här också
ERROR:__main__:Och icke-ASCII-grejer också, som Öresund och Malmö
I det här exemplet visas också hur du kan ställa in den loggningsnivå som fungerar som tröskel för spårning. I det här fallet skrevs alla meddelanden ut eftersom vi ställde in tröskeln på DEBUG
.
Om du vill ställa in loggningsnivån från ett kommandoradsalternativ som t.ex:
--log=INFO
och du har värdet på parametern som skickas för --log
i någon variabel loglevel, kan du använda:
getattr(logging, loglevel.upper())
för att få det värde som du skickar till basicConfig()
via argumentet level. Du kanske vill felkontrollera alla värden som användaren matar in, kanske som i följande exempel:
# loggnivån är bunden till det strängvärde som erhålls från # kommandoradsargumentet
# argumentet på kommandoraden. Konvertera till versaler för att användaren ska kunna
# ange --log=DEBUG eller --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError('Ogiltig loggnivå: %s' % loglevel)
logging.basicConfig(nivå=numerisk_nivå, ...)
Anropet till basicConfig()
bör komma före alla anrop till en loggers metoder såsom debug()
, info()
, etc. Annars kan det hända att loggningshändelsen inte hanteras på önskat sätt.
Om du kör ovanstående skript flera gånger kommer meddelandena från de olika körningarna att läggas till i filen example.log. Om du vill att varje körning ska börja om på nytt och inte komma ihåg meddelandena från tidigare körningar kan du ange argumentet filemode genom att ändra anropet i exemplet ovan till:
logging.basicConfig(filnamn='exempel.log', filemode='w', nivå=logging.DEBUG)
Utdata blir samma som tidigare, men loggfilen läggs inte längre till, så meddelanden från tidigare körningar går förlorade.
Loggning av variabeldata¶
Om du vill logga variabeldata använder du en formatsträng för händelsebeskrivningsmeddelandet och lägger till variabeldata som argument. Till exempel:
import loggning
logging.warning('%s innan du %s', 'Titta', 'hoppa!')
kommer att visas:
VARNING:root:Se dig för innan du hoppar!
Som du kan se används den gamla %-s-typen av strängformatering för att slå samman variabeldata i meddelandet med händelsebeskrivningen. Detta är för bakåtkompatibilitet: loggningspaketet är äldre än nyare formateringsalternativ som str.format()
och string.Template
. Dessa nyare formateringsalternativ stödjs, men att utforska dem ligger utanför ramen för denna handledning: se Använda särskilda formateringsstilar i hela din ansökan för mer information.
Ändra formatet på de meddelanden som visas¶
Om du vill ändra det format som används för att visa meddelanden måste du ange vilket format du vill använda:
import loggning
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('Det här meddelandet ska visas på konsolen')
logging.info('Det här bör också visas')
logging.warning('Och det här också')
som skulle skriva ut:
DEBUG:Detta meddelande ska visas på konsolen
INFO:Det här bör också visas
WARNING:Och det här också
Lägg märke till att ”root”, som fanns i tidigare exempel, har försvunnit. För en fullständig uppsättning saker som kan visas i formatsträngar kan du hänvisa till dokumentationen för LogRecord-attribut, men för enkel användning behöver du bara levelname (allvarlighetsgrad), message (händelsebeskrivning, inklusive variabeldata) och kanske för att visa när händelsen inträffade. Detta beskrivs i nästa avsnitt.
Visning av datum/tid i meddelanden¶
Om du vill visa datum och tid för en händelse skriver du ’%(asctime)s’ i formatsträngen:
import loggning
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('är när den här händelsen loggades.')
som borde skriva ut något liknande detta:
2010-12-12 11:41:42,612 är när denna händelse loggades.
Standardformatet för visning av datum/tid (visas ovan) är som ISO8601 eller RFC 3339. Om du behöver mer kontroll över formateringen av datum/tid kan du ange ett datefmt-argument till basicConfig
, som i det här exemplet:
import loggning
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('är när den här händelsen loggades.')
vilket skulle visa något i stil med detta:
12/12/2010 11:46:36 AM är när denna händelse loggades.
Formatet för datefmt-argumentet är detsamma som stöds av time.strftime()
.
Nästa steg¶
Det avslutar den grundläggande handledningen. Det borde räcka för att du ska komma igång med loggning. Det finns mycket mer som loggningspaketet erbjuder, men för att få ut det bästa av det måste du investera lite mer av din tid i att läsa de följande avsnitten. Om du är redo för det, ta lite av din favoritdryck och fortsätt.
Om dina loggningsbehov är enkla kan du använda exemplen ovan för att integrera loggning i dina egna skript, och om du stöter på problem eller inte förstår något kan du ställa en fråga i Help-kategorin i diskussionsforumet Python så får du hjälp inom kort.
Är du fortfarande kvar? Du kan fortsätta att läsa de kommande avsnitten, som ger en något mer avancerad/fördjupad handledning än den grundläggande ovan. Efter det kan du ta en titt på Kokbok för loggning.
Handledning för avancerad loggning¶
Loggningsbiblioteket är modulärt uppbyggt och erbjuder flera kategorier av komponenter: loggrar, hanterare, filter och formaterare.
Loggar exponerar det gränssnitt som applikationskoden använder direkt.
Hanterare skickar loggposter (som skapats av loggar) till lämplig destination.
Filter ger en mer finfördelad möjlighet att bestämma vilka loggposter som ska matas ut.
Formatterare specificerar layouten för loggposter i den slutliga utskriften.
Information om logghändelser skickas mellan loggar, hanterare, filter och formaterare i en LogRecord
-instans.
Loggning utförs genom att anropa metoder på instanser av klassen Logger
(hädanefter kallad loggers). Varje instans har ett namn, och de är konceptuellt ordnade i en namnrymdshierarki med punkter (punkter) som separatorer. Till exempel är en logger med namnet ”scan” överordnad loggrarna ”scan.text”, ”scan.html” och ”scan.pdf”. Loggernamn kan vara vad som helst och anger det område i en applikation där ett loggat meddelande har sitt ursprung.
Ett bra sätt att namnge loggar är att använda en logger på modulnivå, i varje modul som använder loggning, som namnges enligt följande:
logger = loggning.getLogger(__name__)
Det innebär att loggarnas namn följer paket-/modulhierarkin och att det är intuitivt uppenbart var händelser loggas bara genom loggarnas namn.
Roten i hierarkin av loggrar kallas root logger. Det är den logger som används av funktionerna debug()
, info()
, warning()
, error()
och critical()
, som bara anropar rotloggerns metod med samma namn. Funktionerna och metoderna har samma signaturer. Rotloggarens namn skrivs ut som ’root’ i den loggade utdata.
Det är naturligtvis möjligt att logga meddelanden till olika destinationer. Stöd ingår i paketet för att skriva loggmeddelanden till filer, HTTP GET/POST-platser, e-post via SMTP, generiska socklar, köer eller OS-specifika loggmekanismer som syslog eller Windows NT-händelseloggen. Destinationerna betjänas av handler-klasser. Du kan skapa en egen klass för loggdestinationer om du har särskilda krav som inte uppfylls av någon av de inbyggda hanterarklasserna.
Som standard anges ingen destination för loggade meddelanden. Du kan ange en destination (t.ex. konsol eller fil) genom att använda basicConfig()
som i självstudieexemplen. Om du anropar funktionerna debug()
, info()
, warning()
, error()
och critical()
kommer de att kontrollera om någon destination har angetts; och om ingen destination har angetts kommer de att ange en destination för konsolen (sys.stderr
) och ett standardformat för det visade meddelandet innan de delegerar till rotloggaren att göra den faktiska utmatningen av meddelandet.
Standardformatet som anges av basicConfig()
för meddelanden är:
allvarlighetsgrad:loggarens namn:meddelande
Du kan ändra detta genom att skicka en formatsträng till basicConfig()
med nyckelordsargumentet format. För alla alternativ för hur en formatsträng konstrueras, se Formateringsobjekt.
Loggning av flöde¶
Flödet av information om logghändelser i loggers och handlers illustreras i följande diagram.
Loggers¶
Logger
-objekt har en trefaldig uppgift. För det första exponerar de flera metoder för programkod så att programmen kan logga meddelanden under körning. För det andra bestämmer loggerobjekten vilka loggmeddelanden som ska hanteras baserat på allvarlighetsgrad (standardfiltreringsmöjligheten) eller filterobjekt. För det tredje vidarebefordrar loggerobjekt relevanta loggmeddelanden till alla intresserade logghanterare.
De mest använda metoderna för loggerobjekt kan delas in i två kategorier: konfiguration och sändning av meddelanden.
Dessa är de vanligaste konfigurationsmetoderna:
Logger.setLevel()
anger den lägsta allvarlighetsgraden för loggmeddelanden som en logger ska hantera, där debug är den lägsta inbyggda allvarlighetsgraden och critical är den högsta inbyggda allvarlighetsgraden. Om allvarlighetsgraden till exempel är INFO, kommer loggern endast att hantera INFO-, WARNING-, ERROR- och CRITICAL-meddelanden och ignorera DEBUG-meddelanden.Logger.addHandler()
ochLogger.removeHandler()
lägger till och tar bort hanterarobjekt från loggerobjektet. Handläggare beskrivs mer i detalj i Handläggare.Logger.addFilter()
ochLogger.removeFilter()
lägger till och tar bort filterobjekt från loggerobjektet. Filter beskrivs mer i detalj i Filtrera objekt.
Du behöver inte alltid anropa dessa metoder för varje logger som du skapar. Se de två sista styckena i detta avsnitt.
När loggerobjektet har konfigurerats kan följande metoder skapa loggmeddelanden:
Logger.debug()
,Logger.info()
,Logger.warning()
,Logger.error()
ochLogger.critical()
skapar alla loggposter med ett meddelande och en nivå som motsvarar deras respektive metodnamn. Meddelandet är egentligen en formatsträng, som kan innehålla standardsträngsyntaxen för substitution av%s
,%d
,%f
och så vidare. Resten av argumenten är en lista med objekt som motsvarar substitutionsfälten i meddelandet. När det gäller**kwargs
bryr sig loggningsmetoderna bara om ett nyckelord iexc_info
och använder det för att avgöra om undantagsinformation ska loggas.Logger.exception()
skapar ett loggmeddelande som liknarLogger.error()
. Skillnaden är attLogger.exception()
dumpar en stack trace tillsammans med det. Anropa denna metod endast från en undantagshanterare.Logger.log()
tar en loggnivå som ett explicit argument. Detta är en lite mer utförlig metod för att logga meddelanden än att använda de bekvämlighetsmetoder för loggnivåer som anges ovan, men det är så här man loggar på anpassade loggnivåer.
getLogger()
returnerar en referens till en loggerinstans med det angivna namnet om det finns, eller root
om så inte är fallet. Namnen är periodseparerade hierarkiska strukturer. Flera anrop till getLogger()
med samma namn kommer att returnera en referens till samma loggerobjekt. Loggrar som ligger längre ned i den hierarkiska listan är barn till loggrar högre upp i listan. Om man till exempel har en logger med namnet foo
, är loggrar med namnen foo.bar
, foo.bar.baz
och foo.bam
alla ättlingar till foo
.
Loggrar har ett begrepp som kallas effektiv nivå. Om en nivå inte uttryckligen har angetts för en logger används istället nivån för dess förälder som dess effektiva nivå. Om föräldern inte har någon explicit nivå inställd undersöks dess förälder, och så vidare - alla förfäder genomsöks tills en explicit inställd nivå hittas. Rotloggaren har alltid en explicit nivå inställd (WARNING
som standard). När man bestämmer om en händelse ska behandlas används loggarens effektiva nivå för att avgöra om händelsen ska skickas vidare till loggarens hanterare.
Barnloggare sprider meddelanden upp till de hanterare som är associerade med deras förfädersloggare. Därför är det inte nödvändigt att definiera och konfigurera hanterare för alla loggrar som ett program använder. Det räcker med att konfigurera hanterare för en logger på högsta nivån och skapa underordnade loggrar efter behov. (Du kan dock stänga av propagering genom att ställa in attributet propagate för en logger till False
)
Handläggare¶
Handler
-objekt är ansvariga för att skicka lämpliga loggmeddelanden (baserat på loggmeddelandets allvarlighetsgrad) till hanterarens angivna destination. Logger
-objekt kan lägga till noll eller fler hanterarobjekt till sig själva med en addHandler()
-metod. Som ett exempel på ett scenario kan en applikation vilja skicka alla loggmeddelanden till en loggfil, alla loggmeddelanden med fel eller högre till stdout och alla kritiska meddelanden till en e-postadress. Detta scenario kräver tre individuella hanterare där varje hanterare ansvarar för att skicka meddelanden med en viss allvarlighetsgrad till en viss plats.
Standardbiblioteket innehåller en hel del handläggartyper (se Användbara handläggare); i handledningarna används huvudsakligen StreamHandler
och FileHandler
i exemplen.
Det finns väldigt få metoder i en hanterare som applikationsutvecklare behöver bry sig om. De enda hanterarmetoder som verkar relevanta för programutvecklare som använder de inbyggda hanterarobjekten (dvs. inte skapar egna hanterare) är följande konfigurationsmetoder:
Metoden
setLevel()
anger, precis som i loggerobjekt, den lägsta allvarlighetsgrad som ska skickas till lämplig destination. Varför finns det tvåsetLevel()
-metoder? Den nivå som anges i loggern bestämmer vilken allvarlighetsgrad meddelanden ska ha för att skickas vidare till dess hanterare. Den nivå som anges i varje hanterare avgör vilka meddelanden som hanteraren ska skicka vidare.setFormatter()
väljer ett Formatter-objekt som denna hanterare ska använda.addFilter()
ochremoveFilter()
konfigurerar respektive avkonfigurerar filterobjekt på handlers.
Programkod bör inte direkt instansiera och använda instanser av Handler
. Istället är klassen Handler
en basklass som definierar det gränssnitt som alla handläggare ska ha och fastställer ett visst standardbeteende som underordnade klasser kan använda (eller åsidosätta).
Formatterare¶
Formateringsobjekt konfigurerar den slutliga ordningen, strukturen och innehållet i loggmeddelandet. Till skillnad från basklassen logging.Handler
kan programkod instansiera formateringsklasser, även om du sannolikt kan underklassa formateringsklassen om ditt program behöver ett speciellt beteende. Konstruktören tar emot tre valfria argument – en meddelandeformatsträng, en datumformatsträng och en stilindikator.
- logging.Formatter.__init__(fmt=None, datefmt=None, style='%')¶
Om det inte finns någon formatsträng för meddelandet är standardvärdet att använda det obearbetade meddelandet. Om det inte finns någon sträng för datumformat är standarddatumformatet:
%Y-%m-%d %H:%M:%S
med millisekunderna tillagda i slutet. style
är en av '%'
, '{'
, eller '$'
. Om något av dessa inte anges kommer '%'
att användas.
Om style
är '%'
, använder meddelandeformatsträngen %(<dictionary key>)s
stiliserad strängersättning; de möjliga nycklarna finns dokumenterade i LogRecord-attribut. Om stilen är '{'
antas meddelandeformatsträngen vara kompatibel med str.format()
(med nyckelordsargument), medan om stilen är '$'
bör meddelandeformatsträngen överensstämma med vad som förväntas av string.Template.substitute()
.
Ändrad i version 3.2: Lagt till parametern style
.
Följande meddelandeformatsträng loggar tiden i ett mänskligt läsbart format, meddelandets allvarlighetsgrad och innehållet i meddelandet, i den ordningen:
'%(asctime)s - %(levelname)s - %(message)s'
Formaterare använder en användarkonfigurerbar funktion för att konvertera skapelsetiden för en post till en tupel. Som standard används time.localtime()
; för att ändra detta för en viss formateringsinstans, ställ in attributet converter
för instansen till en funktion med samma signatur som time.localtime()
eller time.gmtime()
. Om du vill ändra det för alla formaterare, t.ex. om du vill att alla loggningstider ska visas i GMT, ställer du in attributet converter
i klassen Formatter (till time.gmtime
för GMT-visning).
Konfigurera loggning¶
Programmerare kan konfigurera loggning på tre olika sätt:
Skapa loggrar, hanterare och formaterare explicit med Python-kod som anropar konfigurationsmetoderna som listas ovan.
Skapar en konfigurationsfil för loggning och läser den med hjälp av funktionen
fileConfig()
.Skapar en ordbok med konfigurationsinformation och skickar den till funktionen
dictConfig()
.
För referensdokumentation om de två sista alternativen, se Funktioner för konfiguration. Följande exempel konfigurerar en mycket enkel logger, en konsolhanterare och en enkel formaterare med hjälp av Python-kod:
import logging
# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
Om du kör den här modulen från kommandoraden får du följande resultat:
$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - felsökningsmeddelande
2005-03-19 15:10:26,620 - simple_example - INFO - infomeddelande
2005-03-19 15:10:26,695 - simple_example - WARNING - varningsmeddelande
2005-03-19 15:10:26,697 - simple_example - ERROR - felmeddelande
2005-03-19 15:10:26,773 - simple_example - CRITICAL - kritiskt meddelande
Följande Python-modul skapar en logger, en hanterare och en formaterare som är nästan identiska med dem i exemplet ovan, med den enda skillnaden att objekten heter:
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
Här är filen logging.conf:
[loggar]
keys=root,enkeltExempel
[hanterare]
keys=konsolhanterare
[formatterare]
nycklar=simpleFormatter
[logger_root]
nivå=DEBUG
hanterare=konsolHanterare
[logger_simpleExample]
nivå=DEBUG
hanterare=konsolhanterare
kvalnamn=enkeltExempel
sprida=0
[handler_consoleHandler]
class=StreamHandler
nivå=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
Resultatet är nästan identiskt med det icke-konfig-filbaserade exemplet:
$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debugmeddelande
2005-03-19 15:38:55,979 - simpleExample - INFO - infomeddelande
2005-03-19 15:38:56,054 - simpleExample - WARNING - varningsmeddelande
2005-03-19 15:38:56,055 - simpleExample - ERROR - felmeddelande
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - kritiskt meddelande
Du kan se att metoden med konfigurationsfiler har några fördelar jämfört med Python-kodmetoden, främst separering av konfiguration och kod och möjligheten för icke-kodare att enkelt ändra loggningsegenskaperna.
Varning
Funktionen fileConfig()
tar en standardparameter, disable_existing_loggers
, som är satt till True
av bakåtkompatibilitetsskäl. Det här kanske inte är vad du vill, eftersom det kommer att leda till att alla loggar som inte är rotloggar och som fanns före fileConfig()
-anropet inaktiveras om de (eller en förfader) inte uttryckligen namnges i konfigurationen. Se referensdokumentationen för mer information och ange False
för den här parametern om du vill.
Den ordbok som skickas till dictConfig()
kan också ange ett booleskt värde med nyckeln disable_existing_loggers
, som om det inte anges explicit i ordboken också som standard tolkas som True
. Detta leder till att loggern inaktiveras på det sätt som beskrivs ovan, vilket kanske inte är vad du vill - i så fall ska du ange nyckeln explicit med värdet False
.
Observera att klassnamnen som refereras till i konfigurationsfiler måste vara antingen relativa till logningsmodulen eller absoluta värden som kan lösas med hjälp av normala importmekanismer. Du kan alltså använda antingen WatchedFileHandler
(relativt till logningsmodulen) eller mypackage.mymodule.MyHandler
(för en klass som definieras i paketet mypackage
och modulen mymodule
, där mypackage
finns tillgänglig på Pythons importväg).
I Python 3.2 har ett nytt sätt att konfigurera loggning införts, där man använder ordböcker för att lagra konfigurationsinformation. Detta ger en överuppsättning av funktionaliteten i det config-filbaserade tillvägagångssätt som beskrivs ovan och är den rekommenderade konfigurationsmetoden för nya applikationer och driftsättningar. Eftersom en Python-ordbok används för att lagra konfigurationsinformation, och eftersom du kan fylla i ordboken på olika sätt, har du fler alternativ för konfigurationen. Du kan t.ex. använda en konfigurationsfil i JSON-format eller, om du har tillgång till YAML-bearbetningsfunktioner, en fil i YAML-format för att fylla i konfigurationsordboken. Du kan naturligtvis också konstruera ordlistan i Python-kod, ta emot den i inlagd form via en socket eller använda den metod som passar bäst för din applikation.
Här är ett exempel på samma konfiguration som ovan, i YAML-format för det nya ordboksbaserade tillvägagångssättet:
version: 1
formaterare:
enkel:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
hanterare:
console:
klass: logging.StreamHandler
nivå: DEBUG
formaterare: enkel
ström: ext://sys.stdout
loggar:
simpleExample:
nivå: DEBUG
hanterare: [konsol]
sprida: nej
root:
nivå: DEBUG
hanterare: [konsol]
Mer information om loggning med hjälp av en ordbok finns i Funktioner för konfiguration.
Vad händer om ingen konfiguration anges?¶
Om ingen loggningskonfiguration anges kan det uppstå en situation där en loggningshändelse måste matas ut, men inga hanterare kan hittas för att mata ut händelsen.
Händelsen matas ut med hjälp av en ”sista utvägens hanterare”, som lagras i lastResort
. Denna interna hanterare är inte associerad med någon logger, utan fungerar som en StreamHandler
som skriver ut händelsebeskrivningsmeddelandet till det aktuella värdet för sys.stderr
(och därmed respekterar eventuella omdirigeringar som kan vara i kraft). Ingen formatering görs på meddelandet - bara det rena händelsebeskrivningsmeddelandet skrivs ut. Hanterarens nivå är inställd på WARNING
, så alla händelser med denna och högre allvarlighetsgrad kommer att skrivas ut.
Ändrad i version 3.2: För versioner av Python före 3.2 är beteendet som följer:
Om
raiseExceptions
ärFalse
(produktionsläge), avbryts händelsen i tysthet.Om
raiseExceptions
ärTrue
(utvecklingsläge), skrivs meddelandet ”No handlers could be found for logger X.Y.Z” ut en gång.
För att få samma beteende som före 3.2 kan lastResort
sättas till None
.
Konfigurera loggning för ett bibliotek¶
När du utvecklar ett bibliotek som använder loggning bör du vara noga med att dokumentera hur biblioteket använder loggning, t.ex. namnen på de loggar som används. Viss hänsyn måste också tas till dess loggningskonfiguration. Om programmet inte använder loggning och bibliotekskoden gör anrop till loggning kommer (som beskrivs i föregående avsnitt) händelser av allvarlighetsgraden WARNING
och högre att skrivas ut till sys.stderr
. Detta anses vara det bästa standardbeteendet.
Om du av någon anledning inte vill att dessa meddelanden ska skrivas ut utan någon loggningskonfiguration kan du koppla en ”do-nothing”-hanterare till toppnivåloggaren för ditt bibliotek. Detta gör att meddelandet inte skrivs ut, eftersom en hanterare alltid kommer att hittas för bibliotekets händelser: den producerar bara inte någon utdata. Om biblioteksanvändaren konfigurerar loggning för applikationsanvändning kommer förmodligen den konfigurationen att lägga till några hanterare, och om nivåerna är lämpligt konfigurerade kommer loggningsanrop som görs i bibliotekskoden att skicka utdata till dessa hanterare, som vanligt.
En hanterare som inte gör något ingår i loggningspaketet: NullHandler
(sedan Python 3.1). En instans av denna hanterare kan läggas till i toppnivåloggaren i det loggningsnamnrum som används av biblioteket (om du vill förhindra att bibliotekets loggade händelser matas ut till sys.stderr
i avsaknad av loggningskonfiguration). Om all loggning av ett bibliotek foo görs med hjälp av loggar med namn som matchar ’foo.x’, ’foo.x.y’, etc. så är koden:
import logging
logging.getLogger('foo').addHandler(logging.NullHandler())
bör ha önskad effekt. Om en organisation producerar ett antal bibliotek kan det angivna loggernamnet vara ”orgname.foo” i stället för bara ”foo”.
Anteckning
Det rekommenderas starkt att du inte loggar till rotloggaren i ditt bibliotek. Använd i stället en logger med ett unikt och lätt identifierbart namn, t.ex. __name__
för bibliotekets paket eller modul på högsta nivån. Om du loggar till rotloggaren blir det svårt eller omöjligt för programutvecklaren att konfigurera loggningens ordalydelse eller hanterare i ditt bibliotek som de vill.
Anteckning
Det rekommenderas starkt att du inte lägger till några andra hanterare än NullHandler
till ditt biblioteks loggrar. Detta beror på att konfigurationen av hanterare är förbehållen den programutvecklare som använder ditt bibliotek. Applikationsutvecklaren känner sin målgrupp och vet vilka hanterare som är lämpligast för deras applikation: om du lägger till hanterare ”under huven” kan du mycket väl störa deras förmåga att utföra enhetstester och leverera loggar som uppfyller deras krav.
Loggningsnivåer¶
De numeriska värdena för loggningsnivåerna anges i följande tabell. Dessa är främst intressanta om du vill definiera dina egna nivåer och vill att de ska ha specifika värden i förhållande till de fördefinierade nivåerna. Om du definierar en nivå med samma numeriska värde, skriver den över det fördefinierade värdet; det fördefinierade namnet försvinner.
Nivå |
Numeriskt värde |
---|---|
”KRITISK |
50 |
|
40 |
”VARNING |
30 |
|
20 |
|
10 |
|
0 |
Nivåer kan också kopplas till loggrar, antingen genom att de ställs in av utvecklaren eller genom att en sparad loggningskonfiguration laddas. När en loggmetod anropas på en logger jämför loggern sin egen nivå med den nivå som är associerad med metodanropet. Om loggarens nivå är högre än metodanropets genereras inget loggmeddelande. Detta är den grundläggande mekanismen för att kontrollera loggutmatningens ordrikedom.
Loggningsmeddelanden kodas som instanser av klassen LogRecord
. När en logger bestämmer sig för att faktiskt logga en händelse skapas en instans av LogRecord
från loggmeddelandet.
Loggade meddelanden är föremål för en avsändningsmekanism genom användning av handlers, som är instanser av underklasser av Handler
-klassen. Hanterare ansvarar för att ett loggat meddelande (i form av en LogRecord
) hamnar på en viss plats (eller en uppsättning platser) som är användbar för målgruppen för meddelandet (t.ex. slutanvändare, supportpersonal, systemadministratörer, utvecklare). Hanterare får LogRecord
-instanser som är avsedda för särskilda destinationer. Varje logger kan ha noll, en eller flera handläggare associerade med sig (via metoden addHandler()
i Logger
). Förutom alla handläggare som är direkt associerade med en logger, anropas alla handläggare som är associerade med alla förfäder till loggern för att skicka meddelandet (såvida inte flaggan propagate för en logger är satt till ett falskt värde, då slutar överföringen till förfädernas handläggare).
Precis som för loggar kan hanterare ha nivåer associerade med sig. En hanterares nivå fungerar som ett filter på samma sätt som en loggares nivå gör. Om en hanterare bestämmer sig för att faktiskt skicka en händelse används metoden emit()
för att skicka meddelandet till dess destination. De flesta användardefinierade subklasser av Handler
kommer att behöva åsidosätta denna emit()
.
Anpassade nivåer¶
Det är möjligt att definiera egna nivåer, men det bör inte vara nödvändigt, eftersom de befintliga nivåerna har valts utifrån praktisk erfarenhet. Om du är övertygad om att du behöver egna nivåer bör du dock vara mycket försiktig när du gör detta, och det är möjligen en mycket dålig idé att definiera egna nivåer om du utvecklar ett bibliotek. Om flera biblioteksförfattare definierar sina egna anpassade nivåer finns det nämligen en risk att loggningsutdata från flera bibliotek som används tillsammans blir svåra att kontrollera och/eller tolka för den utvecklare som använder dem, eftersom ett visst numeriskt värde kan betyda olika saker för olika bibliotek.
Användbara handläggare¶
Förutom basklassen Handler
finns många användbara underklasser:
StreamHandler
-instanser skickar meddelanden till strömmar (filliknande objekt).FileHandler
-instanser skickar meddelanden till diskfiler.BaseRotatingHandler
är basklassen för handlers som roterar loggfiler vid en viss punkt. Det är inte meningen att den ska instansieras direkt. Använd iställetRotatingFileHandler
ellerTimedRotatingFileHandler
.RotatingFileHandler
-instanser skickar meddelanden till diskfiler, med stöd för maximal loggfilsstorlek och rotation av loggfiler.TimedRotatingFileHandler
-instanser skickar meddelanden till diskfiler och roterar loggfilen med vissa tidsintervaller.SocketHandler
-instanser skickar meddelanden till TCP/IP-socklar. Sedan 3.4 stöds även Unix domain sockets.DatagramHandler
-instanser skickar meddelanden till UDP-sockets. Sedan 3.4 stöds även Unix domain sockets.SMTPHandler
instanser skickar meddelanden till en angiven e-postadress.SysLogHandler
-instanser skickar meddelanden till en Unix syslog-daemon, eventuellt på en fjärrmaskin.NTEventLogHandler
-instanser skickar meddelanden till en händelselogg i Windows NT/2000/XP.MemoryHandler
-instanser skickar meddelanden till en buffert i minnet, som rensas när specifika kriterier uppfylls.HTTPHandler
instanser skickar meddelanden till en HTTP-server med antingenGET
ellerPOST
semantik.WatchedFileHandler
-instanser övervakar den fil de loggar till. Om filen ändras stängs den och öppnas igen med hjälp av filnamnet. Denna hanterare är endast användbar på Unix-liknande system; Windows stöder inte den underliggande mekanism som används.QueueHandler
-instanser skickar meddelanden till en kö, t.ex. de som implementeras i modulernaqueue
ellermultiprocessing
.NullHandler
-instanser gör ingenting med felmeddelanden. De används av biblioteksutvecklare som vill använda loggning, men som vill undvika meddelandet ”No handlers could be found for logger XXX” som kan visas om biblioteksanvändaren inte har konfigurerat loggning. Se Konfigurera loggning för ett bibliotek för mer information.
Tillagd i version 3.1: Klassen NullHandler
.
Tillagd i version 3.2: Klassen QueueHandler
.
Klasserna NullHandler
, StreamHandler
och FileHandler
definieras i kärnpaketet för loggning. De andra hanterarna definieras i en undermodul, logging.handlers
. (Det finns också en annan undermodul, logging.config
, för konfigurationsfunktioner)
Loggade meddelanden formateras för presentation genom instanser av klassen Formatter
. De initialiseras med en formatsträng som är lämplig att använda med % operatorn och en ordbok.
För formatering av flera meddelanden i en batch kan instanser av BufferingFormatter
användas. Förutom formatsträngen (som används för varje meddelande i batchen) finns det möjlighet att använda formatsträngar för header och trailer.
När filtrering baserad på loggar- och/eller hanterarnivå inte är tillräcklig kan instanser av Filter
läggas till i både Logger
och Handler
(genom deras addFilter()
-metod). Innan de bestämmer sig för att bearbeta ett meddelande vidare, frågar både loggar och handlare alla sina filter om tillstånd. Om något filter returnerar ett falskt värde bearbetas inte meddelandet vidare.
Den grundläggande Filter
-funktionen gör det möjligt att filtrera efter ett specifikt loggarnamn. Om den här funktionen används tillåts meddelanden som skickas till den namngivna loggern och dess underordnade loggar att passera genom filtret, medan alla andra meddelanden släpps igenom.
Undantag som uppstår under loggning¶
Loggningspaketet är utformat så att det kan hantera undantag som uppstår vid loggning i produktion. Detta för att fel som uppstår vid hanteringen av loggningshändelser - t.ex. felkonfigurering av loggning, nätverksfel eller andra liknande fel - inte ska leda till att programmet som använder loggning avslutas i förtid.
undantagen SystemExit
och KeyboardInterrupt
sväljs aldrig. Andra undantag som inträffar under emit()
-metoden i en Handler
-underklass skickas till dess handleError()
-metod.
Standardimplementationen av handleError()
i Handler
kontrollerar om variabeln på modulnivå, raiseExceptions
, är inställd. Om den är inställd skrivs en spårning ut till sys.stderr
. Om variabeln inte är inställd slukas undantaget.
Anteckning
Standardvärdet för raiseExceptions
är True
. Detta beror på att du under utveckling vanligtvis vill bli underrättad om eventuella undantag som inträffar. Det rekommenderas att du ställer in raiseExceptions
till False
för produktionsanvändning.
Använda godtyckliga objekt som meddelanden¶
I de föregående avsnitten och exemplen har det antagits att det meddelande som skickas när händelsen loggas är en sträng. Detta är dock inte den enda möjligheten. Du kan skicka ett godtyckligt objekt som ett meddelande, och dess __str__()
-metod kommer att anropas när loggningssystemet behöver konvertera det till en strängrepresentation. Om du vill kan du faktiskt undvika att beräkna en strängrepresentation helt och hållet - till exempel avger SocketHandler
en händelse genom att plocka upp den och skicka den över kabeln.
Optimering¶
Formatering av meddelandeargument skjuts upp tills det inte kan undvikas. Men det kan också vara dyrt att beräkna de argument som skickas till loggarmetoden, och du kanske vill undvika att göra det om loggern bara kommer att kasta bort din händelse. För att bestämma vad du ska göra kan du anropa metoden isEnabledFor()
som tar ett nivåargument och returnerar true om händelsen skulle skapas av Logger för den anropsnivån. Du kan skriva kod så här:
if logger.isEnabledFor(logging.DEBUG):
logger.debug('Meddelande med %s, %s', expensive_func1(),
expensive_func2())
så att anropen till expensive_func1
och expensive_func2
aldrig görs om loggarens tröskelvärde är högre än DEBUG
.
Anteckning
I vissa fall kan isEnabledFor()
i sig vara dyrare än du vill (t.ex. för djupt nästlade loggrar där en explicit nivå bara sätts högt upp i loggerhierarkin). I sådana fall (eller om du vill undvika att anropa en metod i täta loopar) kan du cacha resultatet av ett anrop till isEnabledFor()
i en lokal variabel eller instansvariabel och använda det istället för att anropa metoden varje gång. Ett sådant cachat värde skulle bara behöva beräknas på nytt när loggningskonfigurationen ändras dynamiskt medan programmet körs (vilket inte är så vanligt).
Det finns andra optimeringar som kan göras för specifika applikationer som behöver mer exakt kontroll över vilken loggningsinformation som samlas in. Här är en lista över saker du kan göra för att undvika bearbetning under loggning som du inte behöver:
Vad du inte vill samla in |
Hur man undviker att samla in det |
---|---|
Information om varifrån samtalen gjordes. |
Sätt |
Information om gängning. |
Ställ in |
ID för aktuell process ( |
Sätt |
Aktuellt processnamn när |
Sätt |
Nuvarande |
Ställ in |
Observera också att kärnmodulen för loggning bara innehåller de grundläggande hanterarna. Om du inte importerar logging.handlers
och logging.config
kommer de inte att ta upp något minne.
Övriga resurser¶
Se även
- Modul
logging
API-referens för loggningsmodulen.
- Modul
logging.config
Konfigurations-API för loggningsmodulen.
- Modul
logging.handlers
Användbara hanterare som ingår i loggningsmodulen.