You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
11 KiB
285 lines
11 KiB
# coding=utf-8
|
|
'''
|
|
单例模式日志 -- 使用一次关闭 handler
|
|
这种方法优缺点:
|
|
缺点: 输出的format 需要自己定义 ,并过滤
|
|
过滤要看是否以什么开头,或包含什么
|
|
优点: 不占用文件资源,占用系统资源小
|
|
调用 log.info( ) log.error()
|
|
'''
|
|
|
|
import logging
|
|
import logging.handlers
|
|
import os
|
|
import time
|
|
import threading
|
|
# from config import LOG_PATH,LOG_LEVEL,LOG_ENABLED,LOG_FORMAT, \
|
|
# LOG_TO_CONSOLE,LOG_TO_FILE
|
|
|
|
MY_LOGGER_NAME = "DefaultLogger"
|
|
LOG_ENABLED = True # 是否启用日志
|
|
LOG_TO_CONSOLE = True # 是否启用控制台输出日志
|
|
LOG_TO_FILE = False # 是否启用文件输出
|
|
LOG_COLOR_ENABLE = True # 是否启用颜色日志
|
|
|
|
LOGGER_DIR = "logs"
|
|
LOGGER_PATH = os.path.join( os.path.dirname(__file__), LOGGER_DIR )
|
|
LOGGER_FILENAME = os.path.join( LOGGER_PATH, 'logs.log' )
|
|
|
|
"""
|
|
logging.INFO , logging.DEBUG , logging.WARNING , logging.ERROR ,
|
|
"""
|
|
LOG_LEVEL = logging.INFO # 日志等级DEBUG INFO WARNIG ERROR
|
|
# LOG_LEVEL = logging.DEBUG
|
|
# LOG_LEVEL = logging.WARNING
|
|
|
|
"""
|
|
# LOG_FORMAT = " %(name)s - %(module)s - %(filename)s - %(lineno)d | %(levelname)s : %(message)s"
|
|
# LOG_FORMAT = "%(levelname)s - %(asctime)s - process: %(process)d - threadname: %(thread)s " \
|
|
# "- %(filename)s - %(funcName)s - %(lineno)d - %(module)s " \
|
|
# "- %(message)s "
|
|
# LOG_FORMAT = "%(asctime)s - %(thread)s " \
|
|
# "- %(levelname)s ::: %(message)s "
|
|
# '[%(asctime)s] |%(thread)s| %(levelname)-6s: %(message)s'
|
|
# fm = '%(levelname):%(levelno)s:%(name)s:%(funcName)s:%(asctime):%(pathname):%(filename):%(module):%(thread):%(threadName)'
|
|
# 此处日志颜色,修改日志颜色是通过 Filter实现的
|
|
"""
|
|
LOG_FORMAT = '%(levelname)s\t[%(asctime)s] %(package)s:%(classname)s:%(funcname)s \t>> %(message)s'
|
|
|
|
"""
|
|
# 此处日志颜色,修改日志颜色是通过 Filter实现的
|
|
"""
|
|
LOG_FORMAT_COLOR_DICT = {
|
|
'ERROR' : "\033[31mERROR\033[0m",
|
|
'INFO' : "\033[36mINFO\033[0m",
|
|
'DEBUG' : "\033[1mDEBUG\033[0m",
|
|
'WARN' : "\033[33mWARN\033[0m",
|
|
'WARNING' : "\033[33mWARNING\033[0m",
|
|
'CRITICAL': "\033[35mCRITICAL\033[0m",
|
|
}
|
|
|
|
"""
|
|
# Filter 用法, 以package class function 过滤 __package__ __class__
|
|
# log.error( f"{__package__}::{__class__.__name__}::{sys._getframe().f_code.co_name} >> ")
|
|
# log.error( f"PacakgeName::ClassName::FunctionName:: ")
|
|
# LOGGER_FILTER_PACKAGE=[] 为空, 则Filter不起作用
|
|
# 不为空,则只显示定义的报名
|
|
# LOGGER_FILTER_CLASS=[] 为空, 则Filter不起作用
|
|
# 不为空,则只显示定义的类或
|
|
# LOGGER_FILTER_FUNCNAME=[] 为空, 则Filter不起作用
|
|
# 不为空,则只显示定义的函数
|
|
"""
|
|
# LOGGER_FILTER_PACKAGE = [ "test_logger" ] # 包名,文件名去 .py??
|
|
LOGGER_FILTER_PACKAGE = [ ]
|
|
LOGGER_FILTER_CLASS = [ ] # 类名,文件名去 .py??
|
|
# LOGGER_FILTER_CLASS = [ "LogTest" ]
|
|
# LOGGER_FILTER_FUNCNAME = [ "test1","test" ] # 函数名
|
|
LOGGER_FILTER_FUNCNAME = [ ]
|
|
LOGGER_FILTER_LEVELNAME = [ ] # INFO DEBUG WARNING
|
|
|
|
class PackageFilter(logging.Filter):
|
|
def __init__(self, filter_word:list = []):
|
|
self.filter_word = filter_word
|
|
pass
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
if self.filter_word is not None:
|
|
return record.package in self.filter_word
|
|
|
|
class ClassFilter(logging.Filter):
|
|
def __init__(self, filter_word:list = []):
|
|
self.filter_word = filter_word
|
|
pass
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
if self.filter_word is not None:
|
|
return record.classname in self.filter_word
|
|
|
|
pass
|
|
|
|
class FunctionFilter(logging.Filter):
|
|
def __init__(self, filter_word:list = []):
|
|
self.filter_word = filter_word
|
|
pass
|
|
def filter(self, record:logging.LogRecord) -> bool:
|
|
if self.filter_word is not None:
|
|
return record.funcname in self.filter_word
|
|
|
|
class LevelNameFilter(logging.Filter):
|
|
def __init__(self, filter_word:list = []):
|
|
self.filter_word = filter_word
|
|
pass
|
|
def filter(self, record:logging.LogRecord) -> bool:
|
|
if self.filter_word is not None:
|
|
return record.levelname in self.filter_word
|
|
|
|
class ColorFilter(logging.Filter):
|
|
def __init__(self,):
|
|
pass
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
record.levelname = LOG_FORMAT_COLOR_DICT.get(record.levelname)
|
|
return True
|
|
|
|
class Log(object):
|
|
_instance_lock = threading.Lock()
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
if not hasattr(Log, "_instance"):
|
|
with Log._instance_lock:
|
|
if not hasattr(Log, "_instance"):
|
|
Log._instance = object.__new__(cls)
|
|
return Log._instance
|
|
|
|
def __init__(self, loggername = "DefaultLog" ):
|
|
# 文件命名 os.path.join(): 将多个路径组合后返回
|
|
self.logger_filepath = LOGGER_FILENAME
|
|
self.loggername = loggername
|
|
self.level = LOG_LEVEL
|
|
|
|
# 日志输出格式
|
|
fm = LOG_FORMAT
|
|
self.formatter = logging.Formatter( fm )
|
|
|
|
# 生成记录器对象
|
|
self.logger = logging.getLogger( self.loggername )
|
|
self.logger.setLevel(LOG_LEVEL)
|
|
|
|
# 日志过滤
|
|
self.__add_filter()
|
|
|
|
def __console(self, level, message, extra={} ):
|
|
# 添加 handler
|
|
self.__add_handler()
|
|
|
|
# 判断日志级别
|
|
if level == logging.INFO:
|
|
self.logger.info( message, extra=extra)
|
|
elif level == logging.DEBUG:
|
|
self.logger.debug(message,extra=extra)
|
|
elif level == logging.WARNING:
|
|
self.logger.warning(message,extra=extra)
|
|
elif level == logging.ERROR:
|
|
self.logger.error(message,extra=extra)
|
|
|
|
# removeHandler在记录日志之后移除句柄,避免日志输出重复问题
|
|
self.__remove_handler()
|
|
# if LOG_TO_FILE and self.file_handler:
|
|
# self.logger.removeHandler(self.file_handler)
|
|
# # 关闭打开的文件
|
|
# self.file_handler.close()
|
|
# if LOG_TO_CONSOLE and self.stream_handler:
|
|
# self.logger.removeHandler(self.stream_handler)
|
|
# # 关闭打开的文件
|
|
# self.stream_handler.close()
|
|
pass
|
|
|
|
# debug < info< warning< error< critical
|
|
# debug模式
|
|
def debug(self, message, package="Unknown", classname="Unknown", funcname="Unknown" ):
|
|
self.__console(logging.DEBUG, message, extra={"package":package, "classname":classname, "funcname":funcname} )
|
|
# self.__remove_handler()
|
|
# info模式
|
|
def info(self, message, package="Unknown", classname="Unknown", funcname="Unknown" ):
|
|
self.__console(logging.INFO, message, extra={"package":package, "classname":classname, "funcname":funcname} )
|
|
# self.__remove_handler()
|
|
# warning模式
|
|
def warning(self, message, package="Unknown", classname="Unknown", funcname="Unknown"):
|
|
self.__console(logging.WARNING, message, extra={"package":package, "classname":classname, "funcname":funcname} )
|
|
# self.__remove_handler()
|
|
|
|
# error模式
|
|
def error(self, message, package="Unknown", classname="Unknown", funcname="Unknown"):
|
|
self.__console(logging.ERROR, message, extra={"package":package, "classname":classname, "funcname":funcname} )
|
|
# self.__remove_handler()
|
|
|
|
def __add_filter(self ):
|
|
if len( LOGGER_FILTER_PACKAGE ) > 0 :
|
|
self.logger.addFilter( PackageFilter( filter_word=LOGGER_FILTER_PACKAGE ) )
|
|
if len( LOGGER_FILTER_CLASS ) > 0 :
|
|
self.logger.addFilter( ClassFilter( filter_word=LOGGER_FILTER_CLASS ) )
|
|
if len( LOGGER_FILTER_FUNCNAME ) > 0 :
|
|
self.logger.addFilter( FunctionFilter( filter_word=LOGGER_FILTER_FUNCNAME ) )
|
|
if len(LOGGER_FILTER_LEVELNAME) > 0 :
|
|
self.logger.addFilter( LevelNameFilter( filter_word=LOGGER_FILTER_LEVELNAME ) )
|
|
|
|
def __add_handler(self ):
|
|
if LOG_ENABLED and LOG_TO_FILE:
|
|
# 考虑使用 RotatingFileHandler TimedRotatingFileHandler防止日志过大
|
|
# RotatingFileHandler("test", "a", 4096, 2, "utf-8")
|
|
# TimedRotatingFileHandler(filename=LOG_PATH+"thread_", when="D", interval=1, backupCount=7)
|
|
self.file_handler = logging.handlers.TimedRotatingFileHandler(filename=self.logger_filepath, when='D', interval=1, backupCount=30, encoding='utf-8')
|
|
# self.file_handler = logging.FileHandler( self.logger_filepath, encoding='utf-8' )
|
|
self.file_handler.setFormatter( self.formatter )
|
|
# self.file_handler.setLevel( LOG_LEVEL )
|
|
# if LOG_COLOR_ENABLE: # 文件日志无需加彩色
|
|
# self.file_handler.addFilter( ColorFilter( ) )
|
|
self.logger.addHandler(self.file_handler)
|
|
|
|
if LOG_ENABLED and LOG_TO_CONSOLE:
|
|
# 创建一个StreamHandler,用于输出到控制台
|
|
|
|
self.stream_handler = logging.StreamHandler()
|
|
self.stream_handler.setFormatter(self.formatter)
|
|
# self.stream_handler.setLevel( LOG_LEVEL )
|
|
if LOG_COLOR_ENABLE:
|
|
self.stream_handler.addFilter( ColorFilter( ) )
|
|
self.logger.addHandler(self.stream_handler)
|
|
|
|
def __remove_handler(self ):
|
|
if LOG_TO_FILE and self.file_handler:
|
|
self.logger.removeHandler(self.file_handler)
|
|
if len(self.logger.handlers)>0:
|
|
self.logger.handlers.pop()
|
|
# 关闭打开的文件
|
|
self.file_handler.close()
|
|
if LOG_TO_CONSOLE and self.stream_handler:
|
|
self.logger.removeHandler(self.stream_handler)
|
|
if len(self.logger.handlers)>0:
|
|
self.logger.handlers.pop()
|
|
# 关闭控制台
|
|
self.stream_handler.close()
|
|
|
|
def __remove_handler2(self ):
|
|
if LOG_ENABLED and LOG_TO_CONSOLE:
|
|
self.logger.removeHandler(self.stream_handler)
|
|
self.logger.handlers.pop()
|
|
# 关闭控制台
|
|
self.stream_handler.close()
|
|
if LOG_ENABLED and LOG_TO_FILE:
|
|
self.logger.removeHandler(self.file_handler)
|
|
self.logger.handlers.pop()
|
|
# 关闭打开的文件
|
|
self.file_handler.close()
|
|
|
|
|
|
log = Log( loggername = "DefaultLog")
|
|
|
|
"""
|
|
filename: 指定日志文件名
|
|
filemode: 和file函数意义相同,指定日志文件的打开模式,’w’或’a’
|
|
format: 指定输出的格式和内容,format可以输出很多有用信息。显示的条目可以是以下内容:
|
|
%(levelname): 日志级别的名字格式
|
|
%(levelno)s: 日志级别的数字表示
|
|
%(name)s: 日志名字 loggername
|
|
%(funcName)s: 函数名字
|
|
%(asctime): 日志时间,可以使用datefmt去定义时间格式,如上图。
|
|
%(pathname): 脚本的绝对路径
|
|
%(filename): 脚本的名字
|
|
%(module): 模块的名字
|
|
%(thread): thread id
|
|
%(threadName): 线程的名字
|
|
"""
|
|
|
|
"""
|
|
文件名行号 函数名, 要在调用的时候想办法了
|
|
# 绝对路径
|
|
print( __file__ )
|
|
print( sys.argv[0] )
|
|
|
|
# 文件名
|
|
print( os.path.basename(__file__) )
|
|
print( os.path.basename(sys.argv[0]) )
|
|
|
|
self.__class__.__name__
|
|
self.__class__.__name__, get_current_function_name()
|
|
|
|
logger名 __name__
|
|
""" |