美文网首页
logging日志

logging日志

作者: 一个摸鱼AI喵 | 来源:发表于2021-09-29 09:27 被阅读0次

logging日志

学习目标

  • 能够知道logging日志的使用

1. logging日志的介绍

在现实生活中,记录日志非常重要,比如:银行转账时会有转账记录;飞机飞行过程中,会有个黑盒子(飞行数据记录器)记录着飞机的飞行过程,那在咱们python程序中想要记录程序在运行时所产生的日志信息,怎么做呢?

可以使用 logging 这个包来完成

记录程序日志信息的目的是:

  1. 可以很方便的了解程序的运行情况

  2. 可以分析用户的操作行为、喜好等信息

  3. 方便开发人员检查bug

2. logging日志级别介绍

日志等级可以分为5个,从低到高分别是:

  1. DEBUG

  2. INFO

  3. WARNING

  4. ERROR

  5. CRITICAL

日志等级说明:

  • DEBUG:程序调试bug时使用

  • INFO:程序正常运行时使用

  • WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误

  • ERROR:程序出错误时使用,如:IO操作失败

  • CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用

  • 默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。

  • 日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL

3. logging日志的使用

在 logging 包中记录日志的方式有两种:

  1. 输出到控制台

  2. 保存到日志文件

日志信息输出到控制台的示例代码:

import logging

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')

运行结果:

WARNING:root:这是一个warning级别的日志信息
ERROR:root:这是一个error级别的日志信息
CRITICAL:root:这是一个critical级别的日志信息

说明:

  • 日志信息只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING

logging日志等级和输出格式的设置:

import logging

# 设置日志等级和输出日志格式
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')

运行结果:

2019-02-13 20:41:33,080 - hello.py[line:6] - DEBUG: 这是一个debug级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:7] - INFO: 这是一个info级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:8] - WARNING: 这是一个warning级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:9] - ERROR: 这是一个error级别的日志信息
2019-02-13 20:41:33,080 - hello.py[line:10] - CRITICAL: 这是一个critical级别的日志信息

代码说明:

  • level 表示设置的日志等级

  • format 表示日志的输出格式, 参数说明:

    • %(levelname)s: 打印日志级别名称

    • %(filename)s: 打印当前执行程序名

    • %(lineno)d: 打印日志的当前行号

    • %(asctime)s: 打印日志的时间

    • %(message)s: 打印日志信息

日志信息保存到日志文件的示例代码:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    filename="log.txt",
                    filemode="a",   
                    datefmt=''%a %d %b %Y %H:%M:%S',  # 日期格式同time.strftime()
                    handlers=[fh])  

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')

其中,fh = logging.FileHandler("./logs/flask.log",encoding= 'utf-8')

格式format说明

%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(processName)s:进程名
%(message)s: 打印日志信息

运行结果:

4. logging日志在mini-web项目中应用

web.py 程序使用logging日志示例:

  1. 程序入口模块设置logging日志的设置
import socket
import threading
import sys
import framework
import logging

# logging日志的配置
logging.basicConfig(level=logging.DEBUG,
                 format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                 filename="log.txt",
                 filemode="w")
  1. INFO级别的日志输出,示例代码:
 # 判断是否是动态资源请求
 if request_path.endswith(".html"):
     """这里是动态资源请求,把请求信息交给框架处理"""
     logging.info("动态资源请求:" + request_path)
     ...
 else:
     """这里是静态资源请求"""
     logging.info("静态资源请求:" + request_path)
     ...
  1. WARNING级别的日志输出,示例代码:
 # 获取命令行参数判断长度
 if len(sys.argv) != 2:
     print("执行命令如下: python3 xxx.py 9000")
     logging.warning("用户在命令行启动程序参数个数不正确!")
     return

 # 判断端口号是否是数字
 if not sys.argv[1].isdigit():
     print("执行命令如下: python3 xxx.py 9000")
     logging.warning("用户在命令行启动程序参数不是数字字符串!")
     return

framework.py 程序使用logging日志示例:

  1. ERROR级别的日志输出,示例代码:
 # 处理动态资源请求
 def handle_request(env):
     # 获取动态请求资源路径
     request_path = env["request_path"]
     print("接收到的动态资源请求:", request_path)
     # 遍历路由列表,选择执行的函数
     for path, func in route_list:
         if request_path == path:
             result = func()
             return result
     else:
         logging.error("没有设置相应的路由:" + request_path)
         # 没有找到动态资源
         result = not_found()
         return result

说明:

  • logging日志配置信息在程序入口模块设置一次,整个程序都可以生效。

    • logging.basicConfig 表示 logging 日志配置操作

