Python 实现日志库

编写时遇到的问题

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()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值