Python的Logging模块

news2024/11/15 21:31:38

在这里插入图片描述

1.日志的相关概念🍃

日志是指记录系统或应用程序运行状态、事件和错误信息的文件或数据。在计算机系统中,日志通常用于故障排除、性能分析、安全审计等方面。日志可以记录各种信息,如系统启动和关闭时间、应用程序的运行状态、用户登录和操作记录、网络通信数据等。通过分析日志,可以了解系统或应用程序的运行情况,及时发现问题并进行处理

1.1 日志的作用🌵

  1. 记录系统或应用程序的运行情况,错误信息,警告信息,调试信息
  2. 可以快速定位问题,分析系统或应用程序的运行情况
  3. 分析用户的操作行为,类型喜好,地域分布等信息
  4. 监控系统资源使用情况,统计访问量等

1.2 日志的等级⚓️

日志级别使用场景
DEBUG用于调试阶段,输出详细的调试信息,通常不会在生产环境中使用
INFO用于输出程序运行的一般信息,例如程序启动、停止等
WARNING用于输出警告信息,例如程序运行时出现了一些不严重的问题,但需要引起注意
ERROR用于输出错误信息,例如程序运行时出现了一些严重的问题,需要及时处理
CRITICAL用于输出严重的错误信息,例如程序崩溃、系统崩溃等,需要立即处理

1.3 日志字段信息与日志格式🔅

日志字段信息是指日志中记录的各种信息,例如时间,IP地址,请求方法,请求URL,响应状态码,响应时间等等。而日志格式则是指日志记录的排版方式,即每个字段信息在日志中的位置,格式和分隔符等.

常见的日志格式有以下几种:

  1. Common Log Format (CLF):最常见的日志格式,包含了时间、请求方法、请求URL、协议版本、响应状态码和响应大小等信息
  2. Combined Log Format:在CLF的基础上增加了用户代理、来源地址和Referer等信息
  3. W3C Extended Log Format:比CLF和Combined Log Format更加详细,可以记录更多的信息,例如请求头、响应头、Cookie等
  4. JSON格式:将日志信息以JSON格式进行记录,可以更加灵活地处理和分析日志数据

2.Python的Logging模块简介🍃

2.1 基本概念🌾

Python的Logging模块是Python标准库中的一个模块,用于记录程序运行时的日志信息。它提供了一种灵活的方式来控制日志记录的级别、格式和输出位置等。使用Logging模块可以帮助我们更好地理解程序的运行情况,以及在出现问题时更方便地进行调试和排查。

Logging模块中最基本的组件是Logger对象,它负责记录日志信息。Logger对象可以设置日志级别、输出格式和输出位置等属性。另外,Logging模块还提供了一些Handler对象,用于将日志信息输出到不同的位置,比如控制台、文件、网络等。Formatter对象则用于设置日志输出的格式。

使用Logging模块记录日志信息的基本流程如下:

  1. 创建Logger对象,设置日志级别和输出格式等属性
  2. 创建Handler对象,设置输出位置和输出格式等属性
  3. 将Handler对象添加到Logger对象中
  4. 使用Logger对象记录日志信息