5. 小结

  • 记录python程序中日志信息使用 logging 包来完成

  • logging日志等级有5个:

    1. DEBUG

    2. INFO

    3. WARNING

    4. ERROR

    5. CRITICAL

  • 打印(记录)日志的函数有5个:

    1. logging.debug函数, 表示: 打印(记录)DEBUG级别的日志信息

    2. logging.info函数, 表示: 打印(记录)INFO级别的日志信息

    3. logging.warning函数, 表示: 打印(记录)WARNING级别的日志信息

    4. logging.error函数, 表示: 打印(记录)ERROR级别的日志信息

    5. logging.critical函数, 表示: 打印(记录)CRITICAL级别的日志信息

将日志输出到屏幕及文件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
#创建日志对像
logger=logging.getLogger("TEST-LOG")
logger.setLevel(logging.ERROR) #日志级别

#创建日志输出到屏幕及设置级别
ch=logging.StreamHandler()
ch.setLevel(logging.DEBUG)

#创建日志输出到文件及设置级别
fh=logging.FileHandler("access.log",encoding='UTF-8')
fh.setLevel(logging.WARNING)

#设置日志格式
formatter=logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(messages)s')
#将格式付给屏幕和文件
ch.setFormatter(formatter)
fh.setFormatter(formatter)

#将两个输出付给对像logger
logger.addHandler(ch)
logger.addHandler(fh)
#生成日志
logger.debug("debug messsage")
logger.info("info.message")
logger.warn("warn.message")
logger.error("error.message")
logger.critical("critical.message")

logger可直接在func里用全局变量. 但在class无效,需要传入

只设置了文件日志,docker logs 也会打印日志信息,但不会打印formatter部分。

参考别人的一种设置(测试,起了文件,但没写进去)

logconfig_dict = {
    'version':1,
    'disable_existing_loggers': True,
    'loggers':{
        "gunicorn.error": {
            "level": "DEBUG",# 打日志的等级可以换的,下面的同理
            "handlers": ["error_file"], # 对应下面的键
            "propagate": 1,
            "qualname": "gunicorn.error"
        },
 
        "gunicorn.access": {
            "level": "INFO",
            "handlers": ["access_file"],
            "propagate": 0,
            "qualname": "gunicorn.access"
        }
    },
    'handlers':{
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "access"
            },
        "error_file": {
            "encoding":"utf8",
            "level":"ERROR",
            "class": "logging.handlers.RotatingFileHandler",
            # "maxBytes": 1024*1024*1024,# 打日志的大小,我这种写法是1个G
            # "backupCount": 1,# 备份多少份,经过测试,最少也要写1,不然控制不住大小
            "formatter": "generic",# 对应下面的键
            # 'mode': 'w+',
            "filename": "./logs/erro.log"# 打日志的路径
        },
        "access_file": {
            "encoding":"UTF-8",
            "level":"INFO",
            "class": "logging.handlers.RotatingFileHandler",
            # "maxBytes": 1024*1024*1024,
            # "backupCount": 1,
            "formatter": "generic",
            "filename": "./logs/access.log",
        }
    },
    'formatters':{
        "generic": {
            "format": "[%(asctime)s] %(levelname)s [%(filename)s:%(lineno)s] %(message)s", # 打日志的格式
            "datefmt": "[%Y-%m-%d %H:%M:%S %z]",# 时间显示方法
            "class": "logging.Formatter"
        },
        "access": {
            "format": "[%(asctime)s] %(levelname)s [%(filename)s:%(lineno)s] %(message)s",
            "class": "logging.Formatter"
        }
    }
}

Python模块之Logging(四)——常用handlers的使用

一、StreamHandler

流handler——包含在logging模块中的三个handler之一。

能够将日志信息输出到sys.stdout, sys.stderr 或者类文件对象(更确切点,就是能够支持write()和flush()方法的对象)。

只有一个参数:

class logging.StreamHandler(stream=None)

日志信息会输出到指定的stream中,如果stream为空则默认输出到sys.stderr。

二、FileHandler

logging模块自带的三个handler之一。继承自StreamHandler。将日志信息输出到磁盘文件上。

构造参数:

class logging.FileHandler(filename, mode='a', encoding=None, delay=False)

模式默认为append,delay为true时,文件直到emit方法被执行才会打开。默认情况下,日志文件可以无限增大。

三、NullHandler

空操作handler,logging模块自带的三个handler之一。 没有参数。

四、WatchedFileHandler

位于logging.handlers模块中。用于监视文件的状态,如果文件被改变了,那么就关闭当前流,重新打开文件,创建一个新的流。由于newsyslog或者logrotate的使用会导致文件改变。这个handler是专门为linux/unix系统设计的,因为在windows系统下,正在被打开的文件是不会被改变的。 参数和FileHandler相同:

class logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)

五、RotatingFileHandler

位于logging.handlers支持循环日志文件。

class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0)

参数maxBytes和backupCount允许日志文件在达到maxBytes时rollover.当文件大小达到或者超过maxBytes时,就会新创建一个日志文件。上述的这两个参数任一一个为0时,rollover都不会发生。也就是就文件没有maxBytes限制。backupcount是备份数目,也就是最多能有多少个备份。命名会在日志的base_name后面加上.0-.n的后缀,如example.log.1,example.log.1,…,example.log.10。当前使用的日志文件为base_name.log。

六、TimedRotatingFileHandler

定时循环日志handler,位于logging.handlers,支持定时生成新日志文件。

class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)

参数when决定了时间间隔的类型,参数interval决定了多少的时间间隔。如when=‘D’,interval=2,就是指两天的时间间隔,backupCount决定了能留几个日志文件。超过数量就会丢弃掉老的日志文件。

when的参数决定了时间间隔的类型。两者之间的关系如下:

 'S'         |  秒
 'M'         |  分
 'H'         |  时
 'D'         |  天
 'W0'-'W6'   |  周一至周日
 'midnight'  |  每天的凌晨

utc参数表示UTC时间。

七、其他handler——SocketHandler、DatagramHandler、SysLogHandler、NtEventHandler、SMTPHandler、MemoryHandler、HTTPHandler

这些handler都不怎么常用,所以具体介绍就请参考官方文档 其他handlers

下面使用简单的例子来演示handler的使用:

例子一——不使用配置文件的方式(StreamHandler):

import logging
# set up logging to file - see previous section for more details
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename='/temp/myapp.log',
                    filemode='w')

# define a Handler which writes INFO messages or higher to the sys.stderr
# 
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
#设置格式
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
#告诉handler使用这个格式
console.setFormatter(formatter)
# add the handler to the root logger
#为root logger添加handler
logging.getLogger('').addHandler(console)
# Now, we can log to the root logger, or any other logger. First the root...
#默认使用的是root logger
logging.info('Jackdaws love my big sphinx of quartz.')
# Now, define a couple of other loggers which might represent areas in your
# application:
logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')
logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')

输出到控制台的结果:

root        : INFO     Jackdaws love my big sphinx of quartz.
myapp.area1 : INFO     How quickly daft jumping zebras vex.
myapp.area2 : WARNING  Jail zesty vixen who grabbed pay from quack.
myapp.area2 : ERROR    The five boxing wizards jump quickly.

例子二——使用配置文件的方式(TimedRotatingFileHandler) :

log.conf 日志配置文件:

[loggers]
keys=root,test.subtest,test
[handlers]
keys=consoleHandler,fileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=INFO
handlers=consoleHandler,fileHandler
[logger_test]
level=INFO
handlers=consoleHandler,fileHandler
qualname=tornado
propagate=0
[logger_test.subtest]
level=INFO
handlers=consoleHandler,fileHandler
qualname=rocket.raccoon
propagate=0
[handler_consoleHandler] #输出到控制台的handler
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler] #输出到日志文件的handler
class=logging.handlers.TimedRotatingFileHandler
level=DEBUG
formatter=simpleFormatter
args=('rocket_raccoon_log','midnight')
[formatter_simpleFormatter]
format=[%(asctime)s-%(name)s(%(levelname)s)%(filename)s:%(lineno)d]%(message)s
datefmt=logging.config.fileConfig('conf/log.conf')
logger = getLogging()

获取logger方法:

def getLogging():
 return logging.getLogger("test.subtest")

配置logger并且调用:

logging.config.fileConfig('conf/log.conf')
logger = getLogging()
logger.info("this is an example!")

控制台和日志文件中都会输出:

[2016-07-01 09:22:06,470-test.subtest(INFO)main.py:55]this is an example!

滚动日志与过期删除(按时间)

# coding:utf-8
import logging
import time
import re
from logging.handlers import TimedRotatingFileHandler
from logging.handlers import RotatingFileHandler


def backroll():
    #日志打印格式
    log_fmt = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s'
    formatter = logging.Formatter(log_fmt)
    #创建TimedRotatingFileHandler对象
    log_file_handler = TimedRotatingFileHandler(filename="ds_update", when="M", interval=2, backupCount=2)
    #log_file_handler.suffix = "%Y-%m-%d_%H-%M.log"
    #log_file_handler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}.log$")
    log_file_handler.setFormatter(formatter)
    logging.basicConfig(level=logging.INFO)
    log = logging.getLogger()
    log.addHandler(log_file_handler)
    #循环打印日志
    log_content = "test log"
    count = 0
    while count < 30:
        log.error(log_content)
        time.sleep(20)
        count = count + 1
    log.removeHandler(log_file_handler)


