Nice programing

Flask의 우수한 디버그 로그 메시지를 프로덕션 파일에 어떻게 작성합니까?

nicepro 2020. 12. 29. 08:28
반응형

Flask의 우수한 디버그 로그 메시지를 프로덕션 파일에 어떻게 작성합니까?


잘 작동하고 가끔 오류를 생성하는 Flask 응용 프로그램이 있습니다 debug=True.

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('test message'))를 사용하여 단일 오류 로그 메시지를 파일에 저장하는 것이라고 생각 하지만 해당 메시지 하나만 인쇄합니다. 오류가 발생한 직후에 다른 오류를 기록하려는 시도는 무시됩니다.


왜 작동하지 않는지 모르겠지만 어떻게하고 있는지 알 수 있습니다.

우선, 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 블록에 코드를 넣을 필요조차 없습니다. 하지만 에러 핸들러를 호출하기 전에 무언가를하고 싶다면 (예를 들어 롤백 세션 또는 트랜잭션) 다음을 수행하십시오.

    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, 사용자 에이전트 등. Flask app.debug==FalseFlask.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
        )

그런 다음, 구성시, 바인드 FileHandlerapp.logger와 이동합니다. 나는 StreamHandler많은 서버 (예 : uWSGI)가 자신의 독점적 인 단어 쓸모없는-돌릴 수없는 메시지로 오염시키고 싶어하는 원인을 사용하지 않습니다 .

Flask 확장을 두려워하지 마십시오. 조만간 그렇게해야합니다.)


저는 logging모듈 전문가는 아니지만 그것에 대한 경험 + Python + Flask에 대한 몇 년간의 경험과 관련하여 몇 가지 관찰을 고려하여 좋은 로깅 구성을 가질 수 있습니다.

  • 모든 함수 (경로)의 시작 부분 에서 요청이 이루어진 정확한 시간을 등록하기 위해 성공 여부에 관계없이 독립적 으로 타임 스탬프 개체를 만듭니다.

  • 모든 성공적인 요청을 등록 하려면 @ app.after_request 사용

  • 일반 오류 + 트레이스 백을 등록 하려면 @ app.errorhandler 사용

다음은이 아이디어를 보여주는 예입니다.

#/usr/bin/python3
""" Demonstration of logging feature for a Flask App. """

from logging.handlers import RotatingFileHandler
from flask import Flask, request, jsonify
from time import strftime

__author__ = "@ivanleoncz"

import logging
import traceback


app = Flask(__name__)

@app.route("/")
@app.route("/index")
def get_index():
    """ Function for / and /index routes. """
    return "Welcome to Flask! "


@app.route("/data")
def get_data():
    """ Function for /data route. """
    data = {
            "Name":"Ivan Leon",
            "Occupation":"Software Developer",
            "Technologies":"[Python, Flask, JavaScript, Java, SQL]"
    }
    return jsonify(data)


@app.route("/error")
def get_nothing():
    """ Route for intentional error. """
    return foobar # intentional non-existent variable


@app.after_request
def after_request(response):
    """ Logging after every request. """
    # This 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):
    """ Logging after every Exception. """
    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=10000, backupCount=3)        
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.ERROR)
    logger.addHandler(handler)
    app.run(host="127.0.0.1",port=8000)

동시에 표준 출력 및 파일에 logrotate에와 로그에 대한 자세한 정보 : 이 전시회 개요


If you are using gunicorn to run your Flask app, you can log all Flask exceptions to the gunicorn logs by adding the gunicorn error handlers to the Flask logger:

In module/__init__.py:

@app.before_first_request
def setup_logging():
    if not app.debug:
        import logging
        gunicorn_logger = logging.getLogger('gunicorn.error')
        for handler in gunicorn_logger.handlers:
            app.logger.addHandler(handler)

ReferenceURL : https://stackoverflow.com/questions/14037975/how-do-i-write-flasks-excellent-debug-log-message-to-a-file-in-production

반응형