Дублирование журнала при использовании модуля протоколирования Python - программирование
Подтвердить что ты не робот

Дублирование журнала при использовании модуля протоколирования 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. Как я могу это решить?

4b9b3361

Ответ 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