【flask系列(二)】:flask日志系统

573 阅读4分钟

本篇博文主要讲解在flask框架中如何集成日志系统、通过具体事例讲解加载配置文件的多种方式,旨在帮助对flask中日志系统不熟悉,不会配置日志的小伙伴们简单快速的配置日志。 本系列往期博文:

一、flask框架中集成配置文件

flask框架中集成日志系统是通过 logger属性,该属性是一个缓存的property属性(通过 @cached_property装饰器修饰,是一个flask内部自定义的类),该属性内部调用 create_logger()函数,返回python内置模块 logger对象

@cached_property
def logger(self) -> logging.Logger:
    return create_logger(self)  # 调用create_logger()函数

1.1 create_logger函数内部实现

在create_logger()函数中,发现系统是通过 app.name属性来获取日志对象,如果日志对象没有设置日志等级或者日志处理器,那么就使用默认的等级(Debug)和 默认处理器(StreamHandler,控制台输出日志),如果想在系统中使用flask中logger对象,那么在日志配置文件中,添加 app.name属性对应的日志对象

@LocalProxy
def wsgi_errors_stream() -> t.TextIO:
    if request:
        return request.environ["wsgi.errors"]  # type: ignore[no-any-return]

    return sys.stderr


def has_level_handler(logger: logging.Logger) -> bool:
    level = logger.getEffectiveLevel()
    current = logger

    while current:
        if any(handler.level <= level for handler in current.handlers):
            return True

        if not current.propagate:
            break

        current = current.parent  # type: ignore

    return False
    
default_handler = logging.StreamHandler(wsgi_errors_stream)  # type: ignore
default_handler.setFormatter(
    logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
)

# 创建内置的logger对象
def create_logger(app: App) -> logging.Logger:
    logger = logging.getLogger(app.name)

    if app.debug and not logger.level:
        logger.setLevel(logging.DEBUG)

    if not has_level_handler(logger):
        logger.addHandler(default_handler)

    return logger

1.2 app.name属性由来

在flask中,app.name也是一个缓存的property属性,其属性值是依据 import_name 属性而来,大致可以理解成 app.name属性值就是 import_name 属性值。

@cached_property
def name(self) -> str:  # type: ignore
    if self.import_name == "__main__":
        fn: str | None = getattr(sys.modules["__main__"], "__file__", None)
        if fn is None:
           return "__main__"
      return os.path.splitext(os.path.basename(fn))[0]
   return self.import_name

二、flask项目加载日志配置文件

如果对flask项目中配置对象不熟悉的小伙伴,可以看看这期博文《flask系列(二):flask项目加载配置文件》

日志对象字典格式:
LOGGING_DIC = {
        'version': 1,  # 表示日志的版本
        'disable_existing_loggers': False,  # 表示是否禁用已经存在的日志记录对象
        # 指定日志格式化器
        'formatters': {
            'standard': {
                'format': """[%(threadName)s:%(thread)d][task_id:%(name)s][%(asctime)s][%(pathname)s][%(filename)s:%(lineno)d][%(levelname)s]%(message)s"""  # 其中name为getLogger()指定的名字;lineno为调用日志输出函数的语句所在的代码行
            },
            'simple': {
                'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
            },
        },

        # 指定日志过滤器
        'filters': {},  # filter可以不定义

        # 指定日志处理器
        'handlers': {
            # 打印到终端的日志
            'console': {
                'level': 'DEBUG',
                'class': 'logging.StreamHandler',  # 打印到屏幕
                'formatter': 'simple'
            },
            # 打印到文件的日志,收集info及以上的日志
            'default': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件, 指定日志记录器
                'formatter': 'standard',
                'filename': os.path.join(os.path.dirname(os.path.dirname(__file__)), 'flasklog/日志记录.log'),  # 日志文件
                'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M  (*****)
                'backupCount': 10,
                'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
            },
        },

        # 指定日志记录对象
        'loggers': {
            # 这里键名是根据flask实例化时, import_name参数传入的值
            'flaskone.app': {
                # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
                'handlers': ['default', 'console'],
                'level': 'INFO',  # 这里的等级的优先级要高于 handlers配置项里面的等级
                'propagate': False,  # 向上(更高level的logger)传递
           },
       },
   }

2.1 使用config.from_object方法加载日志配置文件

配置文件结构如图: 配置文件结构 日志文件加载如下:

def create_app():
    """创建应用"""
    app = Flask(__name__, instance_relative_config=True)

    # 通过环境变量加载配置文件
    env = os.environ.get("FLASK_ENV", "development")
    app.config.from_object(get_settings(env))  # 通过from_object()方法加载配置文件

    dictConfig(app.config.get("LOGGING_DIC"))
    app.logger.info("日志已经加载了")

    # 添加命令行
    register_cli(app)

    return app

2.2 使用config.from_mapping方法加载日志配置文件

通过from_mapping加载配置文件

2.3 使用config.from_file方法加载日志配置文件

以toml文件为例: toml文件的配置 日志文件加载如下: 通过from_file方法加载日志文件

2.4 使用config.from_pyfile方法加载日志文件

日志文件结构如下: 日志文件结构 日志文件加载如下: from_pyfile加载配置文件

三、使用日志对象记录日志

flask框架中的logger属性,返回的是python标准库logging中logger对象,可以使用logging模块中定义的日志记录方法来记录日志,flask框架中没有额外的定义日志记录方法。

四、小结

本篇文章主要介绍了flask框架中日志系统的集成,以及介绍了多种加载日志文件的方式。在项目中,日志通常是写在配置文件中,但是也是变相的在介绍配置文件的加载方式。希望小伙伴根据项目的实际情况,灵活的运用。如果本片文章中出现讲解错误 或者 讲解不合理的地方,请大家在评论区留言,让我们一起变得更强。