#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   1.logging模块的基础使用.py
@Time    :   2023/06/26 17:25:45
@Author  :   haohe
'''
import logging

# 创建logger对象
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)

# 创建FileHandler对象
fh = logging.FileHandler('mylog.log')
fh.setLevel(logging.DEBUG)

# 创建Formatter对象
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)

# 将FileHandler对象添加到Logger对象中
logger.addHandler(fh)

# 记录日志信息
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')

在这里插入图片描述

在上面的代码样例中,创建了一个名为"mylogger"的logger对象,并设置了日志级别为DEBUG。然后创建一个FileHandler对象,将日志信息输出到名为"mylog.log"的文件中,并设置了输出格式。最后我们将FileHandler对象添加到Logger对象中,并使用Logger对象记录了5条不同级别的日志信息

2.2 使用场景🎍

logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日志级别,但是这是不被推荐的,尤其是在开发供别人使用的库时,因为这会导致日志级别的混乱

日志等级(level)描述
DEBUG最详细的日志信息,典型应用场景是问题诊断
INFO信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切按照预期
WARNING当某些意外的事情发生时记录的信息(如磁盘可用空间较低),但是此时应用程序正常运行
ERROR由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CAITICAL当发生严重错误,导致应用程序不能继续运行时记录的信息

开发应用程序或部署开发环境时,可以使用DEBUG或INFO级别的日志获取尽可能详细的日志信息来进行开发或部署调试;应用上线或部署生产环境时,应该使用WARNING或ERROR或CRITICAL级别的日志来降低机器的I/O压力和提高获取错误日志信息的效率。日志级别的指定通常都是在应用程序的配置文件中进行指定的。

说明:

​ 上面列表中的日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的;
​ 当为某个应用程序指定一个日志级别后,应用程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的日志信息,nginx、php等应用程序以及这里要提高的python的logging模块都是这样的。同样,logging模块也可以指定日志记录器的日志级别,只有级别大于或等于该指定日志级别的日志记录才会被输出,小于该等级的日志记录将会被丢弃

2.3 使用Logging模块记录日志👻

2.3.1 最简单的日志输出✈️

import logging

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

--->输出
WARNING:root:This is a warning log.
ERROR:root:This is a error log.
CRITICAL:root:This is a critical log.

分析输出内容

  1. 为什么没有打印debug和info日志

    `因为logging模块提供的日志记录函数所使用的日志器设置的日志级别是WARNING,因此只有WARNING级别的日志记录以及大于它的ERROR和CRITICAL级别的日志记录被输出了,而小于它的DEBUG和INFO级别的日志记录被丢弃了
    
  2. 打印出的日志字段意思

    `日志级别:日志器名称:日志内容
    
  3. 为什么是这样的格式输出

    `logging模块提供的日志记录函数所使用的日志器设置的日志格式默认是BASIC_FORMAT,其指为:"%(levelname)s:%(name)s:%(message)s"
    
    `扩展:Logging的其他样式
    '%(asctime)s - %(levelname)-10s - %(filename)s - %(funcName)s:%(lineno)d - %(message)s'
    时间 - 日志级别 - 文件名称 - 函数名称:代码位置(行号) - 日志内容
    

2.3.2 修改默认设置🌴

logging.basicConfig()函数说明

logging.basicConfig(*kwargs)

该函数可接收的关键字参数如下:

参数名称描述
filename指定日志输出目标文件的文件名,指定该参数后日志信息就不会输出到控制台上
filemode指定日志文件的打开模式,默认为’a’.需要注意的是,该选项要在filename被指定时才有效
format指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序
datefmt指定日志记录中日期和时间的格式,该选项要在format中包含时间字段%(asctime)s时才有效
level指定日志器的日志级别
stream指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
style指定format格式字符串的风格,可取值为’%‘、’{‘和’$‘,默认为’%’
handlers创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常

2.3.3 格式字符串字段🌴

字段/属性名称使用格式描述
asctime%(asctime)s日志事件发生的事件–人类可读时间
created%(created)f日志事件发生的时间–时间戳,就是当时调用time.time()函数返回的值
relativeCreated%(relativeCreated)d日志事件发生的时间相对于logging模块加载时间的相对毫秒数
msecs%(msecs)d日志事件发生事件的毫秒部分
levelname%(levelname)s该日志记录的文字形式的日志级别(‘DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’)
levelno%(levelno)s该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
name%(name)s所使用的日志器名称,默认是’root’,因为默认使用的是 rootLogger
message%(message)s日志记录的文本内容,通过 msg % args计算得到的
pathname%(pathname)s调用日志记录函数的源码文件的全路径
filename%(filename)spathname的文件名部分,包含文件后缀
module%(module)sfilename的名称部分,不包含后缀
lineno%(lineno)d调用日志记录函数的源代码所在的行号
funcName%(funcName)s调用日志记录函数的函数名
process%(process)d进程ID
processName%(processName)s进程名称,Python 3.1新增
thread%(thread)d%(thread)d
threadName%(thread)s线程名称

2.3.4 配置日志输出🐥

1.简单配置日志器的日志级别

import logging
# 配置日志器的日志级别为debug,高于debug或等于debug的都会报出
logging.basicConfig(level=logging.DEBUG)
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')

在这里插入图片描述

2.在配置日志器日志级别的基础上,配置日志输出目标文件和日志格式

import logging
# 定义日志输出格式
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 log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

在这里插入图片描述

3.在上面的基础上,再来设置日期/时间格式

import logging
# 定义日志输出格式
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"
logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT,datefmt=DATE_FORMAT)
logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

在这里插入图片描述

2.3.5 配置说明

  1. logging.basicConfig()函数是一个一次性的简单配置工具,也就是说只有在第一次调用该函数时会起作用,后续再次调用该函数时完全不会产生任何操作,多次调用的设置并不是累加操作

  2. 日志器(logger)是Python中logging模块的核心组件之一,它负责产生日志记录。在使用logging模块时,我们需要创建一个或多个日志器对象,然后使用这些日志器对象来记录日志信息

    日志器对象可以通过调用logging.getLogger(name)方法来创建,其中name参数是一个字符串,用于标识日志器对象的名称。如果多次调用该方法并传入相同的name参数,将返回同一个日志器对象

    日志器对象可以设置多个处理器(handler),每个处理器可以将日志记录输出到不同的位置,如控制台、文件、网络等。日志器对象还可以设置日志级别(level),只有日志级别高于等于该级别的日志记录才会被处理器处理。

    在使用日志器对象记录日志时,可以调用其不同级别的方法,如logger.debug()、logger.info()、logger.warning()、logger.error()、logger.critical(),分别对应不同的日志级别

  3. 如果要记录的日志中包含变量数据,可使用一个格式字符串作为这个时间的描述信息(loggin.debug,logging.info函数的第一个参数),然后将变量数据作为第二个参数*args的值进行传递

    logging.warning('%s is %d years old.','Tom',10)
    ==>
    WARNING:root:Tom is 10 years old.
    
  4. logging.debug()logging.info()等方法的定义中,除了msg和args参数外,还有一个**kwargs参数,支持3个关键字参数:

    • exc_info:其值为布尔值,如果该参数的值设置为True,则会将异常信息添加到日志信息中,如果没有异常信息则将None添加到日志信息中
    • stack_info:其值为布尔值,默认值为False。如果该参数的值为Ture,栈信息将会被添加到日志信息中
    • extra:这是一个字典(dict)参数,可以用来自定义消息格式中所包含的字段,但是它的key不能与logging模块定义的字段冲突
    import logging
    # 定义日志输出格式
    LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
    DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"
    logging.basicConfig(format=LOG_FORMAT,datefmt=DATE_FORMAT)
    logging.warning("Some one delete the log file.",exc_info=True,stack_info=True,extra={'user':'Tom','ip':'192.168.1.1'})
    

在这里插入图片描述

3.Logging模块日志处理流程🍃

3.1 Logging日志模块的四大组件🌧

组件名称对应类名功能描述
日志器Logger提供应用程序可一直使用的接口
处理器Handler指定Logger创建的日志记录输入方式
过滤器Filter提供了更细粒度的控制工具来决定保留那条日志
格式器Formatter决定日志记录的最终输出格式

各种组件之间的关系描述:

日志器(Logger)需要通过处理器(Handler)将日志信息输出到目标位置,如文件,终端或者网络等. 日志器(Logger)可以设置多个处理器(Handler)将同一条日志记录输出到不同的位置。每个处理器(Handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志,每个处理器(Handler)都可以设置自己的格式器(Formatter)实现同一条日志以不同的格式输出到不同的地方

3.2 Logging日志模块相关类及其常用方法介绍⛈

3.2.1 Logger类🏝

`Logger对象是Python中logging模块的核心组件之一,它用于记录应用程序的日志信息。Logger对象可以创建多个实例,每个实例都有一个唯一的名称,用于标识不同的日志记录器。Logger对象可以通过调用其方法来记录不同级别的日志信息,例如debug、info、warning、error和critical等级别的信息。Logger对象还可以设置日志记录的格式、输出位置和级别等属性,以满足不同的日志记录需求。在工作中,Logger对象通常用于记录应用程序的运行状态、错误信息和调试信息等,以便开发人员和运维人员能够及时发现和解决问题

常用的方法如下:

1.设置日志的配置方法🌨

`设置日志器将会处理最低严重级别的日志消息
Logger.setLevel()
# 关于Logger.setLevel()方法的说明:
# 内建等级中,级别最低的是DEBUG,级别最高的是CRITICAL。例如setLevel(logging.INFO),此时函数参数为INFO,那么该logger将只会处理INFO、WARNING、ERROR和CRITICAL级别的日志,而DEBUG级别的消息将会被忽略/丢弃
`为该logger对象添加和移除一个handler对象
Logger.addHandler() / Logger.removeHandler()
`为该logger对象添加和移除一个filter对象
Logger.addFilter()  / Logger.removeFilter()

2.创建日志记录的方法​🌨

`创建一个与它们的方法名对应等级的日志记录
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical()
`创建一个类似于Logger.error()的日志消息
Logger.exception()
`需要获取一个明确的日志level参数来创建一个日志记录
Logger.log()

"说明:"
# Logger.exception()与Logger.error()的区别在于:Logger.exception()将会输出退栈追踪信息,另外通常只是在一个exception handler中调用该方法

# Logger.log()与Logger.debug(),Logging.info()等方法相比,虽然需要多传一个level参数,显得不是那么方便,但是当需要记录自定义level的日志时还是需要使用Logger.log()方法完成

​那么,怎样得到一个Logger对象呢?一种方式是通过Logger类的实例化方法创建一个Logger类的实例,但是我们通常都是用第二种方式–logging.getLogger()方法。

​logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,如果不提供该参数,则其值为’root’ 若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用

3.2.2 Handler类🏝

`Handler对象的作用是(基于日志消息的level)将消息分发到Handler指定的位置(文件,网络,邮件等).Logger对象可以通过addHandler()方法为自己添加0个或多个Handler对象.比如,一个应用程序可能想要实现以下几个日志需求:
1)把所有日志都发送到一个日志文件中;
2)把所有严重级别大于等于error的日志发送到stdout(标准输出);
3)把所有严重级别为critical的日志发送到一个email邮件地址。
这种场景就需要3个不同的handlers,每个handler复杂发送一个特定严重级别的日志到一个特定的位置

常用的方法如下:

1.设置日志的配置方法🌨

`设置handler将会处理的日志消息的最低级别
Handler.setLevel()
`为handler设置一个格式器对象
Handler.setFormatter()
`为handler添加和删除一个过滤器对象
Handler.addFilter() / Handler.removeFilter()

2.实例化和使用Handler对象方法🌧

Handler名称方法作用
StreamHandlerlogging.StreamHandler日志输出到流,可以是sys.stderr,sys.stdout或者文件
FileHandlerlogging.FileHandler日志输出到文件
BaseRotatingHandlerlogging.handlers.BaseRotatingHandler基本的日志回滚方式
RotatingHandlerlogging.handlers.RotatingHandler日志回滚方式,支持日志文件最大数量和日志文件回滚
TimeRotatingHandlerlogging.handlers.TimeRotatingHandler日志回滚方式,在一定时间区域内回滚日志文件
SocketHandlerlogging.handlers.SocketHandler远程输出日志到TCP/IP sockets
DatagramHandlerlogging.handlers.DatagramHandler远程输出日志到UDP sockets
SMTPHandlerlogging.handlers.SMTPHandler远程输出日志到邮件地址
SysLogHandlerlogging.handlers.SysLogHandler日志输出到syslog
NTEventLogHandlerlogging.handlers.NTEventLogHandler远程输出日志到Windows NT/2000/XP的事件日志
MemoryHandlerlogging.handlers.MemoryHandler日志输出到内存中的指定buffer
HTTPHandlerlogging.handlers.HTTPHandler通过"GET"或者"POST"远程输出到HTTP服务器
NullHandlerlogging.NullHandler()忽略所有的日志消息

3.方法使用示例🌩

  1. logging.FileHandler()方法使用案例

    #!/bin/bash/python3 
    # -*- encoding: utf-8 -*-
    '''
    @File    :   4.logging模块的Handler处理器.py
    @Time    :   2023/06/28 15:40:54
    @Author  :   haohe
    '''
    import logging
    
    # 创建一个日志记录器
    logger = logging.getLogger('my.log')
    logger.setLevel(logging.DEBUG) # 设置处理器(Handler)处理的日志消息最低级别
    
    # 创建一个文件处理器
    file_handler = logging.FileHandler('my_log_file.log')
    file_handler.setLevel(logging.INFO)
    
    # 创建一个格式化器
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    # 将格式化器添加到文件处理器
    file_handler.setFormatter(formatter)
    # 将文件处理器添加到日志记录器
    logger.addHandler(file_handler)
    
    # 记录日志
    logger.debug('This is a debug message')
    logger.info('This is an info message')
    logger.warning('This is a warning message')
    logger.error('This is an error message')
    logger.critical('This is a critical message')
    """
        代码注解:创建了一个名为my_logger的日志记录器,并将其日志级别设置为DEBUG。然后,创建了一个名为my_log_file.log的文件处理器,并将其日志级别设置为INFO
        接下来,创建了一个格式化器,并将其添加到文件处理器中。最后,将文件处理器添加到日志记录器中。
    通过调用logger.debug()、logger.info()等方法,可以记录不同级别的日志消息。这些消息将被写入到my_log_file.log文件中,并按照指定的格式进行记录
    请注意,如果指定的文件不存在,logging.FileHandler会自动创建该文件。如果文件已经存在,新的日志消息将被追加到文件的末尾
    """
    

在这里插入图片描述

  1. logging.handlers.HTTPHandler()

    `前提准备:使用tornado编写一个日志接口
    #!/bin/bash/python3 
    # -*- encoding: utf-8 -*-
    # 导入Tornado和logging模块
    import tornado.ioloop
    import tornado.web
    import tornado
    import logging
    
    # 配置日志记录器
    logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(levelname)s - %(message)s')
    
    # 创建一个Tornado请求处理类
    class LogHandler(tornado.web.RequestHandler):
        def get(self):
            logging.debug("This is a debug message")
            logging.info("This is an info message")
            logging.warning("This is a warning message")
            logging.error("This is an error message")
            self.write("Log messages have been recorded.")
    # 在这个处理类中,我们使用logging模块记录了不同级别的日志信息,并通过write方法返回一个响应
    # 创建Tornado应用并定义路由
    app = tornado.web.Application([
        (r"/log",LogHandler),
    ])
    
    # 启动Tornado服务器
    if __name__ == "__main__":
        app.listen(8888)
        tornado.ioloop.IOLoop.current().start()
    
    # 现在,你可以通过访问http://localhost:8888/log来测试这个日志接口。在浏览器中打开该URL后,你将看到日志信息被记录,并返回了一个响应
    

在这里插入图片描述

`通过POST方式发送给HTTP服务器
#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
import logging
from logging import handlers
# 创建一个logger对象
logger = logging.getLogger('my.log')
# 设置logger对象处理的最低日志级别
logger.setLevel(logging.DEBUG)

# 创建一个HTTPHandler对象
http_handler = logging.handlers.HTTPHandler('47.113.146.118:8888','/log',method='POST')

# 创建一个格式化对象
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# 将将格式化器添加到HTTP处理对象中
http_handler.setFormatter(formatter)
# 将HTTPHandler对象添加到Logger对象中
logger.addHandler(http_handler)

# 发送日志消息
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

在这里插入图片描述

解决AttributeError: module 'tornado' has no attribute 'web'报错
问题原因在于:tornado.web.asynchronous在tornado5.1版本中已弃用,并在tornado6.0中已删除,用coroutines代替,而使用pip install tornado 默认装的是最新版
解决办法:

pip uninstall tornado 
pip install tornado==5.1.1 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
  1. logging.handlers.SMTPHandler()

    #!/bin/bash/python3 
    # -*- encoding: utf-8 -*-
    '''
    @File    :   7.logging模块的Handler处理器3.py
    @Time    :   2023/06/28 16:35:45
    @Author  :   haohe
    '''
    import logging
    from logging.handlers import SMTPHandler
    # 定义变量
    mailhost=('smtp.163.com',465)# SMTP服务器地址
    fromaddr='h1*7****9615@163.com' # 发件人地址
    toaddrs='eamil.online@qq.com' # 收件人地址,可以是个列表
    subject='Error Log'  # 邮件主题
    credentials=('h1*7****9615@163.com'','X**********J') # 验证信息主要是为了验证身份,前一个值为发送人的邮箱,后一个值为授权码
    # timeout=20, # 配置超时时间
    secure=True # 配置SSL加密
    # 创建SMTPHandler实例
    smtp_handler = SMTPHandler(mailhost,fromaddr,toaddrs,subject,credentials,secure)
    # 设置日志级别
    smtp_handler.setLevel(logging.ERROR)
    # 创建Logger对象
    logger = logging.getLogger('mylog')
    logger.addHandler(smtp_handler)
    
    # 记录日志
    logger.error('An error occurred')
    # 关闭Logger对象
    logger.removeHandler(smtp_handler)
    

3.2.3 Formater类🌴

Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成

Formater类的构造方法定义如下:

logging.Formatter.__init__(fmt=None,datefmt=None,style='%')

可见,该构造方法接收3个可选参数:

  • fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
  • datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
  • style:Python 3.2新增的参数,可取值为 ‘%’, ‘{‘和 ‘$’,如果不指定该参数则默认使用’%’

3.2.4 Filter类🌲

Filter类可以被Handler和Logger用来做比level更细粒度,更复杂的过滤功能.Filter是一个过滤基类,它只允许某个logger层级下的日志事件通过过滤

Filter类的构造方法定义如下:

class logging.Filter(name='')
	filter(record)
# 比如一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:'A.B','A.B,C','A.B.C.D','A.B.D'
# 而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤

Filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤

说明:

  1. 在某些情况下,也可以在filter(record)方法内部改变该record,比如添加,删除或修改一些属性
  2. 也可以通过filter统计符合某些条件的日志记录
#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   8.logging模块的Filter过滤方法.py
@Time    :   2023/06/28 18:08:25
@Author  :   haohe
'''
import logging
import sys

