zl程序教程

您现在的位置是:首页 >  Python

当前栏目

python--日志系统

2023-02-19 12:20:17 时间

日志作用

通过log的分析,可以方便用户了解系统或软件、应用的运行情况;如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。

简单来讲就是,我们通过记录和分析日志可以了解一个系统或软件程序运行情况是否正常,也可以在应用程序出现故障时快速定位问题。比如,做运维的同学,在接收到报警或各种问题反馈后,进行问题排查时通常都会先去看各种日志,大部分问题都可以在日志中找到答案。再比如,做开发的同学,可以通过IDE控制台上输出的各种日志进行程序调试。对于运维老司机或者有经验的开发人员,可以快速的通过日志定位到问题的根源。可见,日志的重要性不可小觑。日志的作用可以简单总结为以下3点:程序调试

● 了解软件程序运行情况,是否正常

● 软件程序运行故障分析与问题定位

如果应用的日志信息足够详细和丰富,还可以用来做用户行为分析,如:分析用户的操作行为、类型洗好、地域分布以及其它更多的信息,由此可以实现改进业务、提高商业利益。

日志实现

通常大家在项目过程中,使用print进行简单的日志输出分析问题,但是输出一时爽,在最终要发布版本时,又要去查找print的代码行去进行删除。运行过程中我们也不可能时时刻刻盯着控制台分析。所以我们需要更专业的能够按级别控制日志输出,并且能够根据关键词,发生时间,发生代码行记录等日志库。

几乎所有开发语言都会内置日志相关功能,或者会有比较优秀的第三方库来提供日志操作功能,比如:log4j,log4php等。它们功能强大、使用简单。Python自身也提供了一个用于记录日志的标准库模块--logging。

下面我们就来介绍logging的使用

日志级别可以控制最终要输出对日志,在开发阶段我们可以控制输出所有日志,在运行阶段就只输出ERROR级别日志。日志级别严重程度:DEBUG<INFO<WARNING<ERROT<CRITICAL

日志等级(level)

描述

DEBUG

最详细的日志信息,典型应用场景是 问题诊断

INFO

信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作

WARNING

当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的

ERROR

由于一个更严重的问题导致某些功能不能正常运行时记录的信息

CRITICAL

当发生严重错误,导致应用程序不能继续运行时记录的信息

输出控制台

import logging
import logging.config

logging.debug('This is a debug message')
logging.info('This is a info message')
logging.warning('This is a warning message')
logging.error('This is a error message')

以上代码只会在控制台输出warning和error,因为默认的打印级别是warning。如果我们想输出所有日志,需要添加level=logging.DEBUG

logging.basicConfig(level=logging.DEBUG)
logging.debug('This is a debug message')
logging.info('This is a nomal message')
logging.warning('This is a warning message')

输出文件

以上代码日志会输出到控制台,如果我们想输出到文件呢?

# 写入文件,控制台不会输出
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT)
logging.debug('This is a debug message')
logging.info('This is a nomal message')
logging.warning('This is a warning message')

以上代码会生成一个my.log文件,日志输出写入文件,但是控制台不会输出。

输出控制台和文件

如果我们既要在文件输出,又想要在控制台打印呢?那就要创建自己的handle了。

首先可以看看basicConfig的实现:

def basicConfig(**kwargs):
    _acquireLock()
    try:
        if len(root.handlers) == 0:
            handlers = kwargs.pop("handlers", None)
            if handlers is None:
                # 如果handlers没有设置,流和文件名不能一起指定
                if "stream" in kwargs and "filename" in kwargs:
                    raise ValueError("'stream' and 'filename' should not be "
                                     "specified together")
            else:
                # 如果handlers不为空,流和文件名不能和文件名一起指定
                if "stream" in kwargs or "filename" in kwargs:
                    raise ValueError("'stream' or 'filename' should not be "
                                     "specified together with 'handlers'")
            # 没有设置handlers,如果设置了文件名就创建一个FileHandler,日志会输出到文件;否则创建一个StreamHandler,日志输出到控制台
            if handlers is None:
                filename = kwargs.pop("filename", None)
                mode = kwargs.pop("filemode", 'a')
                if filename:
                    h = FileHandler(filename, mode)
                else:
                    stream = kwargs.pop("stream", None)
                    h = StreamHandler(stream)
                handlers = [h]
            dfs = kwargs.pop("datefmt", None)
            style = kwargs.pop("style", '%')
            if style not in _STYLES:
                raise ValueError('Style must be one of: %s' % ','.join(
                                 _STYLES.keys()))
            fs = kwargs.pop("format", _STYLES[style][1])
            fmt = Formatter(fs, dfs, style)
            # 依次添加handlers进行处理
            for h in handlers:
                if h.formatter is None:
                    h.setFormatter(fmt)
                root.addHandler(h)
            level = kwargs.pop("level", None)
            if level is not None:
                root.setLevel(level)
            if kwargs:
                keys = ', '.join(kwargs.keys())
                raise ValueError('Unrecognised argument(s): %s' % keys)
    finally:
        _releaseLock()

