python日志

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 就会带来问题。

上一篇:Django的日志操作,记录一下自己的使用


下一篇:力扣做题17-电话号码的字母组合