线程行为不正常的python记录器

时间:2017-04-22 11:47:26

标签: python logging python-multithreading

我正在使用python 2.7来记录一些包含在线程调用中的消息。但是,我没有使用1次消息,而是将日志发生了3次。好吧,我将范围设置为等于5,但是,消息发生了15次。

以下是代码:

import sys
import logging
import threading  
import logging.handlers
import time 
import os 

ETT="TVAR 1"
objectname="TVAR 2"
URLFORLOG="TVAR 3"

def setup_custom_logger(name):
    fileLogName='visualreclogfile.log'
    #formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
    formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s')

    handlerCH = logging.StreamHandler()
    handlerCH.setFormatter(formatter)

    handlerFILE = logging.handlers.RotatingFileHandler(fileLogName, maxBytes=(1048576*5), backupCount=7)
    handlerFILE.setFormatter(formatter)


    logger = logging.getLogger(name)
    logger.setLevel(logging.INFO)

    logger.addHandler(handlerCH)
    logger.addHandler(handlerFILE)

    return logger

def LoggingFileForELK(MessageToBeLogged):
    logger = setup_custom_logger('root')
    logger.info(MessageToBeLogged)


def mainFunction():
    Messages=("*** CONTENT LOGGING *** OBJECT UUID : %s WITH NAME KEY : %s HAS URL : %s ") %(ETT,objectname,URLFORLOG)
    MessageToBeLogged=str(Messages)
    LoggingFileForELK(MessageToBeLogged)





threads = []
for i in range(5):
    t = threading.Thread(target=mainFunction)
    threads.append(t)
    t.start()
    time.sleep(0.5)
for  t in threads:
    t.join()

以下是我的结果:

2017-04-22 12:36:59,010 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:36:59,512 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:36:59,512 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,018 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,018 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,018 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,520 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,520 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,520 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:00,520 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:01,022 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:01,022 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:01,022 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:01,022 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3
2017-04-22 12:37:01,022 - INFO - *** CONTENT LOGGING *** OBJECT UUID : TVAR 1 WITH NAME KEY : TVAR 2 HAS URL : TVAR 3

我查看了这个stackoverflow thread,但没有回答。

我看不到错误的零件代码......

1 个答案:

答案 0 :(得分:1)

这个重复的东西实际上与threading无关,而是与生成线程并在每个线程上运行for loop的{​​{1}}有关。在每次迭代时,当您认为创建新的记录器对象时,实际上只是引用相同的记录器对象,因为您提供的记录器名称相同(在这种情况下为' root')。但是,您还要在每次迭代时向此logger对象添加更多处理程序。因此,在迭代mainFunction,您有1个记录器对象,1个文件处理程序和1个流处理程序。但是当你点击迭代i = 0时,你现在仍然有1个记录器对象,但是有2个文件处理程序(指向同一个文件)和2个流处理程序。这意味着在迭代结束时i = 1,您已经在文件中添加了3行,并在标准流中打印了3行。遵循此递增逻辑,在第5次迭代结束时,您将最终得到1个记录器对象,5个文件处理程序和5个流处理程序。实质上,这就是文件和标准流中重复行背后的内容。

修复方法是重新定义i = 1函数,以便在记录器对象尚不存在时生成新的处理程序。基本上,你必须有一些形式的容器(在这种情况下是一个字典),它跟踪你创建的记录器及其处理程序。如果使用已存在的记录器名称调用setup_custom_logger,则该函数将仅返回现有记录器;但是否则会生成一个带有处理程序的新记录器。

我已经调整了你的脚本来添加修复:

setup_custom_logger

编辑:

这是记录器及其句柄的正常行为。即使没有线程,只要您引用相同的记录器并向其添加处理程序,这将表现相同。这里的问题是import sys import logging import threading import logging.handlers import time import os ETT="TVAR 1" objectname="TVAR 2" URLFORLOG="TVAR 3" # container to keep track of loggers loggers = {} def setup_custom_logger(name): global loggers # return existing logger if it exists if name in loggers: return loggers.get(name) else: # else, create a new logger with handlers fileLogName='visualreclogfile.log' #formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s') formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s') handlerCH = logging.StreamHandler() handlerCH.setFormatter(formatter) handlerFILE = logging.handlers.RotatingFileHandler(fileLogName, maxBytes=(1048576*5), backupCount=7) handlerFILE.setFormatter(formatter) logger = logging.getLogger(name) logger.setLevel(logging.INFO) logger.addHandler(handlerCH) logger.addHandler(handlerFILE) loggers[name] = logger return logger def LoggingFileForELK(MessageToBeLogged): logger = setup_custom_logger('root') logger.info(MessageToBeLogged) def mainFunction(): Messages=("*** CONTENT LOGGING *** OBJECT UUID : %s WITH NAME KEY : %s HAS URL : %s ") %(ETT,objectname,URLFORLOG) MessageToBeLogged=str(Messages) LoggingFileForELK(MessageToBeLogged) threads = [] for i in range(5): t = threading.Thread(target=mainFunction) t.start() threads.append(t) time.sleep(0.1) for t in threads: t.join() ,而不是for loop进程。如果您从脚本中删除了threading部分,则仍会获得重复的行。例如,以下内容将返回与原始线程版本相同的行数:

threading

这表明 NOT 导致日志记录处理行为异常的线程,而是每次向记录器对象添加新处理程序的过程迭代。底线是:import sys import logging import threading import logging.handlers import time import os ETT="TVAR 1" objectname="TVAR 2" URLFORLOG="TVAR 3" def setup_custom_logger(name): fileLogName='visualreclogfile.log' #formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s') formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s') handlerCH = logging.StreamHandler() handlerCH.setFormatter(formatter) handlerFILE = logging.handlers.RotatingFileHandler(fileLogName, maxBytes=(1048576*5), backupCount=7) handlerFILE.setFormatter(formatter) logger = logging.getLogger(name) logger.setLevel(logging.INFO) logger.addHandler(handlerCH) logger.addHandler(handlerFILE) return logger def LoggingFileForELK(MessageToBeLogged): logger = setup_custom_logger('root') logger.info(MessageToBeLogged) def mainFunction(): Messages=("*** CONTENT LOGGING *** OBJECT UUID : %s WITH NAME KEY : %s HAS URL : %s ") %(ETT,objectname,URLFORLOG) MessageToBeLogged=str(Messages) LoggingFileForELK(MessageToBeLogged) for i in range(5): mainFunction() 非常具有线程感知能力,并且可以与任何线程应用程序一起正常工作,只要 您不会不必要地复制处理程序 。我并不精通其他比logging模块更好的工作,但可能会有更多的东西。我只是不知道。

我希望这会有所帮助。