Python的Logging模块_python logging-程序员宅基地

技术标签: python  linux  服务器  

在这里插入图片描述

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)s pathname的文件名部分,包含文件后缀
module %(module)s filename的名称部分,不包含后缀
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名称 方法 作用
StreamHandler logging.StreamHandler 日志输出到流,可以是sys.stderr,sys.stdout或者文件
FileHandler logging.FileHandler 日志输出到文件
BaseRotatingHandler logging.handlers.BaseRotatingHandler 基本的日志回滚方式
RotatingHandler logging.handlers.RotatingHandler 日志回滚方式,支持日志文件最大数量和日志文件回滚
TimeRotatingHandler logging.handlers.TimeRotatingHandler 日志回滚方式,在一定时间区域内回滚日志文件
SocketHandler logging.handlers.SocketHandler 远程输出日志到TCP/IP sockets
DatagramHandler logging.handlers.DatagramHandler 远程输出日志到UDP sockets
SMTPHandler logging.handlers.SMTPHandler 远程输出日志到邮件地址
SysLogHandler logging.handlers.SysLogHandler 日志输出到syslog
NTEventLogHandler logging.handlers.NTEventLogHandler 远程输出日志到Windows NT/2000/XP的事件日志
MemoryHandler logging.handlers.MemoryHandler 日志输出到内存中的指定buffer
HTTPHandler logging.handlers.HTTPHandler 通过"GET"或者"POST"远程输出到HTTP服务器
NullHandler logging.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****[email protected]' # 发件人地址
    toaddrs='[email protected]' # 收件人地址,可以是个列表
    subject='Error Log'  # 邮件主题
    credentials=('h1*7****[email protected]'','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级别及其以上写入文本中

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_60981735/article/details/131521374

智能推荐

十九、Sleuth整合Zipkin链路跟踪-程序员宅基地

文章浏览阅读612次。通过分布式链路追踪将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等_sleuth整合zipkin

游戏中遮挡剔除方案总结_ue5 单个静态网格体遮挡剔除-程序员宅基地

文章浏览阅读2.1k次。这是侑虎科技第507篇文章,感谢作者FrankZhou供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)作者主页:https://www.zhihu.com/people/pkhere,作者也是U Sparkle活动参与者,UWA欢迎更多开发朋友加入U Sparkle开发者计划,这个舞台有你更精彩!遮挡剔除是当一..._ue5 单个静态网格体遮挡剔除

第六章 输入输出系统(汤小丹版操作系统笔记) 超全超详细!!!_计算机操作系统汤小丹 各章节对应算法说明-程序员宅基地

文章浏览阅读2.7k次,点赞6次,收藏59次。操作系统原理-第六章 输入输出系统(汤小丹第四版《操作系统》)内容设及:[ I/O系统的功能模型和接口 ,I/O设备和设备控制器,中断机构和中断处理程序,驱动程序,与设备无关软件,用户软件,缓冲区管理,磁盘存储器的性能与调度]。_计算机操作系统汤小丹 各章节对应算法说明

Java&MYSQL超市管理系统10428-计算机毕业设计项目选题推荐(附源码)-程序员宅基地

文章浏览阅读104次。本超市管理系统采用的数据库是Mysql,使用JAVA技术开发。在设计过程中,充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。

无线路由器桥接设置-程序员宅基地

文章浏览阅读1.5k次。作者本人家因为户型结构的问题,路由器信号覆盖的一直不是很好,卧室中的角落常常只有一格甚至断线,睡前刷个小视频都不开心!!(敲黑板),于是懒癌患者的我,最近终于想动弹一下,着手解决一下这个困扰多时的问题。 在几种解决方案中,我选择了无线桥接的这种方法,一方面是因为设置方便,另一方面是因为只需要一台或多台(根据具体情况定,需要覆盖的面积有多大)路由器即可,也不需要网线,当然你也可以选择有线的桥接,这样网络会更稳定一些。 先说调试方法(以下每一步调试完,如果页面显示需要重启路由器,重启即可)..._无线路由器桥接设置

jsp利用内置对象response,request实现登录功能_response.sendredirect("登录成功!");}else{response.send-程序员宅基地

文章浏览阅读823次。题目:当用户名与密码一致时提交后跳转到页面显示”登陆成功“,不一致时跳转到登陆不成功_response.sendredirect("登录成功!");}else{response.sendredirect("form.jsp");}

