Дублирование журнала при использовании модуля протоколирования Python
Я использую python logger. Ниже приведен мой код:
import os
import time
import datetime
import logging
class Logger :
def myLogger(self):
logger = logging.getLogger('ProvisioningPython')
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
Проблема заключается в том, что я получаю несколько записей в файле журнала для каждого вызова logger.info
. Как я могу это решить?
Ответы
Ответ 1
logging.getLogger()
уже одноэлементный. (Документация)
Проблема в том, что каждый раз, когда вы вызываете myLogger()
, он добавляет другой экземпляр к экземпляру, который вызывает дубликаты журналов.
Возможно, что-то подобное?
import os
import time
import datetime
import logging
loggers = {}
def myLogger(name):
global loggers
if loggers.get(name):
return loggers.get(name)
else:
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler = logging.FileHandler(
'/root/credentials/Logs/ProvisioningPython'
+ now.strftime("%Y-%m-%d")
+ '.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
loggers[name] = logger
return logger
Ответ 2
import datetime
import logging
class Logger :
def myLogger(self):
logger=logging.getLogger('ProvisioningPython')
if not len(logger.handlers):
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
сделал трюк для меня
используя python 2.7
Ответ 3
С Python 3.2 вы можете просто проверить, присутствуют ли обработчики, и если да, очистите их перед добавлением новых обработчиков. Это довольно удобно при отладке, а код включает в себя инициализацию вашего логгера
if (logger.hasHandlers()):
logger.handlers.clear()
logger.addHandler(handler)
Ответ 4
Вы вызываете Logger.myLogger()
более одного раза. Храните экземпляр регистратора, который он возвращает где-то и повторно использует.
Также имейте в виду, что если вы зарегистрируетесь до добавления любого обработчика, будет создан по умолчанию StreamHandler(sys.stderr)
.
Ответ 5
Реализация регистратора уже является одиночным.
Несколько вызовов для logging.getLogger('someLogger') возвращают ссылку к одному и тому же объекту журнала. Это верно не только в том же модулем, но и между модулями, если он находится в одном и том же Python интерпретатора. Это верно для ссылок на один и тот же объект; Кроме того, код приложения может определять и настраивать родителя logger в одном модуле и создать (но не настроить) дочерний регистратор в отдельный модуль, и все вызовы регистратора для ребенка пройдут до родитель. Вот основной модуль
Source- Использование ведения журнала в нескольких модулях
Таким образом, вы должны использовать это -
Предположим, что мы создали и сконфигурировали регистратор под названием "main_logger" в основном модуле (который просто настраивает регистратор, ничего не возвращает).
# get the logger instance
logger = logging.getLogger("main_logger")
# configuration follows
...
Теперь в подмодуле, если мы создаем дочерний логгер после иерархии именования "main_logger.sub_module_logger", нам не нужно его настраивать в подмодуле. Достаточно просто создать регистратор по иерархии именования.
# get the logger instance
logger = logging.getLogger("main_logger.sub_module_logger")
# no configuration needed
# it inherits the configuration from the parent logger
...
И он не добавит дублирующий обработчик.
См. этот вопрос для немного более подробного ответа.
Ответ 6
Ваш регистратор должен работать как singleton. Вы не должны создавать его более одного раза.
Вот пример того, как это может выглядеть:
import os
import time
import datetime
import logging
class Logger :
logger = None
def myLogger(self):
if None == self.logger:
self.logger=logging.getLogger('ProvisioningPython')
self.logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler=logging.FileHandler('ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
return self.logger
s = Logger()
m = s.myLogger()
m2 = s.myLogger()
m.info("Info1")
m2.info("info2")
Ответ 7
Это дополнение к ответу @rm957377, но с объяснением того, почему это происходит. Когда вы запускаете лямбда-функцию в AWS, они вызывают вашу функцию из экземпляра упаковки, который остается активным для нескольких вызовов. Это означает, что если вы addHandler()
в коде своей функции, он будет продолжать добавлять дублирующиеся обработчики в одноэлементный журнал при каждом запуске функции. Синглтон регистрации сохраняется через несколько вызовов вашей лямбда-функции.
Чтобы решить эту проблему, вы можете очистить ваши обработчики, прежде чем устанавливать их через:
logging.getLogger().handlers.clear()
logging.getLogger().addHandler(...)
Ответ 8
Двойное (или тройное или..- на основе количества перезагрузок) выход регистратора может также произойти, когда вы перезагружаете свой модуль с помощью importlib.reload
(по той же причине, что и в принятом ответе). Я добавляю этот ответ только для будущей справки, так как мне потребовалось некоторое время, чтобы понять, почему мой вывод дублируется (тройным).
Ответ 9
Один простой способ похож на
logger.handlers[:] = [handler]
Таким образом, вы избегаете добавления нового обработчика к "обработчикам" основного списка.
Ответ 10
Вы можете получить список всех обработчиков для конкретного регистратора, чтобы вы могли сделать что-то вроде этого
logger = logging.getLogger(logger_name)
handler_installed = False
for handler in logger:
# Here your condition to check for handler presence
if isinstance(handler, logging.FileHandler) and handler.baseFilename == log_filename:
handler_installed = True
break
if not handler_installed:
logger.addHandler(your_handler)
В приведенном выше примере мы проверяем, уже ли обработчик для указанного файла подключен к регистратору, но доступ к списку всех обработчиков дает вам возможность определить, к какому критерию следует добавить другой обработчик или нет.
Ответ 11
У этой проблемы сегодня. Поскольку мои функции были @staticmethod, вышеупомянутые предложения были разрешены со случайным().
Посмотрите что-то вроде:
import random
logger = logging.getLogger('ProvisioningPython.{}'.format(random.random()))
Ответ 12
В большинстве случаев, когда это происходит, нужно вызывать logger.getLogger() только один раз для каждого модуля. Если у вас есть несколько классов, как я, я мог бы назвать это так:
LOGGER = logger.getLogger(__name__)
class MyClass1:
log = LOGGER
def __init__(self):
self.log.debug('class 1 initialized')
class MyClass2:
log = LOGGER
def __init__(self):
self.log.debug('class 2 initialized')
Оба будут иметь свое собственное полное имя пакета и метод регистрации.
Ответ 13
Я уже использовал logger
в качестве Singleton и проверил, if not len(logger.handlers)
, но все равно получил дубликаты: это был отформатированный вывод с последующим неотформатированным.
Решение в моем случае: logger.propagate = False
Кредиты на этот ответ и документы.
Ответ 14
from logging.handlers import RotatingFileHandler
import logging
import datetime
# stores all the existing loggers
loggers = {}
def get_logger(name):
# if a logger exists, return that logger, else create a new one
global loggers
if name in loggers.keys():
return loggers[name]
else:
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
now = datetime.datetime.now()
handler = logging.FileHandler(
'path_of_your_log_file'
+ now.strftime("%Y-%m-%d")
+ '.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
loggers.update(dict(name=logger))
return logger