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 ...
随机推荐
- struts.xml 文件添加DTD文件
在编辑struts.xml 文件时,“alt + /”无提示信息,需要在myeclipse 中添加消息头中的文件,步骤如下: 1. 选中该段复制 2. Preferences——>XML Cat ...
- .net 非阻塞事件获取返回异步回调结果
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Redis 实例排除步骤
Redis 应用案例 - 在问题中不断成长 原创 2017-02-05 杜亦舒 本文翻译整理自 Andy Grunwald 发布的一篇文章,写的是作者所在公司使用 Redis 时遇到的问题,以及处理 ...
- [elk]es增删改查最佳实战
PUT app01 GET app01/_settings GET _all/_settings PUT app01/_settings { "number_of_replicas" ...
- python之pilow验证码
pilow的基本操作 """ Created on Fri Jun 1 12:36:38 2018 @author: Frank """ f ...
- 解决错误: ios property has a previous declaration
今天维护一个项目的时候,我新添加了一个库,运行的时候报错了: ios property has a previous declaration 上网查了一下没有找到思路,不过根据提示自己试了一下,原来这 ...
- shader 编程入门(一)
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨) ...
- 转-subl配置全栈开发环境
为 Sublime Text 3 设置 Python 的全栈开发环境 Sublime Text 3 (ST3) 是一个轻量级的跨平台文字编辑器,尤以其轻快的速度,易用性和强大的社区支持而著称.它一经面 ...
- mysql获取插入的id主键
long _retId = -1; Connection con = null; try { con = queryRunner.getDataSource().getConnection(); qu ...
- jsp中判断对象是否存在
<!-- 如果user对象存在,则显示用户名,如果不存在,则显示空值--> <input type="text" id="userName" ...