# 创建自定义的过滤器类
class MyFilter(logging.Filter):
    def filter(self, record):
     """
     在这里写过滤逻辑
     返回True表示接收该日志,返回False表示拒绝该记录
     """
     return record.levelno >= logging.WARNING # 只接受警告级别(warning)及以上的记录
# 创建Logger对象
logger = logging.getLogger('mylog')
# 创建并添加过滤器
filter = MyFilter()
logger.addFilter(filter)

# 添加日志处理器
stdout = logging.StreamHandler(stream=sys.stdout)

# 创建一个格式化对象
LOG_FORMAT = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
stdout.setFormatter(LOG_FORMAT)
logger.addHandler(stdout)

# 发送日志消息
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

在这里插入图片描述

3.3 Logging日志模块处理流程🚞

在这里插入图片描述

  1. 配置日志记录器:创建一个Logger对象,用于记录和管理日志信息
  2. 配置日志处理器:创建一个或多个Handler对象,用于指定日志的输出目标。可以使用不同的Handler对象将日志记录到不同的目标,如控制台、文件、电子邮件等
  3. 配置日志过滤器:创建一个Filter对象,过滤器可以根据日志记录的级别、名称或其他属性来决定是否接受该记录
  4. 配置日志格式化器:创建一个Formatter对象,用于指定日志的格式。可以使用不同的Formatter对象来定义不同的日志格式
  5. 日志经过处理,过滤,格式化后的结果会输出到指定位置

