溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

logger模塊怎么在python中使用

發(fā)布時(shí)間:2021-01-20 16:06:24 來(lái)源:億速云 閱讀:237 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)logger模塊怎么在python中使用,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

logging模塊介紹

Python的logging模塊提供了通用的日志系統(tǒng),熟練使用logging模塊可以方便開(kāi)發(fā)者開(kāi)發(fā)第三方模塊或者是自己的Python應(yīng)用。同樣這個(gè)模塊提供不同的日志級(jí)別,并可以采用不同的方式記錄日志,比如文件,HTTP、GET/POST,SMTP,Socket等,甚至可以自己實(shí)現(xiàn)具體的日志記錄方式。下文我將主要介紹如何使用文件方式記錄log。

logging模塊包括logger,handler,filter,formatter這四個(gè)基本概念。

logging模塊與log4j的機(jī)制是一樣的,只是具體的實(shí)現(xiàn)細(xì)節(jié)不同。模塊提供logger,handler,filter,formatter。

logger:提供日志接口,供應(yīng)用代碼使用。logger最長(zhǎng)用的操作有兩類:配置和發(fā)送日志消息??梢酝ㄟ^(guò)logging.getLogger(name)獲取logger對(duì)象,如果不指定name則返回root對(duì)象,多次使用相同的name調(diào)用getLogger方法返回同一個(gè)logger對(duì)象。

handler:將日志記錄(log record)發(fā)送到合適的目的地(destination),比如文件,socket等。一個(gè)logger對(duì)象可以通過(guò)addHandler方法添加0到多個(gè)handler,每個(gè)handler又可以定義不同日志級(jí)別,以實(shí)現(xiàn)日志分級(jí)過(guò)濾顯示。

filter:提供一種優(yōu)雅的方式?jīng)Q定一個(gè)日志記錄是否發(fā)送到handler。

formatter:指定日志記錄輸出的具體格式。formatter的構(gòu)造方法需要兩個(gè)參數(shù):消息的格式字符串和日期字符串,這兩個(gè)參數(shù)都是可選的。

與log4j類似,logger,handler和日志消息的調(diào)用可以有具體的日志級(jí)別(Level),只有在日志消息的級(jí)別大于logger和handler的級(jí)別。

import logging
logging.basicConfig(level=logging.DEBUG,
    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%a, %d %b %Y %H:%M:%S',
    filename='myapp.log',
    filemode='w')
#################################################################################################
#定義一個(gè)StreamHandler,將INFO級(jí)別或更高的日志信息打印到標(biāo)準(zhǔn)錯(cuò)誤,并將其添加到當(dāng)前的日志處理對(duì)象#
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
#################################################################################################
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
 
屏幕上打印:
root  : INFO  This is info message
root  : WARNING This is warning message
./myapp.log文件中內(nèi)容為:
Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message

logging.StreamHandler: 日志輸出到流,可以是sys.stderr、sys.stdout或者文件

logging.FileHandler: 日志輸出到文件

日志回滾方式,實(shí)際使用時(shí)用RotatingFileHandler和TimedRotatingFileHandler

logging.handlers.BaseRotatingHandler

logging.handlers.RotatingFileHandler

logging.handlers.TimedRotatingFileHandler

logging.handlers.SocketHandler: 遠(yuǎn)程輸出日志到TCP/IP sockets

logging.handlers.DatagramHandler: 遠(yuǎn)程輸出日志到UDP sockets

logging.handlers.SMTPHandler: 遠(yuǎn)程輸出日志到郵件地址

logging.handlers.SysLogHandler: 日志輸出到syslog

logging.handlers.NTEventLogHandler: 遠(yuǎn)程輸出日志到Windows NT/2000/XP的事件日志

logging.handlers.MemoryHandler: 日志輸出到內(nèi)存中的制定buffer

logging.handlers.HTTPHandler: 通過(guò)"GET"或"POST"遠(yuǎn)程輸出到HTTP服務(wù)器

import logging
import sys
# 獲取logger實(shí)例,如果參數(shù)為空則返回root logger
logger = logging.getLogger("AppName")
# 指定logger輸出格式
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')
# 文件日志
file_handler = logging.FileHandler("test.log")
file_handler.setFormatter(formatter) # 可以通過(guò)setFormatter指定輸出格式
# 控制臺(tái)日志
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter # 也可以直接給formatter賦值
# 為logger添加的日志處理器,可以自定義日志處理器讓其輸出到其他地方
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 指定日志的最低輸出級(jí)別,默認(rèn)為WARN級(jí)別
logger.setLevel(logging.INFO)
# 輸出不同級(jí)別的log
logger.debug('this is debug info')
logger.info('this is information')
logger.warn('this is warning message')
logger.error('this is error message')
logger.fatal('this is fatal message, it is same as logger.critical')
logger.critical('this is critical message')
# 2016-10-08 21:59:19,493 INFO : this is information
# 2016-10-08 21:59:19,493 WARNING : this is warning message
# 2016-10-08 21:59:19,493 ERROR : this is error message
# 2016-10-08 21:59:19,493 CRITICAL: this is fatal message, it is same as logger.critical
# 2016-10-08 21:59:19,493 CRITICAL: this is critical message
# 移除一些日志處理器
logger.removeHandler(file_handler)

python:logging模塊

10 DECEMBER 2015

概述

python的logging模塊(logging是線程安全的)給應(yīng)用程序提供了標(biāo)準(zhǔn)的日志信息輸出接口。logging不僅支持把日志輸出到文件,還支持把日志輸出到TCP/UDP服務(wù)器,EMAIL服務(wù)器,HTTP服務(wù)器,UNIX的syslog系統(tǒng)等。在logging中主要有四個(gè)概念:logger、handler、filter和formatter,下面會(huì)分別介紹。

logger

Logger對(duì)象扮演了三重角色:

它給應(yīng)用程序暴漏了幾個(gè)方法,以便應(yīng)用程序能在運(yùn)行時(shí)記錄日志。

Logger對(duì)象根據(jù)日志的級(jí)別或根據(jù)Filter對(duì)象,來(lái)決定記錄哪些日志。

Logger對(duì)象負(fù)責(zé)把日志信息傳遞給相關(guān)的handler。

在Logger對(duì)象中,最常使用的方法分為兩類:configuration,message sending。 configuration方法包括:

setLevel(level)

setLevel(level)方法用來(lái)設(shè)置logger的日志級(jí)別,如果日志的級(jí)別低于setLevel(level)方法設(shè)置的值,那么logger不會(huì)處理它。logging模塊內(nèi)建的日志級(jí)別有:

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0 (數(shù)值越大級(jí)別越高)
addFilter(filter)
removeFilter(filter)
addHandler(handler)
removeHandler(handler)

message sending方法包括:

debug(log_message, [*args[, **kwargs]])

使用DEBUG級(jí)別,記錄log_message % args。

為了記錄異常信息,需要將關(guān)鍵字參數(shù)exc_info設(shè)置為一個(gè)true值。

比如:

logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
info(log_message, [*args[, **kwargs]])

使用INFO級(jí)別,記錄log_message % args。

為了記錄異常信息,需要將關(guān)鍵字參數(shù)exc_info設(shè)置為一個(gè)true值。

比如:

logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
warning(log_message, [*args[, **kwargs]])

使用WARNING級(jí)別,記錄log_message % args。

為了記錄異常信息,需要將關(guān)鍵字參數(shù)exc_info設(shè)置為一個(gè)true值。

比如:

logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1)
error(log_message, [*args[, **kwargs]])

使用Error級(jí)別,記錄log_message % args。

為了記錄異常信息,需要將關(guān)鍵字參數(shù)exc_info設(shè)置為一個(gè)true值。

比如:

logger.error("Houston, we have a %s", "major problem", exc_info=1)
critical(log_message, [*args[, **kwargs]])

使用CRITICAL級(jí)別,記錄log_message % args。

為了記錄異常信息,需要將關(guān)鍵字參數(shù)exc_info設(shè)置為一個(gè)true值。

比如:

logger.critical("Houston, we have a %s", "major disaster", exc_info=1)
exception(message[, *args]) 
self.error(*((msg,) + args), **{'exc_info': 1})
log(log_level, log_message, [*args[, **kwargs]])

使用整型的級(jí)別level,記錄log_message % args。

為了記錄異常信息,需要將關(guān)鍵字參數(shù)exc_info設(shè)置為一個(gè)true值。

比如:

logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
logging.getLogger([name])

方法返回一個(gè)Logger實(shí)例的引用,如果提供了name參數(shù),那么它就是這個(gè)Logger實(shí)例的名稱,如果沒(méi)提供name參數(shù),那么這個(gè)Logger實(shí)例的名稱是root。

可以通過(guò)Logger實(shí)例的name屬性,來(lái)查看Logger實(shí)例的名稱。

