Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance tracing does not work with Sanic integration if AppFactory is used #2902

Open
Panaetius opened this issue Mar 25, 2024 · 2 comments
Labels
Integration: Sanic Type: Bug Something isn't working

Comments

@Panaetius
Copy link

How do you use Sentry?

Self-hosted/on-premise

Version

1.43.0

Steps to Reproduce

When using Sanic with a dynamic application as mentioned on https://sanic.dev/en/guide/running/app-loader.html the performance tracing doesn't work. The patched _startup method is never called in this case, so the corresponding signals aren't set up.

For instance:

import argparse
import asyncio

import sentry_sdk
from sanic import Sanic
from sanic.log import logger
from sanic.worker.loader import AppLoader
from sentry_sdk.integrations.asyncio import AsyncioIntegration
from sentry_sdk.integrations.sanic import SanicIntegration


def create_app() -> Sanic:
    """Create a Sanic application."""
    app = Sanic("app")
    
    @app.listener("before_server_start")
    async def setup_sentry(_):
        sentry_sdk.init(
            dsn=config.sentry.dsn,
            environment=config.sentry.environment,
            integrations=[AsyncioIntegration(), SanicIntegration(unsampled_statuses={404, 403, 401})],
            enable_tracing=config.sentry.sample_rate > 0,
            traces_sample_rate=config.sentry.sample_rate,
            before_send=filter_error,
        )

    app = register_all_handlers(app, config)
    

    return app


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-H", "--host", default="0.0.0.0", help="Host to listen on") 
    parser.add_argument("-p", "--port", default=8000, type=int, help="Port to listen on")
    args = vars(parser.parse_args())
    loader = AppLoader(factory=create_app)
    app = loader.load()
    app.prepare(**args)
    Sanic.serve(primary=app, app_loader=loader)

this will still log exceptions but the _startup method in the sanic integration is never called, nor are any of the hub_enter, hub_exit or set_transaction methods. (I left out the register_all_handlers method which just registers our endpoints)
A workaround is to set the signals in sanic manually, e.g.

import argparse
import asyncio

import sentry_sdk
from sanic import Sanic
from sanic.log import logger
from sanic.worker.loader import AppLoader
from sentry_sdk.integrations.asyncio import AsyncioIntegration
from sentry_sdk.integrations.sanic import SanicIntegration, _hub_enter, _hub_exit, _set_transaction


def create_app() -> Sanic:
    """Create a Sanic application."""
    app = Sanic("app")
    
    @app.listener("before_server_start")
    async def setup_sentry(_):
        sentry_sdk.init(
            dsn=config.sentry.dsn,
            environment=config.sentry.environment,
            integrations=[AsyncioIntegration(), SanicIntegration(unsampled_statuses={404, 403, 401})],
            enable_tracing=config.sentry.sample_rate > 0,
            traces_sample_rate=config.sentry.sample_rate,
            before_send=filter_error,
        )

    # we manually need to set the signals because sentry sanic integration doesn't work with using
    # an app factory
    app.signal("http.lifecycle.request")(_hub_enter)
    app.signal("http.lifecycle.response")(_hub_exit)
    app.signal("http.routing.after")(_set_transaction)

    app = register_all_handlers(app, config)
    

    return app


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-H", "--host", default="0.0.0.0", help="Host to listen on") 
    parser.add_argument("-p", "--port", default=8000, type=int, help="Port to listen on")
    args = vars(parser.parse_args())
    loader = AppLoader(factory=create_app)
    app = loader.load()
    app.prepare(**args)
    Sanic.serve(primary=app, app_loader=loader)

which logs performance tracing as expected.

Expected Result

Peformance traces to show up in sentry

Actual Result

No performance traces are logged

@sentrivana
Copy link
Contributor

sentrivana commented Mar 25, 2024

Hey @Panaetius, thanks for the great bug report and for researching how to work around this! We will need to figure out where to hook into to connect the signals in this case -- but it might take a bit of time since we're a bit swamped at the moment. (PRs are always welcome though!)

@Panaetius
Copy link
Author

I tried to figure that out myself and tried a couple of things (like passing in the app in SanicIntegration and patching _startup on the app itself, but that didn't work) and am not familiar enough with the sentry and sanic codebases to have a good idea on where to best do this.

If anyone has an idea on where to hook the signals in the AppFactory case, I'm happy to create a PR. Until then the workaround works well enough for our use-case 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Integration: Sanic Type: Bug Something isn't working
Projects
Status: No status
Development

No branches or pull requests

2 participants