Конфигурация регистратора для входа в файл и печати на стандартный вывод

353

Я использую модуль журналирования Python для записи некоторых строк отладки в файл, который работает довольно хорошо. Кроме того, я бы хотел использовать этот модуль для вывода строк в стандартный вывод. Как мне это сделать? Для записи моих строк в файл я использую следующий код:

import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

а затем вызвать функцию регистратора, как

logger.debug("I am written to the file")

Спасибо за помощь здесь!

stdcerr
источник

Ответы:

451

Просто получите ручку к корневому логгеру и добавьте StreamHandler. StreamHandlerПишет в стандартный поток ошибок. Не уверен, что вам действительно нужен стандартный вывод поверх stderr, но это то, что я использую, когда настраиваю регистратор Python и добавляюFileHandler . Затем все мои логи идут в оба места (что звучит так, как вы хотите).

import logging
logging.getLogger().addHandler(logging.StreamHandler())

Если вы хотите выводить stdoutвместо stderr, вам просто нужно указать это StreamHandlerконструктору.

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

Вы также можете добавить Formatter к нему, чтобы все ваши строки журнала имели общий заголовок.

то есть:

import logging
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
rootLogger = logging.getLogger()

fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)

Печать в формате:

2012-12-05 16:58:26,618 [MainThread  ] [INFO ]  my message
Водонос
источник
19
Вы также можете просто инициализировать StreamHandlerс sys.stdout, и тогда он будет регистрировать это вместо stderr.
Сайлас Рэй
1
@ sr2222 logger.addHandler (sys.stdout) выдает мне NameError: имя 'sys' не определено
stdcerr
21
Ну да ... ты должен import sysсначала. И фактически инициализируем обработчик, т.е.consoleHandler = logging.StreamHandler(sys.stdout)
Сайлас Рэй
15
Потому что, как я уже сказал, это не так, как вы это делаете. Создайте HANDLER с помощью sys.stdout, затем присоедините обработчик к логгеру.
Сайлас Рэй
6
Не забывайте, rootLogger.setLevel(logging.DEBUG)если вы пытаетесь увидеть информацию или отладочные сообщения
storm_m2138
247

logging.basicConfig() может принять аргумент ключевого слова handlers начиная с Python 3.3, что значительно упрощает настройку ведения логов, особенно при настройке нескольких обработчиков с одним и тем же форматером:

handlers- Если указано, это должен быть итерируемый из уже созданных обработчиков для добавления в корневой логгер. Любым обработчикам, у которых еще нет установленного форматера, будет назначен форматер по умолчанию, созданный в этой функции.

Таким образом, вся настройка может быть выполнена с помощью одного вызова:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("debug.log"),
        logging.StreamHandler()
    ]
)

(Или с import sys+StreamHandler(sys.stdout) для требований исходного вопроса - по умолчанию для StreamHandler является запись в stderr. Посмотрите на атрибуты LogRecord если вы хотите настроить формат журнала и добавить такие вещи, как имя файла / строка, информация о потоке и т. Д.)

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

logging.info('Useful message')
logging.error('Something bad happened')
...

Примечание. Если это не работает, возможно, кто-то другой уже инициализировал систему ведения журнала по-другому. Комментарии предлагают сделать logging.root.handlers = []перед звонком basicConfig().

Yirkha
источник
5
не забудьте установить level = logging.INFO или желаемый уровень
Andy Matteson
5
Определение для FileHandler: logging.FileHandler(filename, mode='a', encoding=None, delay=False). Это означает, что когда вы просто хотите войти в ту же папку, вы можете просто использовать FileHandler("mylog.log"). Если вы хотите перезаписывать журнал каждый раз, установите «w» в качестве второго аргумента.
user136036
7
Я пробовал это, но выходной файл пуст, хотя консоль дает вывод .. Любые предложения ..?
Рамеш-Х
4
@ Ramesh-X, это тоже сводило меня с ума. просто сделайте logging.root.handlers = []перед вызовом basicConfig, посмотрите на функцию - это раздражает.
Ихаданни
70

Добавление StreamHandler без аргументов идет в stderr вместо stdout. Если какой-то другой процесс зависит от дампа stdout (т.е. при написании плагина NRPE), убедитесь, что вы явно указали stdout, иначе вы можете столкнуться с неожиданными проблемами.

Вот быстрый пример повторного использования предполагаемых значений и LOGFILE из вопроса:

import logging
from logging.handlers import RotatingFileHandler
from logging import handlers
import sys

log = logging.getLogger('')
log.setLevel(logging.DEBUG)
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(format)
log.addHandler(ch)

fh = handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
fh.setFormatter(format)
log.addHandler(fh)
Hazok
источник
Я пытаюсь это.
Аджай Кумар
19

Либо беги basicConfig с в stream=sys.stdoutкачестве аргумента до создания каких - либо других обработчиков или регистрации сообщений, или вручную добавить , StreamHandlerчто толкает сообщения на стандартный вывод в корневой регистратор (или любой другой регистратор вы хотите, по этому вопросу).

Сайлас Рэй
источник
5

После многократного использования кода Waterboy в нескольких пакетах Python я, наконец, превратил его в крошечный автономный пакет Python, который вы можете найти здесь:

https://github.com/acschaefer/duallog

Код хорошо документирован и прост в использовании. Просто скачайте .pyфайл и включите его в свой проект или установите весь пакет через pip install duallog.

Lexxer
источник
Почему-то не попадает в консоль ни один файл (пусто)
JackTheKnife
5

Вход на stdoutи rotating fileс различными уровнями и форматами:

import logging
import logging.handlers
import sys

if __name__ == "__main__":

    # Change root logger level from WARNING (default) to NOTSET in order for all messages to be delegated.
    logging.getLogger().setLevel(logging.NOTSET)

    # Add stdout handler, with level INFO
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.INFO)
    formater = logging.Formatter('%(name)-13s: %(levelname)-8s %(message)s')
    console.setFormatter(formater)
    logging.getLogger().addHandler(console)

    # Add file rotating handler, with level DEBUG
    rotatingHandler = logging.handlers.RotatingFileHandler(filename='rotating.log', maxBytes=1000, backupCount=5)
    rotatingHandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    rotatingHandler.setFormatter(formatter)
    logging.getLogger().addHandler(rotatingHandler)

    log = logging.getLogger("app." + __name__)

    log.debug('Debug message, should only appear in the file.')
    log.info('Info message, should appear in file and stdout.')
    log.warning('Warning message, should appear in file and stdout.')
    log.error('Error message, should appear in file and stdout.')
Андрей Дебеняк
источник
2

Вот полное, хорошо обернутое решение, основанное на ответе Уотербоя и других источниках. Он поддерживает ведение журнала как в консоли, так и в файле журнала, допускает различные настройки уровня журнала, обеспечивает цветной вывод и легко настраивается (также доступен как Gist ):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# -------------------------------------------------------------------------------
#                                                                               -
#  Python dual-logging setup (console and log file),                            -
#  supporting different log levels and colorized output                         -
#                                                                               -
#  Created by Fonic <https://github.com/fonic>                                  -
#  Date: 04/05/20                                                               -
#                                                                               -
#  Based on:                                                                    -
#  https://stackoverflow.com/a/13733863/1976617                                 -
#  https://uran198.github.io/en/python/2016/07/12/colorful-python-logging.html  -
#  https://en.wikipedia.org/wiki/ANSI_escape_code#Colors                        -
#                                                                               -
# -------------------------------------------------------------------------------

# Imports
import os
import sys
import logging

# Logging formatter supporting colored output
class LogFormatter(logging.Formatter):

    COLOR_CODES = {
        logging.CRITICAL: "\033[1;35m", # bright/bold magenta
        logging.ERROR:    "\033[1;31m", # bright/bold red
        logging.WARNING:  "\033[1;33m", # bright/bold yellow
        logging.INFO:     "\033[0;37m", # white / light gray
        logging.DEBUG:    "\033[1;30m"  # bright/bold black / dark gray
    }

    RESET_CODE = "\033[0m"

    def __init__(self, color, *args, **kwargs):
        super(LogFormatter, self).__init__(*args, **kwargs)
        self.color = color

    def format(self, record, *args, **kwargs):
        if (self.color == True and record.levelno in self.COLOR_CODES):
            record.color_on  = self.COLOR_CODES[record.levelno]
            record.color_off = self.RESET_CODE
        else:
            record.color_on  = ""
            record.color_off = ""
        return super(LogFormatter, self).format(record, *args, **kwargs)

# Setup logging
def setup_logging(console_log_output, console_log_level, console_log_color, logfile_file, logfile_log_level, logfile_log_color, log_line_template):

    # Create logger
    # For simplicity, we use the root logger, i.e. call 'logging.getLogger()'
    # without name argument. This way we can simply use module methods for
    # for logging throughout the script. An alternative would be exporting
    # the logger, i.e. 'global logger; logger = logging.getLogger("<name>")'
    logger = logging.getLogger()

    # Set global log level to 'debug' (required for handler levels to work)
    logger.setLevel(logging.DEBUG)

    # Create console handler
    console_log_output = console_log_output.lower()
    if (console_log_output == "stdout"):
        console_log_output = sys.stdout
    elif (console_log_output == "stderr"):
        console_log_output = sys.stderr
    else:
        print("Failed to set console output: invalid output: '%s'" % console_log_output)
        return False
    console_handler = logging.StreamHandler(console_log_output)

    # Set console log level
    try:
        console_handler.setLevel(console_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set console log level: invalid level: '%s'" % console_log_level)
        return False

    # Create and set formatter, add console handler to logger
    console_formatter = LogFormatter(fmt=log_line_template, color=console_log_color)
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)

    # Create log file handler
    try:
        logfile_handler = logging.FileHandler(logfile_file)
    except Exception as exception:
        print("Failed to set up log file: %s" % str(exception))
        return False

    # Set log file log level
    try:
        logfile_handler.setLevel(logfile_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set log file log level: invalid level: '%s'" % logfile_log_level)
        return False

    # Create and set formatter, add log file handler to logger
    logfile_formatter = LogFormatter(fmt=log_line_template, color=logfile_log_color)
    logfile_handler.setFormatter(logfile_formatter)
    logger.addHandler(logfile_handler)

    # Success
    return True

# Main function
def main():

    # Setup logging
    script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    if (not setup_logging(console_log_output="stdout", console_log_level="warning", console_log_color=True,
                        logfile_file=script_name + ".log", logfile_log_level="debug", logfile_log_color=False,
                        log_line_template="%(color_on)s[%(created)d] [%(threadName)s] [%(levelname)-8s] %(message)s%(color_off)s")):
        print("Failed to setup logging, aborting.")
        return 1

    # Log some messages
    logging.debug("Debug message")
    logging.info("Info message")
    logging.warning("Warning message")
    logging.error("Error message")
    logging.critical("Critical message")

# Call main function
if (__name__ == "__main__"):
    sys.exit(main())
Maxxim
источник
-4

Для 2.7 попробуйте следующее:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
JonM
источник