Logger實(shí)例的名稱是使用句號(hào)(.)分隔的多級(jí)結(jié)構(gòu)。

在這種命名方式中,后面的logger是前面的logger的子(父子logger只是簡(jiǎn)單的通過(guò)命名來(lái)識(shí)別),比如:有一個(gè)名稱為foo的logger,那么諸如foo.bar、foo.bar.baz和foo.bam這樣的logger都是foo這個(gè)logger的子logger。

子logger會(huì)自動(dòng)繼承父logger的定義和配置。

使用相同的名稱多次調(diào)用logging.getLogger([name])方法,會(huì)返回同一個(gè)logger對(duì)象的引用。

這個(gè)規(guī)則不僅僅在同一個(gè)module有效,而且對(duì)在同一個(gè)Python解釋器進(jìn)程的多個(gè)module也有效。

因此應(yīng)用程序可以在一個(gè)module中定義一個(gè)父logger,然后在其他module中繼承這個(gè)logger,而不必把所有的logger都配置一遍。

handler

handler實(shí)例負(fù)責(zé)把日志事件分發(fā)到具體的目的地。logger對(duì)象可以使用addHandler()方法,添加零個(gè)或多個(gè)handler對(duì)象到它自身。一個(gè)常見(jiàn)的場(chǎng)景是:應(yīng)用程序可能希望把所有的日志都記錄到一個(gè)log文件,所有的ERROR及以上級(jí)別的日志都記錄到stdout,所有的CRITICAL級(jí)別的日志都發(fā)送到一個(gè)email地址。這個(gè)場(chǎng)景需要三個(gè)獨(dú)立的handler,每個(gè)handler負(fù)責(zé)把特定級(jí)別的日志發(fā)送到特定的地方。

下面是logging模塊內(nèi)置的handler:

StreamHandler
FileHandler
RotatingFileHandler
TimedRotatingFileHandler
SocketHandler
DatagramHandler
SysLogHandler
NTEventLogHandler
SMTPHandler
MemoryHandler
HTTPHandler

內(nèi)置的handler提供了下面的配置方法:

setLevel(level)

handler對(duì)象的setLevel()方法,與logger對(duì)象的setLevel()方法一樣,也是用于設(shè)置一個(gè)日志級(jí)別,如果日志的級(jí)別低于

setLevel()方法設(shè)置的值,那么handler不會(huì)處理它。

setFormatter(formatter)

addFilter(filter)

removeFilter(filter)

應(yīng)用程序代碼不應(yīng)該直接實(shí)例化和使用handler。logging.Handler是一個(gè)定義了所有的handler都應(yīng)該實(shí)現(xiàn)的接口和建立了子類能夠使用(或重寫)的一些默認(rèn)行為的基類。

自定義Handler 自定義的handler必須繼承自logging.Handler,且實(shí)現(xiàn)下面的方法:

class Handler(Filterer): 
 def emit(self, record):
  """
  Do whatever it takes to actually log the specified logging record.
  This version is intended to be implemented by subclasses and so
  raises a NotImplementedError.
  """
  raise NotImplementedError, 'emit must be implemented '\
         'by Handler subclasses'
 def flush(self):
  """
  Ensure all logging output has been flushed.
  This version does nothing and is intended to be implemented by
  subclasses.
  """
  pass
 def close(self):
  """ 
  Tidy up any resources used by the handler.
  This version does removes the handler from an internal list
  of handlers which is closed when shutdown() is called. Subclasses
  should ensure that this gets called from overridden close()
  methods.
  """
  #get the module data lock, as we're updating a shared structure.
  _acquireLock()
  try: #unlikely to raise an exception, but you never know...
   if self in _handlers:
    del _handlers[self]
   if self in _handlerList:
    _handlerList.remove(self)
  finally:
   _releaseLock()

其中,emit(record)方法負(fù)責(zé)執(zhí)行真正地記錄日志所需的一切事情,在logging.Handler的子類中必須實(shí)現(xiàn)這個(gè)方法。close()方法負(fù)責(zé)清理handler所使用的資源(在Python解釋器退出的時(shí)候,會(huì)調(diào)用所有的handler的flush()和close()方法),logging.Handler的子類應(yīng)該確保在重寫close()方法的時(shí)候,調(diào)用父類的該方法。

下面分析logging.StreamHandler的源代碼:

class StreamHandler(Handler): 
 def __init__(self, strm=None):
  Handler.__init__(self)
  if strm is None:
   strm = sys.stderr
  self.stream = strm
 def flush(self):
  if self.stream and hasattr(self.stream, "flush"):
   self.stream.flush()
 def emit(self, record):
  try:
   msg = self.format(record)
   stream = self.stream
   fs = "%s\n"
   if not hasattr(types, "UnicodeType"): #if no unicode support...
    stream.write(fs % msg)
   else:
    try:
     if (isinstance(msg, unicode) and
      getattr(stream, 'encoding', None)):
      fs = fs.decode(stream.encoding)
      try:
       stream.write(fs % msg)
      except UnicodeEncodeError:
       #Printing to terminals sometimes fails. For example,
       #with an encoding of 'cp1251', the above write will
       #work if written to a stream opened or wrapped by
       #the codecs module, but fail when writing to a
       #terminal even when the codepage is set to cp1251.
       #An extra encoding step seems to be needed.
       stream.write((fs % msg).encode(stream.encoding))
     else:
      stream.write(fs % msg)
    except UnicodeError:
     stream.write(fs % msg.encode("UTF-8"))
   self.flush()
  except (KeyboardInterrupt, SystemExit):
   raise
  except:
   self.handleError(record)

在構(gòu)造函數(shù)中,如果提供了strm參數(shù),那么它就是要輸出到的流,如果沒(méi)提供,那么就會(huì)將日志輸出到標(biāo)準(zhǔn)錯(cuò)誤輸出流sys.stderr。

flush()方法的作用是:刷新self.stream內(nèi)部的I/O緩沖區(qū)。每次emit日志之后都會(huì)調(diào)用這個(gè)方法,將日志從I/O緩沖區(qū)sync到self.stream。

emit(record)方法的作用是:將LogRecord對(duì)象(record)記錄到self.stream。emit(record)方法首先調(diào)用基類logging.Handler提供的format(record)方法,該方法會(huì)根據(jù)設(shè)置的Formatter對(duì)象來(lái)格式化record對(duì)象,得到要記錄的字符串msg。然后對(duì)fs(fs其實(shí)就是在msg的尾部增加一個(gè)換行'\n')進(jìn)行一系列的編碼解碼,將它寫入到self.stream。最后再刷新self.stream。在emit(record)調(diào)用期間發(fā)生的異常,應(yīng)該調(diào)用logging.Handler提供的handleError(record)方法來(lái)處理。

filter

Filter對(duì)象用于對(duì)LogRecord對(duì)象執(zhí)行過(guò)濾,logger和handler都可以使用filter來(lái)過(guò)濾record。下面用一個(gè)列子來(lái)說(shuō)明Filter基類的作用:

如果使用A.B實(shí)例化一個(gè)filter,那么它允許名稱為A.B,A.B.C,A.B.C.D這樣的logger記錄的日志通過(guò),不允許名稱為A.BB,B.A.B這樣的logger記錄的日志通過(guò)。

如果使用空字符串實(shí)例化一個(gè)filter,那么它允許所有的事件通過(guò)。

Filter基類有一個(gè)方法叫filter(record),它用來(lái)決定指定的record(LogRecord對(duì)象)是否被記錄。如果該方法返回0,則不記錄record;返回非0則記錄record。

Filterer(注意:不是Filter)是logger和handler的基類。它提供了方法來(lái)添加和刪除filter,并且提供了filter(record)方法用于過(guò)濾record,該方法默認(rèn)允許record被記錄,但是任何filter都可以否決這個(gè)默認(rèn)行為,如果想要丟棄record,filter(record)方法應(yīng)該返回0,否則應(yīng)該返回非0。

formatter

Formatter對(duì)象用于把一個(gè)LogRecord對(duì)象轉(zhuǎn)換成文本,它定義了日志的格式、結(jié)構(gòu)。與logging.Handler類不同,應(yīng)用程序可以直接實(shí)例化Formatter類,如果需要也可以子類化Formatter,以便定制一些行為。

Formatter的構(gòu)造函數(shù)接受兩個(gè)參數(shù):第一個(gè)參數(shù)是用于日志信息的格式化字符串;第二個(gè)參數(shù)是用于日期的格式化字符串。第二個(gè)參數(shù)可選的,默認(rèn)值是%Y-%m-%d %H:%M:%S。

日志信息的格式化字符串用%(<dictionary key>)s風(fēng)格的字符串做替換。

下面是替換字符串和它們所代表的含義:

