专栏文章 测试平台系列 (3) 给 Hello World 添加日志

米洛 · 2021年11月25日 · 最后由 迷龙 回复于 2021年11月26日 · 3223 次阅读

给 Hello World 添加日志

回顾

通过上篇内容,我们已经使用Flask完成了我们的第一个接口。我们可以看到,使用Flask来编写接口是十分简单的。那么接下来,我们丰富一下上面的例子。

需求

现在的需求来了,在我们平时的开发过程中,总会遇到一些问题。但是又不是必现的,所以我们可以通过一些手段来写入日志,去发现问题。一个很明显的例子就是 try/exception,遇到未知问题的时候可以将问题记录至日志并在事后排查。


日志模块选用

  • logging

我们知道,在 Python 中有系统自带的功能及其强大的 logging 模块供我们使用,它的强大不多做介绍了。网上的 demo 很多,稍微 copy 一个日志类就能使用。

  • logbook

这里我采用的是 github 上的logbook库,单看 api 调用的话,比 logging 方便较多。可能因为我是个喜欢尝鲜的人_^

安装 logbook

在终端输入pip3 install logbook并回车。

分类

日志模块属于 pity 的工具类,笔者将之划分到 app/utils 下。

编写日志类

import logbook

from app import pity


class Log(object):
    handler = None

    def __init__(self, name='pity', filename=pity.config['LOG_NAME']):  # Logger标识默认为app
        """
        :param name: 业务名称
        :param filename: 文件名称
        """
        self.handler = logbook.FileHandler(filename, encoding='utf-8')
        self.logger = logbook.Logger(name)
        self.handler.push_application()

    def info(self, *args, **kwargs):
        return self.logger.info(*args, **kwargs)

    def error(self, *args, **kwargs):
        return self.logger.error(*args, **kwargs)

    def warning(self, *args, **kwargs):
        return self.logger.warning(*args, **kwargs)

    def debug(self, *args, **kwargs):
        return self.logger.debug(*args, **kwargs)

在这里,我们定义了一个Log类,这个类接受的 name 是日志的分类,如果不传入则默认为pity,filename(日志文件名) 默认值为引入 config.py 中配置的LOG_NAME

然后将 logbook 中的 handler 设为写入的文件,并将 info/error/warning/debug 等常用方法封装在 Log 类中。

修改 run.py

from server.app import app
from server.app.utils.logger import Log

@app.route('/')
def hello_world():
    log = Log("hello world专用")
    log.info("有人访问你的网站了")
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

运行 run.py

进入浏览器输入http://localhost:5000

报错截图

发现居然报错了,我们仔细查看下控制台:

控制台报错信息

看提示是没有找到对应的文件或目录, 原来我们的 pity 目录下没有logs目录, 而这种日志库一般也不会帮忙创建目录,所以我们暂时手动在 pity 目录下建立 logs 目录即可。

建立以后重启服务, 再次尝试

成功截图

查看 logs/pity.log 文件

可以看到,日志文件配置生效,妈妈再也不用担心我意外出错了。


将日志类改为单例模式 (选修课)

  • 编辑 utils/decorator.py
'''
    这是一个装饰器方法文件
'''


class SingletonDecorator:
    def __init__(self, cls):
        self.cls = cls
        self.instance = None

    def __call__(self, *args, **kwds):
        if self.instance is None:
            self.instance = self.cls(*args, **kwds)
        return self.instance

如图所示,这是一个单例类的装饰器。首先判断该类的实例是否是 None,为 None 的话则生成新实例,否则返回该实例。这样就确保了只生成一次实例。

当然这只是一个办法,可能在多线程的情况下会出问题。后续的优化和了解,就交给同学们自己了。

  • 给 Log 类加上装饰器
import logbook

from app import pity
from .decorator import SingletonDecorator


# 注意这里
@SingletonDecorator
class Log(object):
    handler = None

    def __init__(self, name='pity', filename=pity.config['LOG_NAME']):  # Logger标识默认为app
        """
        :param name: 业务名称
        :param filename: 文件名称
        """
        self.handler = logbook.FileHandler(filename, encoding='utf-8')
        self.logger = logbook.Logger(name)
        self.handler.push_application()

    def info(self, *args, **kwargs):
        return self.logger.info(*args, **kwargs)

    def error(self, *args, **kwargs):
        return self.logger.error(*args, **kwargs)

    def warning(self, *args, **kwargs):
        return self.logger.warning(*args, **kwargs)

    def debug(self, *args, **kwargs):
        return self.logger.debug(*args, **kwargs)

作业

发现一个小问题,日志的时间似乎不对,可能是哪里出了问题呢?

全部代码地址: https://github.com/wuranxu/pity

今天第二更,希望没有影响首页的大佬们=。=

共收到 4 条回复 时间 点赞

无敌哥赛高

我去催饭 回复

榜一大哥 nb!

无敌哥🐂🍺

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册