logging 日志输出乱码 info_14-1-logging模块及使用

本文详细介绍了Python的logging模块,包括日志级别、默认配置、Formatter、Handler和Logger对象的使用,以及如何通过配置字典实现日志的进阶使用。重点讲述了logger和handler级别设置对日志输出的影响,以及如何在多个文件间共享日志配置。
摘要由CSDN通过智能技术生成

54a4be1a80f7ce1f70832ae6a3d5783c.png

1.1、logging模块

 Python内置的日志模块,日志是对软件执行时所发生事件的一种追踪方式。软件开发人员对他们的代码添加日志调用,借此来指示某事件的发生。一个事件通过一些包含变量数据的描述信息来描述(比如:每个事件发生时的数据都是不同的)。开发者还会区分事件的重要性,重要性也被称为 等级 或 严重性。

正常的项目想看 程序状态变化,尽量不要用print(断点调试/日志)。任何语言、任何程序都要记录日志。

Python中的日志要么输出至控制台,要么保存至文件。

1.2、日志级别

CRITICAL = 50 #FATAL = CRITICAL
ERROR = 40
WARNING = 30 #WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0 #不设置

1.3、默认级别为warning,默认打印到终端

import logging

logging.debug('调试debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('错误error')
logging.critical('严重critical')

'''
WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical
'''

1.4、为logging模块指定全局配置,针对所有logger有效,控制打印到文件中

 可在logging.basicConfig()函数中通过具体参数来更改logging模块默认行为,可用参数有

(1)filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
(2)filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
(3)format:指定handler使用的日志显示格式。 
(4)datefmt:指定日期时间格式。 
(5)level:设置rootlogger(后边会讲解具体概念)的日志级别 
(6)stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

可指定的格式有:

145bcf9e24c9c22de4b4f3bd02cfcfca.png

实际应用

 执行程序为
    import logging
    logging.basicConfig(filename='access.log',
                        format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                        datefmt='请叫我龙哥-v-%Y*%m*%d %H:%M:%S %p',
                        level=10)
    logging.debug('调试debug')
    logging.info('消息info')
    logging.warning('警告warn')
    logging.error('错误error')
    logging.critical('严重critical')

#将filename='access.log'去掉就会往终端打印 
# 输出的日志打印到桌面改成gbk(其实就是写文件,默认变编码windows系统,所以打出来会乱码)
# 默认级别为warning,默认打印到终端

 输出的结果为:
    请叫我龙哥-v-2020*12*20 22:31:07 PM - root - DEBUG -1-日志模块:  调试debug
    请叫我龙哥-v-2020*12*20 22:31:07 PM - root - INFO -1-日志模块:  消息info
    请叫我龙哥-v-2020*12*20 22:31:07 PM - root - WARNING -1-日志模块:  警告warn
    请叫我龙哥-v-2020*12*20 22:31:07 PM - root - ERROR -1-日志模块:  错误error
    请叫我龙哥-v-2020*12*20 22:31:07 PM - root - CRITICAL -1-日志模块:  严重critical

1.5、 logging模块的Formatter,Handler,Logger,Filter对象

 logging模块的四个对象:
(1)logger:产生日志的对象

(2)Filter:过滤日志的对象

(3)Handler:接收日志然后控制打印到不同的地方,FileHandler用来打印到文件中,StreamHandler用来打印到终端

(4)Formatter对象:可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用,以此来控制不同的Handler的日志格式

如何使用

 logging模块的应用  
    '''
    critical=50
    error =40
    warning =30
    info = 20
    debug =10
    '''
import logging

(1)logger对象:负责产生日志,然后交给Filter过滤,然后交给不同的Handler输出
    logger=logging.getLogger(__file__)

(2)Filter对象:不常用,略

(3)Handler对象:接收logger传来的日志,然后控制输出到不同的位置。
    h1=logging.FileHandler('t1.log') #定义一个输出至当前目录文件t1.log的Handler对象.
    h2=logging.FileHandler('t2.log') #定义一个输出至当前目录文件t2.log的Handler对象.
    h3=logging.StreamHandler() # 定义一个输出至终端的Handler对象.

(4)Formatter对象:定义日志格式
    formmater1=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S %p',)

    formmater2=logging.Formatter('%(asctime)s :  %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S %p',)

    formmater3=logging.Formatter('%(name)s %(message)s',)

(5)为Handler对象绑定格式
    h1.setFormatter(formmater1)
    h2.setFormatter(formmater2)
    h3.setFormatter(formmater3)

(6)将Handler添加给logger并设置日志级别,1个logger对象可以绑定多个Handler,用以输出至不同位置。
    logger.addHandler(h1)
    logger.addHandler(h2)
    logger.addHandler(h3)
    logger.setLevel(10) # debug级别及以上的日志都会输出至两个日志文件和终端

(7)测试
    logger.debug('debug')
    logger.info('info')
    logger.warning('warning')
    logger.error('error')
    logger.critical('critical')

1.6、 Logger与Handler的级别

logger是第一级过滤,然后才能到handler,我们可以给logger和handler同时设置level,如果将logger等级设为debug,handler设为info,那么handler对象的输出不会包含debug级别的日志,因为debug < info

# 验证
import logging

# 定义一个Formatter对象
fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

# 定义一个标准输出的Handler对象-输出到终端
std = logging.StreamHandler()

# 将Formatter绑定给Handler,设定日志等级为20
std.setFormatter(fmt)
std.setLevel(20)

# 定义一个Logger名字为root,设定日志等级为10
log1 = logging.getLogger('root')
log1.setLevel(10)

# 将Handler对象绑定给Logger
log1.addHandler(std)

# 产生日志.
log1.debug('debug信息')
log1.info('info信息')

 输出的结果为:
    2020-12-21 16:50:38 PM - root - INFO -code:  info信息 
    可以看到debug信息并没有输出,因为Handler对象的等级为20,只有info及以上级别的信息会输出,debug会被过滤掉。

Formatter、Handler和Logger对象的使用顺序为:

(1)先产生Formatter对象,定义不同日志格式。

(2)产生Handler对象,定义不同的输出位置,定义Handler对象过滤日志的等级。

(3)将Formatter对象绑定给Handler对象,通常输出至终端的日志格式要比输出至文件的日志格式要精简。

(4)产生Logger对象,在括号内定义对象的名称,也就是%(name)s所输出的内容。

(5)将Handler对象绑定给Logger,一个Logger对象可以绑定多个Handler。

1.7、logging的进阶使用

通常我们会使用一个配置字典来定义日志。实例:

1、导入logging模块。

import os
import logging
import logging.config

2、定义三种日志输出格式。

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

3、定义日志文件位置。

# 动态获取目录
logfile_dir = os.path.dirname(os.path.dirname(__file__))
logfile_name = 'f1.log'  # 日志文件名

# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的完整路径
logfile_path = os.path.join(logfile_dir, logfile_name)

4、定义配置字典。通常会放在配置文件中,比如项目conf目录下settings文件中。

LOGGING_DIC = {
    'version': 1,                       # 配置字典的版本.
    'disable_existing_loggers': False,    # 是否关闭已存在的日志,默认False.
    'formatters': {                     # 先定义的多个Formatter对象。
        'standard': {                   #  Formatter对象standard,格式为上述变量内的值.
            'format': standard_format
        },
        'simple': {                     # Formatter对象simple,格式为上述变量内的值.
            'format': simple_format  
        },
    },
    'filters': {},                      # Filter对象,不常用.
    'handlers': {                       # 里面是定义的多个Handler对象。
        'console': {                    # 打印到终端的Handler对象console
            'level': 'DEBUG',           # 级别为DEBUG
            'class': 'logging.StreamHandler',   # 打印到屏幕
            'formatter': 'simple',          # 绑定Formatter对象为simple
        },
        'default': {                    # 打印到文件的Handler对象default。
            'level': 'DEBUG',            # 级别为DEBUG
            'class': 'logging.handlers.RotatingFileHandler',    # 保存到文件
            'formatter': 'standard',        # 绑定Formatter对象为standard
            'filename': logfile_path,       # 指定日志文件路径
            # 指定文件大小,单位为字节.
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5,         # 最大保存5个日志文件,多了就会把最旧的那个文件删除。
            'encoding': 'utf-8',  # 日志文件的编码,通常指定为utf-8
        },
    },
    'loggers': {            # 里面是定义的多个logger对象

        """
        ******此处 'handlers': ['terminal','file1_hanlder','file2_hanlder']与下面不一致意思以后可以自定义******
         '权限相关': {
         'handlers': ['terminal','file1_hanlder','file2_hanlder'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
         'level': 'DEBUG',
         'propagate': False,  # 向上(更高level的logger)传递
        },        
        """

        # ''所定义的配置就是logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': False,  # 向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
    },
}

5、在其他文件中导入配置字典,logging模块是个包,要使用里面的方法可以通过如下方式导入。

from logging import config
from logging import getLogger

# 导入配置字典
from conf import settings

6、加载配置

logging.config.dictConfig(settings.LOGGING_DIC)

7、生成logger对象,输出日志。

logger1 = logging.getLogger('name')
logger1.info('请叫我longge')

8、结果为:

# console Handler对象会输出至屏幕。
[INFO][2020-12-20 20:00:11,247][code.py:127]请叫我longge

# default Handler对象会输出至conf的父目录下f1.log文件中。
[2020-12-20 20:00:11,247][MainThread:9032][task_id:name][code.py:127][INFO][请叫我longge]

9、如果要在其他文件中使用该配置,那么也要重复执行导入配置字典操作,所以我们可以将导入字典这步操作写入字典所在文件中,将之放入一个函数,以后再导入该函数即可:

def get_logger(name):
    logging.config.dictConfig(LOGGING_DIC)
    logger = logging.getLogger(name)
    return logger

#为甚么要自己定义一个get_logger函数
(1)getlogger这个功能是别人写的,系统内置的模块,需要自定义一个函数,以后调用的时候方便,防止忘记这个功能
(2)以后有可能还要添加功能,这个时候就需要使用函数直接在上面添加即可

10、在其他文件中使用

from conf.settings import get_logger
logger1 = get_logger('name')
logger1.info('请叫我longge')

 所得到的的结果也相同,并且还能在产生logger对象时,可以在函数中自定义添加其他功能,更加方便使用。

参考文档:https://docs.python.org/zh-cn/3/library/logging.html?highlight=loggin#module-logging

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值