优雅的log库

loguru

基本使用

2019-10-13 22:46:12.367 | DEBUG    | __main__:<module>:4 - this is a debug message

默认的输出格式 有时间、级别、模块名、行号以及日志信息,不需要手动创建 logger, 直接输出到控制台的

from loguru import logger
 
logger.debug('this is a debug message')

添加文件

from loguru import logger
 
logger.add('runtime.log')
logger.debug('this is a debug')

详细使用

loguru 对输出到文件的配置有非常强大的支持,

支持输出到多个文件分级别分别输出过大创建新文件过久自动删除等等

  • add

add 方法 就相当于给 logger 添加了一个 Handler

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 可以传入一个 file 对象,例如 sys.stderr 或者 open('file.log', 'w') 都可以。
    
    sink 可以直接传入一个 str 字符串或者 pathlib.Path 对象,其实就是代表文件路径的,如果识别到是这种类型,它会自动创建对应路径的日志文件并将日志输出进去。
    
    sink 可以是一个方法,可以自行定义输出实现。
    
    sink 可以是一个 logging 模块的 Handler,比如 FileHandler、StreamHandler 等等,或者上文中我们提到的 CMRESHandler 照样也是可以的,这样就可以实现自定义 Handler 的配置。
    
    sink 还可以是一个自定义的类,具体的实现规范可以参见官方文档

All sinks added to the logger are thread-safe by default.

logger.add('runtime.log', format="{time} {level} {message}", filter="my_module", level="INFO")
  • 删除 sink

添加 sink 之后对其进行删除,相当于重新刷新并写入新的内容

删除的时候根据刚刚 add 方法返回的 id 进行删除即可


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')
  • rotation 配置

1 每 500MB 存储一个文件, 每个 log 文件过大就会新创建一个 log 文件

logger.add('runtime_{time}.log', rotation="500 MB") 
#  time 占位符,这样在生成时可以自动将时间替换进去,生成一个文件名包含时间的 log 文件

2 定时创建 log 文件

logger.add('runtime_{time}.log', rotation='00:00')
# 每天 0 点新创建一个 log 文件输出

logger.add('runtime_{time}.log', rotation='1 week')
# 一周创建一个 log 文件
  • retention 配置

retention 这个参数可以配置日志的最长保留时间

logger.add('runtime.log', retention='10 days')
# 保留最新 10 天的 log
  • compression 配置

文件的压缩格式

logger.add('runtime.log', compression='zip')
  • @logger.catch 获取子线程中的异常
from loguru import logger
from threading import Thread

@logger.catch()
def my_func(a, b):
    return 1 / (a + b)

logger.add("threading_log.log")

logger.info("main start")
t = Thread(target=my_func, args=(0, 0))
t.start()
t.join()

logger.info("main end")



  • enqueue 和 looger的 complete函数

默认情况下,logger是线程安全的。


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

使用 complete函数 和 enqueue参数 保证进程安全, 异步的日志操作


# 多进程
def process():
    logger.info("Message sent from the child")
    logger.complete()

logger.add(sys.stderr, enqueue=True)

process = multiprocessing.Process(target=process)
process.start()
process.join()
# Message sent from the child


# 协程
async def sink(message):
    await asyncio.sleep(0.1)  # IO processing...
    print(message, end="")

async def work():
    logger.info("Start")
    logger.info("End")
    await logger.complete() # complete

logger.add(sink)

asyncio.run(work())
  • backtrace , diagnose Fully descriptive exceptions

显示全部的 trace, 但是 may leak sensitive data in prod

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)
Buy me a 肥仔水!