if __name__ == "__main__":
    backroll()

filename:日志文件名的prefix;

when:是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下: “S”: Seconds “M”: Minutes “H”: Hours “D”: Days “W”: Week day (0=Monday) “midnight”: Roll over at midnight

interval: 滚动周期,单位有when指定,比如:when=’D’,interval=1,表示每天产生一个日志文件

backupCount: 表示日志文件的保留个数

几个重要的概念

  • Logger 记录器,暴露了应用程序代码能直接使用的接口。

  • Handler 处理器,将(记录器产生的)日志记录发送至合适的目的地。

  • Filter 过滤器,提供了更好的粒度控制,它可以决定输出哪些日志记录。

  • Formatter 格式化器,指明了最终输出中日志记录的布局。

Logger 记录器

Logger是一个树形层级结构,在使用接口debug,info,warn,error,critical之前必须创建Logger实例,即创建一个记录器,如果没有显式的进行创建,则默认创建一个root logger,并应用默认的日志级别(WARN),处理器Handler(StreamHandler,即将日志信息打印输出在标准输出上),和格式化器Formatter(默认的格式即为第一个简单使用程序中输出的格式)。

创建方法: logger = logging.getLogger(logger_name)

创建Logger实例后,可以使用以下方法进行日志级别设置,增加处理器Handler。

  • logger.setLevel(logging.ERROR) # 设置日志级别为ERROR,即只有日志级别大于等于ERROR的日志才会输出

  • logger.addHandler(handler_name) # 为Logger实例增加一个处理器

  • logger.removeHandler(handler_name) # 为Logger实例删除一个处理器

Handler 处理器

Handler处理器类型有很多种,比较常用的有三个,StreamHandlerFileHandlerNullHandler,详情可以访问Python logging.handlers

创建StreamHandler之后,可以通过使用以下方法设置日志级别,设置格式化器Formatter,增加或删除过滤器Filter。

  • ch.setLevel(logging.WARN) # 指定日志级别,低于WARN级别的日志将被忽略

  • ch.setFormatter(formatter_name) # 设置一个格式化器formatter

  • ch.addFilter(filter_name) # 增加一个过滤器,可以增加多个

  • ch.removeFilter(filter_name) # 删除一个过滤器

StreamHandler

创建方法: sh = logging.StreamHandler(stream=None)

FileHandler

创建方法: fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)

NullHandler

NullHandler类位于核心logging包,不做任何的格式化或者输出。 本质上它是个“什么都不做”的handler,由库开发者使用。

Formatter 格式化器

使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。

创建方法: formatter = logging.Formatter(fmt=None, datefmt=None)

其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,将使用'%(message)s'。如果不指明datefmt,将使用ISO8601日期格式。

Filter 过滤器

Handlers和Loggers可以使用Filters来完成比级别更复杂的过滤。Filter基类只允许特定Logger层次以下的事件。例如用‘A.B’初始化的Filter允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。

创建方法: filter = logging.Filter(name='')

相关文章

  • logging

    Menu logging定义 logging - basicConfig logging定义 日志模块 loggi...

  • Python39_日志处理

    logging模块 日志基础 日志级别(从低到高): logging.NOTSET:不设置 loging.debu...

  • 日志框架

    日志门面 Apache Commons Logging (之前叫 Jakarta Commons Logging,...

  • appium自动化测试日志收集-logging

    关于日志级别: logging对象和filehandler都可以设置日志级别,logging设置的日志级别是控制台...

  • logging模块

    (一)什么是logging模块? logging是Python内置的日志模块,用于生成程序的日志 (二...

  • springboot通过java -jar启动

    命令参数 00x0 日志配置: 默认日志:使用logging.path、logging.file 参数来指定日志输...

  • python logging模块应用详解

    python的日志模块,logging。 首先介绍日志的级别:日志的严重程度 logging中包含了四个主要的类:...

  • Spring Boot - 配置文件

    1.logging:日志 org.springframework.boot.logging.logback包下提供...

  • Python Logging 指南

    Python Logging 指南 文章翻译自官方文档:Logging HOWTO 基础日志教程 日志记录是一种跟...

  • python常用模块练习题

    练习题 1.logging模块有几个日志级别? logging的日志可以分为debug(), info(), wa...

网友评论

      本文标题:logging日志

      本文链接:https://www.haomeiwen.com/subject/smmlnltx.html