当前位置: 首页 > 后端技术 > Python

如何使用loguru接管程序的所有日志输出?

时间:2023-03-25 20:22:12 Python

背??景与痛点——日志需求:统一的loguru输出格式有多优秀我就不用多介绍了。我们自己的业务代码可以很方便的导入loguru来打印日志。一个统一的输出很混乱但是很多第一三方库集成的日志模块在标准库中进行日志记录,其格式五花八门。希望能实现:用loguru接管所有库的logging,用loguru的格式输出原因?因为方便日志的收集、分析和存储!比如对接loki、sls、elk等。最佳实践-我理想的日志输出格式:我希望每个日志输出看起来像这样{"text":"2022-04-2518:12:40.179|ERROR|__main__::18-Anerrorhasbeen在函数“<模块>”、进程“MainProcess”(60788)、线程“MainThread”(139849590506112)中捕获:\nTraceback(最后一次调用):\n\n>文件\"/home/bot/Desktop/ideaboom/test_logger/loguru_contextualize.py\",第18行,在\ndiv(1,0)\n└\n\nFile\"/home/bot/Desktop/ideaboom/test_logger/loguru_contextualize.py\",line8,indiv\nreturna/b\n│└0\n└1\n\nZeroDivisionError:除以零\n","record":{"elapsed":{"repr":"0:00:00.007519","seconds":0.007519},"exception":{"type":"ZeroDivisionError","value":"除以零",“追溯”:真},“额外”:{“context_id”:“fd77b1d159224d939c1cc0d7a566d09b”,“message_id”:“8ad2c351ef3240998fc10a5015c591c1”},“文件”:{“名称”:“loguru_contextualize.py”,“路径”:“/home/bot/Desktop/ideaboom/test_logger/loguru_contextualize.py"},"function":"","level":{"icon":"?","name":"DEUBG","no":40},"line":18,"message":"在函数''、进程'MainProcess'(60788)、线程'MainThread'(139849590506112)中发现错误:","module":"loguru_contextualize","name":"__main__","process":{"id":60788,"name":"MainProcess"},"thread":{"id":139849590506112,"name":"MainThread"},"time":{"repr":"2022-04-2518:12:40.179070+08:00","timestamp":1650881560.17907}}}对应的代码是这样的:fromloguruimportloggerfrommarkimportBASE_DIRimportlogginglogger.add(BASE_DIR/'run.log',serialize='json')我想让loguru的日志输出到一个文件,然后日志收集程序收集日志文件而不是标准输出。我希望标准输出是给人类的,而不是给日志收集程序的。那个也就是说,日志输出是两份:一份输出到stdout,stderror,可以直接通过tail,docker-composelogs,kubectllogs等命令查看,展示给人类看(格式不需要统一,反正不统一人类也能看懂,而且这个是简单的文本,不是josn)class);另一个是输出到run.log文件,logtail、promtailc等日志收集程序收集run.log中的日志记录。(有统一的日志格式,一行为json)json输出日志的优点:方便收集,不会因为格式问题导致收集错误!栈中换行不用担心,只是需要在行首使用正则匹配表达式,方便解析。连接es和mongodb很方便。缺点是什么?体积膨胀,json比直接文本大n倍代码实现——loguru偷天换日,创建了一个loggers.py文件,内容如下:fromloguruimportloggerfrommarkimportBASE_DIRimportlogginglogger.add(BASE_DIR/'run.log',serialize='json')classInterceptHandler(logging.Handler):defemit(self,record):#检索日志调用发生的上下文,这恰好在第6帧向上logger_opt=logger.opt(depth=6、exception=record.exc_info)logger_opt.log(record.levelno,record.getMessage())#logging.getLogger("uvicorn").setLevel(10)#logging.getLogger("uvicorn").handlers=[]#logging.getLogger("uvicorn").addHandler(InterceptHandler())#logging.getLogger("uvicorn.error").setLevel(10)#logging.getLogger("uvicorn.error").handlers=[]#logging.getLogger("uvicorn.error").addHandler(拦截Handler())#logging.getLogger("fawn.error").setLevel(10)#logging.getLogger("fawn.error").handlers=[]#logging.getLogger("fawn.error").addHandler(InterceptHandler())logger_name_list=[namefornameinlogging.root.manager.loggerDict]#logger_name_list=[namefornameinlogging.root.manager.loggerDictif'.'notinname]forlogger_nameinlogger_name_list:logging.getLogger(logger_name).setLevel(10)logging.getLogger(logger_name).handlers=[]if'.'notinlogger_name:logging.getLogger(logger_name).addHandler(InterceptHandler())print(logger_name_list)print(logging.root.manager.loggerDict)现在实现中,比如uvicorn、peewee都有自己的输出格式,如下:─?pythonapi.pyINFO:Startedserverprocess[2382519]INFO:Waitingforapplicationstartup.INFO:Applicationstartupcomplete.INFO:Uvicornrunningonhttp://0.0.0.0:8000(PressCTRL+Ctoquit)使用上面的代码之后,就变成了:─?uvicornapi:app1?2022-04-3023:09:11.283|信息|uvicorn.server:serve:75-启动服务器进程[2395776]2022-04-3023:09:11.283|信息|45-等待应用程序启动。2022-04-3023:09:11.283|信息|uvicorn.lifespan.on:startup:59-应用程序启动完成。2022-04-3023:09:11.284|信息|uvicorn.server:_log_started_message:206-Uvicornrunningonhttp://127.0.0.1:8000(PressCTRL+Ctoquit)解释一下上面的代码,你知道是什么意思吗?todo参考文章:过滤对象如何使用loguru记录peewee如何使用Loguru在FastAPI中覆盖UvicornLogger