sentry-python icon indicating copy to clipboard operation
sentry-python copied to clipboard

Performance Monitoring doesn't work with Werkzeug DebuggedApplication

Open BoyanYK opened this issue 2 years ago • 2 comments

How do you use Sentry?

Sentry Saas (sentry.io)

Version

1.5.10

Steps to Reproduce

  1. Use Flask Integration
  2. Enable Werkzeug's DebugApplication mode
  3. Set the SENTRY_DSN environment variable
  4. Run web server
  5. Execute a GET request to any endpoint

Minimal example code:

import os

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from werkzeug.debug import DebuggedApplication

import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration

SENTRY_ENVIRONMENT = os.getenv("SENTRY_ENVIRONMENT", "dev")
SENTRY_SAMPLE_RATE = float(os.getenv("SENTRY_SAMPLE_RATE", "0.8"))
SENTRY_TRACE_SAMPLE_RATE = float(os.getenv("SENTRY_TRACE_SAMPLE_RATE", "0.8"))

integrations = [FlaskIntegration(transaction_style="url"), SqlalchemyIntegration()]

sentry_sdk.init(
    integrations=integrations,
    sample_rate=SENTRY_SAMPLE_RATE,
    traces_sample_rate=SENTRY_TRACE_SAMPLE_RATE,
    environment=SENTRY_ENVIRONMENT,
    send_default_pii=True,
)

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///:memory:"
app.wsgi_app = DebuggedApplication(app.wsgi_app, True)
db = SQLAlchemy(app)


@app.route('/db')
def entry_point():
    db.engine.execute("SELECT name FROM sqlite_master")
    return 'Hello World!'

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

Expected Result

Sentry should correctly fetch the transaction name from the endpoint as well as capture spans that occurred during the request execution. We expect the following in the performance tab. These screenshots are from running the same code but with the DebuggedApplication line commented out: image Transaction View image Event Details image

Actual Result

Sentry has the name of generic WSGI request for all transactions in the performance tab of the dashboard. They're only differentiated by their HTTP Method. They also do not correctly include the Spans that make up a transaction. Through debugging, I figured out that somehow the order of the steps is different. Having DebuggedApplication active results in a Transaction.finish() method being called before the transaction has had its name set here

Screenshots of actual result: Performance: image Transaction image Event Details: image

BoyanYK avatar May 02 '22 11:05 BoyanYK

This is obviously not a big issue, however, as I did spent 3 days debugging it I wanted to document this behaviour in case someone else is also struggling with it.

BoyanYK avatar May 02 '22 11:05 BoyanYK

Hey @BoyanYK

Thanks for reporting this. This can definitely be improved! I also think it (hopefully) is not a big change to have proper data when DebuggedApplication is enabled.

Could you submit a PR that fixes this? We are always happy to help merge the PR to improve integrations!

antonpirker avatar May 23 '22 08:05 antonpirker

@BoyanYK After investigating this issue, we would recommend that you fix the issue by simply removing the app.wsgi_app = DebuggedApplication(app.wsgi_app, True) line from your code.

During our investigation, we discovered that the DebuggedApplication line is redundant because when passing debug=True to Flask's app.run function, Flask already automatically wraps the application in Werkzeug's DebuggedApplication internally. Using both the DebuggedApplication and the debug=True option causes the app to be double-wrapped in the debugger (you can observe this when starting the Flask app, since two different debugger PINs are outputted). As you already noted under the "Expected Results" of the issue, removing the DebuggedApplication line fixes the issue with the transaction names that you observed, so we would recommend removing the line to fix the problem.

We are closing this issue for now because this double-wrapping of the Flask application in the DebuggedApplication is not a supported use case, and in this case, the problem can be easily fixed by removing the DebuggedApplication line and simply using Flask's debug=True option.

However, if anyone observes a similar issue with some other Flask middleware, please feel free to submit a new issue which references this one.

szokeasaurusrex avatar Nov 14 '23 11:11 szokeasaurusrex

Thanks for taking the time to follow up on this! This is an interesting finding I definitely wasn't aware of - that the application would be wrapped in a debugger twice - it does confirm that it isn't a bug.

BoyanYK avatar Nov 14 '23 11:11 BoyanYK