4.Logging模块的配置方式🍃

4.1 使用Python代码实现日志配置🛰

import logging
# 创建一个日志器logger并设置其日志级别为DEBUG
logger = logging.getLogger('simple_logger')
logger.setLevel(logging.DEBUG)

# 创建一个流处理器handler并设置其日志级别为DEBUG
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)

# 创建一个格式器formatter并将其添加到处理器handler
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

# 为日志器logger添加上面创建的处理器handler
logger.addHandler(handler)

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

4.2 使用配置文件和fileConfig()函数实现日志配置🎄

`Python代码
import logging

# 读取日志配置文件配置
logging.config.fileConfig('logging.conf')
# 创建一个日志器
logger = logging.getLogger('simpleExample')
# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

`配置文件logging.conf
[loggers]
keys=root,simpleExample

[handlers]
keys=FileHandler,consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=FileHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

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

[handler_FileHandler]
class=FileHandler
args=('logging.log','a')
level=ERROR
formatter=simpleFormatter

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

在这里插入图片描述

4.2.1 关于fileConfig()函数的说明🍔

该函数实际上是对configparser模块的封装

`函数定义
logging.config.fileConfig(fname,defaults=None,disable_existing_loggers=True)
`参数说明
# fname:表示配置文件的文件名或文件对象
# defaults:指定传给ConfigParser的默认值
# disable_existing_loggers:这是一个布尔型值,默认值为True表示禁用已经存在的logger,除非它们明确的出现在日志配置中.如果值为False则对已存在的loggers保持启动状态

