编写时遇到的问题
1.在写这份日志库的时候,第一版是没有并发处理的,而且打印的日志没有没有打印出来代码所在的文件,函数名,行号等,后来从网上查到sys中可以获取到被调用函数所在的文件,调用函数以及行号等
2.在优化的过程中,开始使用的是线程,后来觉得线程实际上还是在软件上的并发,当他执行时,其他代码块在等待,后来修改为进程,但是在使用进程的时候花了比较多的时间,没有考虑到queue.Queue()是线程上的队列,应该使用进程上的队列multiprocessing.Queue()
3.代码使用的是python3.6
4.该日志库使用的时候需要在main函数中初始化对象
直接上代码
# -*- coding: UTF-8 -*-
#FileName : MCLog
#IDE : PyCharm
#Author : 尹增
#Date : 2019/11/7 17:28
#Desc : 日志库
import logging
# 按文件大小滚动备份
from logging.handlers import RotatingFileHandler
# 控制台日志输入颜色
import colorlog
import time
import os
import queue
import threading
import sys
import multiprocessing
log_colors_config = {
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red',
}
class Log_Msg:
def __init__(self,MsgType,MsgContent,fun_name,code_lineno,code_filename):
self.MsgType = MsgType
self.MsgContent = MsgContent
self.fun_name = fun_name
self.code_lineno = code_lineno
self.code_filename = code_filename
def GetLogFormat(self):
strbackFunc = "[%s] [%s:%d]" % (self.code_filename, self.fun_name, self.code_lineno)
strformatter = "[%(asctime)s] [%(levelname)s] " + strbackFunc + " %(message)s"
return strformatter
class MCLogBase():
def __init__(self,defalLevel=logging.DEBUG, logName=__class__.__name__):
# 加上名字,可以实现日志内容隔离,修改的配置不影响其他库日志的打印配置
self.logger = logging.getLogger(logName)
self.logger.setLevel(defalLevel)
self.StopFlag = "Stop"
#self.log_queue = queue.Queue(1000) # log 队列
#self.log_Out = threading.Thread(target=self.OutPut)
#使用进程实现硬件上的并发
self.log_queue = multiprocessing.Queue()
self.log_Out = multiprocessing.Process(target=self.OutPut)
self.log_Out.start()
def DestroyInstance(self):
#log 退出时停止线程
log_msg = Log_Msg(self.StopFlag, "", "", 1, "")
self.log_queue.put(log_msg)
self.log_Out.join()
def __del__(self):
#self.DestroyInstance()
pass
def debug(self, message):
#获取调用该函数的函数名
back_name = sys._getframe().f_back.f_code.co_name
#获取被调用函数所在调用文件的行号
back_lineno = sys._getframe().f_back.f_lineno
#获取被调用的文件路径
back_filename = sys._getframe().f_back.f_code.co_filename
log_msg = Log_Msg("debug", message,back_name,back_lineno,back_filename)
self.log_queue.put(log_msg)
def info(self, message):
back_name = sys._getframe().f_back.f_code.co_name
back_lineno = sys._getframe().f_back.f_lineno
back_filename = sys._getframe().f_back.f_code.co_filename
log_msg = Log_Msg("info", message,back_name,back_lineno,back_filename)
self.log_queue.put(log_msg)
def warning(self, message):
back_name = sys._getframe().f_back.f_code.co_name
back_lineno = sys._getframe().f_back.f_lineno
back_filename = sys._getframe().f_back.f_code.co_filename
log_msg = Log_Msg("warning", message,back_name,back_lineno,back_filename)
self.log_queue.put(log_msg)
def error(self, message):
back_name = sys._getframe().f_back.f_code.co_name
back_lineno = sys._getframe().f_back.f_lineno
back_filename = sys._getframe().f_back.f_code.co_filename
log_msg = Log_Msg("error", message,back_name,back_lineno,back_filename)
self.log_queue.put(log_msg)
def OutPut(self):
while 1:
log_msg = self.log_queue.get() #取出消息
if log_msg.MsgType == self.StopFlag:
break
self.console(log_msg)
def console(self, log_msg):
pass
def showmsg(self,level, message):
if level == 'info':
self.logger.info(message)
elif level == 'debug':
self.logger.debug(message)
elif level == 'warning':
self.logger.warning(message)
elif level == 'error':
self.logger.error(message)
def getLogLevel(self,logType):
if logType == 'info':
return logging.INFO
elif logType == 'debug':
return logging.DEBUG
elif logType == 'warning':
return logging.WARNING
elif logType == 'error':
return logging.ERROR
class MCLogFile(MCLogBase):
def __init__(self,log_path,defalLevel=logging.DEBUG, logName=__class__.__name__):
self.__InitLogPath(log_path)
MCLogBase.__init__(self,defalLevel, logName)
def __Mkdir(self,log_path):
if not os.path.exists(log_path):
os.makedirs(log_path)
def __InitLogPath(self,log_path):
# 初始化log上层目录
#先判断并创建该文件夹
if log_path != "":
self.__Mkdir(log_path)
#然后判断路径是否为文件夹
if not os.path.isdir(log_path):
cur_path = os.path.dirname(os.path.realpath(__file__))
cur_path = os.path.join(cur_path, 'logs')
if log_path != "":
log_path = os.path.join(cur_path, log_path)
else:
log_path = os.path.join(log_path, 'logs')
self.log_path = os.path.join(log_path, time.strftime('%Y%m%d')) # 文件的命名
self.__Mkdir(self.log_path)
def SetLogPathList(self,level):
log_path = os.path.join(self.log_path, level + '.log')
return log_path
def console(self, log_msg):
log_path = self.SetLogPathList(log_msg.MsgType)
fh = RotatingFileHandler(filename=log_path, mode='a', maxBytes=1024 * 1024 * 5, backupCount=5,encoding='utf-8')
fh.setLevel(self.getLogLevel(log_msg.MsgType))
formatter = logging.Formatter(log_msg.GetLogFormat())
fh.setFormatter(formatter)
self.logger.addHandler(fh)
self.logger.setLevel(logging.DEBUG)
# 输出
self.showmsg(log_msg.MsgType,log_msg.MsgContent)
# 避免日志输出重复问题
self.logger.removeHandler(fh)
# 关闭打开的文件
fh.close()
class MCLogConsole(MCLogBase):
def __init__(self,defalLevel=logging.DEBUG, logName=__class__.__name__):
MCLogBase.__init__(self,defalLevel, logName)
def console(self, log_msg):
ch = colorlog.StreamHandler()
ch.setLevel(self.getLogLevel(log_msg.MsgType))
# 日志输出格式
formatter = colorlog.ColoredFormatter('%(log_color)s' + log_msg.GetLogFormat(),log_colors=log_colors_config)
ch.setFormatter(formatter)
self.logger.addHandler(ch)
self.logger.setLevel(logging.DEBUG)
# 输出
self.showmsg(log_msg.MsgType, log_msg.MsgContent)
# 避免日志输出重复问题
self.logger.removeHandler(ch)
class MCLog:
def __init__(self,log_path=""):
self.console = MCLogConsole()
self.file = MCLogFile(log_path)
def debug(self, message):
self.console.debug(message)
self.file.debug(message)
def info(self, message):
self.console.info(message)
self.file.info(message)
def warning(self, message):
self.console.warning(message)
self.file.warning(message)
def error(self, message):
self.console.error(message)
self.file.error(message)
def DestroyInstance(self):
self.file.DestroyInstance()
self.console.DestroyInstance()
def TestLog(pre):
log = MCLog("MCLog")
i = 0
while i < 3:
i += 1
time.sleep(2)
log.debug(pre+"---测试开始----")
log.info(pre+"操作步骤")
log.warning(pre+"----测试结束----")
log.error(pre+"----测试错误----")
log.DestroyInstance()
if __name__ == "__main__":
t1 = threading.Thread(target=TestLog,args=("1.....",))
t1.start()
t2 = threading.Thread(target=TestLog, args=("2.....",))
t2.start()
t3 = threading.Thread(target=TestLog, args=("3.....",))
t3.start()
t4 = threading.Thread(target=TestLog, args=("4.....",))
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()