日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python Logging日志记录模块详解

發(fā)布時間:2024/1/23 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python Logging日志记录模块详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

寫在篇前

??logging是Python的一個標準庫,其中定義的函數(shù)和類為應(yīng)用程序和庫的開發(fā)實現(xiàn)了一個靈活的事件日志系統(tǒng)。Python logging 的配置由四個部分組成:Logger、Handlers、Filter、Formatter。本篇博客將依次介紹這四個主要部分以及l(fā)ogging的基本應(yīng)用。

??在開始之前,我們有必要先了解一下,什么時候我們才有必要使用logging模塊,什么時候拋出異常(raise Exception),什么時候使用簡單的print函數(shù)即可,官方文檔給我們總結(jié)了一個表格:

?

Task you want to performThe best tool for the task
命令行終端輸出或則程序一般的輸出情景print()
報告程序正常運行期間發(fā)生的事件(例如,狀態(tài)監(jiān)視或故障調(diào)查)logging.info()(或則 logging.debug() 用于診斷目的細節(jié)輸出)
發(fā)出有關(guān)特定運行時事件的警告warnings.warn() : 用于如果問題是可以避免的,且應(yīng)修改應(yīng)用程序以消除警告的情景;logging.warning()用于如果應(yīng)用程序無法處理該情況,但仍應(yīng)注意該事件的情景。
報告有關(guān)特定運行時事件的錯誤Raise an exception
報告在不引發(fā)異常的情況下抑制錯誤(例如,長時間運行的服務(wù)器進程中的錯誤處理程序)logging.error(), logging.exception()或則logging.critical() 適用于特定錯誤和應(yīng)用程序域

Loggers

??logger是暴露給代碼進行日志操作的接口。需要注意的是,logger不應(yīng)該直接實例化,而應(yīng)通過模塊級函數(shù)logging.getLogger(name)創(chuàng)建。如果name是具有層級結(jié)構(gòu)的命名方式,則logger之間也會有層級關(guān)系。如name為foo.bar,foo.bar.baz, foo.bam 的logger是foo的子孫,默認子logger日志會向父logger傳播,可以通過logger.propagate=False禁止;對具有相同名稱的getLogger()的多次調(diào)用將始終返回同一Logger對象的引用。logger對象的功能包括以下三項:

  • 向應(yīng)用程序暴露info、debug等方法,用于程序運行時進行日志記錄;
  • 根據(jù)log level(默認過濾工具)或Filter對象確定要處理的日志消息;
  • 將日志消息傳遞給所有感興趣的Log Handler;
  • ??另外,需要理清以下兩個概念:

  • Log Record

    Each message that is written to the Logger is a Log Record. 可以使用makeLogRecord()函數(shù)創(chuàng)建record對象(一般用不上),record對象常用的屬性如下,全部屬性請參考官方文檔:

    • record.levelname # record level
    • record.levelno # record level number
    • record.msg # record承載的日志消息
    • record.pathname # emit該日志消息的程序文件
    • record.lineno # emit該日志消息的程序行號
    • record.getMessage() # 同record.msg

    ?從python 3.2起,logging模塊提供了工廠函數(shù)getLogRecordFactory() 和setLogRecordFactory()方便、支持用戶自定義record屬性。

    old_factory = logging.getLogRecordFactory()def record_factory(*args, **kwargs):record = old_factory(*args, **kwargs)record.custom_attribute = 0xdecafbadreturn recordlogging.setLogRecordFactory(record_factory)
  • Log Level

    ??每個logger都需要設(shè)置一個log level,該log level描述了logger將處理日志消息的級別; 每個log record也具有一個log Level,指示該特定日志消息的級別。 log record還可以包含被記錄事件的metadata,包括諸如堆棧跟蹤或錯誤代碼之類的詳細信息。如果設(shè)置了logger的log level,系統(tǒng)便只會輸出 level 數(shù)值大于或等于該 level 的的日志結(jié)果,例如我們設(shè)置了輸出日志 level 為 INFO,那么輸出級別小于 INFO 的日志,如DEBUG 和 NOSET 級別的消息不會輸出。logger還有一個effective level的概念,如果logger沒有顯式設(shè)置log level,則使用其parent logger的log level作為其effective level,其中root logger的默認log level是WARNING。

    • NOTEST:lowest level

      # Level Num 0logger.setLevel(level=logging.NOTEST)
    • DEBUG: Low level system information for debugging purposes

      # Level Num 10logger.setLevel(level=logging.DEBUGlogger.debug('Debugging')
    • INFO: General system information

      # Level Num 20logger.setLevel(level=logging.INFO)logger.info('This is a log info')
    • WARNING: Information describing a minor problem that has occurred.

      # Level Num 30,默認是該levellogger.setLevel(level=logging.WARNING)logger.warning('Warning exists')logger.warn('Warning exists') # deprecated
    • ERROR: Information describing a major problem that has occurred.

      # Level Num 40logger.setLevel(level=logging.ERROR)logger.error('some error occur')
    • CRITICAL: Information describing a critical problem that has occurred.

      # Level Num 50logger.setLevel(level=logging.NOTEST)logger.critical('critical err occur')logger.fatal('fatal error occur')
  • ??上面各個函數(shù)還有一個統(tǒng)一的調(diào)用方式logger.log(level, msg, exc_info=True),其中,level是指上面標注的level num,exc_info指示是否打印執(zhí)行信息;Logger.exception()創(chuàng)建與 Logger.error()相似的日志消息,不同之處是, Logger.exception()同時還記錄當前的堆棧追蹤,請僅從異常處理程序調(diào)用此方法。

    ??logger對象還有一些其他屬性、方法值得關(guān)注:

    >>>logger = logging.getLogger('main') # 如果不指定name將會返回root logger >>>logger.setLevel(level=logging.DEBUG)>>>logger.disabled # False >>>logger.propagate=False >>>logger.getEffectiveLevel() # 10 >>>logger.isEnabledFor(20) # True>>>logging.disable(20) >>>logger.isEnabledFor(20) # False >>>logger.isEnabledFor(30) # True>>>child_logger = logger.getChild('def.ghi') # <Logger main.def.ghi (DEBUG)># Filter addFilter(filter) removeFilter(filter)# handler logger.hasHandlers() # False addHandler(hdlr) removeHandler(hdlr)

    ??關(guān)于上面各種Level的調(diào)用情景,官方文檔也給出了相應(yīng)說明:

    LevelWhen it’s used
    DEBUGDetailed information, typically of interest only when diagnosing problems.
    INFOConfirmation that things are working as expected.
    WARNINGAn indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
    ERRORDue to a more serious problem, the software has not been able to perform some function.
    CRITICALA serious error, indicating that the program itself may be unable to continue running.

    Handlers

    ?Once a logger has determined that a message needs to be processed, it is passed to a Handler. The handler is the engine that determines what happens to each message in a logger. Like loggers, handlers also have a log level. If the log level of a log record doesn’t meet or exceed the level of the handler, the handler will ignore the message.

    ??handler對象負責將適當?shù)娜罩鞠?#xff08;基于日志消息的log level)分發(fā)給handler的指定目標,如文件、標準輸出流等。logging模塊中主要定義了以下Handlers,其中StreamHandler和FileHandler最為常用。

    logging.StreamHandler # 日志輸出到流,可以是 sys.stderr,sys.stdout 或者文件。 logging.FileHandler # 日志輸出到文件。 logging.handlers.BaseRotatingHandler # 基本的日志回滾方式。 logging.handlers.RotatingHandler # 日志回滾方式,支持日志文件最大數(shù)量和日志文件回滾。 logging.handlers.TimeRotatingHandler # 日志回滾方式,在一定時間區(qū)域內(nèi)回滾日志文件。 logging.handlers.SocketHandler # 遠程輸出日志到TCP/IP sockets。 logging.handlers.DatagramHandler # 遠程輸出日志到UDP sockets。 logging.handlers.SMTPHandler # 遠程輸出日志到郵件地址。 logging.handlers.SysLogHandler # 日志輸出到syslog。 logging.handlers.NTEventLogHandler # 遠程輸出日志到Windows NT/2000/XP的事件日志。 logging.handlers.MemoryHandler # 日志輸出到內(nèi)存中的指定buffer。 logging.handlers.HTTPHandler # 通過”GET”或者”POST”遠程輸出到HTTP服務(wù)器。 logging.NullHandler

    ??以FileHandler為例,創(chuàng)建Handler對象并設(shè)置log Level

    >>> handler = logging.FileHandler('result.log') >>> handler.setLevel(logging.DEBUG) >>> handler.setFormatter(fmt) # fmt是一個Formatter對象,下面再講# Filter addFilter(filter) removeFilter(filter)

    Filters

    ?A filter is used to provide additional control over which log records are passed from logger to handler. Filters can be installed on loggers or on handlers; multiple filters can be used in a chain to perform multiple filtering actions.

    ??logging標準庫只提供了一個base Filter,其構(gòu)造函數(shù)__init__接收name參數(shù),默認的行為是名為name的logger極其子logger的消息能通過過濾,其余皆會被濾掉。當然我們也可以根據(jù)具體的業(yè)務(wù)邏輯自定義Filter,并且也非常簡單,只需要繼承l(wèi)ogging.Filter類重寫filter方法即可,filter方法接收record對象作為參數(shù),返回True代表通過過濾,返回False表示該record被過濾。

    import logginglogger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) handler = logging.StreamHandler() handler.setLevel(logging.DEBUG) handler.set_name('output-log') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',datefmt='%Y/%m/%d %H:%M:%S',style='%')class MyFilter(logging.Filter):def filter(self, record):if 'result' in record.msg:return Falsereturn Truehandler.setFormatter(formatter) handler.addFilter(MyFilter('aa')) logger.addHandler(handler)try:result = 10 / 0 except:logger.error('Faild to get result')logger.error('Faild')# 輸出: 2019/07/13 20:28:51 - __main__ - ERROR - Faild

    ??從python3.2起,自定義filter也可以不繼承l(wèi)ogging.Filter,只需要定義一個函數(shù)并同樣綁定即可:

    def _filter(record):if 'result' in record.msg:return Falsereturn Truehandler.addFilter(_filter)

    Formatters

    ??Formatter用來規(guī)定Log record文本的格式,其使用python formatting string來規(guī)定具體格式。在默認情況下,logging模塊的輸出格式如下:

    import logging logging.warning('%s before you %s', 'Look', 'leap!')# WARNING:root:Look before you leap!

    ??但是,默認的輸出格式不一定能滿足我們的需求,我們可以通過Formatter自定義輸出格式,在日志中添加更多豐富的信息,一些常見的項(實際上以下都是Log Record的屬性)如下所示:

    %(levelno)s:打印日志級別的數(shù)值。%(levelname)s:打印日志級別的名稱。%(pathname)s:打印當前執(zhí)行程序的路徑,其實就是sys.argv[0]。%(filename)s:打印當前執(zhí)行程序名。%(funcName)s:打印日志的當前函數(shù)。%(lineno)d:打印日志的當前行號。%(asctime)s:打印日志的時間。%(thread)d:打印線程ID。%(threadName)s:打印線程名稱。%(process)d:打印進程ID。%(processName)s:打印線程名稱。%(module)s:打印模塊名稱。%(message)s:打印日志信息。

    ??設(shè)置Formatter主要包括兩種方式,一種是通過Formatter類構(gòu)建Formatter實例,并將其綁定到特定的handler上;一種是通過logging.basicConfig設(shè)置:

    import loging import time# 1 logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(funcName)s - %(message)s',datefmt='%Y/%m/%d %H:%M:%S',style='%' # '%', ‘{‘ or ‘$’) # 2 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',datefmt='%Y/%m/%d %H:%M:%S',style='%') formatter.converter = time.localtime() formatter.converter = time.gmtime()

    ??datefmt的設(shè)置,請參考time.strftime()

    logging config

    basicConfig

    ??使用logging模塊的接口函數(shù)basicConfig可以非常方便的進行基本的配置。其中需要注意兩點

    • 該函數(shù)stream 、filename以及handlers這三個參數(shù)是互斥的。
    • logging.basicConfig函數(shù)是一個one-off simple configuration facility,只有第一次調(diào)用會有效,并且它的調(diào)用應(yīng)該在任何日志記錄事件之前。
    import logginglogging.basicConfig(filename='./log.log',filemode='w',format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(funcName)s - %(message)s',datefmt='%Y/%m/%d %H:%M:%S',style='%',level=logging.DEBUG) logger = logging.getLogger(__name__)try:result = 10 / 0 except:logger.error('Faild to get result', exc_info=True)logger.log(50, 'logging critical test')

    stepByStepConfig

    ??這種設(shè)計方式條理清晰,但是會但麻煩一點點,配置的邏輯就是:一個logger可以有多個handler;每個handler可以有一個Formatter;handler和logger都需要設(shè)置一個log Level;根據(jù)需要logger和handler都可以添加多個Filter。

    import logginglogger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO)handler = logging.FileHandler('output.log', mode='a', encoding=None, delay=False) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',datefmt='%Y/%m/%d %H:%M:%S',style='%') handler.setFormatter(formatter) handler.setLevel(logging.DEBUG) handler.set_name('output-log') logger.addHandler(handler) try:result = 10 / 0 except:logger.error('Faild to get result', exc_info=True)

    fileConfig

    ??通過配置文件來配置logging模塊,這是web應(yīng)用中比較常見的一種設(shè)置方式

    import logging import logging.configlogging.config.fileConfig('logging.conf', disable_existing_loggers=True)# 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') [loggers] keys=root,simpleExample[handlers] keys=consoleHandler[formatters] keys=simpleFormatter[logger_root] level=DEBUG # can be one of DEBUG, INFO, WARNING, ERROR, CRITICAL or NOTSET handlers=consoleHandler[logger_simpleExample] level=DEBUG handlers=consoleHandler qualname=simpleExample propagate=0[handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,)[formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=

    ??fileConfig文件的解讀主要基于configparser,它必須包含 [loggers], [handlers] 和 [formatters] section。這是一個比較老的API,不支持配置Filter,估計后面也不會更新了,所以建議大家使用dictConfig。

    dictConfig

    ??通過dictConfig配置即可以通過python代碼構(gòu)建一個dict對象,也可以通過yaml、JSON文件來進行配置。字典中必須傳入的參數(shù)是version,而且目前有效的值也只有1。其他可選的參數(shù)包括以下:

    • formatters

      對于formatter,主要搜尋format和datefmt參數(shù),用來構(gòu)造Formatter實例。

    • filters

      對于filter,主要搜尋name參數(shù)(默認為空字符串),用來構(gòu)造Formatter實例。

    • handlers

      對于handlers,主要包括以下參數(shù),其他的參數(shù)將作為關(guān)鍵字參數(shù)傳遞到handlers的構(gòu)造函數(shù)

      • class (mandatory). This is the fully qualified name of the handler class.
      • level (optional). The level of the handler.
      • formatter (optional). The id of the formatter for this handler.
      • filters (optional). A list of ids of the filters for this handler.
      handlers:console:class : logging.StreamHandlerformatter: brieflevel : INFOfilters: [allow_foo]stream : ext://sys.stdoutfile:class : logging.handlers.RotatingFileHandlerformatter: precisefilename: logconfig.logmaxBytes: 1024backupCount: 3
    • loggers

      對于loggers,主要包括以下參數(shù):

      • level (optional). The level of the logger.
      • propagate (optional). The propagation setting of the logger.
      • filters (optional). A list of ids of the filters for this logger.
      • handlers (optional). A list of ids of the handlers for this logger.
    • root

      這是給root logger的配置項

    • incremental

      是否將此配置文件解釋為現(xiàn)有配置的增量, 默認為False

    • disable_existing_loggers

      是否要禁用現(xiàn)有的非 root logger,默認為True

    ??以下給出一個較為完成的YAML示例,注意體會loggers, handlers, formatters, filters之間的關(guān)聯(lián)性,該配置文件定義了brief和simple兩種formatter;定義了console、file、error三個handler,其中console使用brief formatter,file和error使用simple formatter;main.core logger使用file和error handler,root logger使用console handler:

    version: 1 formatters:brief:format: "%(asctime)s - %(message)s"simple:format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" handlers:console:class : logging.StreamHandlerformatter: brieflevel : INFOstream : ext://sys.stdoutfile:class : logging.FileHandlerformatter: simplelevel: DEBUGfilename: debug.logerror:class: logging.handlers.RotatingFileHandlerlevel: ERRORformatter: simplefilename: error.logmaxBytes: 10485760backupCount: 20encoding: utf8 loggers:main.core:level: DEBUGhandlers: [file, error] root:level: DEBUGhandlers: [console]

    ??將上面的configuration setup可以通過以下方法:

    import logging import yaml import logging.config import osdef setup_logging(default_path='config.yaml', default_level=logging.INFO):if os.path.exists(default_path):with open(default_path, 'r', encoding='utf-8') as f:config = yaml.load(f)logging.config.dictConfig(config)else:logging.basicConfig(level=default_level)

    ??dictConfig通過將dict數(shù)據(jù)傳遞給dictConfigClass,然后返回對象調(diào)用configure函數(shù)使配置生效。

    def dictConfig(config):"""Configure logging using a dictionary."""dictConfigClass(config).configure()
    User-defined objects

    ??logging模塊 dictConfig為了支持handlers,filters和formatters的用戶自定義對象,可以通過助記符()指定一個工廠函數(shù)來實例化自定義對象,下面定義的 custom formatter相當于my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)

    formatters:brief:format: '%(message)s'default:format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'datefmt: '%Y-%m-%d %H:%M:%S'custom:(): my.package.customFormatterFactorybar: bazspam: 99.9answer: 42
    Access to external objects

    ??logging模塊 dictConfig為了支持鏈接外部objects,如sys.stdout,可以使用ext://sys.stderr。內(nèi)部原理是進行正則匹配^(?P<prefix>[a-z]+)://(?P<suffix>.*)$,如果prefix有意義,則會按照prefix預(yù)定義的方式處理suffix;否則保持字符串原樣。

    Access to internal objects

    ??logging模塊 dictConfig為了支持鏈接配置文件內(nèi)部objects,首先,比如logger或則handler中的level值設(shè)置DEBUG,配置系統(tǒng)會自動地將其轉(zhuǎn)換成logging.DEBUG;但是,對于logging模塊不知道的用戶定義對象,需要一種更通用的機制來完成,比如我自定義一個handler,這個handler又需要另一個handler來代理,那么可以這樣定義:

    handlers:file:# configuration of file handler goes herecustom:(): my.package.MyHandleralternate: cfg://handlers.file

    ??舉一個更復雜的例子,如下面文件所定義YAML配置文件,則cfg://handlers.email.toaddrs[0]會被解析到其值為support_team@domain.tld;subject的值可以通過cfg://handlers.email.subject或者cfg://handlers.email[subject]拿到。

    handlers:email:class: logging.handlers.SMTPHandlermailhost: localhostfromaddr: my_app@domain.tldtoaddrs:- support_team@domain.tld- dev_team@domain.tldsubject: Houston, we have a problem.

    Optimization

    ??當你不想收集以下信息時,你可以對你的日志記錄系統(tǒng)進行一定的優(yōu)化:

    你不想收集的內(nèi)容如何避免收集它
    有關(guān)調(diào)用來源的信息將 logging._srcfile 設(shè)置為 None 。這避免了調(diào)用 sys._getframe(),如果 PyPy 支持 Python 3.x ,這可能有助于加速 PyPy (無法加速使用了sys._getframe()的代碼)等環(huán)境中的代碼.
    線程信息將 logging.logThreads 置為 0 。
    進程信息將 logging.logProcesses 置為 0 。

    ??另外,核心的logging模塊只包含基本的handlers,如果你不顯式導入 logging.handlers 和 logging.config ,它們將不會占用任何內(nèi)存。

    實例運用

    ??logging模塊的落腳點當然是實際項目中的運用,比如對于簡單的程序可以參考以下的使用方式,先在一個模塊中創(chuàng)建并定義好root logger,在其他模塊中調(diào)用get_logger函數(shù)創(chuàng)建其子logger。

    import logginglogger = logging.getLogger('logger') logger.propagate = False # wii not pass log messages on to logging.root and its handler logger.setLevel('INFO') logger.addHandler(logging.StreamHandler()) # Logs go to stderr logger.handlers[-1].setFormatter(logging.Formatter('%(message)s')) logger.handlers[-1].setLevel('INFO')def get_logger(name):"""Creates a child logger that delegates to anndata_logger instead to logging.root"""return logger.manager.getLogger(name)

    ??對于更加復雜的場景,用戶也可以重載logging模塊提供的logger類logging.Logger或者logging.RootLogger的自定義logging Level,實現(xiàn)更加靈活的日志記錄功能。比如,以下定義一個新的HINT log Level,通過繼承重寫logging.RootLogger獲得自定義的Logger類。

    import loggingHINT = (INFO + DEBUG) // 2 logging.addLevelName(HINT, 'HINT')class RootLogger(logging.RootLogger):def __init__(self, level):super().__init__(level)self.propagate = FalseRootLogger.manager = logging.Manager(self)def log(self,level: int,msg: str,*,extra: Optional[dict] = None,time: datetime = None,deep: Optional[str] = None,) -> datetime:now = datetime.now(timezone.utc)time_passed: timedelta = None if time is None else now - timeextra = {**(extra or {}),'deep': deep,'time_passed': time_passed}super().log(level, msg, extra=extra)return nowdef critical(self, msg, *, time=None, deep=None, extra=None) -> datetime:return self.log(CRITICAL, msg, time=time, deep=deep, extra=extra)def error(self, msg, *, time=None, deep=None, extra=None) -> datetime:return self.log(ERROR, msg, time=time, deep=deep, extra=extra)def warning(self, msg, *, time=None, deep=None, extra=None) -> datetime:return self.log(WARNING, msg, time=time, deep=deep, extra=extra)def info(self, msg, *, time=None, deep=None, extra=None) -> datetime:return self.log(INFO, msg, time=time, deep=deep, extra=extra)def hint(self, msg, *, time=None, deep=None, extra=None) -> datetime:return self.log(HINT, msg, time=time, deep=deep, extra=extra)def debug(self, msg, *, time=None, deep=None, extra=None) -> datetime:return self.log(DEBUG, msg, time=time, deep=deep, extra=extra)

    寫在篇后

    ??本篇博客基本上涵蓋了python logging模塊大部分的功能,但是也有一些尚未cover。比如logging模塊會默認吞噬除了SystemExit和KeyboardInterrupt的一切異常,因為logging.raiseExceptions默認為True(生產(chǎn)環(huán)境也推薦設(shè)置為True);logging.captureWarnings(capture=True)會重定向warning信息到logging模塊;另外,可以通過logging.setLoggerClass()決定初始化logger的類型,與之對應(yīng)的有l(wèi)ogging.getLoggerClass()

    等等,更多的用法在實踐中再慢慢總結(jié)經(jīng)驗,吸取教訓。

    總結(jié)

    以上是生活随笔為你收集整理的python Logging日志记录模块详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。