4.2.2 配置文件格式说明📇

  1. 配置文件中一定要包含loggershandlersformatters这些片段,它们通过keys这个选项来指定配置文件中已经定义好的loggershandlersformatters

  2. loggershandlersformatters中所指定的日志器,处理器和格式器都需要在下面已单独的片段定义.Section(片段)的命名规则为:

    [loggers]
    keys=root,simpleExample
    
    [handlers]
    keys=FileHandler,consoleHandler
    
    [formatters]
    keys=simpleFormatter
    
  3. 定义logger的Section必须指定levelhandlers这两个option

    • level的可取值为DEBUGINFOWARNINGERRORCRITICALNOTEST.其中NOTEST表示所有级别的日志消息都要记录,包括用户自定义级别.
    • handlers的值是以逗号分隔的handler名字列表,这里出现的handler必须出现在[handlers]这个Section中,并且对应的handler必须在配置文件中有对应的section定义
    [logger_root]
    level=DEBUG
    handlers=FileHandler
    
  4. 对于非root logger来说,除了levelhandler这两个option之外,还需要一些额外的option

    • qualname是必须提供的option,它表示在logger层级中的名字,在应用代码中通过名字得到logger
    • propagate是可选项,默认为1,表示消息会传递给高层次的logger的handler,通常我们需要指定其值为0
    • 对于非root logger的level如果设置为NOTEST,系统会查找高层次的logger来决定此logger的有效level
    [logger_simpleExample]
    level=DEBUG
    handlers=consoleHandler
    qualname=simpleExample
    propagate=0
    
  5. 定义handler的Section中必须指定classargs这两个optionlevelformatter可作为可选参数.

    • class表示用于创建handler的类名
    • args表示传递给class所指定的handler类初始化方法参数,他必须是一个元组(tuple)的形式,即便是只有一个参数也需要是元组的形式
    • level与logger中的level一样
    • formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间,日志器名称等信息
    [handler_FileHandler]
    class=FileHandler
    args=('logging.log','a')
    level=ERROR
    formatter=simpleFormatter
    
  6. 定义formatter的Section的option都是可选的,其中包括format用于指定格式字符串,默认为消息字符串本身;datefmt用于指定asctime的时间格式,默认为%Y-%m-%d %H:%M:%Sclass用于指定格式器类名,默认为logging.Formatter

4.2.3 对于propagate属性说明🌾

  1. 修改logging.conf中simpleExample这个handler定义中的propagate属性值改为1,或者删除这个option

    `Python代码:
    import logging
    import logging.config
    from logging import handlers
    # 读取日志配置文件配置
    logging.config.fileConfig('logging.conf')
    # 创建一个日志器
    logger = logging.getLogger('simpleExample')
    # 日志输出
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    `logging.conf
    ……
    [logger_simpleExample]
    level=DEBUG
    handlers=consoleHandler
    qualname=simpleExample
    propagate=1
    ……
    

在这里插入图片描述

除了在控制台中有输出信息,在logging.log文件中也有内容输出

  1. 用一个没有在配置文件中定义的logger名称来获取logger

    import logging
    
    # 获取日志配置文件内容
    logging.config.fileConfig('logging.conf')
    # 用一个没有在配置文件中定义的logger名称来创建一个日志器logger
    logger = logging.getLogger('simpleExample1')
    # 日志输出
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    

在这里插入图片描述

运行程序后,我们会发现控制台没有任何输出,而logging.log文件中又多了两行输出

这是因为,当一个日志器没有被设置任何处理器是,系统会去查找该日志器的上层日志器上所设置的日志处理器来处理日志记录。simpleExample1在配置文件中没有被定义,因此logging.getLogger(simpleExample1)这行代码这是获取了一个logger实例,并没有给它设置任何处理器,但是它的上级日志器–root logger在配置文件中有定义且设置了一个FileHandler处理器,simpleExample1处理器最终通过这个FileHandler处理器将日志记录输出到logging.log文件中了

4.3 使用字典配置信息和dictConfig()函数实现日志配置

Python 3.2中引入的一种新的配置日志记录的方法——用字典来保存logging配置信息.这相对于上面所讲的基于配置文件来保存logging配置信息的方式来说,功能更加强大,也更加灵活,因为我们可把很多的数据转换成字典。比如,我们可以使用JSON格式的配置文件、YAML格式的配置文件,然后将它们填充到一个配置字典中;或者,我们也可以用Python代码构建这个配置字典,或者通过socket接收pickled序列化后的配置信息。总之,你可以使用你的应用程序可以操作的任何方法来构建这个配置字典。

这个例子中,我们将使用YAML格式来完成与上面同样的日志配置

# 安装PyYAML模块
pip install PyYAML -i http://mirrors.cloud.aliyuncs.com/pypi/simple/

Python代码:

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   11.Logging模块使用Yaml文件.py
@Time    :   2023/07/01 16:20:19
@Author  :   haohe
'''
import logging
import logging.config
import yaml

with open('logging.yaml','r') as f:
    # 使用Yaml模块加载配置文件内容到字典中
    # yaml.load(f,Loader=yaml.FullLoader)方法是使用PyYAML库加载YAML文件的一种常见方式,使用FullLoader类作为加载器,以确保安全加载YAML数据
    dict_conf = yaml.load(f,Loader=yaml.FullLoader)
# 使用修改后的配置字典来配置日志系统
logging.config.dictConfig(dict_conf)
# 获取名为'simpleExample'的日志记录器
logger = logging.getLogger('simpleExample')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

Yaml代码

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
  console_err:
    class: logging.StreamHandler
    level: ERROR
    formatter: simple
    stream: ext://sys.stderr
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console_err]

在这里插入图片描述

4.3.1 关于dictConfig()函数的说明

该函数实际上是对configparser模块的封装

函数定义:

logging.config.dictConfig(config)

该函数可以从一个字典对象中获取日志配置信息,config参数就是这个字典对象

4.3.2 配置字典说明🚼

Key名称描述
version必选项,其值是一个整数值,表示配置格式的版本
formatters可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的格式器名称,value为格式器的配置信息组成的dict,如format和datefmt
filters可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的处理器名称,value为处理器的配置信息组成的dcit,如class,level,formatter和filters,其中class为必选项,其它为可选项;其他配置信息将会传递给class所指定的处理器类的构造函数
loggers可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的日志器名称,value为日志器的配置信息组成的dcit,如level、handlers、filters 和 propagate(yes|no),这些都是可选项
root可选项,这是root logger的配置信息,其值也是一个字典对象。除非在定义其它logger时明确指定propagate值为no,否则root logger定义的handlers都会被作用到其它logger上
incremental可选项,默认值为False。该选项的意义在于,如果这里定义的对象已经存在,那么这里对这些对象的定义是否应用到已存在的对象上。值为False表示,已存在的对象将会被重新定义
disable_existing_loggers可选项,默认值为True。该选项用于指定是否禁用已存在的日志器loggers,如果incremental的值为True则该选项将会被忽略

handlers定义示例:

handlers:
  console:
    class : logging.StreamHandler
    formatter: brief
    level   : INFO
    filters: [allow_foo]
    stream  : ext://sys.stdout
  file:
    class : logging.handlers.RotatingFileHandler
    formatter: precise
    filename: logconfig.log
    maxBytes: 1024
    backupCount: 3

5.Logging模块日志输出添加上下文信息🍃

5.1 extra参数引入上下文信息🍂

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   13.Logging模块extra参数引入上下文信息.py
@Time    :   2023/07/01 18:17:46
@Author  :   haohe
'''
import logging
import sys

fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
logger = logging.getLogger("myPro")
logger.setLevel(logging.DEBUG)
logger.addHandler(h_console)

extra_dict = {"ip": "113.208.78.29", "username": "Petter"}
logger.debug("User Login!", extra=extra_dict)

extra_dict = {"ip": "223.190.65.139", "username": "Jerry"}
logger.info("User Access!", extra=extra_dict)

在这里插入图片描述

5.2 使用LoggerAdapters引入上下问信息🍀

使用LoggerAdapter类来传递上下文信息到日志事件的信息中是一个非常简单的方式,可以把它看做第一种实现方式的优化版–因为它为extra提供了一个默认值。这个类设计的类似于Logger,因此我们可以像使用Logger类的实例那样来调用debug(), info(), warning(),error(), exception(), critical()log()方法。

当创建一个LoggerAdapter的实例时,我们需要传递一个Logger实例和一个包含上下文信息的类字典对象给该类的实例构建方法。当调用LoggerAdapter实例的一个日志记录方法时,该方法会在对日志日志消息和字典对象进行处理后,调用构建该实例时传递给该实例的logger对象的同名的日志记录方法

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   14.Logging模块中的LoggerAdapters.py
@Time    :   2023/07/01 18:22:23
@Author  :   haohe
'''
import logging
import sys

# 初始化一个要传递给LoggerAdapter构造方法的logger实例
fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
init_logger = logging.getLogger("myPro")
init_logger.setLevel(logging.DEBUG)
init_logger.addHandler(h_console)

# 初始化一个要传递给LoggerAdapter构造方法的上下文字典对象
extra_dict = {"ip": "IP", "username": "USERNAME"}

# 获取一个LoggerAdapter类的实例
logger = logging.LoggerAdapter(init_logger, extra_dict)

# 应用中的日志记录方法调用
logger.info("User Login!")
logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"})
logger.extra = {"ip": "113.208.78.29", "username": "Petter"}
logger.info("User Login!")
logger.info("User Login!")

在这里插入图片描述

根据上面的程序输出结果,我们会发现一个问题:传递给LoggerAdapter类构造方法的extra参数值不能被LoggerAdapter实例的日志记录函数(如上面调用的info()方法)中的extra参数覆盖,只能通过修改LoggerAdapter实例的extra属性来修改默认值(如上面使用的logger.extra=xxx),但是这也就意味着默认值被修改了。

解决这个问题的思路应该是:实现一个LoggerAdapter的子类,重写process()方法。其中对于kwargs参数的操作应该是先判断其本身是否包含extra关键字,如果包含则不使用默认值进行替换;如果kwargs参数中不包含extra关键字则取默认值。来看具体实现:

import logging
import sys

class MyLoggerAdapter(logging.LoggerAdapter):

    def process(self, msg, kwargs):
        if 'extra' not in kwargs:
            kwargs["extra"] = self.extra
        return msg, kwargs

if __name__ == '__main__':
    # 初始化一个要传递给LoggerAdapter构造方法的logger实例
    fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
    h_console = logging.StreamHandler(sys.stdout)
    h_console.setFormatter(fmt)
    init_logger = logging.getLogger("myPro")
    init_logger.setLevel(logging.DEBUG)
    init_logger.addHandler(h_console)
    
    # 初始化一个要传递给LoggerAdapter构造方法的上下文字典对象
    extra_dict = {"ip": "IP", "username": "USERNAME"}
    
    # 获取一个自定义LoggerAdapter类的实例
    logger = MyLoggerAdapter(init_logger, extra_dict)
    
    # 应用中的日志记录方法调用
    logger.info("User Login!")
    logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"})
    logger.info("User Login!")
    logger.info("User Login!")

输出结果:

# 使用extra默认值:{"ip": "IP", "username": "USERNAME"}
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!

# info(msg, extra)方法中传递的extra方法已覆盖默认值
2017-05-22 17:35:38,499 - myPro - 113.208.78.29 - Petter - User Login!

# extra默认值保持不变
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!

5.3 使用Filters引入上下文信息🍋

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   15.Logging模块使用Filters引入上下文信息.py
@Time    :   2023/07/01 18:25:03
@Author  :   haohe
'''
import logging
from random import choice
class ContextFilter(logging.Filter):
        ip = 'IP'
        username = 'USER'

        def filter(self, record):
            record.ip = self.ip
            record.username = self.username
            return True

if __name__ == '__main__':
    levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
    users = ['Tom', 'Jerry', 'Peter']
    ips = ['113.108.98.34', '219.238.78.91', '43.123.99.68']

    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)-15s %(name)-5s %(levelname)-8s %(ip)-15s %(username)-8s %(message)s')
    logger = logging.getLogger('myLogger')
    filter = ContextFilter()
    logger.addFilter(filter)
    logger.debug('A debug message')
    logger.info('An info message with %s', 'some parameters')

    for x in range(5):
        lvl = choice(levels)
        lvlname = logging.getLevelName(lvl)
        filter.ip = choice(ips)
        filter.username = choice(users)
        logger.log(lvl, 'A message at %s level with %d %s' , lvlname, 2, 'parameters')

在这里插入图片描述

6.Logging模块日志处理案例🍃

6.1 案例1

1.需求

  1. 要求将所有级别的日志都写入磁盘
  2. all.log文件中记录的日志信息,日志格式为: 日期和事件 - 日志级别 - 日志信息
  3. error.log文件中单独记录error及以上的日志信息,日志格式为: 日期和事件 - 日志级别 - 文件名[:行号] - 日志信息
  4. 要求all.log在每天凌晨进行日志切割

