我正在使用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,但没有回答。
我看不到错误的零件代码......
答案 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
模块更好的工作,但可能会有更多的东西。我只是不知道。
我希望这会有所帮助。