<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>

          使用loguru優(yōu)雅的輸出日志

          共 3775字,需瀏覽 8分鐘

           ·

          2021-11-18 13:32

          Loguru: 更優(yōu)雅的日志記錄解決方案!

          loguru?是一個(gè)?Python?簡易且強(qiáng)大的第三方日志記錄庫,該庫旨在通過添加一系列有用的功能來解決標(biāo)準(zhǔn)記錄器的注意事項(xiàng),從而減少?Python?日志記錄的痛苦。

          1. 引入原因

          簡單且方便的幫助我們輸出需要的日志信息!

          • 使用?Python?來寫程序或者腳本的話,常常遇到的問題就是需要對(duì)日志進(jìn)行刪除。一方面可以幫助我們?cè)诔绦虺鰡栴}的時(shí)候排除問題,二來可以幫助我們記錄需要關(guān)注的信息。

          • 但是,使用自帶自帶的?logging?模塊的話,則需要我們進(jìn)行不同的初始化等相關(guān)工作。對(duì)應(yīng)不熟悉該模塊的同學(xué)來說,還是有些費(fèi)勁的,比如需要配置?Handler/Formatter?等。

          import logging
          logger = logging.getLogger('xxx')handler = logging.StreamHandler()formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')handler.setFormatter(formatter)logger.addHandler(handler)logger.setLevel(logging.DEBUG)
          logger.debug('This is a %s', 'test')
          • 而?loguru?就是一個(gè)可以?開箱即用?的日志記錄模塊,我們不再需要復(fù)雜的初始化操作就可以通過如下命令來記錄日志信息了。

          # pip$ pip install loguru

          2. 功能特性

          有很多優(yōu)點(diǎn),以下列舉了其中比較重要的幾點(diǎn)!

          • 開箱即用,無需準(zhǔn)備

          • 無需初始化,導(dǎo)入函數(shù)即可使用

          • 更容易的文件日志記錄與轉(zhuǎn)存/保留/壓縮方式

          • 更優(yōu)雅的字符串格式化輸出

          • 可以在線程或主線程中捕獲異常

          • 可以設(shè)置不同級(jí)別的日志記錄樣式

          • 支持異步,且線程和多進(jìn)程安全

          • 支持惰性計(jì)算

          • 適用于腳本和庫

          • 完全兼容標(biāo)準(zhǔn)日志記錄

          • 更好的日期時(shí)間處理


          3. 快速上手

          介紹 loguru 的常用操作和功能介紹!

          [1] 開箱即用,無需準(zhǔn)備

            • loguru?并沒有什么黑科技,只是它預(yù)先幫助我們?cè)O(shè)置好了相關(guān)的配置,我們導(dǎo)入之后即可直接使用。

          from loguru import logger
          logger.debug("That's it, beautiful and simple logging!")

          [2] 無需初始化,導(dǎo)入函數(shù)即可使用

          • 如何添加處理程序(handler)呢?

          • 如何設(shè)置日志格式(logs formatting)呢?

          • 如何過濾消息(filter messages)呢?

          • 如何如何設(shè)置級(jí)別(log level)呢?

          # addlogger.add(sys.stderr, \    format="{time} {level} {message}",\    filter="my_module",\    level="INFO")

          [3] 更容易的文件日志記錄與轉(zhuǎn)存/保留/壓縮方式

          # 日志文件記錄logger.add("file_{time}.log")
          # 日志文件轉(zhuǎn)存logger.add("file_{time}.log", rotation="500 MB")logger.add("file_{time}.log", rotation="12:00")logger.add("file_{time}.log", rotation="1 week")
          # 多次時(shí)間之后清理logger.add("file_X.log", retention="10 days")
          # 使用zip文件格式保存logger.add("file_Y.log", compression="zip")

          [4] 更優(yōu)雅的字符串格式化輸出

          logger.info(    "If you're using Python {}, prefer {feature} of course!",    3.6, feature="f-strings")

          [5] 在線程或主線程中捕獲異常

          @logger.catchdef my_function(x, y, z):    # An error? It's caught anyway!    return 1 / (x + y + z)
          my_function(0, 0, 0)

          [6] 可以設(shè)置不同級(jí)別的日志記錄樣式

          • Loguru?會(huì)自動(dòng)為不同的日志級(jí)別,添加不同的顏色進(jìn)行區(qū)分,當(dāng)然我們也是可以自定義自己喜歡的顯示顏色樣式的。

          logger.add(sys.stdout,    colorize=True,    format="{time} {message}")
          logger.add('logs/z_{time}.log', level='DEBUG', format='{time:YYYY-MM-DD :mm:ss} - {level} - {file} - {line} - {message}', rotation="10 MB")

          [7] 支持異步且線程和多進(jìn)程安全

          • 默認(rèn)情況下,添加到?logger?中的日志信息都是線程安全的。但這并不是多進(jìn)程安全的,我們可以通過添加?enqueue?參數(shù)來確保日志完整性。

          • 如果我們想要在異步任務(wù)中使用日志記錄的話,也是可以使用同樣的參數(shù)來保證的。并且通過?complete()?來等待執(zhí)行完成。

          # 異步寫入logger.add("some_file.log", enqueue=True)

          [8] 異常的完整性描述

          • 用于記錄代碼中發(fā)生的異常的?bug?跟蹤,Loguru?通過允許顯示整個(gè)堆棧跟蹤(包括變量值)來幫助您識(shí)別問題。

          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)

          [9] 結(jié)構(gòu)化日志記錄

          • 對(duì)日志進(jìn)行序列化以便更容易地解析或傳遞數(shù)據(jù)結(jié)構(gòu),使用序列化參數(shù),在將每個(gè)日志消息發(fā)送到配置的接收器之前,將其轉(zhuǎn)換為?JSON?字符串。

          • 同時(shí),使用?bind()?方法,可以通過修改額外的?record?屬性來將日志記錄器消息置于上下文中。還可以通過組合?bind()?和?filter?對(duì)日志進(jìn)行更細(xì)粒度的控制。

          • 最后?patch()?方法允許將動(dòng)態(tài)值附加到每個(gè)新消息的記錄?dict?上。

          # 序列化為json格式logger.add(custom_sink_function, serialize=True)
          # bind方法的用處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")
          # 粒度控制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!")
          # patch()方法的用處logger.add(sys.stderr, format="{extra[utc]} {message}")logger = logger.patch(lambda record: record["extra"].update(utc=datetime.utcnow()))

          [10] 惰性計(jì)算

          • 有時(shí)希望在生產(chǎn)環(huán)境中記錄詳細(xì)信息而不會(huì)影響性能,可以使用?opt()?方法來實(shí)現(xiàn)這一點(diǎn)。

          logger.opt(lazy=True).debug("If sink level <= DEBUG: {x}", x=lambda: expensive_function(2**64))
          # By the way, "opt()" serves many usageslogger.opt(exception=True).info("Error stacktrace added to the log message (tuple accepted too)")logger.opt(colors=True).info("Per message colors")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")

          [11] 可定制的級(jí)別

          new_level = logger.level("SNAKY", no=38, color="", icon="??")logger.log("SNAKY", "Here we go!")

          [12] 適用于腳本和庫

          # For scriptsconfig = {    "handlers": [        {"sink": sys.stdout, "format": "{time} - {message}"},        {"sink": "file.log", "serialize": True},    ],    "extra": {"user": "someone"}}logger.configure(**config)
          # For librarieslogger.disable("my_library")logger.info("No matter added sinks, this message is not displayed")logger.enable("my_library")logger.info("This message however is propagated to the sinks")

          [13] 完全兼容標(biāo)準(zhǔn)日志記錄

          • 希望使用?Loguru?作為內(nèi)置的日志處理程序?

          • 需要將?Loguru?消息到標(biāo)準(zhǔn)日志?

          • 想要攔截標(biāo)準(zhǔn)的日志消息到?Loguru?中匯總?

          handler = logging.handlers.SysLogHandler(address=('localhost', 514))logger.add(handler)


          class PropagateHandler(logging.Handler):    def emit(self, record):        logging.getLogger(record.name).handle(record)
          logger.add(PropagateHandler(), format="{message}")
          class InterceptHandler(logging.Handler):    def emit(self, record):        # Get corresponding Loguru level if it exists        try:            level = logger.level(record.levelname).name        except ValueError:            level = record.levelno
          # Find caller from where originated the logged message frame, depth = logging.currentframe(), 2 while 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)

          [14] 方便的解析器

          • 從生成的日志中提取特定的信息通常很有用,這就是為什么?Loguru?提供了一個(gè)?parse()?方法來幫助處理日志和正則表達(dá)式。

          pattern = r"(?Pcaster_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)}

          [15] 通知機(jī)制

          import notifiers
          params = { "username": "[email protected]", "password": "abc123", "to": "[email protected]"}
          # Send a single notificationnotifier = notifiers.get_notifier("gmail")notifier.notify(message="The application is running!", **params)
          # Be alerted on each error messagefrom notifiers.logging import NotificationHandler
          handler = NotificationHandler("gmail", defaults=params)logger.add(handler, level="ERROR")

          [16] Flask 框架集成

          • 現(xiàn)在最關(guān)鍵的一個(gè)問題是如何兼容別的?logger,比如說?tornado?或者?django?有一些默認(rèn)的?logger

          • 經(jīng)過研究,最好的解決方案是參考官方文檔的,完全整合?logging?的工作方式。比如下面將所有的?logging都用?loguru?的?logger?再發(fā)送一遍消息。

          import loggingimport sys
          from pathlib import Path
          from flask import Flaskfrom loguru import logger
          app = Flask(__name__)
          class InterceptHandler(logging.Handler): def emit(self, record): logger_opt = logger.opt(depth=6, exception=record.exc_info) logger_opt.log(record.levelname, record.getMessage())
          def configure_logging(flask_app: Flask): """配置日志""" path = Path(flask_app.config['LOG_PATH']) if not path.exists(): path.mkdir(parents=True) log_name = Path(path, 'sips.log')
          logging.basicConfig(handlers=[InterceptHandler(level='INFO')], level='INFO') # 配置日志到標(biāo)準(zhǔn)輸出流 logger.configure(handlers=[{"sink": sys.stderr, "level": 'INFO'}]) # 配置日志到輸出到文件 logger.add(log_name, rotation="500 MB", encoding='utf-8', colorize=False, level='INFO')

          4. 要點(diǎn)解析

          介紹,主要函數(shù)的使用方法和細(xì)節(jié) - add()的創(chuàng)建和刪除

          • add() - 非常重要的參數(shù)?sink?參數(shù)

            • 具體的實(shí)現(xiàn)規(guī)范可以參見官方文檔

            • 可以實(shí)現(xiàn)自定義?Handler?的配置,比如?FileHandlerStreamHandler?等等

            • 可以自行定義輸出實(shí)現(xiàn)

            • 代表文件路徑,會(huì)自動(dòng)創(chuàng)建對(duì)應(yīng)路徑的日志文件并將日志輸出進(jìn)去

            • 例如?sys.stderr?或者?open('file.log', 'w')?都可以

            • 可以傳入一個(gè)?file?對(duì)象

            • 可以直接傳入一個(gè)?str?字符串或者?pathlib.Path?對(duì)象

            • 可以是一個(gè)方法

            • 可以是一個(gè)?logging?模塊的?Handler

            • 可以是一個(gè)自定義的類

          def add(self, sink, *,    level=_defaults.LOGURU_LEVEL, format=_defaults.LOGURU_FORMAT,    filter=_defaults.LOGURU_FILTER, colorize=_defaults.LOGURU_COLORIZE,    serialize=_defaults.LOGURU_SERIALIZE, backtrace=_defaults.LOGURU_BACKTRACE,    diagnose=_defaults.LOGURU_DIAGNOSE, enqueue=_defaults.LOGURU_ENQUEUE,    catch=_defaults.LOGURU_CATCH, **kwargs):
          • 另外添加?sink?之后我們也可以對(duì)其進(jìn)行刪除,相當(dāng)于重新刷新并寫入新的內(nèi)容。刪除的時(shí)候根據(jù)剛剛?add?方法返回的?id?進(jìn)行刪除即可。可以發(fā)現(xiàn),在調(diào)用?remove?方法之后,確實(shí)將歷史?log?刪除了。但實(shí)際上這并不是刪除,只不過是將?sink?對(duì)象移除之后,在這之前的內(nèi)容不會(huì)再輸出到日志中,這樣我們就可以實(shí)現(xiàn)日志的刷新重新寫入操作

          from loguru import logger
          trace = logger.add('runtime.log')logger.debug('this is a debug message')logger.remove(trace)logger.debug('this is another debug message')

          原文鏈接:https://www.escapelife.site/posts/d4521b7.html

          文章轉(zhuǎn)載:Python編程學(xué)習(xí)圈
          (版權(quán)歸原作者所有,侵刪)

          點(diǎn)擊下方“閱讀原文”查看更多

          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  色婷婷我也去俺也去 | www.91爱爱 | 99精品视频在线观 | 国产女人水真多18毛片18精品 | 日韩精品一区二区在线观看 |