2.分析

  • 要记录所有级别的日志,因此日志器的有效level需要设置为最低级别–DEBUG;
  • 日志需要被发送到两个不同的目的地,因此需要为日志器设置两个handler;另外,两个目的地都是磁盘文件,因此这两个handler都是与FileHandler相关的;
  • all.log要求按照时间进行日志切割,因此他需要用logging.handlers.TimedRotatingFileHandler; 而error.log没有要求日志切割,因此可以使用logging.FileHandler
  • 两个日志文件的格式不同,因此需要对这两个handler分别设置格式器;

3.代码实现

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   9.Logging模块的使用案例.py
@Time    :   2023/06/28 18:26:55
@Author  :   haohe
'''
"""
    日志需求:
        1要求将所有级别的所有日志都写入磁盘文件中
        2all.log文件中记录所有的日志信息,日志格式为:日期和时间 - 日志级别 - 日志信息
        3error.log文件中单独记录error及以上级别的日志信息,日志格式为:日期和时间 - 日志级别 - 文件名[:行号] - 日志信息
        4要求all.log在每天凌晨进行日志切割
"""
import logging
from logging import handlers
import datetime

# 创建日志记录器
logger = logging.getLogger('mylog')

# 设置all.log文件的日志最低级别为DEBUG
logger.setLevel(logging.DEBUG)
# 创建一个格式化器
formatter1 = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
formatter2 = logging.Formatter('$(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s')

# 创建一个日志切割器
All_Handler =  logging.handlers.TimedRotatingFileHandler(filename='all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0))
# filename:日志文件名称,when:指定文件的切割时间间隔,可选值为S(秒),M(分钟),H(小时),D(天),W0-W6(周一至周日),midnight(每天凌晨)
# backupCount:指定保留的日志文件数量
# interval:指定切割时间间隔的数量
All_Handler.setLevel(logging.DEBUG)
All_Handler.setFormatter(formatter1)

# 创建一个文件查看器
Error_Handler = logging.FileHandler('error.log')
Error_Handler.setLevel(logging.ERROR)
Error_Handler.setFormatter(formatter2)

logger.addHandler(All_Handler)
logger.addHandler(Error_Handler)

# 发送日志消息
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

6.2 案例2

1.需求

	Python执行过程中输出的东西会很多,而终端中显示的信息很快就会被覆盖,这时候,将这些信息输入到日志文件中.如果在执行Python代码时,直接调用一个写好的日志方法来实现这个功能

2.需求分析

  1. 需要先构建一个写好的日志脚本
  2. 将脚本和代码放在同一目录下,代码中导入方法

3.代码实现

loguti.py

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   loguti.py
@Time    :   2023/07/01 17:14:33
@Author  :   haohe
'''
import logging.config
import os

config = {
    'version':1,
    # 定义格式器
    'formatters':{
        'simple':{
            'format':'%(asctime)s - %(name)s - %(levelname)s -%(message)s',
        },
    },
    # 定义处理器
    'handlers':{
        # 日志输出流
        'console':{
           'class':'logging.StreamHandler',
           'level':'WARNING',
           'formatter':'simple'
        },
        # 日志文件切割,默认保存10天日志
        'file':{
           'class':'logging.handlers.TimedRotatingFileHandler',
           'filename':'loging.log',
           'level':'INFO',
           'when':'D',
           'backupConut':10,
           'formatter':'simple'
        },
    },
    # 定义日志器
    'loggers':{
        'consoleLogger':{
            'handlers':['console'],
            'level':'INFO',
        },
        'fileLogger':{
            'handlers':['console','file'],
            'level':'INFO',
        }
    }
}


def getFileLogger(log_file,hours=None,days=None,size=None):
    log_dir = os.path.dirname(log_file) # 获取目录名称
    if not os.path.exists(log_dir):
        os.makedirs(log_dir) # 创建目录
    if log_file:
        file_info = {
           'class':'logging.handlers.TimedRotatingFileHandler',
           'filename':log_file,
           'level':'INFO',
           'when':'D',
           'backupCount':10,
           'formatter':'simple'
        }
    if hours:
        file_info["when"] = "H"
        file_info["backupCount"] = hours
    if days:
        file_info["when"] = "D"
        file_info["backupCount"] = days
    if size:
        file_info['class'] = 'logging.handlers.RotatingFileHandler'
        file_info['maxBytes'] = 1024*1024*size
    config['handlers']['file'] = file_info
    logging.config.dictConfig(config=config)
    return logging.getLogger('fileLogger')

def getConsoleLogger():
    logging.config.dictConfig(config=config)
    return logging.getLogger('fileLogger')

if __name__ == '__main__':
    logger = getFileLogger('test.log',days=1)
    logger.info('test')

test.py

#!/bin/bash/python3 
# -*- encoding: utf-8 -*-
'''
@File    :   12.Logging模块调用写好的日出处理脚本.py
@Time    :   2023/07/01 17:43:51
@Author  :   haohe
'''
from logutil import getFileLogger
# 配置日志
logger = getFileLogger('/root/Python_Study/Devops/Logging/logging.log',days=1)

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

在这里插入图片描述

疑问:为什么终端只输出了三条记录,但是文件中却写入了四条记录

答疑:

