本篇博文主要讲解在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方法加载日志配置文件
2.3 使用config.from_file方法加载日志配置文件
以toml文件为例:
日志文件加载如下:
2.4 使用config.from_pyfile方法加载日志文件
日志文件结构如下:
日志文件加载如下:
三、使用日志对象记录日志
flask框架中的logger属性,返回的是python标准库logging中logger对象,可以使用logging模块中定义的日志记录方法来记录日志,flask框架中没有额外的定义日志记录方法。
四、小结
本篇文章主要介绍了flask框架中日志系统的集成,以及介绍了多种加载日志文件的方式。在项目中,日志通常是写在配置文件中,但是也是变相的在介绍配置文件的加载方式。希望小伙伴根据项目的实际情况,灵活的运用。如果本片文章中出现讲解错误 或者 讲解不合理的地方,请大家在评论区留言,让我们一起变得更强。