%(name)s
logger的名稱
%(levelno)s
日志級(jí)別的數(shù)字表現(xiàn)形式
%(levelname)s
日志級(jí)別的文本表現(xiàn)形式
%(pathname)s
調(diào)用logging的源文件的全路徑名
%(filename)s
pathname的文件名部分
%(module)s
模塊名(filename的名稱部分)
%(lineno)d
調(diào)用logging的行號(hào)
%(funcName)s
函數(shù)名
%(created)f
LogRecord的創(chuàng)建時(shí)間(time.time()的返回值)
%(asctime)s
LogRecord的創(chuàng)建時(shí)間的文本表現(xiàn)形式
%(msecs)d
創(chuàng)建時(shí)間的毫秒部分
%(relativeCreated)d 
LogRecord的創(chuàng)建時(shí)間,單位是毫秒。這個(gè)時(shí)間是相對(duì)logging模塊被加載的時(shí)間的(通常就是應(yīng)用程序啟動(dòng)的時(shí)間)。
%(thread)d
線程ID
%(threadName)s
線程名稱
%(process)d
進(jìn)程ID
%(message)s
record.getMessage()的返回結(jié)果。

配置logging

下面是一個(gè)簡(jiǎn)單的例子,它會(huì)向標(biāo)準(zhǔn)輸出打印日志:

import logging 
import sys 
logger = logging.getLogger(__name__) 
filter = logging.Filter(__name__) 
formatter = logging.Formatter("%(asctime)s|%(name)-12s|%(message)s", "%F %T")
stream_handler = logging.StreamHandler(sys.stdout) 
stream_handler.addFilter(filter) 
stream_handler.setLevel(logging.DEBUG) 
stream_handler.setFormatter(formatter)
logger.setLevel(logging.DEBUG) 
logger.addFilter(filter) 
logger.addHandler(stream_handler)
if __name__ == "__main__": 
 logger.info("info")

運(yùn)行這個(gè)腳本,輸出結(jié)果是:

2015-12-16 13:52:17|__main__ |info

使用配置文件,配置logging

下面是一個(gè)使用配置文件,配置logging的例子:

import logging 
import logging.config
logging.config.fileConfig("logging.conf")
if __name__ == "__main__": 
 logger = logging.getLogger("test_logging.sublogger")
 logger.info("info")

logging.conf如下:

[loggers]
keys = root,logger
[handlers]
keys = stream_handler
[formatters]
keys = formatter
[logger_root]
handlers = stream_handler
[logger_logger]
handlers = stream_handler 
level = DEBUG 
propagate = 1 
qualname = test_logging
[handler_stream_handler]
class = StreamHandler 
args = (sys.stdout,) 
formatter = formatter 
level = DEBUG
[formatter_formatter]
format = %(asctime)s|%(name)-12s|%(message)s 
datefmt = %F %T

需要解釋的地方有兩處:第一個(gè)是logger_xxxsection中的propagate選項(xiàng),在logger對(duì)象把record傳遞給所有相關(guān)的handler的時(shí)候,會(huì)(逐級(jí)向上)尋找這個(gè)logger和它所有的父logger的全部handler。在尋找過(guò)程中,如果logger對(duì)象的propagate屬性被設(shè)置為1,那么就繼續(xù)向上尋找;如果某個(gè)logger的propagate屬性被設(shè)置為0,那么就會(huì)停止搜尋。

第二個(gè)是logger_xxxsection中的qualname選項(xiàng),它其實(shí)就是logger的名稱。

使用配置文件的時(shí)候,必須定義root logger。

最酷的listen(port)函數(shù)

logging.config.listen(port)函數(shù)可以讓應(yīng)用程序在一個(gè)socket上監(jiān)聽(tīng)新的配置信息,達(dá)到在運(yùn)行時(shí)改變配置,而不用重啟應(yīng)用程序的目的。

監(jiān)聽(tīng)程序:

import logging.config 
import logging 
import time
logging.config.fileConfig("logging.conf") 
logger = logging.getLogger("test_logging.listen")
t = logging.config.listen(9999) 
t.setDaemon(True) 
t.start()
try: 
 while True:
  logger.info('running.')
  time.sleep(3)
except (KeyboardInterrupt, SystemExit, Exception): 
 logging.config.stopListening()

發(fā)送新的配置信息程序:

import socket 
import struct
HOST = 'localhost' 
PORT = 9999 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect((HOST, PORT)) 
print "connected..." 
data_to_send = open("logging.conf").read() 
s.send(struct.pack(">L", len(data_to_send))) 
s.send(data_to_send) 
print "closing..." 
s.close()

關(guān)于logger模塊怎么在python中使用就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI