Python 日志模块的定制
Python标准logging模块中主要分为四大块内容:
Logger: 定义应用程序使用的接口
Handler: 将Loggers产生的日志输出到目的地
Filter: 对Loggers产生的日志进行过滤
Formatter: 格式化Loggers产生的日志
其中常用的是Logger, Handler, Formatter。
目标
对logging模块进行封装,使其尽量达到几个目标:
- 易用性:尽量保持python原生标准的用法。
- 扩展性:避免将所有模块集中在一个文件中进行配置,而是对不同模块继承扩展。如此可以确保修改时尽量保持简洁性,而非混淆杂乱。
- 文字加色:同一时间段内出现大量日志输出时,可以更清晰地识别出问题所在。
- 输出多个目的地:在生产代码中,并非所有日志信息都输出到console或者file中,而是日志的重要级别,将那些比较重要的发送到console,将那些不太重要但以后分析问题时可能又会用到的发送到file中。
- 非阻塞发送器:记录日志时如果涉及到网络(如socket, smtp),那么这个记录过程可能会阻塞当前线程,在python 3.2+,我们可以非常容易地设计一个非阻塞的发送器(当然了,非阻塞机制的内部实现大多都是队列)。
目录结构
如下的定制日志主要包括两个包,log_config和tools,目录结构如下:

log_config 模块结构

默认配置
- Logging Level:
logging.NOTSET is default.
You can change it by passing ‘level=’ to BTLogger instance in your application code.
- Sending Level:
Send logging messages to console with levels of logging.INFO and higher, and simultaneously to the disk file with levels of logging.DEBUG and higher.
You can change them by passing ‘level=’ to BTStreamHandler/BTFileHandler instance in log_config\__init__.py.
- Log Config:
LOG_PATH = r'C:\log'
LOG_NAME = r'mylogger.log'
You can change them in log_config\__init__.py.
- Console Text Color:
DEBUG
INFO
WARNING
ERROR
CRITICAL
You can change them by passing ‘fore_color=’
and ‘back_color=’ to decorator @text_attribute.set_textcolor(fore_color, back_color)
in log_config\__init__.py.
- There
also exists a NonBlockingHandler class in log_config\handlers.py, which
will use features of version 3.2+. If you unfold it, you can get a
non_blocking_handler.
Demo
ts_log_config.py:
def log_test():
import log_config logger = log_config.BTLogger('mylogger') logger.debug('Debug message')
logger.info('Info message')
logger.warning('Warning message')
logger.error('Error message')
logger.critical('Critical message') def main():
log_test() if __name__ == '__main__':
main()
outputs:

代码实现
..\log_config\__init__.py:
"""
Wrap the classes of logging module so as to expose more flexible interfaces that application code directly uses. By default, when you do some log operations, it will log messages to console with levels of logging.INFO and higher,
and simultaneously to the disk file with levels of logging.DEBUG and higher. Usages:
To use log_config module, simply 'import log_config' in your code, and create a BTLogger instance, then, you can use this
instance to log messages:
import log_config logger = log_config.BTLogger('mylogger') logger.debug('Debug message')
logger.info('Info message')
logger.warning('Warning message')
logger.error('Error message')
logger.critical('Critical message')
""" import os
import sys
import logging
from log_config import formatters
from log_config import handlers
from log_config import text_attribute
from tools import os_tools __author__ = " "
__status__ = "debugging" # {debugging, production}
__version__ = "0.1.0" # major_version_number.minor_version_number.revision_number
__date__ = " " """
# Config root logger
basicConfig(
filename=__name__,
filemode='w',
format='%(asctime)s: %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level='logging.NOTSET'
)
""" # Config log path and file name.
LOG_PATH = r'C:\log'
LOG_NAME = r'mylogger.log'
filename = 'default.log' if not os_tools.makedir(LOG_PATH) else os.path.abspath(os.path.join(LOG_PATH, LOG_NAME)) class BTLogger(logging.Logger):
def __init__(self, name=None, level=logging.NOTSET):
"""
Initialize the BTLogger with basic settings. :param
name: Specify the logger's name. If name is None, default is root logger.
Note:
All BTLogger instances with a given name return the same logger
instance. This means that logger instances never need to be passed
between different parts of an application.
level: The threshold of logging records, messages which are less severe
than level will be ignored.
If the level is set to NOTSET, than:
1> If current logger is root logger, than all messages will
be logged.
2> Or, delegate to its parent logger, until an ancestor with
a level other than NOTSET is found, or the root is reached.
Note:
The root logger is created with level logging.WARNING.
:return:
None
"""
logging.Logger.__init__(self, name, level) # Create handlers
# non_blocking_ch = handlers.NonBlockingHandler(handlers.BTStreamHandler(sys.stdout, logging.DEBUG)).get_handler() # version 3.2+
console_handler = handlers.BTStreamHandler(stream=sys.stdout, level=logging.INFO)
file_handler = handlers.BTFileHandler(filename=filename, level=logging.DEBUG, mode='w') # Set formatters to handlers.
# non_blocking_ch.setFormatter(formatters.BTStreamFormatter()) # version 3.2+
console_handler.setFormatter(formatters.BTStreamFormatter())
file_handler.setFormatter(formatters.BTFileFormatter()) # Add the handlers to logger.
# self.addHandler(non_blocking_ch) # version 3.2+
self.addHandler(console_handler)
self.addHandler(file_handler) # Override methods in logging.Logger(debug, info, warning, error, critical).
# @handlers.NonBlockingHandler.non_blocking # version 3.2+
@text_attribute.set_textcolor('FOREGROUND_BLACK', 'BACKGROUND_WHITE')
def debug(self, msg, *args, **kwargs):
self.log(logging.DEBUG, msg, *args, **kwargs) @text_attribute.set_textcolor('FOREGROUND_BLACK', 'BACKGROUND_GREEN')
def info(self, msg, *args, **kwargs):
self.log(logging.INFO, msg, *args, **kwargs) @text_attribute.set_textcolor('FOREGROUND_BLACK', 'BACKGROUND_YELLOW')
def warning(self, msg, *args, **kwargs):
self.log(logging.WARNING, msg, *args, **kwargs) warn = warning @text_attribute.set_textcolor('FOREGROUND_BLACK', 'BACKGROUND_RED')
def error(self, msg, *args, **kwargs):
self.log(logging.ERROR, msg, *args, **kwargs) @text_attribute.set_textcolor('FOREGROUND_BLACK', 'BACKGROUND_RED')
def critical(self, msg, *args, **kwargs):
self.log(logging.CRITICAL, msg, *args, **kwargs) fatal = critical
..\log_config\formatters.py:
"""
Formatters specify the output format of logging messages.
""" from logging import Formatter # Changed in version 3.2+: The 'style' parameter was added.
'''
class BTStreamFormatter(Formatter):
def __init__(self,
fmt='[%(asctime)s] [%(levelname)s]: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S', style='%'):
Formatter.__init__(self, fmt, datefmt, style) class BTFileFormatter(Formatter):
def __init__(self,
fmt='%(asctime)s: %(levelname)s: %(message)s',
datefmt='%Y/%m/%d %H:%M:%S', style='%'):
Formatter.__init__(self, fmt, datefmt, style)
''' # Be suitable for version 3.2-.
class BTStreamFormatter(Formatter):
def __init__(self,
fmt='[%(asctime)s] [%(levelname)s]: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'):
Formatter.__init__(self, fmt, datefmt) class BTFileFormatter(Formatter):
def __init__(self,
fmt='%(asctime)s: %(levelname)s: %(message)s',
datefmt='%Y/%m/%d %H:%M:%S'):
Formatter.__init__(self, fmt, datefmt)
..\log_config\handlers.py:
"""
Handlers send logging messages(created by BTLogger) to the specific destination.
""" import sys
import logging
from logging import StreamHandler
from logging import FileHandler class BTStreamHandler(StreamHandler):
"""
Write logging records to a stream(e.g., console). Note that this class does not close the stream automatically,
as sys.stdout or sys.stderr may be used.
"""
def __init__(self, stream=sys.stderr, level=logging.INFO):
"""
Initialize the handler. :param
stream: If stream is not specified, sys.stderr is used.
:return
None
"""
StreamHandler.__init__(self, stream)
self.setLevel(level) # The threshold of handling records, default is logging.INFO. class BTFileHandler(FileHandler):
"""
Write logging records to disk files. Inherited from logging.FileHandler, only modify the default mode('a') to ('w').
"""
def __init__(self,
filename='default.log',
level=logging.DEBUG, # Handle messages with levels of logging.DEBUG and higher to file.
mode='w',
encoding=None,
delay=False):
FileHandler.__init__(self, filename, mode, encoding, delay)
self.setLevel(level) # New in version 3.2+: QueueHandler, QueueListener.
# Unfold the following block, and you will get a non_blocking_handler.
'''
from queue import Queue
from logging.handlers import QueueHandler
from logging.handlers import QueueListener
from functools import wraps class NonBlockingHandler(object):
"""
Let logging handlers do their work without blocking the thread you're logging from.
Especially those network-based handler can block, e.g., SocketHandler, SMTPHandler.
"""
def __init__(self, handler_instance):
self.queue = Queue(-1)
self.queue_handler = QueueHandler(self.queue)
self.handler_instance = handler_instance
global _queue_listener
_queue_listener = QueueListener(self.queue, self.handler_instance) def get_handler(self):
return self.handler_instance @classmethod
def non_blocking(cls, func):
@wraps(func)
def wrapper(*args, **kwargs):
_queue_listener.start()
res = func(*args, **kwargs)
_queue_listener.stop()
return res return wrapper
'''
..\log_config\text_attribute.py:
"""
Set the text attributes in console. Currently, you can only set font color. # -----------------------------------------
# Author:
# Created:
# Modified:
# Version: 0.1.0
# ----------------------------------------- Usages:
When you want to design a function which outputs messages to console with different colors, simply use '@text_attribute.set_textcolor(fore_color, back_color)' to decorate your function, e.g.:
@text_attribute.set_textcolor('FOREGROUND_BLACK', 'BACKGROUND_RED')
def func(*args, **kwargs):
# output some messages to console
# ... func(...)
""" import ctypes
from functools import wraps STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12 _CONSOLE_TEXT_COLOR = {
'FOREGROUND_RED': 0x04,
'FOREGROUND_GREEN': 0x02,
'FOREGROUND_BLUE': 0x01,
# 'FOREGROUND_INTENSITY': 0x08,
'FOREGROUND_WHITE': 0x07,
'FOREGROUND_BLACK': 0x00,
'FOREGROUND_YELLOW': 0x06, 'BACKGROUND_RED': 0x40,
'BACKGROUND_GREEN': 0x20,
'BACKGROUND_BLUE': 0x10,
# 'BACKGROUND_INTENSITY': 0x80,
'BACKGROUND_WHITE': 0x70,
'BACKGROUND_BLACK': 0x00,
'BACKGROUND_YELLOW': 0x60
} class TextColor(object):
"""
Set console text color.
"""
std_output_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) @staticmethod
def text_color(fore_color, back_color):
fore_color = 'FOREGROUND_WHITE' if fore_color not in _CONSOLE_TEXT_COLOR else fore_color
back_color = 'BACKGROUND_BLACK' if back_color not in _CONSOLE_TEXT_COLOR else back_color
return _CONSOLE_TEXT_COLOR[fore_color] | _CONSOLE_TEXT_COLOR[back_color] @staticmethod
def set(fore_color='FOREGROUND_WHITE', back_color='BACKGROUND_BLACK', handle=std_output_handle):
is_set = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, TextColor.text_color(fore_color, back_color))
return is_set @staticmethod
def unset():
return TextColor.set() def set_textcolor(fore_color='FOREGROUND_WHITE', back_color='BACKGROUND_BLACK'): def decorate(func):
"""
A decorator which can set the color of console message, include foreground and background.
:param
func: The original function which will be wrapped by a wrapper.
:return:
wrapper
"""
@wraps(func)
def wrapper(*args, **kwargs):
TextColor.set(fore_color, back_color)
res = func(*args, **kwargs)
TextColor.unset()
return res return wrapper return decorate
..\tools\__init__.py: None
..\tools\os_tools.py:
"""
Originated from os module. # -----------------------------------------
# Author:
# Created:
# Modified:
# Version: 0.1.0
# -----------------------------------------
""" import os def makedir(path):
"""
Recursive directory creation, include all intermediate-level directories needed to contain the leaf directory. If specific path already exists, do nothing. Or path is not a directory, return False.
"""
try:
os.makedirs(path, 0o777)
# os.makedirs(path, 0o777, False) # Version 3.2+
except OSError:
if not os.path.isdir(path): # Path is not a valid directory.
return False # Path already exists.
return True
else: # Path does not exist.
return True
Python 日志模块的定制的更多相关文章
- python日志模块logging
python日志模块logging 1. 基础用法 python提供了一个标准的日志接口,就是logging模块.日志级别有DEBUG.INFO.WARNING.ERROR.CRITICAL五种( ...
- python日志模块
许多应用程序中都会有日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系 统的运行状况进行跟踪.在.NET平台中,有非常著名的第三方开源日志组件log4net,c++中,有人们熟悉的log4c ...
- Python 日志模块实例
python 打印对象的所有属性值: def prn_obj(obj): print '\n'.join(['%s:%s' % item for item in obj.__dict__.it ...
- Python日志模块logging用法
1.日志级别 日志一共分成5个等级,从低到高分别是:DEBUG INFO WARNING ERROR CRITICAL. DEBUG:详细的信息,通常只出现在诊断问题上 INFO:确认一切按预期运行 ...
- python日志模块的使用
学习一下python的日志模块logging,可以参考如下博客,写得很详细 https://www.cnblogs.com/yyds/p/6901864.html https://www.cnblog ...
- python日志模块logging学习
介绍 Python本身带有logging模块,其默认支持直接输出到控制台(屏幕),或者通过配置输出到文件中.同时支持TCP.HTTP.GET/POST.SMTP.Socket等协议,将日志信息发送到网 ...
- python日志模块笔记
前言 在应用中记录日志是程序开发的重要一环,也是调试的重要工具.但却很容易让人忽略.之前用flask写的一个服务就因为没有处理好日志的问题导致线上的错误难以察觉,修复错误的定位也很困难.最近恰好有时间 ...
- Python 日志模块详解
前言 我们知道查看日志是开发人员日常获取信息.排查异常.发现问题的最好途径,日志记录中通常会标记有异常产生的原因.发生时间.具体错误行数等信息,这极大的节省了我们的排查时间,无形中提高了编码效率.所以 ...
- Python日志模块的管理(二)
日志模块可以通过封装一个类,也可以通过配置文件取管理 新建1个log.ini文件 [loggers] keys=root [handlers] keys=fileHandler,streamHandl ...
随机推荐
- HTML-HTML5+CSS3权威指南阅读(四、媒体查询)
1.媒体类型 HTML 4和CSS 2目前支持为不同的媒体类型设定专有的样式表, 比如, 一个页面在屏幕上显示时使用无衬线字体, 而在打印时则使用衬线字体, screen 和 print 是两种已定义 ...
- 让python cookie支持特殊字符
让python cookie支持特殊字符 先对cookie做下简单的介绍 cookie的作用: tcp协议能够通过三次握手建立连接.client发送的多次请求能够通过句柄维护同一个连接.可是http协 ...
- MySQL加速查询速度的独门武器:查询缓存
[导读] 与朋友或同事谈到MySQL查询缓存功能的时候,个人喜欢把查询缓存功能Query Cache比作荔枝, 是非常营养的东西,但是一次性吃太多了,就容易上火而流鼻血,虽然不是特别恰当的比喻,但是有 ...
- Myeclipse中误报错误解决办法
下午写jsp页面的时候,用了一个js文件,拖到MyEclipse下了报错,开始还以为是js文件问题,折腾了半天,后来才知道原来是Myeclipse误报错误.真坑爹啊呀~~ 解决方法: 点击你需要忽略错 ...
- 在Windows7和Ubuntu上编译安装MICO
MICO是CORBA标准的一个实现.开源并且被广泛使用. 首先的首先,看用户手册,在页面"http://www.mico.org/docu.html"找到一本教材"MIC ...
- ECC加密算法原理入门介绍
前言 同RSA(Ron Rivest,Adi Shamir,Len Adleman三位天才的名字)一样,ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)也属于公开 ...
- CentOS 7下Java的SecureRandom种子初始化失败解决办法
io.netty.util.internal.ThreadLocalRandom getInitialSeedUniquifierWARNING: Failed to generate a seed ...
- 某某水表-M1卡数据算法分析
# 某某水表-M1卡数据算法分析 ## 卡片数据-----------------------------扇区数据 | 金额:--- |:---13EC 0000 0000 0000 0000 000 ...
- MapReduce编程实例4
MapReduce编程实例: MapReduce编程实例(一),详细介绍在集成环境中运行第一个MapReduce程序 WordCount及代码分析 MapReduce编程实例(二),计算学生平均成绩 ...
- JS——简单的正则表达式验证
<!-- 用户注册:结构层:html;表现层:css;行为层:javascript; html利用ul,li来构造: 注意事项:1.每个Input都要有相应的id,这是在js中去调用的. 2.& ...