Ведение журнала флагов - не удается заставить его записать файл
Хорошо, вот код, где я настраиваю все:
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)