<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          1萬字詳解 python logging日志模塊

          共 7693字,需瀏覽 16分鐘

           ·

          2021-12-24 03:54

          前言

          這篇文章熬了一周,終于寫完了。一個知識點自己理解可能只需要花半個小時,而要想把它寫出來讓別人理解,要花十倍甚至更多的時間。所以說寫技術(shù)文是真的不容易。而它的價值在于它的生命力更長久。即使三五年后給別人看依然會有收獲。對寫作者自己而言,寫的過程也是對知識的一次更通透的理解。

          以下為正文

          說到日志,無論是寫框架代碼還是業(yè)務(wù)代碼,都離不開日志的記錄,他能給我們定位問題帶來極大的幫助。

          記錄日志最簡單的方法就是在你想要記錄的地方加上一句 print , 我相信無論是新手還是老鳥都經(jīng)常這么干。在簡單的代碼中或者小型項目中這么干一點問題都沒有。但是在一些稍大一點的項目,有時候定位一個問題,需要查看歷史日志定位問題,用print就不合時宜了。

          print 打印出來的日志沒有時間,不知道日志記錄的位置,也沒有可讀的日志格式, 還不能把日志輸出到指定文件。。。。除非這些你都全部自己重復(fù)造一遍輪子。

          最佳的做法是使用內(nèi)置的logging模塊, 因為 logging 模塊給開發(fā)者提供了非常豐富的功能。

          比如上圖就是用標(biāo)準(zhǔn)庫logging模塊記錄生成的日志,有日志的具體時間、日志發(fā)生的模塊、有日志級別和日志的具體內(nèi)容等等

          怎么用呢,來看個例子

          導(dǎo)入logging模塊,然后直接使用logging提供的日志消息記錄方法就可以。

          日志級別

          日志級別分為以下5個級別

          日志級別使用場景
          DEBUGdebug級別用來記錄詳細(xì)的信息,方便定位問題進(jìn)行調(diào)試,在生產(chǎn)環(huán)境我們一般不開啟DEBUG
          INFO用來記錄關(guān)鍵代碼點的信息,以便代碼是否按照我們預(yù)期的執(zhí)行,生產(chǎn)環(huán)境通常會設(shè)置INFO級別
          WARNING記錄某些不預(yù)期發(fā)生的情況,如磁盤不足
          ERROR由于一個更嚴(yán)重的問題導(dǎo)致某些功能不能正常運行時記錄的信息
          CRITICAL當(dāng)發(fā)生嚴(yán)重錯誤,導(dǎo)致應(yīng)用程序不能繼續(xù)運行時記錄的信息

          日志級別重要程度逐次提高,python提供了5個對應(yīng)級別的方法。默認(rèn)情況下日志的級別是WARGING, 低于WARING的日志信息都不會輸出。

          從上面代碼中可以看到loging.warging以后的日志內(nèi)容都打印在標(biāo)準(zhǔn)輸出流,也就是命令行窗口,但是logging.debug和info記錄的日志不會打印出來。

          修改日志級別

          如何讓debug級別的信息也輸出?

          當(dāng)然是修改默認(rèn)的日志級別,在開始記錄日志前可以使用logging.basicConfig方法來設(shè)定日志級別

          import?logging
          logging.basicConfig(?level=logging.DEBUG)
          logging.debug("this?is?debug")
          logging.info("this?is?info")
          logging.error("this?is?error")

          設(shè)置為debug級別后,所有的日志信息都會輸出

          DEBUG:root:this?is?debug
          INFO:root:this?is?info
          ERROR:root:this?is?error

          日志記錄到文件

          前面的日志默認(rèn)會把日志輸出到標(biāo)準(zhǔn)輸出流,就是只在命令行窗口輸出,程序重啟后歷史日志沒地方找,所以把日志內(nèi)容永久記錄是一個很常見的需求。同樣通過配置函數(shù)logging.basicConfig可以指定日志輸出到什么地方

          import?logging
          logging.basicConfig(filename="test.log",?level=logging.INFO)
          logging.debug("this?is?debug")
          logging.info("this?is?info")
          logging.error("this?is?error")

          這里我指定日志輸出到文件test.log中,日志級別指定為了 INFO,最后文件中記錄的內(nèi)容如下:

          INFO:root:this?is?info
          ERROR:root:this?is?error

          每次重新運行時,日志會以追加的方式在后面, 如果每次運行前要覆蓋之前的日志,則需指定 filemode='w', 這個和 open 函數(shù)寫數(shù)據(jù)到文件用的參數(shù)是一樣的。

          指定日志格式

          默認(rèn)輸出的格式包含3部分,日志級別,日志記錄器的名字,以及日志內(nèi)容,中間用“:”連接。如果我們想改變?nèi)罩靖袷剑缦爰尤肴掌跁r間、顯示日志器名字,我們是可以指定format參數(shù)來設(shè)置日志的格式

          import?logging
          logging.basicConfig(format='%(asctime)s?%(levelname)s?%(name)s?%(message)s')
          logging.error("this?is?error")

          輸出

          2021-12-15?07:44:16,547?ERROR?root?this?is?error

          日志格式化輸出提供了非常多的參數(shù),除了時間、日志級別、日志消息內(nèi)容、日志記錄器的名字外,還可以指定線程名,進(jìn)程名等等

          到這里為止,日志模塊的基本用法就這些了,也能滿足大部分應(yīng)用場景,更高級的方法接著往下看,可以幫助你更好的處理日志

          記錄器(logger)

          前面介紹的日志記錄,其實都是通過一個叫做日志記錄器(Logger)的實例對象創(chuàng)建的,每個記錄器都有一個名稱,直接使用logging來記錄日志時,系統(tǒng)會默認(rèn)創(chuàng)建 名為 root 的記錄器,這個記錄器是根記錄器。記錄器支持層級結(jié)構(gòu),子記錄器通常不需要單獨設(shè)置日志級別以及Handler(后面會介紹),如果子記錄器沒有單獨設(shè)置,則它的行為會委托給父級。

          記錄器名稱可以是任意名稱,不過最佳實踐是直接用模塊的名稱當(dāng)作記錄器的名字。命名如下

          logger?=?logging.getLogger(__name__)

          默認(rèn)情況下,記錄器采用層級結(jié)構(gòu),上句點作為分隔符排列在命名空間的層次結(jié)構(gòu)中。層次結(jié)構(gòu)列表中位于下方的記錄器是列表中較高位置的記錄器的子級。例如,有個名叫 foo 的記錄器,而名字是 foo.bar,foo.bar.baz,和 foo.bam 的記錄器都是 foo 的子級。

          ├─foo
          │??│??main.py
          │??│??__init__.py
          │??│??
          │??├─bam
          │??│??│??__init__.py
          │??│??│??
          │??│??????????
          │??├─bar
          │??│??│??__init__.py
          │??│??│??
          │??│??├─baz
          │??│??│??│??__init__.py
          │??│??│??│??

          main.py

          import?foo
          from?foo?import?bar
          from?foo?import?bam
          from?foo.bar?import?baz

          if?__name__?==?'__main__':
          ????pass

          foo.py

          import?logging

          logging.basicConfig()
          logger?=?logging.getLogger(__name__)
          logger.setLevel(logging.INFO)

          logger.info("this?is?foo")

          這里我只設(shè)置foo這個記錄器的級別為INFO

          bar.py

          import?logging

          logger?=?logging.getLogger(__name__)
          logger.info("this?is?bar")

          其它子模塊都是像bar.py一樣類似的代碼,都沒有設(shè)置日志級別,最后的輸出結(jié)果是

          INFO:foo:this?is?foo
          INFO:foo.bar:this?is?bar
          INFO:foo.bam:this?is?bam
          INFO:foo.bar.baz:this?is?baz

          這是因為 foo.bar 這個記錄器沒有設(shè)置日志級別,就會向上找到已經(jīng)設(shè)置了日日志級別的祖先,這里剛好找到父記錄器foo的級別為INFO,如果foo也沒設(shè)置的,就會找到根記錄器root,root默認(rèn)的級別為WARGING。

          處理器(Handler)

          記錄器負(fù)責(zé)日志的記錄,但是日志最終記錄在哪里記錄器并不關(guān)心,而是交給了另一個家伙--處理器(Handler)去處理。

          例如一個Flask項目,你可能會將INFO級別的日志記錄到文件,將ERROR級別的日志記錄到標(biāo)準(zhǔn)輸出,將某些關(guān)鍵日志(例如有訂單或者嚴(yán)重錯誤)發(fā)送到某個郵件地址通知老板。這時候你的記錄器添加多個不同的處理器來處理不同的消息日志,以此根據(jù)消息的重要性發(fā)送的特定的位置。

          Python內(nèi)置了很多實用的處理器,常用的有:

          1、StreamHandler 標(biāo)準(zhǔn)流處理器,將消息發(fā)送到標(biāo)準(zhǔn)輸出流、錯誤流
          2、FileHandler 文件處理器,將消息發(fā)送到文件
          3、RotatingFileHandler ?文件處理器,文件達(dá)到指定大小后,啟用新文件存儲日志
          4、TimedRotatingFileHandler 文件處理器,日志以特定的時間間隔輪換日志文件

          處理器操作

          Handler 提供了4個方法給開發(fā)者使用,細(xì)心的你可以發(fā)現(xiàn)了,logger可以設(shè)置level,Handler也可以設(shè)置Level。通過setLevel可以將記錄器記錄的不同級別的消息發(fā)送到不同的地方去。

          import?logging
          from?logging?import?StreamHandler
          from?logging?import?FileHandler

          logger?=?logging.getLogger(__name__)

          #?設(shè)置為DEBUG級別
          logger.setLevel(logging.DEBUG)

          #?標(biāo)準(zhǔn)流處理器,設(shè)置的級別為WARAING
          stream_handler?=?StreamHandler()
          stream_handler.setLevel(logging.WARNING)
          logger.addHandler(stream_handler)

          #?文件處理器,設(shè)置的級別為INFO
          file_handler?=?FileHandler(filename="test.log")
          file_handler.setLevel(logging.INFO)
          logger.addHandler(file_handler)

          logger.debug("this?is?debug")
          logger.info("this?is?info")
          logger.error("this?is?error")
          logger.warning("this?is?warning")

          運行后,在命令行窗口輸出的日志內(nèi)容是:

          this?is?error
          this?is?warning

          輸出在文件的日志內(nèi)容是:

          this is info
          this is error
          this is warning

          盡管我們將logger的級別設(shè)置為了DEBUG,但是debug記錄的消息并沒有輸出,因為我給兩個Handler設(shè)置的級別都比DEBUG要高,所以這條消息被過濾掉了。

          格式器(formatter)

          格式器在文章的前面部分其實已經(jīng)有所介紹,不過那是通過logging.basicConfig來指定的,其實格式器還可以以對象的形式來設(shè)置在Handler上。格式器可以指定日志的輸出格式,要不要展示時間,時間格式什么,要不要展示日志的級別,要不要展示記錄器的名字等等,都可以通過一個格式器對消息進(jìn)行格式化輸出。

          import?logging
          from?logging?import?StreamHandler

          logger?=?logging.getLogger(__name__)

          #?標(biāo)準(zhǔn)流處理器
          stream_handler?=?StreamHandler()
          stream_handler.setLevel(logging.WARNING)

          #?創(chuàng)建一個格式器
          formatter?=?logging.Formatter('%(asctime)s?-?%(name)s?-?%(levelname)s?-?%(message)s')
          #?作用在handler上
          stream_handler.setFormatter(formatter)
          #?添加處理器
          logger.addHandler(stream_handler)

          logger.info("this?is?info")
          logger.error("this?is?error")
          logger.warning("this?is?warning")

          注意,格式器只能作用在處理器上,通過處理器的setFromatter方法設(shè)置格式器。而且一個Handler只能設(shè)置一個格式器。是一對一的關(guān)系。而 logger 與 handler 是一對多的關(guān)系,一個logger可以添加多個handler。handler 和 logger 都可以設(shè)置日志的等級。

          logging.basicConfig

          回到最開始的地方,logging.basicConfig() 方法為我們干了啥?現(xiàn)在你大概能猜出來了。來看python源碼中是怎么說的

          Do basic configuration for the logging system.

          This function does nothing if the root logger already has handlers configured. It is a convenience method intended for use by simple scripts to do one-shot configuration of the logging package.

          The default behaviour is to create a StreamHandler which writes to sys.stderr, set a formatter using the BASIC_FORMAT format string, and add the handler to the root logger.

          A number of optional keyword arguments may be specified, which can alter the default behaviour.

          1、創(chuàng)建一個root記錄器
          2、設(shè)置root的日志級別為warning
          3、為root記錄器添加StreamHandler處理器
          4、為處理器設(shè)置一個簡單格式器

          logging.basicConfig()
          logging.warning("hello")

          這兩行代碼其實就等價于:

          import?sys
          import?logging
          from?logging?import?StreamHandler
          from?logging?import?Formatter


          logger?=?logging.getLogger("root")
          logger.setLevel(logging.WARNING)
          handler?=?StreamHandler(sys.stderr)
          logger.addHandler(handler)
          formatter?=?Formatter("?%(levelname)s:%(name)s:%(message)s")
          handler.setFormatter(formatter)
          logger.warning("hello")

          logging.basicConfig 方法做的事情是相當(dāng)于給日志系統(tǒng)做一個最基本的配置,方便開發(fā)者快速接入使用。它必須在開始記錄日志前調(diào)用。不過如果 root 記錄器已經(jīng)指定有其它處理器,這時候你再調(diào)用basciConfig,則該方式將失效,它什么都不做。

          日志配置

          日志的配置除了前面介紹的將配置直接寫在代碼中,還可以將配置信息單獨放在配置文件中,實現(xiàn)配置與代碼分離。

          日志配置文件 logging.conf

          [loggers]
          keys=root

          [handlers]
          keys=consoleHandler

          [formatters]
          keys=simpleFormatter

          [logger_root]
          level=DEBUG
          handlers=consoleHandler


          [handler_consoleHandler]
          class=StreamHandler
          level=DEBUG
          formatter=simpleFormatter
          args=(sys.stdout,)

          [formatter_simpleFormatter]
          format=%(asctime)s?-?%(name)s?-?%(levelname)s?-?%(message)s

          加載配置文件

          import?logging
          import?logging.config

          #?加載配置
          logging.config.fileConfig('logging.conf')

          #?創(chuàng)建?logger
          logger?=?logging.getLogger()

          #?應(yīng)用代碼
          logger.debug("debug?message")
          logger.info("info?message")
          logger.warning("warning?message")
          logger.error("error?message")

          輸出

          2021-12-23?00:02:07,019?-?root?-?DEBUG?-?debug?message
          2021-12-23?00:02:07,019?-?root?-?INFO?-?info?message
          2021-12-23?00:02:07,019?-?root?-?WARNING?-?warning?message
          2021-12-23?00:02:07,019?-?root?-?ERROR?-?error?message

          到這里算是對logging的一次比較完整的介紹,當(dāng)然,還有很多細(xì)節(jié)并沒有涉及到,因此我給了幾個鏈接供參考。

          參考鏈接:

          https://docs.python.org/3/library/logging.html#

          https://docs.python.org/3/howto/logging.html#logging-advanced-tutorial

          https://awaywithideas.com/python-logging-a-practical-guide/

          https://rmcomplexity.com/article/2020/12/01/introduction-to-python-logging.html


          溫馨提示:文章為有償閱讀,單篇1元即可支持

          瀏覽 96
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色a作片 | h文在线播放 | 国产精品色视频 | 99久久夜色精品 | 免费啪啪网站。 |