Appium [求助] 基于 python 封装的日志处理方法,结果打印日志重复,如何破?

匿名 · 2016年10月12日 · 1964 次阅读

日志封装代码:


class LogSignleton(object):
    def __init__(self,log_config):
        '''单例模式'''
        pass

    def __new__(cls,log_config):

        mutex = threading.Lock()
        mutex.acquire() #上锁,防止多线程下出问题
        if not hasattr(cls,'instance'):
            cls.instance = super(LogSignleton,cls).__new__(cls)
            cls.instance.log_filename = read_config(log_config,'logger','log_file')
            if cls.instance.log_filename is not None: # 判断是否为目录
                try:
                    # 返回的是文件名,不包括前面的路径
                    filename = os.path.basename(cls.instance.log_filename)
                    # 返回的是目录名,不包括文件名
                    filepath = os.path.dirname(cls.instance.log_filename)
                    print filepath
                    # splitext:分离文件名和后缀 split:分离文件路径和文件
                    parent_path, ext = os.path.splitext(filename)
                    # 定义时间显示格式
                    tm = time.strftime('%Y%m%d%H%M%S', time.localtime())
                    # 重新组装日志文件名
                    filename = parent_path + '_' + tm + ext
                    cls.instance.log_filename = filepath + '/' + filename
                except Exception:
                    raise
            cls.instance.max_bytes_each = int(read_config(log_config,'logger','max_bytes_each'))
            cls.instance.backup_count = int(read_config(log_config,'logger','backup_count'))
            cls.instance.format = read_config(log_config,'logger','format')
            cls.instance.log_level_in_console = int(read_config(log_config,'logger','log_level_in_console'))
            cls.instance.log_level_in_logfile = int(read_config(log_config,'logger','log_level_in_logfile'))
            cls.instance.logger_name = read_config(log_config,'logger','logger_name')
            cls.instance.console_log_on = int(read_config(log_config,'logger','console_log_on'))
            cls.instance.logfile_log_on = int(read_config(log_config,'logger','logfile_log_on'))
            cls.instance.logger = logging.getLogger(cls.instance.logger_name)
            cls.instance.__config_logger()
        mutex.release() #释放锁
        return cls.instance

    def get_logger(self):
        return self.logger


    def __config_logger(self):
        fmt = self.format.replace('|','%')
        formatter = logging.Formatter(fmt)
        if self.console_log_on == 1: #如果开启控制台日志
            console = logging.StreamHandler()
            console.setFormatter(formatter)
            self.logger.addHandler(console)
            self.logger.setLevel(self.log_level_in_console)

        if self.logfile_log_on == 1: #如果开启文件日志
            rt_file_handler = RotatingFileHandler(self.log_filename,maxBytes=self.max_bytes_each,backupCount=self.backup_count)
            rt_file_handler.setFormatter(formatter)
            self.logger.addHandler(rt_file_handler)
            self.logger.setLevel(self.log_level_in_logfile)


在 Global.py 文件中调用:

logsignleton = LogSignleton(conf_path)
logger = logsignleton.get_logger()

然后在其他文件中导入此 Global.py:

from Global import *

调用:

logger.debug('向右滑动')

控制台和日志文件中都是重复的,下面的操作都只进行了一次:

/Users/***/Documents/Project/UI/output/log
/Users/***/Documents/Project/UI/output/log
****************************************  开始测试  ****************************************
2016-10-12 17:44:18,098 : UI.py[line:38] [ DEBUG ] 向右滑动
2016-10-12 17:44:18,098 : UI.py[line:38] [ DEBUG ] 向右滑动
2016-10-12 17:44:18,098 : Element.py[line:89] [ DEBUG ] 获取当前窗口大小
2016-10-12 17:44:18,098 : Element.py[line:89] [ DEBUG ] 获取当前窗口大小
共收到 10 条回复 时间 点赞

麻烦 md 用代码块,这样看代码,缩进都没了,好累。。。

md 怎么用请看回复框右下角的 排版说明 链接。

你这样使用日志好奇怪...

匿名 #3 · 2016年10月13日

#1 楼 @chenhengjie123 多谢提醒,已经改正过来了,请帮忙看看问题

匿名 #4 · 2016年10月13日

#2 楼 @jacexh 对啊,我也觉得好奇怪,还没找到原因,感觉像实例化了 2 次,可是我用了单例,还是不行,还不知道哪里出错了

应该不是实例化了两次,是别的问题。

话说,有一点没理解,你的 debug 方法是哪里的?你给的代码里没有具体 debug 方法的源码,而且 LogSignleton 也没有继承 logging 类。

可以试下在 debug 方法加个断点,然后 step into 进去看看具体调用了什么。

#4 楼 @Tank007
应该不是实例化了 2 次哦,单例是实例化对象在内存中的地址是同一个地址。
单例模式的_new() 方法是在__init_() 之前被调用

cls.instance.logger = logging.getLogger(cls.instance.logger_name)

这行代码在运行的时候,相当于 cls.instance 已经有一个类属性 logger
当你在初始化实例时,并调用 get_logger() 方法时,实际上

logsignleton = LogSignleton(conf_path)

又返回了一个 logger 对象。
所以你在使用的时候,logger.debug('向右滑动') 就返回两次输出。一个是类实例的输出,一个是类本身的属性输出。


修改下 get_logger 方法:

def get_logger(self,logger_name):
    return logging.getLogger(logger_name)
# 这行注释掉试试看:
ls.instance.logger = logging.getLogger(cls.instance.logger_name)

以上是猜测,未做本地验证,不知道 debug 的对不对,若错误了,请指出,谢谢

匿名 #7 · 2016年10月13日

#6 楼 @xie_0723 还是不行啊,依然重复,和你稍微有点不一样,你那有语法错误,我的修改如下:

# cls.instance.logger = logging.getLogger(cls.instance.logger_name) #注释掉了

然后修改了

def get_logger(self):
    return logging.getLogger(self.logger_name)

还有这个地方,因为用到了 cls.instance.logger

def __config_logger(self):
    fmt = self.format.replace('|','%')
    formatter = logging.Formatter(fmt)
            #还有修改这里
    logger = logging.getLogger(self.logger_name)
    if self.console_log_on == 1: #如果开启控制台日志
        console = logging.StreamHandler()
        console.setFormatter(formatter)
        logger.addHandler(console)
        logger.setLevel(self.log_level_in_console)

    if self.logfile_log_on == 1: #如果开启文件日志
        rt_file_handler = RotatingFileHandler(self.log_filename,maxBytes=self.max_bytes_each,backupCount=self.backup_count)
        rt_file_handler.setFormatter(formatter)
        logger.addHandler(rt_file_handler)
        logger.setLevel(self.log_level_in_logfile)

#7 楼 @Tank007 打断点看看? 那没招了,坐等 ds 来。

很简单,你看下 logging.root.handlers 或者 logging.getlogger().handlers 下是否都有对象,清除多出来的就行

另外我觉得这个类是没有必要的,建议楼主看下这文章:http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python

匿名 #10 · 2016年10月13日

#9 楼 @jacexh 多谢,已经解决了,是我自己的代码问题,有另外一个文件虽然没用到,但是这个文件实例化了 logger,注释掉就好了,代码没的问题

匿名 关闭了讨论 10月13日 18:24
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册