Почему объекты print () и logger передаются на консольные выходные данные в неожиданном порядке?

#python #logging #python-logging

#python #ведение журнала #python-ведение журнала

Вопрос:

Я пытаюсь использовать функцию печати до и после использования двух объектов logger, которые будут выводить выходные данные на консоль. Дело в том, что я получаю действительно беспорядочный вывод, который не в ожидаемом порядке.

Я попытался изменить print () на logger_object.info () и это работает так, как задумано.

Полный код с неожиданным выводом :

 import logging

# Create logger objects and set  level
loggerA = logging.getLogger()
loggerA.setLevel(logging.DEBUG)

loggerB = logging.getLogger()
loggerB.setLevel(logging.DEBUG)

# Set log output format
log_format = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s', datefmt='%m/%d/%Y - %I:%M:%S %p')

# Create a console stream handler
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(log_format)

# Add handlers to the loggers
loggerA.addHandler(stream_handler)
loggerB.addHandler(stream_handler)


def main():
    for i in range(10):
        print('Starting loop number {}'.format(i))
        loggerA.info("loop number {}".format(i))
        loggerB.info("loop number {}".format(i))
        print('Finished loop number {}'.format(i))

if __name__ == '__main__':
    main()
  

Добавление объекта loggerC для замены print() на loggerC.info () :

 ...
loggerC = logging.getLogger()
loggerC.setLevel(logging.DEBUG)
...
def main():
    for i in range(10):
        loggerC('Starting loop number {}'.format(i))
        loggerA.info("loop number {}".format(i))
        loggerB.info("loop number {}".format(i))
        loggerC('Finished loop number {}'.format(i))
...
  

Первый случай использования неожиданного вывода print() — это :

 04/06/2019 - 10:10:34 AM - INFO - loggerA - loop number 1
Starting loop number 1
04/06/2019 - 10:10:34 AM - INFO - loggerB - loop number 1
04/06/2019 - 10:10:34 AM - INFO - loggerA - loop number 2
04/06/2019 - 10:10:34 AM - INFO - loggerB - loop number 2
Finished loop number 1
Starting loop number 2
Finished loop number 2
Starting loop number 3
Finished loop number 3
04/06/2019 - 10:10:34 AM - INFO - loggerA - loop number 3
04/06/2019 - 10:10:34 AM - INFO - loggerB - loop number 3
  

Второй случай с использованием loggerC.info () вместо print (), который является ожидаемым результатом :

 04/06/2019 - 10:12:21 AM - INFO - loggerC - Starting loop number 1
04/06/2019 - 10:12:21 AM - INFO - loggerA - loop number 1
04/06/2019 - 10:12:21 AM - INFO - loggerB - loop number 1
04/06/2019 - 10:12:21 AM - INFO - loggerC - Finished loop number 1
04/06/2019 - 10:12:21 AM - INFO - loggerC - Starting loop number 2
04/06/2019 - 10:12:21 AM - INFO - loggerA - loop number 2
04/06/2019 - 10:12:21 AM - INFO - loggerB - loop number 2
04/06/2019 - 10:12:21 AM - INFO - loggerC - Finished loop number 2
04/06/2019 - 10:12:21 AM - INFO - loggerC - Starting loop number 3
04/06/2019 - 10:12:21 AM - INFO - loggerA - loop number 3
04/06/2019 - 10:12:21 AM - INFO - loggerB - loop number 3
04/06/2019 - 10:12:21 AM - INFO - loggerC - Finished loop number 3
  

Ответ №1:

Ваша проблема, вероятно, связана со стандартными потоками. По умолчанию print использует std::out и logger.info использует std::err . Оба потока привязаны к вашему терминалу, но у них могут быть разные flush обновления или триггеры.

Вот почему ваши выходные данные смешаны, функции записывают в разные потоки, и они по-разному выводятся на терминал, что приводит к очевидному противоречивому результату.

Измените ваш первый код, чтобы заставить logger.info использовать один и тот же поток, чем print решите вашу проблему:

 import sys
stream_handler = logging.StreamHandler(sys.stdout)
  

Теперь он возвращает правильный вывод:

 -- Starting loop number 0
04/06/2019 - 07:41:34 AM - INFO - root - loop number 0
04/06/2019 - 07:41:34 AM - INFO - root - loop number 0
-- Finished loop number 0
-- Starting loop number 1
04/06/2019 - 07:41:34 AM - INFO - root - loop number 1
04/06/2019 - 07:41:34 AM - INFO - root - loop number 1
-- Finished loop number 1
-- Starting loop number 2
04/06/2019 - 07:41:34 AM - INFO - root - loop number 2
04/06/2019 - 07:41:34 AM - INFO - root - loop number 2
-- Finished loop number 2
-- Starting loop number 3
04/06/2019 - 07:41:34 AM - INFO - root - loop number 3
04/06/2019 - 07:41:34 AM - INFO - root - loop number 3
-- Finished loop number 3
  

Поскольку оба print и logger.info передают один и тот же поток в правильном порядке всякий раз, когда он сбрасывается на ваш терминал, результат правильный.

Вы также можете сохранить разные потоки и принудительно std::out сбрасывать поток явно:

 def main():
    for i in range(10):
        print('-- Starting loop number {}'.format(i))
        # Force std::out stream (fed by print) to be flushed to the terminal
        # before logger feeds std::err and also flushes
        sys.stdout.flush() 
        loggerA.info("loop number {}".format(i))
        loggerB.info("loop number {}".format(i))
        print('-- Finished loop number {}'.format(i))
        sys.stdout.flush()
  

Эта вторая версия также выдает желаемый результат.

Также помните, что это logger по определению потокобезопасно, но print таковым не является. Если вы собираетесь создать модуль с использованием thread, используйте только logger для отслеживания выполнения.