软硬件环境
- 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
也就是标准错误
接下来看看 handler
、formatter
、filter
的配置,这几个名词跟 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.")
关于 rotation
部分,loguru
支持按文件大小、时间、保留时长等
logger.add("log.log", rotation="500 MB") # 超过文件大小后拆分
logger.add("log.log", rotation="12:00") # 每天12点
logger.add("log.log", rotation="1 week") # 保留一周
可以看到当需要拆分文件来存储日志时,文件名是 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]: