转载:https://blog.csdn.net/weixin_43790276/article/details/101944628
logging 模块是 Python 内置的标准模块,用于输出代码日志。
一、logging 模块简介
在工作中,运行的代码量是非常大的,为了更方便的管理代码运行,监控代码运行的过程,需要在代码中添加一些必要的日志输出。
Python 内置了 logging 模块,在 Python 中,可以使用 logging 模块来实现与日志相关的功能。如输出运行日志到控制台,将运行日志写入文件,日志文件滚动存储等。
相对直接 print 打印运行信息而言,使用 logging 模块输出日志可以设置日志等级,指定输出位置,写入文件等,使用起来方便很多。在一个成熟的项目中,打印的日志量是非常大的,logging 模块还可以帮助我们管理日志,以便更好地维护项目和定位问题。
logging 模块主要包含四个部分:
Loggers: 提供程序调用的接口,在代码中调用 api 来记录日志
Handlers: 对日志信息进行不同的处理,如记录日志的方式
Formatters: 定义日志的打印格式
Filters:对日志信息进行过滤, 自定义日志是否输出的判断
二、logging 模块的基本使用
# coding=utf-8 import logging file_name = 'logger.txt' formatter = '%(asctime)s -- %(filename)s[line:%(lineno)d] %(levelname)s\t%(message)s' logging.basicConfig(format=formatter, level=logging.DEBUG) # logging.basicConfig(filename=file_name, format=formatter, level=logging.DEBUG) logger = logging.getLogger(__name__) logger.debug('debug10') logger.info('info20') logger.warning('warning30') logger.error('error40') logger.critical('critical50')
三、basicConfig的参数说明
1. filename: 日志输出到文件的文件名
2. filemode: 文件读写模式,r[+]、w[+]、a[+]
3. format: 日志输出的格式
4. datefat: 日志附带日期时间的格式
5. style:格式占位符,默认为 "%" 和 “{}”
6. level:设置日志输出级别
7. stream: 定义输出流,用来初始化 StreamHandler 对象,不能和 filename 参数一起使用,否则会 ValueError 异常
8. handles: 定义处理器,用来创建 Handler 对象,不能和 filename 、stream 参数一起使用,否则也会抛出 ValueError 异常
四、日志信息同时输出控制台和写入文件
import logging file_name = 'logger.txt' formatter = '%(asctime)s -- %(filename)s[line:%(lineno)d] %(levelname)s\t%(message)s' logging.basicConfig(filename=file_name, format=formatter, level=logging.DEBUG) logger = logging.getLogger(__name__) console = logging.StreamHandler() # console.setLevel(logging.DEBUG) console.setLevel(20) console_formatter = logging.Formatter('%(asctime)s - %(filename)s - [line]:%(lineno)d - %(levelname)s - %(message)s') console.setFormatter(console_formatter) logger.addHandler(console) logger.debug('debug->10') logger.info('info->20') logger.warning('warning->30') logger.error('error->40') logger.exception('exception->40') logger.critical('critical->50') logger.fatal('fatal->50')
在使用 basicConfig() 方法实现日志输出时,如果不指定 filename 参数,则日志信息被输出在控制台,如果指定 filename 参数,则日志被写入文件中。
在实际开发中,通常是即需要写入文件,也需要控制台输出。
这时,可以再定义一个日志处理对象,一个对象写文件,一个对象输出控制台。如上面通过 logging 中的 StreamHandler() 创建一个 console 对象,然后通过 addHandler() 将 console 加入到 logger 对象中。console 可以设置与 basicConfig() 不一样的日志输出格式,可以设置不一样的日志输出等级,是互相独立的。
五、logging 的日志级别
在 logging 中,日志主要有5个等级。
1. DEBUG: 对应数值10,打印调式信息。
2. INFO: 对应数值20,处理请求,状态变化等日常信息。
3. WARNING: 对应数值30,比较重要的提醒信息,警告信息。
4. ERROR: 对应数值40,程序发生了报错,如连接错误,编译错误。
5. CRITICAL: 对应数值50,特别严重的问题,如服务器故障等。
对于日志级别来说,对应的数值表示日志信息的重要程度,或问题的严重程度,数值越大,日志等级越高。
通常,日志级别越低,量越大,级别越高,量越少(天天有重大故障项目就不用做了),在日志的输出过程中,可以指定输出级别,来过滤掉低级别的日志信息。指定日志级别后,只会输出大于等于指定级别的日志,如指定 DEBUG 则会输出所有5个级别的日志,指定 INFO 则不再输出 DEBUG 级别的日志(参考上面代码)。
上面5个日志等级对应的日志输出方法分别为它们的小写。debug(),inifo(), ... 除此之外,还有两个方法,exception() 和 fatal() 。exception() 对应的日志等级与 error() 相同,都是40,exception() 默认返回一个空值。fatal() 有对应的日志等级 FATAL ,fatal() 对应的日志等级与 critical() 相同,都是50,在源码中就是 fatal = critical,两个完全相同。
关于日志级别,我们也可以自定义,自定义日志等级时注意不要和默认的日志等级数值相同。设置日志输出等级的时候,也可以通过数值来指定,只有大于数值的日志等级会被输出(参考上面的代码)。
六、logging 日志的输出格式
1. asctime:日志的输出时间,默认为 YYYY-mm-DD HH:MM:SS,SSS,如: 2019-10-02 21:29:41,016,一直精确到毫秒。可以额外指定 datefmt 参数来指定该变量的格式
2. name: 日志对象的名称
3. filename: 不包含路径的文件名
4. pathname:包含路径的文件名
5. funcName: 日志记录所在的函数名
6. levelname: 日志的级别名称
7. message: 具体的日志信息
8. lineno: 日志记录的代码所在的行号
9. pathname: 完整路径
10. process: 当前进程ID
11. processName: 当前进程名称
12. thread: 当前线程ID
13. threadName: 当前线程名称
这些日志信息是可选的,需要哪些可以自定义,其中 asctime 和 message 是必须有的,不然就失去日志的意义了。常用的有 filename,lineno,levelname,其他的根据情况选用。
定义日志输出格式时,%() 是用来为实际的数据占位用的,后面的 s ,d 表示数据的类型,s 表示字符串, d 表示整数。
Python logging模块切分和轮转日志
一、logging 中常用的日志处理方法和类
1. StreamHandler:logging.StreamHandler,日志输出到流,可以是sys.stderr,sys.stdout或者文件,这个方法通常用来将日志信息输出到控制台
2. FileHandler:logging.FileHandler,日志输出到文件,指定文件,将日志信息写入到文件中
3. BaseRotatingHandler:logging.handlers.BaseRotatingHandler,基本的日志轮转方式,这个类是日志轮转的基类,后面日志按时间轮转,按大小轮转的类都继承于此。轮转的意思就是保留一定数量的日志量,如设置保持7天日志,则会自动删除旧的日志,只保留最近7天
4. RotatingHandler:logging.handlers.RotatingHandler,继承BaseRotatingHandler,支持日志文件按大小轮转
5. TimeRotatingHandler:logging.handlers.TimeRotatingHandler,继承BaseRotatingHandler,支持日志文件按时间轮转
6. SocketHandler:logging.handlers.SocketHandler,远程输出日志到TCP/IP sockets
7. DatagramHandler:logging.handlers.DatagramHandler,远程输出日志到UDP sockets
8. SMTPHandler:logging.handlers.SMTPHandler,远程输出日志到邮件地址
9. MemoryHandler:logging.handlers.MemoryHandler,日志输出到内存中的指定buffer
10. HTTPHandler:logging.handlers.HTTPHandler,通过"GET"或者"POST"远程输出到HTTP服务器
二、logging 控制台输出和文件写入
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) formatter = '%(asctime)s -<>- %(filename)s -<>- [line]:%(lineno)d -<>- %(levelname)s -<>- %(message)s' file_handler = logging.FileHandler('log.txt') file_handler.setLevel(level=logging.INFO) log_formatter = logging.Formatter(formatter) file_handler.setFormatter(log_formatter) console_handler = logging.StreamHandler() console_handler.setLevel(level=logging.INFO) console_formatter = logging.Formatter(formatter) console_handler.setFormatter(console_formatter) logger.addHandler(file_handler) logger.addHandler(console_handler) logger.info('info') logger.error('error')
上面的代码运行后,日志信息即写入了 log.txt 文件中,也打印到了控制台。如果日志文件中没有指定路径,则生成的日志文件与当前运行的py文件处于同一目录,指定了就会生成到指定的目录下。
通过 FileHandler() 方法来定义日志写入的文件,日志格式,日志等级,通过 StreamHandler() 方法定义日志打印到控制台的格式和等级。
然后通过 addHandler() 方法将两个日志处理对象添加到 logger 中,从而实现日志的打印和写文件。
三、日志文件按时间切分
import logging from logging.handlers import TimedRotatingFileHandler import time logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) formatter = '%(asctime)s -<>- %(filename)s -<>- [line]:%(lineno)d -<>- %(levelname)s -<>- %(message)s' time_rotate_file = TimedRotatingFileHandler(filename='time_rotate', when='S', interval=2, backupCount=5) time_rotate_file.setFormatter(logging.Formatter(formatter)) time_rotate_file.setLevel(logging.INFO) console_handler = logging.StreamHandler() console_handler.setLevel(level=logging.INFO) console_handler.setFormatter(logging.Formatter(formatter)) logger.addHandler(time_rotate_file) logger.addHandler(console_handler) while True: logger.info('info') logger.error('error') time.sleep(1)
上面的代码是无限循环,永远也不会停止,为了演示,我将写入文件的日志写信也打印到了控制台。运行代码后,将日志写到文件中,每个文件只保存两秒钟的数据,只保留最新的5个日志文件,文件名是 time_rotate 加时间字符串。
使用 logging.handlers 中的 TimedRotatingFileHandler 类,可以帮助我们实现日志按时间来切分和轮转。
在实际工作中,日志量是很大的,不可能将全部日志写到同一个文件中,这样无法删除旧的日志,且这个文件会越来越大,直到撑爆磁盘。日志按时间切分和轮转的方式根据具体情况来定,如按月切分,保留3年,按天切分,保留30天,按小时切分,保留7天等等,这些 TimedRotatingFileHandler 都可以帮助我们实现。
TimedRotatingFileHandler 的主要参数:
1. filename: 指定日志文件的名字,会在指定的位置创建一个 filename 文件,然后会按照轮转数量创建对应数量的日志文件,每个轮转文件的文件名为 filename 拼接时间,默认YY-mm-DD_HH-MM-SS,可以自定义。
2. when: 指定日志文件轮转的时间单位
S - Seconds
M - Minutes
H - Hours
D - Days
midnight - roll over at midnight
W{0-6} - roll over on a certain day; 0 - Monday
3. interval: 指定日志文件轮转的周期,如 when='S', interval=10,表示每10秒轮转一次,when='D', interval=7,表示每周轮转一次。
4. backupCount: 指定日志文件保留的数量,指定一个整数,则日志文件只保留这么多个,自动删除旧的文件。
四、日志文件按大小切分
import logging from logging.handlers import RotatingFileHandler import time logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) formatter = '%(asctime)s -<>- %(filename)s -<>- [line]:%(lineno)d -<>- %(levelname)s -<>- %(message)s' size_rotate_file = RotatingFileHandler(filename='size_rotate', maxBytes=1*1024, backupCount=5) size_rotate_file.setFormatter(logging.Formatter(formatter)) size_rotate_file.setLevel(logging.INFO) console_handler = logging.StreamHandler() console_handler.setLevel(level=logging.INFO) console_handler.setFormatter(logging.Formatter(formatter)) logger.addHandler(size_rotate_file) logger.addHandler(console_handler) while True: logger.info('info') logger.error('error') time.sleep(1)
运行代码后,将日志写到文件中,每个文件只保存 1kb 的数据,只保留最新的5个日志文件,文件名是 size_rotate 加编号,编号从1开始,最新的日志永远保存在 size_rotate.1 中,编号越大,日志时间越靠前。
使用 logging.handlers 中的 RotatingFileHandler 类,可以帮助我们实现日志按文件大小来切分和轮转。
日志按时间切分和轮转的方式根据具体情况来定,如一个文件最多 1G,100M等,保留文件数可以按需定义,这些 RotatingFileHandler 都可以帮助我们实现。
RotatingFileHandler 的主要参数:
1. filename: 指定日志文件的名字,会在指定的位置创建一个 filename 文件,然后会按照轮转数量创建对应数量的日志文件,每个轮转文件的文件名为 filename 拼接编号,编号从1开始。
2. maxBytes: 设置日志文件的大小,单位是字节,如 1kb 是1024,1M 是 1024*1024 ,1G 是 1024*1024*1024 。
3. mode: 设置文件的写入模式,默认 mode='a' ,即追加写入。
4. backupCount: 指定日志文件保留的数量,指定一个整数,日志文件只保留这么多个,自动删除旧的文件。
五、实现日志对象单例
在一个项目中,项目的代码是分很多功能模块的,在同一个项目中,最好保证使用的是同一个日志对象,所有日志都由同一个对象来输出,才能保证所有日志写到一个文件之中,这就需要使用单例来实现。
import logging from logging.handlers import RotatingFileHandler from threading import Lock class LoggerProject(object): def __init__(self): self.mutex = Lock() self.formatter = '%(asctime)s -<>- %(filename)s -<>- [line]:%(lineno)d -<>- %(levelname)s -<>- %(message)s' def _create_logger(self): _logger = logging.getLogger(__name__) _logger.setLevel(level=logging.INFO) return _logger def _file_logger(self): size_rotate_file = RotatingFileHandler(filename='size_rotate', maxBytes=1024*1024, backupCount=5) size_rotate_file.setFormatter(logging.Formatter(self.formatter)) size_rotate_file.setLevel(logging.INFO) return size_rotate_file def _console_logger(self): console_handler = logging.StreamHandler() console_handler.setLevel(level=logging.INFO) console_handler.setFormatter(logging.Formatter(self.formatter)) return console_handler def pub_logger(self): logger = self._create_logger() self.mutex.acquire() logger.addHandler(self._file_logger()) logger.addHandler(self._console_logger()) self.mutex.release() return logger log_pro1 = LoggerProject() log_pro2 = LoggerProject() logger1 = log_pro1.pub_logger() logger2 = log_pro2.pub_logger() logger1.info('aaa') logger2.info('aaa') print('logger1: ', id(logger1)) print('logger2: ', id(logger2)) print('log_pro1: ', id(log_pro1)) print('log_pro2: ', id(log_pro2))
将创建 logger 对象的代码封装到一个类中,然后定义一个返回 logger 对象的方法,实例化这个类的不同对象,id 不相同,但是通过它们调用类的方法返回的 logger 对象,id 值是相等的,是同一个实例。只是这个实例会在多个线程中运行,会造成线程安全问题,所以在代码中加了锁来避免线程安全问题。
这样创建出来的 logger 对象已经实现单例了,如果想连类的对象也实现单例,写一个单例装饰器装饰这个类就行了。
单例参考:https://blog.csdn.net/weixin_43790276/article/details/101390615
线程安全参考:https://blog.csdn.net/weixin_43790276/article/details/91069959
标签:文件,console,python,filename,logging,日志,logger From: https://www.cnblogs.com/xingxia/p/python_logging.html