python中的log日志多线程安全
作者:peach_orange
python log日志多线程安全
python中的日志文件为logger,常用的有两个-RotatingFileHandler;TimedRotatingFileHandler。
文件没满足分割条件前,保存在‘info.log’(自己命名的文件)中,如果满足分割条件,会生成‘info.log.1’。
下一次满足分割条件后,将‘info.log’保存成‘info.log.1’,而‘info.log.1’顺延成‘info.log.2’;满足最多保存的个数后,会将其删掉。
RotatingFileHandler,是按大小划分日志文件,使用方法如下。
RotatingFileHandler是按文件大小自动分割保存,下文中设置的是1M保存一个文件,最多保存30个。
此种方式支持多线程安全,支持软件的开关机(TimedRotatingFileHandler不支持,一旦关机,会重新计时)
import logging from logging import handlers def log_init(): log = logging.getLogger('error.log') # log保存位置 format_str = log.Formatter('%(asctime)s - \ # log时间戳格式 %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s') log.setLevel(logging.DEBUG) # log日志等级(往下的内容不保存) sh = logging.StreamHandler() # 往屏幕上输出 # filename:log文件名;maxBytes:超过最大尺寸,log会另存一个文件; # backupCount:最多保存多少个日志;encoding:保存编码格式 th = handlers.RotatingFileHandler(filename='error.log', maxBytes=1024*1024, \ backupCount=30, encoding='uft-8') th.setFormatter(format_str) # 设置文件里写入的格式 log.addHandler(sh) log.addHandler(th) return log if __name__ == '__main__': log = log_init() log.debug('debug_msg') log.info('info_msg') log.warning('warning_msg') log.error('error_msg') log.critical('critical_msg')
TimedRotatingFileHandler是按日期划分日志文件,使用方法如下。
TimedRotatingFileHandler是按文件大小自动分割保存,下文中设置的是每天保存一个log文件,最多保存30个。
此种方式支持多线程不安全,不支持软件的关机(软件关机后,会重新计时,如果经常关机的项目,会只记录在一个文件中)。
import logging from logging import handlers def log_init(): log = logging.getLogger('error.log') # log保存位置 format_str = log.Formatter('%(asctime)s - \ # log时间戳格式 %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s') log.setLevel(logging.DEBUG) # log日志等级(往下的内容不保存) sh = logging.StreamHandler() # 往屏幕上输出 # filename:log文件名;when:多久另存一个文件(S/M/H/D/W/midnight); # backupCount:最多保存多少个日志;encoding:保存编码格式 th = handlers.TimedRotatingFileHandler(filename='error.log', when='D', \ backupCount=30, encoding='uft-8') th.setFormatter(format_str) # 设置文件里写入的格式 log.addHandler(sh) log.addHandler(th) return log if __name__ == '__main__': log = log_init() log.debug('debug_msg') log.info('info_msg') log.warning('warning_msg') log.error('error_msg') log.critical('critical_msg')
如果想多线程使用TimedRotatingFileHandler,就需要自己写一个线程,然后每一个log往消息队列中丢,用线程处理这个消息队列。
Queue是线程安全的,所以作为消息队列使用没有影响。
import time import _thread import logging from logging import handlers from queue import Queue logs_queue = Queue() class LogMsg(object): def __init__(self, type, msg): self._type = type self._msg = msg def log_init(): log = logging.getLogger('error.log') # log保存位置 format_str = log.Formatter('%(asctime)s - \ # log时间戳格式 %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s') log.setLevel(logging.DEBUG) # log日志等级(往下的内容不保存) sh = logging.StreamHandler() # 往屏幕上输出 # filename:log文件名;when:多久另存一个文件(S/M/H/D/W/midnight); # backupCount:最多保存多少个日志;encoding:保存编码格式 th = handlers.TimedRotatingFileHandler(filename='error.log', when='D', \ backupCount=30, encoding='uft-8') th.setFormatter(format_str) # 设置文件里写入的格式 log.addHandler(sh) log.addHandler(th) return log def cop_log_queue(logger): while True: if logs_queue.empty(): time.sleep(1) log_msg = logs_queue.get() if log_msg._type == 'debug': logger.debug(log_msg._msg) elif log_msg._type == 'info': logger.info(log_msg._msg) elif log_msg._type == 'warning': logger.warning(log_msg._msg) elif log_msg._type == 'error': logger.error(log_msg._msg) elif log_msg._type == 'critical': logger.critical(log_msg._msg) def error_thread1(): while True: logs_queue.put(LogMsg('debug','debug_msg')) time.sleep(1) def error_thread2(): while True: logs_queue.put(LogMsg('info','info_msg')) time.sleep(1) if __name__ == '__main__': logger = log_init() _thread.start_new_thread(cope_log_queue, (logger, )) _thread.start_new_thread(error_thread1, ()) _thread.start_new_thread(error_thread2, ())
python之log日志
记录程序日志信息的目的是
- 可以很方便的了解程序的运行情况
- 可以分析用户的操作行为、喜好等信息
- 方便开发人员检查bug
logging日志级别介绍
日志等级可以分为5个,从低到高分别是:
- DEBUG
- INFO
- WARNING
- ERROR
- CRITICAL
日志等级说明:
- DEBUG:程序调试bug时使用
- INFO:程序正常运行时使用
- WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误
- ERROR:程序出错误时使用,如:IO操作失败
- CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用
- 默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
- 日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL
logging日志的使用
在 logging 包中记录日志的方式有两种:
- 输出到控制台
- 保存到日志文件
日志信息输出到控制台的示例代码:
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="w") logging.debug('这是一个debug级别的日志信息') logging.info('这是一个info级别的日志信息') logging.warning('这是一个warning级别的日志信息') logging.error('这是一个error级别的日志信息') logging.critical('这是一个critical级别的日志信息')
会在此目录中自动生成log.txt文件,生成日志数据
运行结果:
2022-01-15 11:31:14,529 - web.py[line:59] - INFO: 动态资源请求:/index.html
2022-01-15 11:31:22,542 - web.py[line:63] - INFO: 静态资源请求:/df
2022-01-15 11:46:14,034 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:46:14,036 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:47:24,065 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:47:26,655 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:47:45,224 - web.py[line:75] - INFO: 静态资源请求:/favicon.ico
2022-01-15 11:52:15,723 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:52:23,513 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:52:31,315 - web.py[line:60] - INFO: 动态资源请求:/index.html
2022-01-15 11:52:36,838 - web.py[line:75] - INFO: 静态资源请求:/favicon.ico
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")
2.INFO级别的日志输出,示例代码:
# 判断是否是动态资源请求 if request_path.endswith(".html"): """这里是动态资源请求,把请求信息交给框架处理""" logging.info("动态资源请求:" + request_path) ... else: """这里是静态资源请求""" logging.info("静态资源请求:" + request_path) ...
3.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日志示例:
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 日志配置操作
小结
记录python程序中日志信息使用 logging 包来完成
logging日志等级有5个:
- DEBUG
- INFO
- WARNING
- ERROR
- CRITICAL
打印(记录)日志的函数有5个:
- logging.debug函数, 表示: 打印(记录)DEBUG级别的日志信息
- logging.info函数, 表示: 打印(记录)INFO级别的日志信息
- logging.warning函数, 表示: 打印(记录)WARNING级别的日志信息
- logging.error函数, 表示: 打印(记录)ERROR级别的日志信息
- logging.critical函数, 表示: 打印(记录)CRITICAL级别的日志信息
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。