可以看到内部会依次对加入的handlers做处理,下面我们创建输入到文件大handler和输出到控制台的handler

    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    BASIC_FORMAT = "%(asctime)s:%(levelname)s:%(message)s"
    DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
    formatter = logging.Formatter(BASIC_FORMAT, DATE_FORMAT)
    # 输出到控制台的handler
    chlr = logging.StreamHandler()
    chlr.setFormatter(formatter)
    chlr.setLevel(logging.INFO)
    
    # 输出到文件的handler
    fhlr = logging.FileHandler('file.log')
    fhlr.setFormatter(formatter)
    fhlr.setLevel(logging.WARNING)
    
    logger.addHandler(chlr)
    logger.addHandler(fhlr)
    
    logging.debug('This is a debug message')
    logging.info('This is a info message')
    logging.warning('This is a warning message')
    logging.error('This is a error message')

以上代码,日志就可以既输出到文件,又输出到控制台了。

实例

结合我们上问介绍的配置文件,我们也可以对日志添加配置项,配置打印日志的级别,日志的handle等。

日志配置文件:

[loggers]
keys=root,main

[handlers]
keys=consoleHandler,fileHandler

[logger_root]
level=INFO
handlers=consoleHandler

[logger_main]
level=INFO
qualname=main
handlers=fileHandler

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=fmt
args=(sys.stdout,)

[handler_fileHandler]
class=logging.handlers.RotatingFileHandler
level=INFO
formatter=fmt
args=('file_log.log','a',100*1024*1024,5,'utf-8',)

[formatters]
keys=fmt

[formatter_fmt]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
# 文件配置方式
# 读取日志配置文件内容
# 创建一个日志器logger
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('main')

# 日志输出
logger.debug('This is a debug message')
logger.info('This is a info message')
logger.warning('This is a warning message')
logger.error('This is a error message')

颜色彩蛋

在使用print输出时我们可以在打印时添加颜色配置。

格式:

开头部分:\033[显示方式;前景色;背景色m + 结尾部分:\033[0m

常见开头格式:

\033[0m 默认字体正常显示,不高亮

\033[32;0m 红色字体正常显示

\033[1;32;40m 显示方式: 高亮 字体前景色:绿色 背景色:黑色

\033[0;31;46m 显示方式: 正常 字体前景色:红色 背景色:青色

数值表示的参数含义:

显示方式: 0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、 5(闪烁)、25(非闪烁)、7(反显)、27(非反显)

前景色: 30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋 红)、36(青色)、37(白色)

背景色: 40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋 红)、46(青色)、47(白色)

实例:

def print_color():
    print('\033[1;31m红色字体\033[0m')
    print('\033[1;32m绿色字体\033[0m')
    print('\033[1;33m黄色字体\033[0m')

使用logging,同样可以添加颜色配置,安装和使用colorlog库。

实例:

def log_color():
    from colorlog import ColoredFormatter

    formatter = ColoredFormatter(
        "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s",
        datefmt=None,
        reset=True,
        log_colors={
            'DEBUG': 'cyan',
            'INFO': 'green',
            'WARNING': 'yellow',
            'ERROR': 'red',
            'CRITICAL': 'red,bg_white',
        },
        secondary_log_colors={},
        style='%'
    )
    console_handle = colorlog.StreamHandler()
    console_handle.setFormatter(formatter)
    logger = logging.getLogger()
    logger.addHandler(console_handle)

    logger.debug('This is a debug message')
    logger.info('This is a info message')
    logger.warning('This is a warning message')
    logger.error('This is a error message')

参考:

https://www.cnblogs.com/yyds/p/6901864.html

https://www.cnblogs.com/yyds/p/6885182.html