基于Flask的优雅日志记录

前言

在之前的文章中我们讲过Flask项目的创建Flask项目的部署。但在实际项目的运行中,少补了会发生一些我们无法预知的错误。而这个时候日志的输出就发挥着重要的左右。本文将基于Flask服务对日志格式就行修改,并按日期作为文件名进行本地化存储。

Logging

讲到日志,我们总不能每一次都print()这么low的对吧?所以这里先介绍一下logging模块。它提供一套完成的日志API,可以完美与我们的Flask服务契合。

日记级别

这里日志级别其实已经是全球国际惯例了,基本所有的服务(不管什么语言)都是这么定义的:

  • CRITICAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG

配置

logging主要是由一个API来创建自定义日志格式的,那就是basicConfig。这里点进去看看函数,发现可以填这些参数

filename  Specifies that a FileHandler be created, using the specified
          filename, rather than a StreamHandler.
filemode  Specifies the mode to open the file, if filename is specified
          (if filemode is unspecified, it defaults to 'a').
format    Use the specified format string for the handler.
datefmt   Use the specified date/time format.
style     If a format string is specified, use this to specify the
          type of format string (possible values '%', '{', '$', for
          %-formatting, :meth:`str.format` and :class:`string.Template`
          - defaults to '%').
level     Set the root logger level to the specified level.
stream    Use the specified stream to initialize the StreamHandler. Note
          that this argument is incompatible with 'filename' - if both
          are present, 'stream' is ignored.
handlers  If specified, this should be an iterable of already created
          handlers, which will be added to the root handler. Any handler
          in the list which does not have a formatter assigned will be
          assigned the formatter created in this function.

如果只是简单使用的话,对应的参数实际意义其实也非常明显了。但这里额外讲讲format这个参数的定义。这里表格是搬运而来的,大佬还是整理的完整(原文地址,支持原创

参数描述
%(name)sLogger的名字
%(levelno)s数字形式的日志级别
%(levelname)s文本形式的日志级别
%(pathname)s调用日志输出函数的模块的完整路径名,可能没有
%(filename)s调用日志输出函数的模块的文件名
%(module)s调用日志输出函数的模块名
%(funcName)s调用日志输出函数的函数名
%(lineno)d调用日志输出函数的语句所在的代码行
%(created)f当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d线程ID。可能没有
%(threadName)s线程名。可能没有
%(process)d进程ID。可能没有
%(message)s用户输出的消息

改造

讲了那么多有的没的,现在开始动手在我们的flask应用中改造优雅的日志输出。

初始化配置

首先在自己的配置包中创建一个新的py文件,这里我命名为LogHandler.py。然后里面是代码,具体每一行我都有注释的,大家可以直接复制或个性修改。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File  : LogHandle.py
# @Author: Richard Chiming Xu
# @Date  : 2021/1/18
# @Desc  :

import os
import logging
import time
from logging.handlers import RotatingFileHandler

# log配置,实现日志自动按日期生成日志文件
def make_dir(make_dir_path):
    path = make_dir_path.strip()
    if not os.path.exists(path):
        os.makedirs(path)

def getLogHandler():
    # 日志地址
    log_dir_name = "Logs"
    # 文件名,以日期作为文件名
    log_file_name = 'logger-' + time.strftime('%Y-%m-%d', time.localtime(time.time())) + '.log'
    # 创建日志文件
    log_file_folder = os.path.abspath(
        os.path.join(os.path.dirname(__file__), os.pardir)) + os.sep + log_dir_name
    make_dir(log_file_folder)
    log_file_str = log_file_folder + os.sep + log_file_name

    # 默认日志等级的设置
    logging.basicConfig(level=logging.WARNING)
    # 创建日志记录器,指明日志保存路径,每个日志的大小,保存日志的上限
    file_log_handler = RotatingFileHandler(log_file_str, maxBytes=1024 * 1024, backupCount=10, encoding='UTF-8')
    # 设置日志的格式                   发生时间    日志等级     日志信息文件名      函数名          行数        日志信息
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
    # 将日志记录器指定日志的格式
    file_log_handler.setFormatter(formatter)

    # 日志等级的设置
    # file_log_handler.setLevel(logging.WARNING)

    return file_log_handler

添加配置

创建完日志的配置文件之后,我们还需要在flask app创建的时候把我们的配置文件给注册到flask logger里面。这里我就简短只标重点好了。

    # 创建Flask应用
    app = Flask(__name__)
	# 添加日志配置
    app.logger.addHandler(LogHandle.getLogHandler())
    # 激活上下文
    ctx = app.app_context()
    ctx.push()

打印日志

添加完成之后,我们只需要在代码中引入flask app,然后使用就可以了。我一般会在上面配置中激活上下文,然后引入current_app使用。注意:这里flask app必须要上下文衔接统一(一个线程中)。经测试,使用multiprocessing开启多线程是无法使用的。

current_app.logger.info('我打印了一个日志输出')

最终的日志打印格式如下:

2021-01-26 08:47:14,393 - INFO - 类名.py - 方法名 - 行数 - 日志内容

请求日志拦截

再深化一点点,flask其实有提供api的拦截器。大致就是拦截所有的请求,然后先统一执行我们这段代码,再执行对应api逻辑。我们可以通过这个特性配合我们的日志输出,最后进行统一的request日志打印,从而节省大量的重复代码。

# 每一次请求前执行
@app.before_request
def log_each_request():
    app.logger.info('【请求方法】{}【请求路径】{}【请求地址】{}'.format(request.method, request.path, request.remote_addr))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值