python logging模块 常用handlers
-
StreamHandler:流handler——包含在logging模块中的三个handler之一,主要用来屏幕打印日志。
-
FileHandler:logging模块自带的三个handler之一,继承自StreamHandler。将日志信息输出到磁盘文件上。
构造函数:
class logging.FileHandler(filename, mode='a', encoding=None, delay=False)
模式默认为append,delay为true时,文件直到emit方法被执行才会打开。默认情况下,日志文件可以无限增大。
-
NullHandler:空操作handler,logging模块自带的三个handler之一。 没有参数。
-
WatchedFileHandler:位于logging.handlers模块中。用于监视文件的状态,如果文件被改变了,那么就关闭当前流,重新打开文件,创建一个新的流。由于newsyslog或者logrotate的使用会导致文件改变。这个handler是专门为linux/unix系统设计的,因为在windows系统下,正在被打开的文件是不会被改变的。
参数和FileHandler相同:class logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)
-
RotatingFileHandler:位于logging.handlers模块中,按照日志文件大小循环生成新的日志文件。
class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0)
maxBytes表示单个文件大小的最大值,超过便生成新的文件;backupCount是备份数目,也就是生成的日志文件的最大数,当超过改数目时,会覆盖(或者说丢弃)最旧的日志文件。日志文件的命令方式为在最后面加.0、.1、…、.n。
-
TimedRotatingFileHandler:位于logging.handlers模块中,支持定时生成新日志文件。
class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)
参数when决定了时间间隔的类型,参数interval决定了多少的时间间隔。如when=‘D’,interval=2,就是指两天的时间间隔,backupCount决定了能留几个日志文件。超过数量就会丢弃掉老的日志文件。utc参数表示UTC时间。
-
logging.handlers.SocketHandler:远程输出日志到TCP/IP sockets
-
logging.handlers.DatagramHandler:远程输出日志到UDP sockets
-
logging.handlers.SMTPHandler:远程输出日志到邮件地址
-
logging.handlers.SysLogHandler:日志输出到syslog
-
logging.handlers.NTEventLogHandler:远程输出日志到Windows NT/2000/XP的事件日志
-
logging.handlers.MemoryHandler:日志输出到内存中的制定buffer
-
logging.handlers.HTTPHandler: 通过"GET"或"POST"远程输出到HTTP服务器
python打印字符串日志
import logging
from logging.handlers import RotatingFileHandler
from logging.handlers import DatagramHandler
from logging.handlers import SocketHandler
# 获取日志对象
logger = logging.getLogger()
# 配置打印的日志格式和内容
fmt = ('%(asctime)s - %(process)d - %(threadName)s - %(levelname)s - %(filename)s - %(lineno)d %(message)s')
formatter = logging.Formatter(fmt)
# 设置日志打印等级
logger.setLevel("INFO") # 选择自己的日志等级
# 屏幕打印设置
screen_handler = logging.StreamHandler()
screen_handler.setFormatter(formatter)
logger.addHandler(screen_handler)
# 将日志写到文件
# 选择自己的filename
file_handler = RotatingFileHandler(filename, maxBytes=30 * 1024 * 1024, backupCount=3)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# 将日志发送到服务器
# UDP形式发送
datagram_handler = NlogHandler(host, port) # 选择自己的host和port
datagram_handler.setFormatter(formatter)
logger.addHandler(datagram_handler)
# TCP形式发送
socket_handler = NlogHandlerTCP(host, port) # 选择自己的host和port
socket_handler.setFormatter(formatter)
logger.addHandler(socket_handler)
NlogHandler继承了DatagramHandler,作用是能够以主观希望的格式通过UDP将日志打到日志服务器上。
class NlogHandler(DatagramHandler):
def emit(self, record):
"""
Emit a record.
The record is formatted, and then sent to the syslog server. If
exception information is present, it is NOT sent to the server.
"""
try:
msg = self.format(record) + '\000' + "\n"
"""
We need to convert record level to lowercase, maybe this will
change in the future.
"""
if self.sock is None:
self.createSocket()
self.sock.sendto(msg.encode(), (self.host, self.port))
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
NlogHandlerTCP继承了SocketHandler,作用是能够以主观希望的格式通过TCP将日志打到日志服务器上。
class NlogHandlerTCP(SocketHandler):
def emit(self, record):
"""
Emit a record.
The record is formatted, and then sent to the syslog server. If
exception information is present, it is NOT sent to the server.
"""
try:
msg = self.format(record) + '\000' + "\n"
"""
We need to convert record level to lowercase, maybe this will
change in the future.
"""
if self.sock is None:
self.createSocket()
self.sock.sendto(msg.encode(), (self.host, self.port))
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
python打印json格式日志
import logging
import structlog
from structlog import configure, processors, stdlib, threadlocal
from pythonjsonlogger import jsonlogger
# 获取日志对象
logger = logging.getLogger()
# 配置打印的日志格式和内容
fmt = ('%(asctime)s - %(process)d - %(threadName)s - %(levelname)s - %(filename)s - %(lineno)d %(message)s')
# 将日志格式配置为json格式,并且输出时不要将中文转换成ascii值
formatter = jsonlogger.JsonFormatter(fmt=fmt, json_ensure_ascii=False)
# 设置日志打印等级
logger.setLevel("INFO")
# 屏幕打印设置
screen_handler = logging.StreamHandler()
screen_handler.setFormatter(formatter)
logger.addHandler(screen_handler)
# 将日志写到文件
# 选择自己的filename
file_handler = RotatingFileHandler(filename, maxBytes=30 * 1024 * 1024, backupCount=3)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# 将日志发送到服务器
# UDP形式发送
datagram_handler = NlogHandler(host, port) # 选择自己的host和port
datagram_handler.setFormatter(formatter)
logger.addHandler(datagram_handler)
# TCP形式发送
socket_handler = NlogHandlerTCP(host, port) # 选择自己的host和port
socket_handler.setFormatter(formatter)
logger.addHandler(socket_handler)
以上代码已经具备打印json格式日志的功能了,例如:
logging.info('你好')
{"asctime": "2019-07-25 12:46:34,979", "process": 17944, "threadName": "MainThread", "levelname": "INFO", "filename": "sturct_log_new.py", "lineno": 59, "message": "你好"}
这里我再进一步扩展python打印json日志的用法,以上输入的日志字段是固定,就是我们一开始设置的asctime、process、threadName、levelname、filename、lineno和message,但是如果我想要在日志中输出其他非特定的信息,如加一个key值为flag的,value为"标记"的信息,上面就没办法办到。由此,可以想到structlog模块,这个模块支持输出结构化的日志如json格式,并且bind()方法能够绑定一些字段进行输出。经过我的研习,接着上面的代码再添加下面这段代码:
configure(
context_class=threadlocal.wrap_dict(dict),
logger_factory=stdlib.LoggerFactory(),
wrapper_class=stdlib.BoundLogger,
processors=[
stdlib.filter_by_level,
# stdlib.add_logger_name,
# stdlib.add_log_level,
stdlib.PositionalArgumentsFormatter(),
# processors.TimeStamper(fmt="iso"),
processors.StackInfoRenderer(),
processors.format_exc_info,
processors.UnicodeDecoder(),
stdlib.render_to_log_kwargs]
)
能够实现更加高级的日志打印功能。
logger = structlog.getLogger()
logger.bind(province='江苏省', city='苏州市')
message = {"message": {"status": 200, "response_text": 'hello world'}}
logger.info(message, http='GET')
logger.info('你好')
{"asctime": "2019-07-25 12:59:24,160", "process": 100764, "threadName": "MainThread", "levelname": "INFO", "filename": "sturct_log_new.py", "lineno": 63, "message": {"status": 200, "response_text": "hello world"}, "province": "江苏省", "city": "苏州市", "http": "GET"}
{"asctime": "2019-07-25 12:59:24,160", "process": 100764, "threadName": "MainThread", "levelname": "INFO", "filename": "sturct_log_new.py", "lineno": 64, "message": "你好", "province": "江苏省", "city": "苏州市"}
这里再打印日志时,需要调用structlog的logger对象,logger对象一旦bind()一些字段和值后,每一条日志都会输出这些字段和值,可以通过unbind()解除;此外,每次打印日志时可以根据自己的需要临时性地输出一些信息,如上面的logger.info(http=‘GET’),如果不指定key,默认为message。因为输出的日志为json格式,再做一些统计时非常方便,我之所以会采用这种方式打印日志就是因为需要对日志做统计分析。
最后,值得一提的是在单进程环境下,使用上面两个 RotatingHandler 不会有问题。但是当有多个进程向同一个日志文件写入日志的时候,这两个 RotatingHandler 就会带来问题。