思路:使用自定义封装日志器进行日志的输出:按单个文件,按文件大小,按时间。如果初始化异常则使用默认日志器
步骤如下:
1. 默认日志器,默认日志器使用按天分割日志
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Author : holidaylzz
# Time : 2020/5/10
import time
import yaml.scanner
import logging
import logging.config
from logging.handlers import TimedRotatingFileHandler
from core.util.pathUtil import PathUtil
def basic_logger():
"""
默认日志器
:return:
"""
path_util = PathUtil()
# 在项目中创建日志文件路径,已创建则忽略
path_util.makedirs('log')
# 日志格式
fmt = logging.Formatter('%(asctime)s - %(thread)s - [%(levelname)s] %(pathname)s - %(funcName)s : %(message)s')
# 日志等级
level = logging.INFO
# 日志器
logger = logging.getLogger()
logger.setLevel(level)
# 使用多个不同等级的handler输出到不同日志文件
#info handler
info_filename = path_util.get_project_file_path('log/apiTest.log')
tft_info_handler = TimedRotatingFileHandler(filename=info_filename, when='midnight', backupCount=10, encoding='UTF-8')
tft_info_handler.setLevel(logging.INFO)
tft_info_handler.setFormatter(fmt)
logger.addHandler(tft_info_handler)
# error handler
error_filename = path_util.get_project_file_path('log/apiTest_error.log')
tft_error_handler = TimedRotatingFileHandler(filename=error_filename, when='midnight', backupCount=10, encoding='UTF-8')
tft_error_handler.setLevel(logging.ERROR)
tft_error_handler.setFormatter(fmt)
logger.addHandler(tft_error_handler)
# warn handler
warn_filename = path_util.get_project_file_path('log/apiTest_warn.log')
tft_warn_handler = TimedRotatingFileHandler(filename=warn_filename, when='midnight', backupCount=10, encoding='UTF-8')
tft_warn_handler.setLevel(logging.WARN)
tft_warn_handler.setFormatter(fmt)
logger.addHandler(tft_warn_handler)
return logger
2.自定义日志器封装
1.自定义日志器封装类
# 和上面的代码放在同一个文件logger.py
class Logger(object):
def __init__(self, logger_name="timedRotatingFile_module"):
"""
日志器初始化
:param logger_name:默认使用timedRotatingFile_module
"""
log_conf = 'logging.yml'
try:
# read logging.yml
with open(log_conf, 'rt') as f:
config = yaml.safe_load(f.read())
logging.config.dictConfig(config)
self.__logger = logging.getLogger(logger_name)
# 异常处理,出现异常使用默认日志器,进行日志输出
except FileNotFoundError as e:
self.__logger = basic_logger()
self.__logger.exception(e)
except yaml.scanner.ScannerError as e:
self.__logger = basic_logger()
self.__logger.exception(e)
except (ValueError, TypeError, AttributeError, ImportError) as e:
self.__logger = basic_logger()
self.__logger.error('logger config error!')
self.__logger.exception(e)
except Exception as e:
self.__logger = basic_logger()
self.__logger.exception(e)
@property
def logger(self):
"""
返回日志器
:return:
"""
return self.__logger
2.logging.yml配置文件
# version must be 1
# logger配置文件
#########################################logging yaml profile##################################################
version: 1
disable_existing_loggers: False
formatters:
simple:
format: "%(asctime)s - %(thread)s - [%(levelname)s] %(pathname)s - %(funcName)s : %(message)s"
#filters:
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
info_file_handler:
class: logging.FileHandler
level: INFO
formatter: simple
filename: ../../../log/apiTest.log
encoding: utf8
error_file_handler:
class: logging.FileHandler
level: ERROR
formatter: simple
filename: ../../../log/apiTest_error.log
encoding: utf8
warn_file_handler:
class: logging.FileHandler
level: WARN
formatter: simple
filename: ../../../log/apiTest_warn.log
encoding: utf8
info_file_timedRotatingFileHandler:
class: logging.handlers.TimedRotatingFileHandler
level: INFO
formatter: simple
filename: ../../../log/apiTest.log
when: 'midnight'
backupCount: 20
encoding: utf8
error_file_timedRotatingFileHandler:
class: logging.handlers.TimedRotatingFileHandler
level: ERROR
formatter: simple
filename: ../../../log/apiTest_error.log
when: 'midnight'
backupCount: 20
encoding: utf8
warn_file_timedRotatingFileHandler:
class: logging.handlers.TimedRotatingFileHandler
level: WARN
formatter: simple
filename: ../../../log/apiTest_warn.log
when: 'midnight'
backupCount: 20
encoding: utf8
info_file_rotatingFileHandler:
class: logging.handlers.RotatingFileHandler
level: INFO
formatter: simple
filename: ../../../log/apiTest.log
maxBytes: 2147483648
backupCount: 20
encoding: utf8
error_file_rotatingFileHandler:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: simple
filename: ../../../log/apiTest_error.log
maxBytes: 2147483648
backupCount: 20
encoding: utf8
warn_file_rotatingFileHandler:
class: logging.handlers.RotatingFileHandler
level: WARN
formatter: simple
filename: ../../../log/apiTest_warn.log
maxBytes: 2147483648
backupCount: 20
encoding: utf8
loggers:
file_module:
level: INFO
handlers: [info_file_handler,error_file_handler,warn_file_handler]
propagate: no
timedRotatingFile_module:
level: INFO
handlers: [info_file_timedRotatingFileHandler,error_file_timedRotatingFileHandler,warn_file_timedRotatingFileHandler]
propagate: no
rotatingFile_module:
level: INFO
handlers: [info_file_rotatingFileHandler,error_file_rotatingFileHandler,warn_file_rotatingFileHandler]
propagate: no
root:
level: INFO
handlers: [console]
3. 自测
def func(num):
try:
ret = num+1/num
return ret
except Exception as e:
logger.exception(e)
if __name__ == '__main__':
logger = Logger().logger
try:
for i in range(10):
data = func(i)
logger.info(data)
except Exception as e:
logger.exception(e)
输出结果:
生成3个日志文件:info,error,warn
其中为了适应项目部署,减少绝对路径配置的麻烦,使用自己写的路径处理工具类,使用项目根目录与日志目录拼接做为日志的绝对路径。
import sys
import os
class PathUtil(object):
"""路径处理工具类"""
def __init__(self):
# 判断调试模式
# Python获得一些有关系统的各种信息的时候就不得不想到os的environ()
debug_vars = dict((a, b) for a, b in os.environ.items() if a.find('IPYTHONENABLE') >= 0)
# 根据不同场景获取根目录
if len(debug_vars) > 0:
# 当前为debug运行时
self.rootPath = sys.path[2]
elif getattr(sys, 'frozen', False):
# 当前为exe运行时
self.rootPath = os.getcwd()
else:
# 正常执行
self.rootPath = sys.path[1]
# 替换斜杠
self.rootPath = self.rootPath.replace("\\", "/")
def get_project_file_path(self, filename):
"""
项目根目录拼接文件项目路径 ,如 logs/default.log
:param filename: 文件项目路径, logs/default.log
:return:
"""
filePath = '%s/%s' % (self.rootPath, filename)
return filePath
def makedirs(self, directory):
"""
在项目中创建文件路径,已创建则忽略
:param directory:项目文件目录
:return:
"""
realPath = '%s/%s' % (self.rootPath, directory)
os.makedirs(realPath, exist_ok=True)
写的不好和有问题的地方请多多指教!
笔芯~