Как написать отличное сообщение журнала отладки Flask к файлу в процессе производства?

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

if __name__ == '__main__': app.run(debug=True) 

Я получаю полезные сообщения об ошибках, такие как:

 Traceback (most recent call last): File "./main.py", line 871, in index_route KeyError: 'stateIIIII' 

Я хотел бы получить сообщения об ошибках, подобные сохраненным в файл, когда я запускаю приложение в процессе производства (используя Lighttpd + fastcgi).

Изучив различные вопросы StackOverflow, http://flask.pocoo.org/docs/errorhandling/ , http://docs.python.org/2/library/logging.html , список рассылки Flask и несколько блогов, кажется, что нет «простого» способа просто отправить все большие сообщения об ошибках в файл – мне нужно использовать модуль протоколирования Python для настройки вещей. Поэтому я придумал следующий код.

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

 app = Flask(__name__) if app.debug is not True: import logging from logging.handlers import RotatingFileHandler file_handler = RotatingFileHandler('python.log', maxBytes=1024 * 1024 * 100, backupCount=20) file_handler.setLevel(logging.ERROR) app.logger.setLevel(logging.ERROR) app.logger.addHandler(file_handler) 

Затем я поместил код для каждого маршрута в оператор try / except и использовал traceback, чтобы выяснить, из какой строки возникла ошибка, и напечатать приятное сообщение об ошибке:

 def some_route(): try: # code for route in here (including a return statement) except: exc_type, exc_value, exc_traceback = sys.exc_info() app.logger.error(traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2)) return render_template('error.html') 

И затем прямо в конце файла я удаляю debug = True. Хотя я не думаю, что мне нужно это сделать, поскольку приложение запускается сервером fastcgi (?), Когда он запускается на производстве. Последние две строки моего кода приложения выглядят следующим образом:

 if __name__ == '__main__': app.run() 

Я изо всех сил пытаюсь заставить это работать. Я думаю, что лучше всего мне удалось получить одно сообщение журнала ошибок, которое будет сохранено в файле, используя (app.logger.error («тестовое сообщение»)), но оно печатает только одно сообщение. Попытка зарегистрировать другую ошибку сразу после этого просто игнорируется.

3 Solutions collect form web for “Как написать отличное сообщение журнала отладки Flask к файлу в процессе производства?”

Я не знаю, почему он не работает, но я могу сказать, как это делается.

Прежде всего, вам не нужно устанавливать уровень app.logger. Поэтому удалите эту строку app.logger.setLevel() .

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

  @app.errorhandler(500) def internal_error(exception): app.logger.error(exception) return render_template('500.html'), 500 

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

Поскольку это обрабатывает все исключения, вам даже не нужно класть код в try / except block. Хотя, если вы хотите что-то сделать до вызова обработчика ошибок (например, для сеанса отката или транзакции), сделайте следующее:

  try: #code except: #code raise 

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

 if app.debug is not True: import logging from logging.handlers import RotatingFileHandler file_handler = RotatingFileHandler('python.log', maxBytes=1024 * 1024 * 100, backupCount=20) file_handler.setLevel(logging.ERROR) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") file_handler.setFormatter(formatter) app.logger.addHandler(file_handler) 

Для тех, кто читает это позже.

Я думаю, что лучше подумать о том, чтобы вводить более полезную информацию в сообщения об ошибках. URL, клиентский IP-адрес, пользовательский агент и т. Д. Флажок регистрирует исключения внутри (в app.debug==False mode) с функцией Flask.log_exception . Итак, вместо записи вручную в @app.errorhandler я делаю что-то вроде этого:

 class MoarFlask(Flask): def log_exception(self, exc_info): """...description omitted...""" self.logger.error( """ Request: {method} {path} IP: {ip} User: {user} Agent: {agent_platform} | {agent_browser} {agent_browser_version} Raw Agent: {agent} """.format( method = request.method, path = request.path, ip = request.remote_addr, agent_platform = request.user_agent.platform, agent_browser = request.user_agent.browser, agent_browser_version = request.user_agent.version, agent = request.user_agent.string, user=user ), exc_info=exc_info ) 

Затем во время настройки app.logger FileHandler с app.logger и продолжайте. Я не использую StreamHandler потому что многие серверы (например, uWSGI) любят загрязнять его своими собственными сообщениями, StreamHandler -бесполезного, а не разворачиваемого.

Не бойтесь расширять колбу. Вы будете вынуждены сделать это рано или поздно;)

Что касается моего опыта, я хотел бы сделать следующие замечания:

  • use @ app.after_request , для регистрации каждого успешного запроса

  • use @ app.errorhandler , для регистрации общих ошибок (Tracebacks)

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


Вот пример, который демонстрирует эту идею:

 #/usr/bin/env python3 import logging from logging.handlers import RotatingFileHandler from flask import Flask, request, jsonify from time import strftime import traceback app = Flask(__name__) @app.route("/") def get_index(): return "Welcome to Flask! " @app.route("/data") def get_hello(): data = { "Name":"Ivan Leon", "Occupation":"Software Developer", "Technologies":"[Python, Flask, MySQL, Android]" } return jsonify(data) @app.route("/error") def get_json(): return non_existent_variable # ---> intentional <--- @app.after_request def after_request(response): # this if avoids the duplication of registry in the log, # since that 500 is already logged via @app.errorhandler if response.status_code != 500: ts = strftime('[%Y-%b-%d %H:%M]') logger.error('%s %s %s %s %s %s', ts, request.remote_addr, request.method, request.scheme, request.full_path, response.status) return response @app.errorhandler(Exception) def exceptions(e): ts = strftime('[%Y-%b-%d %H:%M]') tb = traceback.format_exc() logger.error('%s %s %s %s %s 5xx INTERNAL SERVER ERROR\n%s', ts, request.remote_addr, request.method, request.scheme, request.full_path, tb) return "Internal Server Error", 500 if __name__ == '__main__': handler = RotatingFileHandler('app.log', maxBytes=100000, backupCount=3) logger = logging.getLogger('__name__') logger.setLevel(logging.INFO) logger.addHandler(handler) app.run() 

И ваш файл журнала будет таким, и в то же время, сохраняя реестр каждого HTTP-запроса на вашем экране:

 [2017-Aug-09 01:51] 127.0.0.1 GET http /? 200 OK [2017-Aug-09 01:51] 127.0.0.1 GET http /data? 200 OK [2017-Aug-09 01:51] 127.0.0.1 GET http /error? 5xx INTERNAL SERVER ERROR Traceback (most recent call last): File "/home/ivanlmj/git/env_flask_templates/lib/python3.4/site-packages/flask/app.py", line 1612, in full_dispatch_request rv = self.dispatch_request() File "/home/ivanlmj/git/env_flask_templates/lib/python3.4/site-packages/flask/app.py", line 1598, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "test.py", line 26, in get_json return non_existent_variable # ---> intentional <--- NameError: name 'non_existent_variable' is not defined 
Python - лучший язык программирования в мире.