конфігурація реєстратора для входу у файл та друку до stdout


353

Я використовую модуль реєстрації Python для того, щоб записати рядки налагодження у файл, який працює досить добре. Тепер крім цього, я хотів би використовувати цей модуль для друку рядків для stdout. Як це зробити? Для того щоб записати мої рядки у файл, я використовую наступний код:

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")

Дякую за допомогу тут!

Відповіді:


451

Просто дістаньте ручку до кореневого реєстратора та додайте StreamHandler. StreamHandlerПише в стандартний потік помилок. Не впевнений, чи дійсно вам потрібен stdout над 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.
Silas Ray

1
@ sr2222 logger.addHandler (sys.stdout) дає мені NameError: ім'я 'sys' не визначено
stdcerr

21
Ну так ... ви повинні import sysспочатку. І фактично ініціалізувати обробник, тобтоconsoleHandler = logging.StreamHandler(sys.stdout)
Silas Ray

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().


5
не забудьте також встановити рівень = logging.INFO або бажаний рівень
Енді Маттесон

5
Визначення для FileHandler: logging.FileHandler(filename, mode='a', encoding=None, delay=False). Це означає, що коли ви просто хочете увійти в одну і ту ж папку, ви можете просто використовувати FileHandler("mylog.log"). Якщо ви хочете кожен раз перезаписувати журнал, встановіть "w" як другий аргумент.
користувач136036

7
Я спробував це, але вихідний файл порожній, хоча консоль дає вихід. Будь-які пропозиції ..?
Рамеш-X

4
@ Рамеш-X, це також зводило мене з розуму. безпосередньо logging.root.handlers = []перед викликом basicConfig, погляньте на функцію - це дратує.
ihadanny

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)

Я намагаюся це.
Аджай Кумар

19

Або запускайте basicConfigз stream=sys.stdoutаргументом перед налаштуванням будь-яких інших оброблювачів або ведення журналів будь-яких повідомлень, або вручну додайте файл, StreamHandlerякий підштовхує повідомлення до stdout до кореневого журналу (або будь-якого іншого реєстратора, який ви хочете, з цього питання).


5

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

https://github.com/acschaefer/duallog

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


Чомусь не потрапляє на консоль, ані файл (порожній)
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

Ось повне, красиво обгорнуте рішення, засноване на відповіді Waterboy та різних інших джерелах. Він підтримує ведення журналу як до консольного, так і до файлу журналу, дозволяє здійснювати різні налаштування рівня журналу, забезпечує кольоровий вихід та легко налаштовується (також доступний як 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())

-4

Для 2.7 спробуйте наступне:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.