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 ...
随机推荐
- Solr 缓存配置
http://www.blogjava.net/xiaohuzi2008/archive/2012/12/03/392376.html
- ADB 高级应用
一.利用无线来查看adb shell > adb tcpip 5555 连接: > adb connect IP:5555 见后文<调试注意事项> 二.模拟按键 ...
- 网络中常见的ping命令协议
ICMP是"Internet Control Message Ptotocol"(Internet控制消息协议)的缩写.它是TCP/IP协议族的一个子协议,用于在IP主机.路由器之 ...
- ubuntu命令改变文档权限和所有者
chgrp :改变档案所属群组 chown :改变档案拥有者 chmod :改变档案的权限, SUID, SGID, SBIT等等的特性,可读.可写.可执行 1 chgrp 例子 chgrp [-R] ...
- swift -变量的定义与使用
使⽤用let来声明常量,使⽤用var来声明变量. ⼀一个常量的值在编译时并不须要获取,可是你仅仅能为它赋值⼀一次.也就是说你能够⽤用常量来表⽰示这样⼀一个值:你仅仅须要决定⼀一次,可是须要使⽤用非常多 ...
- Oracle 版本号说明
Oracle 的版本非常多,先看11g的一个版本说明: 注意: 在oracle 9.2 版本号之后, oracle 的maintenance release number 是在第二数字位更改. 而在之 ...
- Ubuntu 12.04下PostgreSQL-9.1安装与配置详解(在线安装) [转]
说明: 我是用root用户在终端登陆的,如果是非root用户,那在命令前需要加上"sudo",你懂的... 第一步:在Ubuntu下安装Postgresql ...
- a5调试
1 generating rsa key...[ 4.452000] mmc0: error -110 whilst initialising SD card[ 5.602000] mmc ...
- Ehcache2 的配置(不使用配置文件)
EhCache是一个开放源码的,基于标准的高速缓存系统. 网上关于EhCache的使用配置很多,但是一般是基于配置文件的.但是实际应用中.我们可能需要动态的管理缓存,这时候单纯配置文件就不够用了. 所 ...
- [转]Mac系统安装软件提示软件已损坏的解决办法
很多朋友下载本站提供的Mac软件资源,安装提示“xxx软件已损坏,打不开,您应该将它移到废纸篓”的提示,其实并不是软件本身有问题,而是Mac系统的一个安全机制问题,按照如下方法操作,即可打开并安装本站 ...