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) 收藏 举报