随便推点

新辰门禁系统安装遇到的问题-程序员宅基地

文章浏览阅读108次。新辰门禁系统安装遇到的问题 公司前台因为系统坏了,所以重新安装新辰门禁系统,刚开始先导出access数据库,然后重新安装系统,安装好系统之后安装access数据库,数据库装好,直接安装Setup_Chinese(PRC).exe”进入安装界面。 直接安装好之后重新启动,系统密码为空直接进入导入数据库,但是问题出现了,因为刚开始看了一下门禁信息是好的,但是早..._win10 无法安装门禁数据库

【毕业设计】 基于单片机的移动共享充电宝设计与实现 - 物联网嵌入式 stm32 c51_keil c51 充电宝程序-程序员宅基地

文章浏览阅读2.7k次,点赞17次,收藏44次。Hi,大家好,这里是丹成学长,今天向大家介绍一个学长做的单片机项目基于单片机的移动充电宝设计与实现大家可用于 课程设计 或 毕业设计单片机-嵌入式毕设选题大全及项目分享:https://blog.csdn.net/m0_71572576/article/details/125409052为单片机设备供电:为手机供电:硬件原理图:单片机-嵌入式毕设选题大全及项目分享:https://blog.csdn.net/m0_71572576/article/details/125409052..._keil c51 充电宝程序

【有限元分析】matlab平面三角形单元有限元分析_三角形区域有限元分析matlab代码-程序员宅基地

文章浏览阅读1.5k次。代码下载链接图示结构,单元划分如图,已知,μ、E, a,承受均布压力q (μ、E, a, q自己给数字,每人不同)试用有限元法(平面三角形单元)划分四个单元如图,解此平面应力问题。求出应力,应变及支座反力。(可用MATLAB, maple等编程求解,提交程序及结果)% %clcclear;%—鐗╃悊鍙傛暟------------------E=7e10; %寮规�фā閲�,鍗曚綅Pat=0.01; %鍗曞厓鍘氬害,鍗曚綅ma=1; _三角形区域有限元分析matlab代码

vs 怎么调试html5,关于html5:使用-VSCode-如何进行远程开发调试-程序员宅基地

文章浏览阅读758次。对于大型的 Golang 我的项目往往我都会应用 Goland 这样的业余 IDE,然而因为我本地开发环境硬件资源偏低,不能很顺畅的应用 Goland,这个时候咱们能够思考应用 VSCode 来代替 Goland,而且 VSCode 还反对近程开发,所以我索性将开发环境放在近程机器上,而后用 VSCode 近程开发模式进行连贯,最次要的是大部分咱们的我的项目都是间接跑在 Linux 下面的,这个时..._vscode远程同步h5

计算机的起源与发展历程_计算机起源及发展简史-程序员宅基地

文章浏览阅读3.8w次,点赞8次,收藏51次。时间到了1890年,在那个年代,美国是大量移民者良好的目的地,因此美国的人口急剧增长,当时美国宪法规定国家每10年需要进行一次人口大普查来记录和分析美国各地的人口数据,以方便配置各种社会资源,1880年的普查人工用了7年的时间进行统计,也就是说,他们在休息两年之后就要开始第11次普查了,面临越来越多的人口数据,如果纯粹靠人力来统计的话,预计这次的普查需要13年甚至更长的时间。布尔的创新奠定了计算机科学的基础,他的理论为逻辑运算和信息处理提供了坚实的数学基础,为现代计算机的逻辑设计和运算方式奠定了基石。_计算机起源及发展简史

Synopse mORMot框架样例学习03 - NamedPipe Client-Server_mormot2例子-程序员宅基地

文章浏览阅读596次。前边的例01和例02分别展示了静态服务器及嵌入式SQLite3服务器的实现方法,例03是命名管道客户端-服务器,例01中用JSON文件存储数据(按我的理解,每一个SQLRecord就是一个表格,在存储的时候多个表格应该需要多个JSON文件取存储),例02中使用SQLite3数据库存储,只需要一个.db3数据库文件就能存放所有的SQLRecord(表格),例03中实现了客户端-服务器的通讯,客户端的..._mormot2例子

推荐文章

热门文章

相关标签