日志打印之自定义logger handler

By:授客 QQ1033553122

#实践环境

WIN 10

Python 3.6.5

#实践代码

handler.py

#!/usr/bin/env python
# -*- coding:utf-8 -*- '''
@Author : shouke
''' import logging
import logging.config class MyLogHandler(logging.Handler, object):
"""
自定义日志handler
""" def __init__(self, name, other_attr=None, **kwargs):
logging.Handler.__init__(self)
print('初始化自定义日志处理器:', name)
print('其它属性值:', other_attr) def emit(self, record):
"""
emit函数为自定义handler类时必重写的函数,这里可以根据需要对日志消息做一些处理,比如发送日志到服务器 发出记录(Emit a record)
"""
try:
msg = self.format(record)
print('获取到的消息为:', msg)
for item in dir(record):
if item in ['process', 'processName', 'thread', 'threadName']:
print(item, ':', getattr(record, item))
except Exception:
self.handleError(record) # 测试
logging.basicConfig()
logger = logging.getLogger("logger")
logger.setLevel(logging.INFO)
my_log_handler = MyLogHandler('LoggerHandler')
logger.addHandler(my_log_handler)
logger.info('hello,shouke') 运行handler.py,结果输出如下
初始化自定义日志处理器: LoggerHandler
其它属性值: None
获取到的消息为: hello,shouke
process : 27932
processName : MainProcess
thread : 45464
threadName : MainThread
INFO:logger:hello,shouke

  

#通过字典配置添加自定义handler

mytest.py(与handler.py位于同一层级目录)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
#
# '''
# @CreateTime: 2020/12/29 14:08
# @Author : shouke
# '''
#
import logging
import logging.config LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"format":"%(asctime)s %(filename)s %(lineno)s %(levelname)s : %(message)s",
},
"plain": {
"format": "%(message)s",
}
},
"handlers": {
"customer_handler":{
"class":"study.MyLogHandler",
"formatter":"default",
# 注意,class,formatter,level,filters之外的参数将默认传递给由class指定类的构造函数
"name":"LoggerHandler",
"other_attr":"something others"
},
"console": {
"class": "logging.StreamHandler",
"formatter": "default",
},
},
"loggers": {
"customer_logger":{
"handlers": ["customer_handler", "console"],
"level": logging.INFO,
"propagate": False,
}
}
} logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger('customer_logger')
logger.info('hello,shouke') 运行mytest.py,输出结果如下
初始化自定义日志处理器: LoggerHandler
其它属性值: something others
获取到的消息为: 2021-01-01 17:51:54,661 mytest.py 48 INFO : hello,shouke
process : 36280
processName : MainProcess
thread : 37316
threadName : MainThread
INFO:logger:hello,shouke
2021-01-01 17:51:54,661 mytest5.py 48 INFO : hello,shouke。

  

##问题:为什么mytest.py中的代码,不能放在study.py中?

如下,在study.py模块,MyLogHandler类之后追加下述代码

LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"format":"%(asctime)s %(filename)s %(lineno)s %(levelname)s : %(message)s",
}
},
"handlers": {
"customer_handler":{
"class":"study.MyLogHandler",
"formatter":"default",
"name":"LoggerHandler",
"other_attr":"something others"
}
},
"loggers": {
"customer_logger":{
"handlers": ["customer_handler"],
"level": logging.INFO,
"propagate": False,
}
}
} logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger('customer_logger')
logger.info('hello,shouke') 运行mytest.py,输出结果如下 初始化自定义日志处理器: LoggerHandler
其它属性值: something others
获取到的消息为: 2021-01-09 10:48:24,090 study.py 66 INFO : hello,shouke
process : 17276
processName : MainProcess
thread : 14516
threadName : MainThread
初始化自定义日志处理器: LoggerHandler
其它属性值: something others
获取到的消息为: 2021-01-09 10:48:24,090 study.py 66 INFO : hello,shouke
process : 17276
processName : MainProcess
thread : 14516
threadName : MainThread

  

如上,可以看到,自定义类构造函数被重复执行,日志消息被重复处理

##原因分析

logging.config.dictConfig(config)函数内部调用了DictConfigurator(config).configure(),而configure函数内DictConfigurator部,根据incremental,handlers等当前日志配置,被执行的分支代码中,会调用

DictConfigurator类实例的configure_handler()方法,该方法中,根据当前配置,又会再次调用DictConfigurator类实例的resolve(self, s)方法,参数s接收handler中class配置项目的值。具体代码如下:

class DictConfigurator(BaseConfigurator):
# ... 略
def resolve(self, s):
"""
Resolve strings to objects using standard import and attribute
syntax.
""" name = s.split('.') # 本例中,s为study.MyLogHandler
used = name.pop(0) # used 为study
try:
found = self.importer(used) # 这里importer = staticmethod(__import__) # 所以此处代码等同于__import__(used) # 这里导入自定义日志处理器所在模块会导致模块中的logging.config.dictConfig(config)代码再次被执行
for frag in name:
used += '.' + frag
try:
found = getattr(found, frag)
except AttributeError:
self.importer(used)
found = getattr(found, frag)
return found
except ImportError:
e, tb = sys.exc_info()[1:]
v = ValueError('Cannot resolve %r: %s' % (s, e))
v.__cause__, v.__traceback__ = e, tb
raise v def configure_formatter(self, config):
"""Configure a formatter from a dictionary."""
if '()' in config:
factory = config['()'] # for use in exception handler
try:
result = self.configure_custom(config)
except TypeError as te:
if "'format'" not in str(te):
raise
#Name of parameter changed from fmt to format.
#Retry with old name.
#This is so that code can be used with older Python versions
#(e.g. by Django)
config['fmt'] = config.pop('format')
config['()'] = factory
result = self.configure_custom(config)
else:
fmt = config.get('format', None)
dfmt = config.get('datefmt', None)
style = config.get('style', '%')
cname = config.get('class', None)
if not cname:
c = logging.Formatter
else:
c = _resolve(cname)
result = c(fmt, dfmt, style)
return result # ... 略
def configure_handler(self, config):
"""Configure a handler from a dictionary."""
config_copy = dict(config) # for restoring in case of error
formatter = config.pop('formatter', None)
if formatter:
try:
formatter = self.config['formatters'][formatter]
except Exception as e:
raise ValueError('Unable to set formatter '
'%r: %s' % (formatter, e))
level = config.pop('level', None)
filters = config.pop('filters', None) if '()' in config:
c = config.pop('()')
if not callable(c):
c = self.resolve(c)
factory = c
else:
cname = config.pop('class')
klass = self.resolve(cname)
#Special case for handler which refers to another handler
if issubclass(klass, logging.handlers.MemoryHandler) and\
'target' in config:
try:
th = self.config['handlers'][config['target']]
if not isinstance(th, logging.Handler):
config.update(config_copy) # restore for deferred cfg
raise TypeError('target not configured yet')
config['target'] = th
except Exception as e:
raise ValueError('Unable to set target handler '
'%r: %s' % (config['target'], e))
elif issubclass(klass, logging.handlers.SMTPHandler) and\
'mailhost' in config:
config['mailhost'] = self.as_tuple(config['mailhost'])
elif issubclass(klass, logging.handlers.SysLogHandler) and\
'address' in config:
config['address'] = self.as_tuple(config['address'])
factory = klass
props = config.pop('.', None)
kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
try:
result = factory(**kwargs) except TypeError as te:
if "'stream'" not in str(te):
raise
#The argument name changed from strm to stream
#Retry with old name.
#This is so that code can be used with older Python versions
#(e.g. by Django)
kwargs['strm'] = kwargs.pop('stream')
result = factory(**kwargs)
if formatter:
result.setFormatter(formatter)
if level is not None:
result.setLevel(logging._checkLevel(level))
if filters:
self.add_filters(result, filters)
if props:
for name, value in props.items():
setattr(result, name, value)
return result

  

至此,logging.config.dictConfig(config)放自定义日志处理类模块中,导致自定义日志处理类重复被执行的原因已经清楚了。

configure函数内部,根据incremental,handlers等当前日志配置,被执行的分支代码中,也可能执行DictConfigurator类实例的configure_formatter()方法,类似的,该方法中也会调用一个名为_resolve的方法,具体代码如下

def _resolve(name):
"""Resolve a dotted name to a global object."""
name = name.split('.')
used = name.pop(0)
found = __import__(used)
for n in name:
used = used + '.' + n
try:
found = getattr(found, n)
except AttributeError:
__import__(used)
found = getattr(found, n)
return found

  

如果自定义Formatter,把logging.config.dictConfig(config)放自定义日志格式化类模块中,也可能导致重复执行

Python 日志打印之自定义logger handler的更多相关文章

  1. python 日志打印之logging使用介绍

    python 日志打印之logging使用介绍 by:授客QQ:1033553122 测试环境: Python版本:Python 2.7   简单的将日志打印到屏幕 import logging lo ...

  2. Python 日志打印之logging.config.dictConfig使用总结

    日志打印之logging.config.dictConfig使用总结 By:授客 QQ:1033553122 #实践环境 WIN 10 Python 3.6.5 #函数说明 logging.confi ...

  3. Python 日志打印之logging.getLogger源码分析

    日志打印之logging.getLogger源码分析 By:授客 QQ:1033553122 #实践环境 WIN 10 Python 3.6.5 #函数说明 logging.getLogger(nam ...

  4. python(36):python日志打印,保存,logging模块学习

    1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message') logging.info('This is info messa ...

  5. python自定义logger handler

    _filefmt=os.path.join("logs","%Y-%m-%d.log") class MyLoggerHandler(logging.Handl ...

  6. python 以单例模式封装logging相关api实现日志打印类

    python 以单例模式封装logging相关api实现日志打印类   by:授客QQ:1033553122 测试环境: Python版本:Python 2.7   实现功能: 支持自由配置,如下lo ...

  7. python日志,支持彩色打印和文件大小切片写入和写入mongodb

    1.项目中使用了自定义的ColorHandler和MongoHandler,使用了内置的RotatingFileHandler和三方库的ConcurrentRotatingFileHandler. 支 ...

  8. 涨姿势:Java 分业务、分级别实现自定义日志打印

    自定义日志级别 通常的日志框架都有以下几个级别,从低到高TRACE,DEBUG,INFO,WARN,ERROR,FATAL. 默认情况,假如我们定义日志打印级别INFO,它会把大于等于INFO级别的日 ...

  9. python 项目实战之logging日志打印

    官网介绍:https://docs.python.org/2/library/logging.html 一. 基础使用 1.1 logging使用场景 日志是什么?这个不用多解释.百分之九十的程序都需 ...

随机推荐

  1. 容器编排系统之DaemonSet、Job和CronJob控制器

    前文我们了解了k8s上的pod控制器中的常用的两种控制器ReplicaSet和Deployment控制器的相关话题,回顾请参考:https://www.cnblogs.com/qiuhom-1874/ ...

  2. spark踩坑--WARN ProcfsMetricsGetter: Exception when trying to compute pagesize的最全解法

    spark踩坑--WARN ProcfsMetricsGetter: Exception when trying to compute pagesize的最全解法 问题描述 大概是今年上半年的时候装了 ...

  3. Mybatis-Plus的Service方法使用 之 泛型方法default <V> List<V> listObjs(Function<? super Object, V> mapper)

    首先 我们先看到的这个方法入参是:Function<? super Object , V> mapper ,这是jdk1.8为了统一简化书写格式引进的函数式接口 . 简单 解释一下我对Fu ...

  4. idea2020 没有 Autoscroll from Source

    2018版本: 2020版本: 最后在官网的网站中找到了解决方案,原来是改名了: 网址:https://intellij-support.jetbrains.com/hc/en-us/communit ...

  5. 根据来源编号对明细进行分组 跟库存做对比 用到的技术 list根据某个字段分组 Double Long 比较大小

    public R startProcess(@RequestBody ShouldCredentialPayable bean) { System.out.println("应付贷项参数be ...

  6. Hadoop支持的压缩格式对比和应用场景以及Hadoop native库

    对于文件的存储.传输.磁盘IO读取等操作在使用Hadoop生态圈的存储系统时是非常常见的,而文件的大小等直接影响了这些操作的速度以及对磁盘空间的消耗. 此时,一种常用的方式就是对文件进行压缩.但文件被 ...

  7. 【wp】2020XCTF_逆向

    前几天的XCTF最后一场终于打完了,三场比赛下来对逆向部分的大概感觉是从第一场的啥都不会做(一道lua+一道apk)到后来的终于能有参与度,至少后两场的题目都是pc逆向,虽然特殊架构但好歹能做(tcl ...

  8. Redis+LUA整合使用

    .前言 从本章节开始我们就开始讲解一些 Redis 的扩展应用了,之前讲的主从.哨兵和集群都相当重要,也许小公司用不到集群这么复杂的架构,但是也要了解各知识点的原理,只要了解了原理,无论什么时候是有, ...

  9. JVM内存模型总结,有各版本JDK对比、有元空间OOM监控案例、有Java版虚拟机,综合实践学习!

    作者:小傅哥 博客:https://bugstack.cn Github:https://github.com/fuzhengwei/CodeGuide/wiki 沉淀.分享.成长,让自己和他人都能有 ...

  10. 鸿蒙HarmonyOS应用开发落地实践,Harmony Go 技术沙龙落地北京

    12月26日,华为消费者BG软件部开源中心与51CTO Harmony OS技术社区携手,共同主办了主题为"Harmony OS 应用开发落地实践"的 Harmony Go 技术沙 ...