def integration_enabled_params(request): if request.param == "auto": return {"_experiments": {"auto_enabling_integrations": True}} elif request.param == "manual": return {"integrations": [flask_sentry.FlaskIntegration()]} else: raise ValueError(request.param)
def test_errors(sentry_init, capture_exceptions, capture_events, app, debug, testing): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) app.debug = debug app.testing = testing @app.route("/") def index(): 1 / 0 exceptions = capture_exceptions() events = capture_events() client = app.test_client() try: client.get("/") except ZeroDivisionError: pass exc, = exceptions assert isinstance(exc, ZeroDivisionError) event, = events assert event["exception"]["values"][0]["mechanism"]["type"] == "flask"
def test_flask_files_and_form(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()], request_bodies="always") data = {"foo": "a" * 2000, "file": (BytesIO(b"hello"), "hello.txt")} @app.route("/", methods=["POST"]) def index(): assert list(request.form) == ["foo"] assert list(request.files) == ["file"] assert not request.get_json() capture_message("hi") return "ok" events = capture_events() client = app.test_client() response = client.post("/", data=data) assert response.status_code == 200 (event,) = events assert event["_meta"]["request"]["data"]["foo"] == { "": {"len": 2000, "rem": [["!limit", "x", 509, 512]]} } assert len(event["request"]["data"]["foo"]) == 512 assert event["_meta"]["request"]["data"]["file"] == { "": {"len": 0, "rem": [["!raw", "x", 0, 0]]} } assert not event["request"]["data"]["file"]
def test_flask_formdata_request_appear_transaction_body( sentry_init, capture_events, app): """ Test that ensures that transaction request data contains body, even if no exception was raised """ sentry_init(integrations=[flask_sentry.FlaskIntegration()], traces_sample_rate=1.0) data = {"username": "******", "age": "26"} @app.route("/", methods=["POST"]) def index(): assert request.form["username"] == data["username"] assert request.form["age"] == data["age"] assert not request.get_data() assert not request.get_json() set_tag("view", "yes") capture_message("hi") return "ok" events = capture_events() client = app.test_client() response = client.post("/", data=data) assert response.status_code == 200 event, transaction_event = events assert "request" in transaction_event assert "data" in transaction_event["request"] assert transaction_event["request"]["data"] == data
def test_does_not_leak_scope(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() with configure_scope() as scope: scope.set_tag("request_data", False) @app.route("/") def index(): with configure_scope() as scope: scope.set_tag("request_data", True) def generate(): for row in range(1000): with configure_scope() as scope: assert scope._tags["request_data"] yield str(row) + "\n" return Response(stream_with_context(generate()), mimetype="text/csv") client = app.test_client() response = client.get("/") assert response.data.decode() == "".join( str(row) + "\n" for row in range(1000)) assert not events with configure_scope() as scope: assert not scope._tags["request_data"]
def test_flask_large_json_request(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) data = {"foo": {"bar": "a" * 2000}} @app.route("/", methods=["POST"]) def index(): assert request.json == data assert request.data == json.dumps(data).encode("ascii") assert not request.form capture_message("hi") return "ok" events = capture_events() client = app.test_client() response = client.post("/", content_type="application/json", data=json.dumps(data)) assert response.status_code == 200 event, = events assert event[""]["request"]["data"]["foo"]["bar"] == { "": { "len": 2000, "rem": [["!limit", "x", 509, 512]] } } assert len(event["request"]["data"]["foo"]["bar"]) == 512
def test_flask_medium_formdata_request(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) data = {"foo": "a" * 2000} @app.route("/", methods=["POST"]) def index(): assert request.form["foo"] == data["foo"] assert not request.data assert not request.json capture_message("hi") return "ok" events = capture_events() client = app.test_client() response = client.post("/", data=data) assert response.status_code == 200 event, = events assert event[""]["request"]["data"]["foo"] == { "": { "len": 2000, "rem": [["!limit", "x", 509, 512]] } } assert len(event["request"]["data"]["foo"]) == 512
def test_flask_too_large_raw_request(sentry_init, input_char, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()], request_bodies="small") data = input_char * 2000 @app.route("/", methods=["POST"]) def index(): assert not request.form if isinstance(data, bytes): assert request.data == data else: assert request.data == data.encode("ascii") assert not request.json capture_message("hi") return "ok" events = capture_events() client = app.test_client() response = client.post("/", data=data) assert response.status_code == 200 event, = events assert event[""]["request"]["data"] == { "": { "len": 2000, "rem": [["!config", "x", 0, 2000]] } } assert not event["request"]["data"]
def test_tracing_success(sentry_init, capture_events, app): sentry_init(traces_sample_rate=1.0, integrations=[flask_sentry.FlaskIntegration()]) @app.before_request def _(): set_tag("before_request", "yes") @app.route("/message_tx") def hi_tx(): set_tag("view", "yes") capture_message("hi") return "ok" events = capture_events() with app.test_client() as client: response = client.get("/message_tx") assert response.status_code == 200 message_event, transaction_event = events assert transaction_event["type"] == "transaction" assert transaction_event["transaction"] == "hi_tx" assert transaction_event["contexts"]["trace"]["status"] == "ok" assert transaction_event["tags"]["view"] == "yes" assert transaction_event["tags"]["before_request"] == "yes" assert message_event["message"] == "hi" assert message_event["transaction"] == "hi_tx" assert message_event["tags"]["view"] == "yes" assert message_event["tags"]["before_request"] == "yes"
def test_error_in_errorhandler(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) app.debug = False app.testing = False @app.route("/") def index(): raise ValueError() @app.errorhandler(500) def error_handler(err): 1 / 0 events = capture_events() client = app.test_client() with pytest.raises(ZeroDivisionError): client.get("/") event1, event2 = events (exception, ) = event1["exception"]["values"] assert exception["type"] == "ValueError" exception = event2["exception"]["values"][-1] assert exception["type"] == "ZeroDivisionError"
def test_scoped_test_client(sentry_init, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) @app.route("/") def index(): return "ok" with app.test_client() as client: response = client.get("/") assert response.status_code == 200
def test_flask_login_not_configured(sentry_init, app, capture_events, monkeypatch): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) assert flask_sentry.flask_login events = capture_events() client = app.test_client() client.get("/message") event, = events assert event.get("user", {}).get("id") is None
def test_dont_override_sentry_trace_context(sentry_init, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) @app.route("/") def index(): return render_template_string("{{ sentry_trace }}", sentry_trace="hi") with app.test_client() as client: response = client.get("/") assert response.status_code == 200 assert response.data == b"hi"
def register_error_handlers(app): @app.register_error_handler(OleaException) def handle_olea_exceptions(e): return jsonify({'code': e.code, 'parms': e.parms}), 409 if not app.config.get('IGNORE_ERRORS', False): sentry_sdk_init(dsn=app.config['SENTRY_DSN'], integrations=[ flask.FlaskIntegration(), sqlalchemy.SqlalchemyIntegration(), redis.RedisIntegration() ])
def test_has_context(sentry_init, app, capture_events): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() client = app.test_client() response = client.get("/message") assert response.status_code == 200 event, = events assert event["transaction"] == "hi" assert "data" not in event["request"] assert event["request"]["url"] == "http://localhost/message"
def test_flask_login_not_installed(sentry_init, app, capture_events, monkeypatch): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) monkeypatch.setattr(flask_sentry, "flask_login", None) events = capture_events() client = app.test_client() client.get("/message") event, = events assert event.get("user", {}).get("id") is None
def test_transaction_style(sentry_init, app, capture_events, transaction_style, expected_transaction): sentry_init(integrations=[ flask_sentry.FlaskIntegration(transaction_style=transaction_style) ]) events = capture_events() client = app.test_client() response = client.get("/message") assert response.status_code == 200 (event, ) = events assert event["transaction"] == expected_transaction
def test_bad_request_not_captured(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() @app.route("/") def index(): abort(400) client = app.test_client() client.get("/") assert not events
def test_flask_login_partially_configured(sentry_init, app, capture_events, monkeypatch): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() login_manager = LoginManager() login_manager.init_app(app) client = app.test_client() client.get("/message") event, = events assert event.get("user", {}).get("id") is None
def configure(): """Configure Sentry logging integration for Celery. See the `official instructions for Celery integration <https://docs.sentry.io/platforms/python/celery/>`_. Notes ----- Add the API key username/pasword pair to your netrc file. """ # Catching NetrcParseError confuses sphinx. if SPHINX: # pragma: no cover return # Delayed import from .. import app scheme, netloc, *rest = urlparse(DSN) try: auth = netrc().authenticators(netloc) if not auth: raise ValueError('No netrc entry found for {}'.format(netloc)) except (NetrcParseError, OSError, ValueError): log.exception( 'Disabling Sentry integration because we could not load ' 'the username and password for %s from the netrc file', netloc) return # The "legacy" Sentry DSN requires a "public key" and a "private key", # which are transmitted as the username and password in the URL. # However, as of Sentry 9, then "private key" part is no longer required. username, _, _ = auth dsn = urlunparse((scheme, '{}@{}'.format(username, netloc), *rest)) version = 'gwcelery-{}'.format(_version.get_versions()['version']) environment = app.conf['sentry_environment'] sentry_sdk.init(dsn, environment=environment, release=version, integrations=[ celery.CeleryIntegration(), condor.CondorIntegration(), flask.FlaskIntegration(), redis.RedisIntegration(), requests.RequestsIntegration(), subprocess.SubprocessIntegration(), tornado.TornadoIntegration() ])
def test_sentry_trace_context(sentry_init, app, capture_events): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() @app.route("/") def index(): sentry_span = Hub.current.scope.span capture_message(sentry_span.to_traceparent()) return render_template_string("{{ sentry_trace }}") with app.test_client() as client: response = client.get("/") assert response.status_code == 200 assert response.data.decode( "utf-8") == '<meta name="sentry-trace" content="%s" />' % ( events[0]["message"], )
def test_tracing_success(sentry_init, capture_events, app): sentry_init(traces_sample_rate=1.0, integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() with app.test_client() as client: response = client.get("/message") assert response.status_code == 200 message_event, transaction_event = events assert transaction_event["type"] == "transaction" assert transaction_event["transaction"] == "hi" assert transaction_event["contexts"]["trace"]["status"] == "ok" assert message_event["message"] == "hi" assert message_event["transaction"] == "hi"
def test_logging(sentry_init, capture_events, app): # ensure that Flask's logger magic doesn't break ours sentry_init(integrations=[ flask_sentry.FlaskIntegration(), LoggingIntegration(event_level="ERROR"), ]) @app.route("/") def index(): app.logger.error("hi") return "ok" events = capture_events() client = app.test_client() client.get("/") event, = events assert event["level"] == "error"
def test_flask_session_tracking(sentry_init, capture_envelopes, app): sentry_init( integrations=[flask_sentry.FlaskIntegration()], release="demo-release", _experiments=dict( auto_session_tracking=True, ), ) @app.route("/") def index(): with configure_scope() as scope: scope.set_user({"ip_address": "1.2.3.4", "id": "42"}) try: raise ValueError("stuff") except Exception: logging.exception("stuff happened") 1 / 0 envelopes = capture_envelopes() with app.test_client() as client: try: client.get("/", headers={"User-Agent": "blafasel/1.0"}) except ZeroDivisionError: pass Hub.current.client.flush() (first_event, error_event, session) = envelopes first_event = first_event.get_event() error_event = error_event.get_event() session = session.items[0].payload.json assert first_event["exception"]["values"][0]["type"] == "ValueError" assert error_event["exception"]["values"][0]["type"] == "ZeroDivisionError" assert session["status"] == "crashed" assert session["did"] == "42" assert session["errors"] == 2 assert session["init"] assert session["attrs"]["release"] == "demo-release" assert session["attrs"]["ip_address"] == "1.2.3.4" assert session["attrs"]["user_agent"] == "blafasel/1.0"
def test_flask_empty_json_request(sentry_init, capture_events, app, data): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) @app.route("/", methods=["POST"]) def index(): assert request.get_json() == data assert request.get_data() == json.dumps(data).encode("ascii") assert not request.form capture_message("hi") return "ok" events = capture_events() client = app.test_client() response = client.post("/", content_type="application/json", data=json.dumps(data)) assert response.status_code == 200 (event,) = events assert event["request"]["data"] == data
def register_error_handlers(app): from flask_json import json_response # - - - - - - - - - - - - - - - - - - - - - - - @app.errorhandler(BaseError) def handle_olea_exceptions(e: BaseError): return json_response(status_=e.http_code, data_=e) # - - - - - - - - - - - - - - - - - - - - - - - from sentry_sdk import init as sentry_init from sentry_sdk.integrations import flask, redis, sqlalchemy if not app.config.get('IGNORE_ERRORS', False): sentry_init(dsn=app.config['SENTRY_DSN'], integrations=[ flask.FlaskIntegration(), sqlalchemy.SqlalchemyIntegration(), redis.RedisIntegration(), ], traces_sample_rate=0.2)
def test_class_based_views(sentry_init, app, capture_events): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() @app.route("/") class HelloClass(View): def dispatch_request(self): capture_message("hi") return "ok" app.add_url_rule("/hello-class/", view_func=HelloClass.as_view("hello_class")) with app.test_client() as client: response = client.get("/hello-class/") assert response.status_code == 200 (event,) = events assert event["message"] == "hi" assert event["transaction"] == "hello_class"
def test_500(sentry_init, capture_events, app): sentry_init(integrations=[flask_sentry.FlaskIntegration()]) app.debug = False app.testing = False @app.route("/") def index(): 1 / 0 @app.errorhandler(500) def error_handler(err): return "Sentry error: %s" % last_event_id() events = capture_events() client = app.test_client() response = client.get("/") (event,) = events assert response.data.decode("utf-8") == "Sentry error: %s" % event["event_id"]
def test_errorhandler_for_exception_swallows_exception(sentry_init, app, capture_events, exc_cls): # In contrast to error handlers for a status code, error # handlers for exceptions can swallow the exception (this is # just how the Flask signal works) sentry_init(integrations=[flask_sentry.FlaskIntegration()]) events = capture_events() @app.route("/") def index(): 1 / 0 @app.errorhandler(exc_cls) def zerodivision(e): return "ok" with app.test_client() as client: response = client.get("/") assert response.status_code == 200 assert not events
def create_wsgi(): application = FlaskApp(__name__) config = settings.load_config(( settings.FlaskSettings, settings.SentrySetting, )) application.app.config.from_object(config) CORS(application.app) application.add_api(specification='v1/openapi.yaml', resolver=VersionResolver('app.http.v1.handlers'), strict_validation=True, validate_responses=True) @application.app.teardown_appcontext def shutdown_session(exception): models.session.remove() sentry_init(dsn=config.SENTRY_DSN, integrations=[sentry_flask.FlaskIntegration()]) return application