flask十九:日志

一.原生日志logging

log日志四大组件:

  • logger:提供应用程序代码,直接使用的接口
  • handlers:用于将日志记录发送到指定的位置。处理者、处理方式。
    • FileHandler:logging.FileHandler,将日志输出到文件
    • RotatingHandler:logging.handlers.RotatingHandler,日志回滚方式,支持日志文件最大数量和日志文件回滚
    • SMTPHandler:logging.handlers.SMTPHandler,远程输出日志到邮件地址
    • HTTPHandler:logging.handlers.HTTPHandler,通过GET、POST远程输出到HTTP服务器
  • Filters:更细的日志过滤功能,用于决定哪些日志将被输出。过滤器。
  • Formatters:日志输出格式

示例:

import logging

logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("logs/trade_new.log")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

 

添加过滤器:

my_filter = logging.Filter(__name__)   # 过滤器:只输出什么内容,如只输出包含(__name__)文件名称的日志
handler.addFilter(my_filter)

 

示例2:以时间切割日志,重新FileHandler

from logging import FileHandler
import os
import errno
import datetime

class MidnightRotatingFileHandler(FileHandler):
    def __init__(self, filename):
        self._filename = filename
        self._rotate_at = self._next_rotate_datetime()
        super(MidnightRotatingFileHandler, self).__init__(filename, mode='a')

    @staticmethod
    def _next_rotate_datetime():
        # rotate at midnight
        now = datetime.datetime.now()
        return now.replace(hour=0, minute=0, second=0) + datetime.timedelta(days=1)

    def _open(self):
        now = datetime.datetime.now()
        log_today = "%s.%s" % (self._filename, now.strftime('%Y-%m-%d'))
        try:
            # create the log file atomically
            fd = os.open(log_today, os.O_CREAT | os.O_EXCL)
            # if coming here, the log file was created successfully
            os.close(fd)
        except OSError as e:
            if e.errno != errno.EEXIST:
                # should not happen
                raise
        self.baseFilename = log_today
        return super(MidnightRotatingFileHandler, self)._open()

    def emit(self, record):
        now = datetime.datetime.now()
        if now > self._rotate_at:
            # time to rotate
            self._rotate_at = self._next_rotate_datetime()
            self.close()
        super(MidnightRotatingFileHandler, self).emit(record)

 

当多进程创建新的日志文件时,是否会丢失很小一部分日志?

由于 uWSGI 的多个 worker 可能同时也可能相差很小一段时间对日志文件进行回滚操作,这会导致先前回滚的存档日志被后来的 worker 的回滚操作覆盖,导致日志丢失。

如何原子性地创建文件?

以 os.O_CREAT | os.O_EXCL 模式打开文件就可以了。如果日志文件已经存在,打开文件就失败。

 

二.Flask日志

同django的日志一样定义,settings.py

LOGER_DIR = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOGER_DIR):
    os.makedirs(LOGER_DIR)
LOGGING = {
    'version': 1,  # 版本
    'disable_existing_loggers': False,  # 是否禁用其它的logger
    # 自定义日志格式,这里定义了3种格式
    'formatters': {
        'standard': {
            'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                      '[%(levelname)s][%(message)s]'
        },
        'simple': {
            'format': '[%(asctime)s][%(levelname)s][%(pathname)s:%(filename)s:%(lineno)d]%(message)s'
        },
        'collect': {
            'format': '%(message)s'
        }
    },
    # 过滤器:是否开启或关闭debug,根据settings.py里面的DEBUG值来决定(require_debug_true)
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    # 自定义handler
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],  # 只有在Django debug为True时才在屏幕打印日志
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(LOGER_DIR, "all.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 10,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(LOGER_DIR, "error.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        'collect': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(LOGER_DIR, "collect.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'collect',
            'encoding': "utf-8"
        },
        'robot': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(LOGER_DIR, "robot.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': "utf-8"
        },
        'robot_run': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(LOGER_DIR, "robot_run.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': "utf-8"
        },
        'tax': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(LOGER_DIR, "tax.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': "utf-8"
        },
    },
    # 代码入口: 自定义了几种logger,默认logger是''
    'loggers': {
        # 默认的logger应用如下配置
        '': {
            # 'handlers': ['default', 'error', 'console'],  # 上线之后可以把'console'移除
            'handlers': ['default', 'error'],  # 上线之后可以把'console'移除
            'level': 'DEBUG',
            'propagate': True,  # 是否继承父类日志信息
        },
        # 名为 'collect'的logger还单独处理
        'collect': {
            'handlers': ['console', 'collect'],
            'level': 'INFO',
        },
        # 名为 'robot'的logger还单独处理
        'robot': {
            'handlers': ['console', 'robot'],  # 上线之后可以把'console'移除
            # 'handlers': ['robot'],
            'level': 'DEBUG',
            'propagate': True,  # 是否继承父类日志信息
        },
        # 名为 'robot'的logger还单独处理
        'robot_run': {
            'handlers': ['console', 'robot_run'],  # 上线之后可以把'console'移除
            # 'handlers': ['robot'],
            'level': 'DEBUG',
            'propagate': True,  # 是否继承父类日志信息
        },
        # 名为 'tax'的logger还单独处理
        'tax': {
            'handlers': ['console', 'tax'],  # 上线之后可以把'console'移除
            # 'handlers': ['tax'],
            'level': 'DEBUG',
        }
    },
}

 

posted on 2020-06-23 13:05  myworldworld  阅读(503)  评论(0)    收藏  举报

导航