Ведение журнала флагов - не удается заставить его записать файл

Хорошо, вот код, где я настраиваю все:

if __name__ == '__main__':
    app.debug = False

    applogger = app.logger

    file_handler = FileHandler("error.log")
    file_handler.setLevel(logging.DEBUG)

    applogger.setLevel(logging.DEBUG)
    applogger.addHandler(file_handler)

    app.run(host='0.0.0.0')

Что происходит, это

  • создается ошибка .log.
  • Ничего не написано на нем
  • Несмотря на то, что он не добавляет StreamHandler и устанавливает отладку на false, я все равно получаю все до STDOUT (это может быть правильно, но все же кажется странным).

Неужели я полностью здесь где-то или что происходит?

Ответы

Ответ 1

Почему бы не сделать это так:

if __name__ == '__main__':
    init_db()  # or whatever you need to do

    import logging
    logging.basicConfig(filename='error.log',level=logging.DEBUG)

    app.run(host="0.0.0.0")

Если вы сейчас запустите приложение, вы увидите, что error.log содержит:

INFO:werkzeug: * Running on http://0.0.0.0:5000/

Для получения дополнительной информации посетите http://docs.python.org/2/howto/logging.html

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

import logging, logging.config, yaml
logging.config.dictConfig(yaml.load(open('logging.conf')))

Теперь добавьте код отладки, чтобы мы увидели, что наша настройка работает:

logfile    = logging.getLogger('file')
logconsole = logging.getLogger('console')
logfile.debug("Debug FILE")
logconsole.debug("Debug CONSOLE")

Все, что осталось, это программа "logging.conf". Позвольте использовать это:

version: 1
formatters:
  hiformat:
    format: 'HI %(asctime)s - %(name)s - %(levelname)s - %(message)s'
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: hiformat
    stream: ext://sys.stdout
  file:
    class: logging.FileHandler
    level: DEBUG
    formatter: simple
    filename: errors.log
loggers:
  console:
    level: DEBUG
    handlers: [console]
    propagate: no
  file:
    level: DEBUG
    handlers: [file]
    propagate: no
root:
  level: DEBUG
  handlers: [console,file]

Эта конфигурация сложнее, чем необходимо, но также показывает некоторые функции модуля протоколирования.

Теперь, когда мы запускаем наше приложение, мы видим этот вывод (werkzeug- и console-logger):

HI 2013-07-22 16:36:13,475 - console - DEBUG - Debug CONSOLE
HI 2013-07-22 16:36:13,477 - werkzeug - INFO -  * Running on http://0.0.0.0:5000/

Также обратите внимание, что использовался пользовательский форматтер с "HI".

Теперь просмотрите файл "errors.log". Он содержит:

2013-07-22 16:36:13,475 - file - DEBUG - Debug FILE
2013-07-22 16:36:13,477 - werkzeug - INFO -  * Running on http://0.0.0.0:5000/

Ответ 2

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

1) Флажок, по-видимому, будет игнорировать все ваши пользовательские протоколирования, если он не работает в рабочем режиме

2) debug = False недостаточно, чтобы запустить его в режиме производства. Для этого вам нужно обернуть приложение на любом сервере WSGI.

После того, как я запустил приложение с сервера gevent WSGI (и перенесение инициализации журнала в более подходящее место), все работает нормально

Ответ 3

Результат, который вы видите в консоли вашего приложения, находится из основного регистратора Werkzeug, к которому можно получить доступ через logging.getLogger('werkzeug').

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

Дополнительная информация и пример кода: Запросить флажок в журнале доступа.

Ответ 4

Мне не нравились другие ответы, поэтому я продолжал это делать, и мне показалось, что я должен был настроить мою конфигурацию регистрации AFTER Flask для ее настройки.

@app.before_first_request
def initialize():

    logger = logging.getLogger("your_package_name")
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter(
    """%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s"""
    )
    ch.setFormatter(formatter)
    logger.addHandler(ch)

Мое приложение структурировано как

/package_name
    __main__.py <- where I put my logging configuration
    __init__.py <- conveniance for myself, not necessary
    /tests
    /package_name <- Actual flask app
    __init__.py
    /views
    /static
    /templates
    /lib

Следуя этим указаниям http://flask.pocoo.org/docs/0.10/patterns/packages/

Ответ 5

Это работает:

if __name__ == '__main__':
    import logging
    logFormatStr = '[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s'
    logging.basicConfig(format = logFormatStr, filename = "global.log", level=logging.DEBUG)
    formatter = logging.Formatter(logFormatStr,'%m-%d %H:%M:%S')
    fileHandler = logging.FileHandler("summary.log")
    fileHandler.setLevel(logging.DEBUG)
    fileHandler.setFormatter(formatter)
    streamHandler = logging.StreamHandler()
    streamHandler.setLevel(logging.DEBUG)
    streamHandler.setFormatter(formatter)
    app.logger.addHandler(fileHandler)
    app.logger.addHandler(streamHandler)
    app.logger.info("Logging is set up.")
    app.run(host='0.0.0.0', port=8000, threaded=True)

Ответ 6

Почему бы не взять погружение в код и посмотреть...

Модуль, на который мы приземляемся, flask.logging.py, который определяет функцию с именем create_logger(app). Проверка этой функции даст несколько подсказок о потенциальных виновниках при устранении проблем с журналом с помощью Flask.

Первой возможной причиной конфликтов в этой функции является следующая строка:

logger = getLogger(app.logger_name)

Посмотрим, почему:

Переменная app.logger_name устанавливается в методе Flask.__init__() значением import_name, которое является самим параметром приема Flask(__name__). То есть app.logger_name присваивается значение __name__, которое, скорее всего, будет именем вашего основного пакета, пусть в этом примере назовите его "awesomeapp".

Теперь представьте, что вы решили настроить и создать свой собственный журнал вручную. Как вы думаете, есть ли шансы, что если ваш проект будет назван "awesomeapp", вы также будете использовать это имя для настройки своего регистратора, я думаю, что это довольно вероятно.

my_logger = logging.getLogger('awesomeapp') # doesn't seem like a bad idea
fh = logging.FileHandler('/tmp/my_own_log.log')
my_logger.setLevel(logging.DEBUG)
my_logger.addHandler(fh)

Это имеет смысл сделать это... за исключением нескольких проблем.

Когда свойство Flask.logger вызывается в первый раз, оно, в свою очередь, вызывает функцию flask.logging.create_logger(), и будут выполняться следующие действия:

logger = getLogger(app.logger_name)

Помните, как вы назвали ваш логгер после проекта и как app.logger_name делится этим именем? Что происходит в строке вышеприведенного кода, так это то, что функция logging.getLogger() теперь загрузила ваш ранее созданный регистратор, и следующие инструкции вот-вот сойдутся с ним так, что вы поцарапаете себе голову позже. Например,

del logger.handlers[:]

Poof, вы просто потеряли все обработчики, которые вы ранее зарегистрировали в своем журнале.

Другие вещи, которые происходят внутри функции, не вникая в детали. Он создает и регистрирует два объекта logging.StreamHandler, которые могут выплевывать объекты sys.stderr и/или Response. Один для "debug" уровня журнала, а другой для "production".

class DebugLogger(Logger):
    def getEffectiveLevel(self):
        if self.level == 0 and app.debug:
            return DEBUG
        return Logger.getEffectiveLevel(self)

class DebugHandler(StreamHandler):
    def emit(self, record):
        if app.debug and _should_log_for(app, 'debug'):
            StreamHandler.emit(self, record)

class ProductionHandler(StreamHandler):
    def emit(self, record):
        if not app.debug and _should_log_for(app, 'production'):
            StreamHandler.emit(self, record)

debug_handler = DebugHandler()
debug_handler.setLevel(DEBUG)
debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT))

prod_handler = ProductionHandler(_proxy_stream)
prod_handler.setLevel(ERROR)
prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT))

logger.__class__ = DebugLogger
logger.addHandler(debug_handler)
logger.addHandler(prod_handler)

С приведенными выше деталями для освещения должно стать яснее, почему наш ручной журнал регистрации, похоже, ведет себя беспорядочно, когда Flask принимает участие. Однако новая информация дает нам новые возможности. Если вы все же хотите сохранить отдельный регистратор, самый простой подход - назвать его чем-то другим, чем проект (например, my_logger = getLogger('awesomeapp_logger')). Другой вариант, если вы хотите быть совместимым с протоколами протоколирования в Flask, заключается в регистрации объекта logging.FileHandler на Flask.logger с использованием аналогичного подхода к Flask.

def enable_file_logging(app):
    import logging
    from flask.logging import _should_log_for

    logging_path = app.config['LOGGING_PATH']
    class DebugFileHandler(logging.FileHandler):
        def emit(self, record):
            if app.debug and _should_log_for(app, 'debug'):
                logging.FileHandler.emit(self, record)

    debug_file_handler = DebugFileHandler('/tmp/my_own_log.log')
    app.logger.addHandler(debug_file_handler)

app = Flask(__name__)
enable_file_logging(app)