Заставить Python logger выводить все сообщения на стандартный вывод в дополнение к файлу журнала

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

Наличие вашей программы явно освобождает ее ресурсы в любом случае может быть хорошей практикой по разным причинам, например:

  • Если у вас есть дополнительные ресурсы, которые не очищены ОС при выходе, например временные файлы или любой тип изменяет на состояние внешнего ресурса, тогда вам понадобится код для работы со всеми этими вещами при выходе, и это часто элегантно сочетается с освобождением памяти.
  • ] Если ваша программа начинает иметь более продолжительный срок службы, вам не нужно, чтобы только способ освобождения памяти для выхода. Например, вы можете конвертировать свою программу в сервер (демон), который продолжает работать при обработке многих запросов для отдельных единиц работы, или ваша программа может стать небольшой частью более крупной программы.

Однако, здесь есть причина, чтобы пропустить освобождение памяти: эффективное завершение работы. Например, предположим, что ваше приложение содержит большой кеш в памяти. Если он выходит, он проходит через всю структуру кэша и освобождает его по одному фрагменту за раз, что не приносит никакой пользы и тратит ресурсы. В частности, рассмотрим случай, когда страницы памяти, содержащие ваш кеш, были заменены на диск операционной системой; прогуливаясь по структуре и освобождая ее, вы сразу возвращаете все эти страницы в память , теряя значительное время и энергию без реальной выгоды и, возможно, даже заставляя другие программы в системе

Как пример, существуют высокопроизводительные серверы, которые работают, создавая процесс для каждого запроса, а затем завершая его завершение; таким образом, им даже не нужно отслеживать выделение памяти , и никогда вообще не делать освобождение или сборку мусора, поскольку все просто возвращается в свободную память операционной системы в конце процесса. (То же самое можно сделать в процессе с использованием специализированного распределителя памяти, но требует очень тщательного программирования, по сути дела, создавая собственное понятие «легкие процессы» в процессе ОС.)

376
задан Ben 18 October 2018 в 07:56
поделиться

8 ответов

Весь вывод журнала обрабатывается обработчиками; просто добавьте logging.StreamHandler() к корневому логгеру.

Вот пример настройки обработчика потока (с использованием stdout вместо значения по умолчанию stderr) и добавления его в корневой логгер:

import logging
import sys

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

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)
528
ответ дан Martijn Pieters 18 October 2018 в 07:56
поделиться

Самый простой способ войти в стандартный вывод:

import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
418
ответ дан Stevoisiak 18 October 2018 в 07:56
поделиться

Вы можете создать два обработчика для файла и стандартного вывода, а затем создать один регистратор с аргументом handlers для basicConfig . Это может быть полезно, если у вас одинаковый log_level и формат вывода для обоих обработчиков:

import logging
import sys

file_handler = logging.FileHandler(filename='tmp.log')
stdout_handler = logging.StreamHandler(sys.stdout)
handlers = [file_handler, stdout_handler]

logging.basicConfig(
    level=logging.DEBUG, 
    format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
    handlers=handlers
)

logger = logging.getLogger('LOGGER_NAME')
47
ответ дан Anton Protopopov 18 October 2018 в 07:56
поделиться

Простейший способ войти в файл и в stderr:

import logging

logging.basicConfig(filename="logfile.txt")
stderrLogger=logging.StreamHandler()
stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logging.getLogger().addHandler(stderrLogger)
30
ответ дан Stevoisiak 18 October 2018 в 07:56
поделиться

Вот очень простой пример:

import logging
l = logging.getLogger("test")

# Add a file logger
f = logging.FileHandler("test.log")
l.addHandler(f)

# Add a stream logger
s = logging.StreamHandler()
l.addHandler(s)

# Send a test message to both -- critical will always log
l.critical("test msg")

Вывод будет отображать «test msg» на stdout, а также в файле.

1
ответ дан Kiki Jewell 18 October 2018 в 07:56
поделиться

Поскольку никто не поделился аккуратными двумя вкладышами, я поделюсь своим:

logging.basicConfig(filename='logs.log', level=logging.DEBUG, format="%(asctime)s:%(levelname)s: %(message)s")
logging.getLogger().addHandler(logging.StreamHandler())
4
ответ дан Lexander 18 October 2018 в 07:56
поделиться

Возможно использование нескольких обработчиков.

import logging
import auxiliary_module

# create logger with 'spam_application'
log = logging.getLogger('spam_application')
log.setLevel(logging.DEBUG)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
log.addHandler(fh)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.setFormatter(formatter)
log.addHandler(ch)

log.info('creating an instance of auxiliary_module.Auxiliary')
a = auxiliary_module.Auxiliary()
log.info('created an instance of auxiliary_module.Auxiliary')

log.info('calling auxiliary_module.Auxiliary.do_something')
a.do_something()
log.info('finished auxiliary_module.Auxiliary.do_something')

log.info('calling auxiliary_module.some_function()')
auxiliary_module.some_function()
log.info('done with auxiliary_module.some_function()')

# remember to close the handlers
for handler in log.handlers:
    handler.close()
    log.removeFilter(handler)

Пожалуйста, смотрите: https://docs.python.org/2/howto/logging-cookbook.html

53
ответ дан Vallentin 18 October 2018 в 07:56
поделиться

Вот решение, основанное на мощном, но плохо документированном методе logging.config.dictConfig . Вместо того, чтобы отправлять каждое сообщение журнала на стандартный вывод, он отправляет сообщения с уровнем журнала ERROR и выше на stderr, а все остальное на stdout. Это может быть полезно, если другие части системы прослушивают stderr или stdout.

import logging
import logging.config
import sys

class _ExcludeErrorsFilter(logging.Filter):
    def filter(self, record):
        """Filters out log messages with log level ERROR (numeric value: 40) or higher."""
        return record.levelno < 40


config = {
    'version': 1,
    'filters': {
        'exclude_errors': {
            '()': _ExcludeErrorsFilter
        }
    },
    'formatters': {
        # Modify log message format here or replace with your custom formatter class
        'my_formatter': {
            'format': '(%(process)d) %(asctime)s %(name)s (line %(lineno)s) | %(levelname)s %(message)s'
        }
    },
    'handlers': {
        'console_stderr': {
            # Directs log messages with log level ERROR or higher to stderr
            'class': 'logging.StreamHandler',
            'level': 'ERROR',
            'formatter': 'my_formatter',
            'stream': sys.stderr
        },
        'console_stdout': {
            # Directs log messages with log level lower than ERROR to stdout
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filters': ['exclude_errors'],
            'stream': sys.stdout
        },
        'file': {
            # Directs all log messages to a file
            'class': 'logging.FileHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filename': 'my.log',
            'encoding': 'utf8'
        }
    },
    'root': {
        # In general, this should be kept at 'NOTSET' to ensure it does
        # not interfere with the log levels set for each handler
        'level': 'NOTSET',
        'handlers': ['console_stderr', 'console_stdout', 'file']
    },
}

logging.config.dictConfig(config)
4
ответ дан Elias Strehle 18 October 2018 в 07:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: