我正在参加「掘金·启航计划」
loguru
loguru Github: Python logging made (stupidly) simple (github.com)
loguru API Reference — loguru documentation
安装
pip install loguru
用法
loguru不用像logging那样做很多配置操作,直接从loguru导入logger即可,需要注意的是,此logger是经过实例化后的全局唯一logger对象,意味着即使创建多个logger对象最终也是写入到这个全局的logger中。
基本使用
from loguru import logger
logger.info("hello world")
详细用法
from loguru import logger
logger.add(
sink=os.path.join(BASE_DIR, 'ssh/logs/service.log'),
rotation='500 MB', # 日志文件最大限制500mb
retention='30 days', # 最长保留30天
format="{time}|{level}|{message}", # 日志显示格式
compression="zip", # 压缩形式保存
encoding='utf-8', # 编码
level='DEBUG', # 日志级别
enqueue=True, # 默认是线程安全的,enqueue=True使得多进程安全
)
logger.debug("详细调试信息")
logger.info("普通信息")
logger.success("成功信息")
logger.warning("警告信息")
logger.error("错误信息")
logger.trace("异常信息")
logger.critical("严重错误信息")
运行如下:
如果想捕获程序中的异常可使用logger.exception,此方法可以定位到具体变量值
try:
a = 0
b = 5/a
except ZeroDivisionError:
logger.exception("ZeroDivisionError")
# 装饰器形式
@logger.catch()
def test():
a = 0
b = 5/a
test()
add方法参数说明
| 参数 | 值 | 含义 |
|---|---|---|
| sink | 文件对象/字符串/Path对象/日志handler | 负责接收格式化的日志并输出到相应对象 |
| format | 如:"{time} {level} {message}" | 自定义日志格式 |
| rotation | 如:500 MB、1 week、00:00 | 超过设定的规则上限就会创建另一份日志 |
| retention | 1 week、30 days、2 months | 超过存储时间就会清空 |
| level | INFO、DEBUG、ERROR等 | 日志级别 |
| compression | "zip"、"tar.gz"、"gz"、"tar"等 | 压缩格式(日志文件很大时可以节省空间) |
| serialize | bool类型 | 为True表示将日志序列化成json格式 |
关于日志严重程度等级排序(copy的官方文档)
| Level name | Severity value | Logger method |
|---|---|---|
TRACE | 5 | logger.trace() |
DEBUG | 10 | logger.debug() |
INFO | 20 | logger.info() |
SUCCESS | 25 | logger.success() |
WARNING | 30 | logger.warning() |
ERROR | 40 | logger.error() |
CRITICAL | 50 | logger.critical() |
filter
现在有这样一个需求:我想把错误日志输出到error.log中,消息包含hello的输出到hello.log中,那么我们可以使用filter来过滤
from loguru import logger
logger.add(os.path.join(BASE_DIR, 'ssh/logs/service.log'), encoding="utf-8")
logger.add(os.path.join(BASE_DIR, 'ssh/logs/error.log'), encoding="utf-8", filter=lambda x: 'ERROR' in str(x['level']).upper())
logger.add(os.path.join(BASE_DIR, 'ssh/logs/hello.log'), encoding="utf-8", filter=lambda x: 'hello' in str(x['message']))
logger.debug("详细调试信息")
logger.error("错误信息")
logger.info("普通信息")
logger.error("hello world")
三个日志文件输出如下:
remove
删除之前添加的的handler,相当于刷新
from loguru import logger
logger.add(os.path.join(BASE_DIR, 'ssh/logs/service.log'), encoding="utf-8")
logger.add(os.path.join(BASE_DIR, 'ssh/logs/error.log'), encoding="utf-8", filter=lambda x: 'ERROR' in str(x['level']).upper())
lg_hello = logger.add(os.path.join(BASE_DIR, 'ssh/logs/hello.log'), encoding="utf-8", filter=lambda x: 'hello' in str(x['message']))
logger.debug("详细调试信息")
logger.error("错误信息")
logger.info("普通信息")
logger.error("hello world")
# 这里移除了hello handler
logger.remove(lg_hello)
logger.error("hello new world")
输出如下,移除hello那个handler之后,hello.log中没有输出hello new world,而error.log则有:
封装
from loguru import logger as lg
@singleton
class AppLogger:
def __init__(self):
self.app_logger = lg
def set_logger(self, filename, filter_type=None, level='DEBUG'):
"""
:param filename: 日志文件名
:param filter_type: 日志过滤,如:将日志级别为ERROR的单独记录到一个文件中
:param level: 日志级别设置
:return:
"""
dic = dict(
sink=self.get_log_path(filename),
rotation='500 MB',
retention='30 days',
format="{time}|{level}|{message}",
encoding='utf-8',
level=level,
enqueue=True,
)
if filter_type:
dic["filter"] = lambda x: filter_type in str(x['level']).upper()
self.app_logger.add(**dic)
return self.app_logger
@property
def get_logger(self):
return self.app_logger
@staticmethod
def get_log_path(filename):
log_path = os.path.join(BASE_LOG_DIR, filename)
return log_path
def trace(self, msg):
self.app_logger.trace(msg)
def debug(self, msg):
self.app_logger.debug(msg)
def info(self, msg):
self.app_logger.info(msg)
def success(self, msg):
self.app_logger.success(msg)
def warning(self, msg):
self.app_logger.warning(msg)
def error(self, msg):
self.app_logger.error(msg)
def critical(self, msg):
self.app_logger.critical(msg)
logger = AppLogger()
logger.set_logger('error.log', filter_type='ERROR')
logger.set_logger('service.log', filter_type='INFO', level='INFO')
logging
logging是python的标准库,主要方法有info()、debug()、warning()、error()、critical() ,默认多线程安全,若要保证多进程安全,可以使用concurrent_log_handler.ConcurrentRotatingFileHandler 。
相比较于loguru,其配置项较多,支持更细化的配置,但输出没有颜色标识,所以综合来说使用loguru效率更高,输出样式也优于logging。项目中两个都有用到,一般用loguru记录系统中每个用户的操作日志,每个业务app用logging记录日志。
class Loggings:
def __init__(self, log_name, level=logging.DEBUG):
"""
:param log_name: 日志文件名称
:param level: 日志级别
"""
log_path = os.path.join(BASE_LOG_DIR, log_name)
self.logger = logging.getLogger(__name__) # 指定输出日志的程序名
self.logger.setLevel(level) #设定全局的日志级别
# 添加日志文件handler,用于输出日志到文件中
# file_handler = logging.FileHandler(filename='log.log', encoding='UTF-8')
# 按时间分割日志,此处为30天生成一个新的日志文件,最多保留5个
console_handler = logging.StreamHandler()
file_handler = logging.handlers.TimedRotatingFileHandler(log_path,
when='D',
interval=30,
backupCount=5, # 保留日志个数
encoding="utf-8")
file_handler.suffix = "%Y_%m_%d.log" # 分割的日志后缀名
# 给logger添加handler
if not self.logger.handlers:
self.logger.addHandler(console_handler)
self.logger.addHandler(file_handler)
formatter = logging.Formatter(
'%(asctime)s | %(levelname)s | %(thread)d | %(name)s | %(filename)s | %(funcName)s | %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
def get_logger(self):
return self.logger