欢迎访问我的网站,希望内容对您有用,感兴趣的可以加入我们的社群。

Python实用模块(二十五)loguru

实用模块 迷途小书童 4年前 (2020-11-10) 6472次浏览 0个评论

软硬件环境

  • windows 10 64bits
  • anaconda with python 3.7
  • loguru 0.5.3

视频看这里

此处是 youtube 的播放链接,需要科学上网。喜欢我的视频,请记得订阅我的频道,打开旁边的小铃铛,点赞并分享,感谢您的支持。

前言

Python实用模块(十四)logging 已经介绍过了 python 内置日志模块 logging。我们要使用 logging,一般来讲,都是需要进行一些基本配置,比如下方的代码片段

import logging

logging.basicConfig(
    filename='test.log',
    level=logging.DEBUG,
    format='[line:%(lineno)d] - %(funcName)s: %(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)

使用起来还是比较麻烦的,特别是在写一些简短的测试代码时,往往没有直接写 print 语句来的方便。本篇介绍的 loguru 模块,使用起来非常的简单,2行代码就可以搞定。

安装

使用 pip 安装

pip install loguru

基本使用

先看最基本的使用

>>> from loguru import logger
>>> logger.debug('Hello loguru.')
2020-11-09 17:11:25.015 | DEBUG    | __main__:<module>:1 - Hello loguru.
>>>

loguru 有且只有一个 logger,默认输出到 stderr 也就是标准错误

接下来看看 handlerformatterfilter 的配置,这几个名词跟 logging 中是一致的。使用 logger.add 方法来进行配置

>>> from loguru import logger
>>> import sys
>>> logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO")
1
>>> logger.debug("Hello loguru.")
2020-11-09 17:19:06.606 | DEBUG    | __main__:<module>:1 - Hello loguru.

下面依次来讲,如需将日志写入文件中

logger.add("log_{time}.log")
logger.debug("Hello loguru.")

loguru

关于 rotation 部分,loguru 支持按文件大小、时间、保留时长等

logger.add("log.log", rotation="500 MB") # 超过文件大小后拆分
logger.add("log.log", rotation="12:00")  # 每天12点
logger.add("log.log", rotation="1 week") # 保留一周

loguru

可以看到当需要拆分文件来存储日志时,文件名是 log.{time}.log 的形式,跟 logging 中的 log.log.* 的形式是有区别的。

为防止日志挤爆硬盘,可以设置日志的保留时长

logger.add("log.log", retention="7 days")

同样的,还可以将日志进行压缩

logger.add("log.log", compression="zip")

loguru 偏爱 f-string 的这种更优雅、更强大的字符串格式化方式,要求 python 版本大于3.6

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

loguru 支持捕捉程序异常(在子线程中也可以这么做),这点太友好了。使用装饰器方法 catch

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

默认情况下,loguru 是线程安全的,但是在多进程中并不是。不过,通过参数 enqueue 可以达到安全的目标

logger.add("log.log", enqueue=True)

loguru 可以记录程序异常时的完整堆栈信息,这有助于查找问题所在。但是在生产环境下却容易暴露敏感信息。

In [1]: from loguru import logger

In [2]: logger.add("out.log", backtrace=True, diagnose=True)  # Caution, may leak sensitive data in prod
   ...:
   ...: def func(a, b):
   ...:     return a / b
   ...:
   ...: def nested(c):
   ...:     try:
   ...:         func(5, c)
   ...:     except ZeroDivisionError:
   ...:         logger.exception("What?!")
   ...:
   ...: nested(0)

执行的结果为

2020-11-09 18:56:13.716 | ERROR    | __main__:nested:10 - What?!
Traceback (most recent call last):

  File "c:\tools\anaconda3\envs\demo\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
                └ ModuleSpec(name='__main__', loader=<zipimporter object "C:\Tools\anaconda3\envs\demo\Scripts\ipython.exe">, origin='C:\\Tools...

  File "c:\tools\anaconda3\envs\demo\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
         │     └ {'__name__': '__main__', '__doc__': None, '__package__': '', '__loader__': <zipimporter object "C:\Tools\anaconda3\envs\demo\...
         └ <code object <module> at 0x000001C83025D660, file "C:\Tools\anaconda3\envs\demo\Scripts\ipython.exe\__main__.py", line 2>

  File "C:\Tools\anaconda3\envs\demo\Scripts\ipython.exe\__main__.py", line 7, in <module>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\__init__.py", line 126, in start_ipython
    return launch_new_instance(argv=argv, **kwargs)
           │                        │       └ {}
           │                        └ None
           └ <bound method Application.launch_instance of <class 'IPython.terminal.ipapp.TerminalIPythonApp'>>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\traitlets\config\application.py", line 845, in launch_instance
    app.start()
    │   └ <function TerminalIPythonApp.start at 0x000001C8320553A8>
    └ <IPython.terminal.ipapp.TerminalIPythonApp object at 0x000001C830436C88>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\terminal\ipapp.py", line 356, in start
    self.shell.mainloop()
    │    └ <traitlets.traitlets.Instance object at 0x000001C83202B408>
    └ <IPython.terminal.ipapp.TerminalIPythonApp object at 0x000001C830436C88>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\terminal\interactiveshell.py", line 564, in mainloop
    self.interact()
    │    └ <function TerminalInteractiveShell.interact at 0x000001C831EB8288>
    └ <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x000001C830377F08>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\terminal\interactiveshell.py", line 555, in interact
    self.run_cell(code, store_history=True)
    │    │        └ 'logger.add("out.log", backtrace=True, diagnose=True)  # Caution, may leak sensitive data in prod\n\ndef func(a, b):\n    ret...
    │    └ <function InteractiveShell.run_cell at 0x000001C8313C3828>
    └ <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x000001C830377F08>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\core\interactiveshell.py", line 2878, in run_cell
    raw_cell, store_history, silent, shell_futures)
    │         │              │       └ True
    │         │              └ False
    │         └ True
    └ 'logger.add("out.log", backtrace=True, diagnose=True)  # Caution, may leak sensitive data in prod\n\ndef func(a, b):\n    ret...

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\core\interactiveshell.py", line 2923, in _run_cell
    return runner(coro)
           │      └ <coroutine object InteractiveShell.run_cell_async at 0x000001C832C3B548>
           └ <function _pseudo_sync_runner at 0x000001C8313B30D8>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\core\async_helpers.py", line 68, in _pseudo_sync_runner
    coro.send(None)
    │    └ <method 'send' of 'coroutine' objects>
    └ <coroutine object InteractiveShell.run_cell_async at 0x000001C832C3B548>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\core\interactiveshell.py", line 3147, in run_cell_async
    interactivity=interactivity, compiler=compiler, result=result)
                  │                       │                └ <ExecutionResult object at 1c833987108, execution_count=2 error_before_exec=None error_in_exec=None info=<ExecutionInfo objec...
                  │                       └ <IPython.core.compilerop.CachingCompiler object at 0x000001C832085388>
                  └ 'last_expr'

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\core\interactiveshell.py", line 3338, in run_ast_nodes
    if (await self.run_code(code, result,  async_=asy)):
              │    │        │     │               └ False
              │    │        │     └ <ExecutionResult object at 1c833987108, execution_count=2 error_before_exec=None error_in_exec=None info=<ExecutionInfo objec...
              │    │        └ <code object <module> at 0x000001C832F291E0, file "<ipython-input-2-13116e8ab604>", line 12>
              │    └ <function InteractiveShell.run_code at 0x000001C8313C3CA8>
              └ <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x000001C830377F08>

  File "c:\tools\anaconda3\envs\demo\lib\site-packages\IPython\core\interactiveshell.py", line 3418, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
         │         │    │               │    └ {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, ...
         │         │    │               └ <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x000001C830377F08>
         │         │    └ <property object at 0x000001C8313BE368>
         │         └ <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x000001C830377F08>
         └ <code object <module> at 0x000001C832F291E0, file "<ipython-input-2-13116e8ab604>", line 12>

  File "<ipython-input-2-13116e8ab604>", line 12, in <module>
    nested(0)
    └ <function nested at 0x000001C833BFF9D8>
> File "<ipython-input-2-13116e8ab604>", line 8, in nested
    func(5, c)
    │       └ 0
    └ <function func at 0x000001C832F179D8>
  File "<ipython-input-2-13116e8ab604>", line 4, in func
    return a / b
           │   └ 0
           └ 5

ZeroDivisionError: division by zero

In [3]:

参考资料

喜欢 (1)

您必须 登录 才能发表评论!