'handlers':{
  # 日志输出流
  'console':{
     'class':'logging.StreamHandler',
     'level':'WARNING',
     'formatter':'simple'
  },
  # 日志文件切割,默认保存10天日志
  'file':{
     'class':'logging.handlers.TimedRotatingFileHandler',
     'filename':'loging.log',
     'level':'INFO',
     'when':'D',
     'backupConut':10,
     'formatter':'simple'
  }

在终端上只输出WARNING级别及其以上,而将INFO级别及其以上写入文本中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/713248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字化就是做系统?广州数字化转型服务公司推荐

数字化转型是一个与数字化技术密切相关的概念&#xff0c;很多不了解数字化转型的企业往往会认为数字化转型就是提升企业的数字化技术应用水平和不断增加数字化系统的功能&#xff0c;也就是将数字化转型认为是做系统。但实际上&#xff0c;开利网络认为&#xff0c;数字化转型…

使用vtk在一个窗口中创建多个几何体

引言 该示例为官网上的例子。在一个窗口中创建了多个不同的几何体。 其效果如下&#xff1a; 示例 开发环境 使用QtCreator4.11.2,Qt5.14.2。使用的vtk9.2的库及其头文件。创建空项目。 示例代码 其pro文件中的内容&#xff1a; QT core#greaterThan(QT_MAJOR_V…

【产品文档】埋点需求文档模板

今天和大家免费分享埋点需求文档模板。埋点文档的目的是帮助开发人员准确地实施埋点&#xff0c;并确保收集到的数据符合预期&#xff0c;以便后续分析和改进产品。 【模板下载】 这个模板可以在 Axure高保真原型哦 小程序里免费下载哦 【文档截图】

微搭低代码实现横向滚动效果

目录 1 添加滚动容器2 滚动内容设置总结 在小程序场景中&#xff0c;有很多横向滚动的效果&#xff0c;比如我们的官方模板电商展示里就有一个横向滚动的效果&#xff0c;本篇我们解读一下横向滚动该如何实现 1 添加滚动容器 不管是横向滚动还是纵向滚动&#xff0c;我们都是依…

【Python】NLP参数控制模板

前言 学过AI的都知道训练一个模型需要调整很多参数&#xff0c;为了有效的管理这些参数、不至于让代码的参数写的乱七八糟&#xff0c;有必要写一套控制参数的模板。 argparser argparser是python当中的参数解析器&#xff0c;在NLP当中主要是用来接受和使用参数的。一个使用它…

Word技巧【自用】

表格断开 做到类似这样的效果&#xff0c;很简单&#xff0c;用边框刷就可以 设定边框的横线&#xff0c;以及磅值&#xff0c;就可以像画三线表一样断开了

vue el-dialog嵌入video实现视频播放功能

video嵌入dialog实现视频播放 业务需求1、实现的效果图3、全部代码(复制粘贴即可实现) 业务需求 弹窗实现视频播放&#xff0c;并且切换不同选项卡播放不同视频 1、实现的效果图 3、全部代码(复制粘贴即可实现) <template><el-button style"margin-left: 60px&q…

警惕: 新的 “RustBucket “恶意软件变种针对macOS用户

研究人员已经揭开了苹果macOS恶意软件RustBucket更新版本的序幕&#xff0c;该版本具有改进的能力&#xff0c;可以建立持久性并避免被安全软件发现。 安全实验室的研究人员在本周发表的一份报告中表示&#xff1a;RustBucket的变种是一个针对macOS系统的恶意软件集合&#xf…

Cisco ISR 1000 Series IOS XE Release Dublin-17.11.1a ED

Cisco ISR 1000 Series IOS XE Release Dublin-17.11.1a ED 思科 1000 系列集成多业务路由器 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-isr-1000/&#xff0c;查看最新版。原创<品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 思科 1000…

关于NISP,收藏这个就够了!

01 全面认知NISP 什么是NISP证书&#xff1f; 为提高各领域人才信息安全意识&#xff0c;落实国家信息安全人才培养战略&#xff0c;缓解我国信息安全领域专业人才存在的供需矛盾&#xff0c;加快我国信息安全专业人才队伍的建设&#xff0c;逐步形成一支政治可靠、技能过硬…

VUE小白学习-2023年6月7日

VUE小白学习-2023年6月7日 前端工程化 日期&#xff1a;2023年6月7日 前端工程化

SVN服务器简单配置过程

一、概述 SVN的全称是Subversion&#xff0c;是一个流行的开源的版本控制系统。Subversion可管理随时间改变的数据&#xff0c; 这些数据放置在一个中央资料档案库(repository) 中。 它就像一个普通的文件服务器, 不过它会记住每一次文件的变动。 这样当需要回退时&#xff0c;…

【Java可执行命令】(十)JAR文件签名工具 jarsigner:通过数字签名及验证保证代码信任与安全,深入解析 Java的 jarsigner命令~

Java可执行命令之jarsigner 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法3.1.1 可选参数&#xff1a;jarsigner -keystore < url>3.1.2 可选参数&#xff1a;jarsigner -storepass <口令>3.1.3 可选参数&#xff1a;jarsigner -keypass <口令>3.1.4 可选参…

数字信号处理复习(一):离散傅里叶变换(DFT)

一&#xff1a;为什么需要离散傅里叶变换 我们知道在傅里叶变换中存在连续信号和离散信号变换从而诞生了有拉普拉斯变换&#xff08;连续信号&#xff09;、Z变换&#xff08;离散信号&#xff09;&#xff0c;这两种变换是方便以前没有计算机时工程师们手动计算傅里叶变换。而…

同构:编程中的数学(文末送书4本)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

多项目管理难在哪,多项目同时进行该如何做好进度管理?

最近&#xff0c;听到群里的项目经理吐槽&#xff0c;手上有10多个项目同时进行&#xff0c;工作起来手忙脚乱&#xff0c;杂乱无章&#xff0c;让他压力特别大。 对于项目经理来说&#xff0c;多项目并行推进的情况已是常态。从工作层面来说&#xff0c;不仅在各项目之间抢资…

解决 fatal: Authentication failed for ‘https://github.com/*/*.git/‘

原因&#xff1a;github 的认证策略发生了改变&#xff0c;在 2021年8月13日 的时候&#xff0c;用户名加密码的认证方式被去掉了&#xff0c;换成了 个人令牌&#xff08;Personal Access Token&#xff09;的校验方式。 官网解决方案&#xff1a;管理个人访问令牌 - GitHub …

数分面试题- 面试智力题

目录标题 1、如何用3L和5L桶量取4L水2、有两圈蚊香&#xff0c;一圈烧完需要1小时&#xff0c;怎么才能确定出15分钟/45分钟时间3、提灯过桥问题4、开关灯问题5、赛道赛跑问题6、砝码称重问题6.1 有10瓶药&#xff0c;每瓶有10粒药&#xff0c;其中有一瓶是变质的。好药每颗重1…

家政保洁维修上门预约系统开发;

家政保洁维修上门预约系统开发&#xff1b; 保洁&#xff0c;家电清洗等上门业务系统&#xff0c;支持派单接单&#xff0c;按区域&#xff0c;就近分配、套餐年卡等&#xff1b; 育婴月嫂系统 保姆筛选&#xff0c;简历主页&#xff0c;推荐跟进&#xff0c;在线合同&#xf…

IPO观察丨背靠百度、专注医疗内容营销,为何健康之路举步维艰?

互联网医疗赛道竞争加剧下&#xff0c;相关企业不仅在市场扩张上角逐&#xff0c;也在IPO道路上持续追赶。 仅就6月份而言&#xff0c;就有药师帮、方舟云康、健康之路等几家企业在持续推进上市进程&#xff0c;其中&#xff0c;药师帮已于6月28日成功登陆港交所&#xff0c;而…