Python日志日志记录配置为了方便ELK收集日志,将日志打印成json格式。开发过程中,使用json格式排查问题不方便。本文使用python的logging模块,逐步添加配置,来说明各个组件的作用。python的原始日志可以使用两种方式打印python。默认打印级别为'WARNING'importlogging#使用logging直接打印日志logging.info('info')#不打印logging.warning('warning')#WARNING:root:warninglogging.error('error')#ERROR:root:error#使用logger打印日志logger=logging.getLogger('demo')logger.info('info')#不打印logger.warning('warning')#WARNING:demo:warninglogger.error('error')#ERROR:demo:error使用字典配置版本为固定字段,目前为1,设置级别为'INFO'importloggingimportlogging.configdefuse_config():"""配置日志行为"""config={"version":1,"root":{"level":"INFO"}}logging.config.dictConfig(config)use_config()#使用logging直接打印日志logging.info('info')#INFO:root:infologging.warning('warning')#WARNING:root:warninglogging.error('error')#ERROR:root:error#使用记录器打印日志logger=logging.getLogger('demo')logger.info('info')#信息:演示:infologger.warning('warning')#WARNING:demo:warninglogger.error('error')#ERROR:demo:errorlogger"loggers"是指定的logger"root"本质上是一个logger,python的默认logger直接logger打印日志,相当于logging.getLogger('root')如果没有找到记录器名称,使用默认记录器(即'root')importloggingimportlogging.configdefuse_config():"""配置日志行为"""config={"version":1,"root":{"level":"WARNING"},"loggers":{"demo":{"level":"ERROR"}}}logging.config.dictConfig(config)use_config()#使用logging直接打印日志logging.info('info')#未打印logging.warning('warning')#WARNING:root:warninglogging.error('error')#ERROR:root:error#使用记录器打印日志logger=logging.getLogger('demo')logger.info('info')#不打印logger.warning('warning')#不打印logger.error('error')#ERROR:demo:error#当没有找到logger时,使用'root'loggerlogger2=logging.getLogger('demo2')logger2.info('info')#不打印logger2.warning('warning')#WARNING:demo2:warninglogger2.error('error')#ERROR:demo2:errorprocessorandformatter"handlers"有很多processor的种类,可以打印屏幕,打印文件,发送邮件,发送http请求等,具体参见UsefulHandlers官方文档。可以在记录器中指定多个处理器。以下示例将屏幕“格式化程序”打印为格式化程序格式:使用python内置格式化程序格式化日志信息importloggingimportlogging.configdefuse_config():"""Configureloggingbehavior"""config={"version":1,"root":{"level":"INFO","handlers":["console_handler"]},"handlers":{"console_handler":{"class":"logging.StreamHandler","formatter":"console_formatter","stream":"ext://sys.stdout"}},"formatters":{"console_formatter":{"format":"%(asctime)s-%(name)s-%(levelname)s-%(message)s"}}}logging.config.dictConfig(config)use_config()logging.info('info')#2022-10-2520:16:59,073-root-INFO-infologging.warning('warning')#2022-10-2520:16:59,073-root-警告-warninglogging.error('error')#2022-10-2520:16:59,074-root-ERROR-errorcustomformattercustomformatter继承logging.Formatter,重写format方法配置,字段名使用“class”表示自定义格式填写导入该类的字符串,以下代码,“log_demo.py”文件中的“MyFormatter”,即“log_demo.MyFormatter”类型import会执行一次代码,重复调用配置方法会添加多个处理器,导致记录中有很多字段需要打印多次,这里就不展示了,自己打印查看str:"""重写日志格式字符串"""returnf[{record.name}][{record.levelname}]{record.msg}"defuse_config():"""配置日志行为"""config={“版本”:1,“root”:{“level”:“INFO”,“handlers”:[“console_handler”]},“handlers”:{“console_handler”:{“class”:“logging.StreamHandler”,“formatter”:“console_formatter","stream":"ext://sys.stdout"}},"formatters":{"console_formatter":{"class":"log_demo.MyFormatter"}}}logging.config.dictConfig(config)if__name__=="__main__":"""在实例化MyFormatter类时,会执行一次代码,将代码放在__main__中,否则会打印两次"""use_config()logging.info('info')#[root][INFO]infologging.warning('warning')#[root][WARNING]warninglogging.error('error')#[root][ERROR]errorextraextrafieldlog方法中有一个“extra”字段,丰富日志信息,打印"重写日志格式字符串"""foriinrecord.__dict__:print(i,end=',')print('')returnf"[{record.name}][{record.levelname}]{record.msg}"defuse_config():"""配置日志行为"""config={"version":1,"root":{"level":"INFO","handlers":["console_handler"]},“handlers”:{“console_handler”:{“class”:“logging.StreamHandler”,“formatter”:“console_formatter”,“stream”:“ext://sys.stdout”}},“formatters”:{"console_formatter":{"class":"log_demo.MyFormatter"}}}logging.config.dictConfig(config)if__name__=="__main__":"""MyFormatter类实例化时,代码会执行一次,代码会放在__main__中,否则会打印两次"""use_config()logging.info('info')#name,msg,args,levelname,levelno,pathname,filename,module,exc_info,exc_text,stack_info,lineno,funcName,created,msecs,relativeCreated,thread,threadName,processName,process,#[root][INFO]info#extraadded'a','b',可以看到record对象也添加了'a','b'两个字段logging.info('extrainfo',extra={'a':1,'b':2})#name,msg,args,levelname,levelno,pathname,filename,module,exc_info,exc_text,stack_info,lineno,funcName,created,msecs,relativeCreated,thread,threadName,processName,process,a,b,#[root][INFO]extrainfo最后的代码添加环境变量“LOG_MODE”来控制打印行为"json"打印json格式字符串时,当提供给ELK收集“文本”,会以文本方式打印出来,方便开发时查看。转换json时加入“ensure_asci”i=False",否则会转成ASCII码importosimportjsonimportloggingimportlogging.configfromdatetimeimportdatetime,timezone,timedeltaclassJsonFormatter(logging.Formatter):__TZ=timezone(timedelta(hours=+8))defformat(self,record:logging.LogRecord)->str:#ELK集合需要使用带时区的时间戳,key必须是"@timestamp"create_time=datetime.fromtimestamp(record.created)kv={'@timestamp':create_time.astimezone(self.__TZ).isoformat()}iflen(record.args)>0:kv['message']=record.msg%record.argselse:kv['message']=record.msg#错误信息ifrecord.exc_info:kv['err_info']=self.formatException(record.exc_info)forkinrecord.__dict__:v=getattr(record,k)#其他类型的json转换可能会报错如果v为None或isinstance(v,(int,float,bool,str)):kv[k]=velse:kv[k]=str(v)返回json.dumps(kv,ensure_ascii=False)defuse_config():"""配置日志行为"""ifos.environ.get('LOG_MODE')=="json":format_data={"class":"log_demo.JsonFormatter"}else:format_data={"format":"[%(asctime)s][%(name)s][%(levelname)s]%(message)s"}config={"version":1,"root":{"level":"INFO","handlers":["console_handler"]},"handlers":{"console_handler":{"class":"logging.StreamHandler","formatter":"console_formatter",“stream”:“ext://sys.stdout”}},“格式化程序”:{“console_formatter”:format_data}}logging.config.dictConfig(config)if__name__=="__main__":use_config()logging.info('info')logging.info('extrainfo',extra={'a':1,'b':2})文本效果#linuxsetenvironmentvariableexportLOG_MODE=text#windowssetenvironmentvariablesetLOG_MODE=textpythonlog_demo.py[2022-10-2619:11:07,468][root][INFO]info[2022-10-2619:11:07,468][root][INFO]extrainfojsoneffect#linuxsetenvironmentvariableexportLOG_MODE=json#windowssetenvironmentvariablesetLOG_MODE=jsonpythonlog_demo.py{"@timestamp":"2022-10-26T20:10:42.168191+08:00","message":"info","name":"root","msg":"info","args":"()","levelname":"INFO","levelno":20,"pathname":"e:\\Work\\Code\\TestCode\\everything\\log_demo.py","filename":"log_demo.py","module":"log_demo","exc_info":空,“exc_text”:空,“stack_info”:空,“lineno”:66,“funcName”:“<模块>”,“创建”:1666786242.168191,“毫秒”:168.19095611572266,“相对创建”:35.9044075012207,“线程”:22716,“threadName”:“MainThread”,"processName":"MainProcess","process":19576}{"@timestamp":"2022-10-26T20:10:42.169210+08:00","message":"extrainfo","name":"root","msg":"extrainfo","args":"()","levelname":"INFO","levelno":20,"pathname":"e:\\Work\\Code\\TestCode\\everything\\log_demo.py","filename":"log_demo.py","module":"log_demo","exc_info":null,"exc_text":null,"stack_info":null,"lineno":67,“功能名称”:"
