Pylons / pyramid

Pyramid - A Python web framework
https://trypyramid.com/
Other
3.97k stars 887 forks source link

Injecting Tracing Context into the Request.Context #3585

Closed jon-whit closed 4 years ago

jon-whit commented 4 years ago

I'm trying to write some middleware using a Tween that extracts some trace context (using OpenTelemetry) and propagates the context by injecting the tracing context into the request context.

Here's some example code:

# tracing.py

def trace_tween_factory(handler, registry):
    settings = registry.settings
    service = settings.get(SETTINGS_SERVICE) or 'pyramid'
    tracer = settings.get(SETTINGS_TRACER) or trace.get_tracer(__name__)
    distributed_tracing = asbool(settings.get(SETTINGS_DISTRIBUTED_TRACING, True))

    def trace_tween(request):
        # Extract the Trace context from the HTTP request header (if it exists)
        ctx = propagator.extract(get_from_carrier=getTraceParent, carrier=request.headers)

        parent = None
        if distributed_tracing:
            s = get_span_from_context(ctx)
            if s.get_context().is_valid():
                parent = s

        span = tracer.start_span(name="pyramid.trace", parent=parent)
        span.set_attribute("http.method", request.method)
        span.set_attribute("http.url", request.path_url)

        request.context = set_span_in_context(span, request.context)

        response = None
        try:
            response = handler(request)
        except HTTPException as e:
            response = e
            raise
        except BaseException:
            span.set_attribute("http.status_code", 500)
            raise
        finally:
            span.end()

        return response

    return trace_tween
# main.py

def main(global_config, **settings):
    with Configurator(settings=..., session_factory=...) as config:
        config.add_tween(tracing.TRACING_TWEEN_NAME, under=pyramid.tweens.INGRESS)
        config.scan()
    return config.make_wsgi_app()

I'm not able to inject the tracing context into the request.context because the context does not yet exist. Looking at the Request Processing doc and the Pyramid Request doc it appears that the request.context

is added to the request by the router at request ingress time

So what's the proper way to do this?

mmerickel commented 4 years ago

If you want to track anything related to the context you'll have to do it starting from the ContextFound event. Tweens are very early in the lifecycle of a request before any sort of url processing / matching has occurred. For things like tracing Pyramid has quite a lot of hooks that you may be interested in depending on what parts of the system you're trying to track.

I'm closing this issue because it's a question and not a bug report. See https://groups.google.com/forum/#!forum/pylons-discuss for a wider community of people who can answer usage questions.

jon-whit commented 4 years ago

@mmerickel ok, thanks. I didn't think I filed it as a bug report... But I suppose I can ask questions elsewhere.

mmerickel commented 4 years ago

No worries! For usage questions we just try to redirect to the mailing list versus features/bugs since github is specific to the maintainers.

digitalresistor commented 4 years ago

Quick note, that context in terms of what Pyramid calls the context (and what is available on request as request.context) may not be the same as what you consider the "context" related to request tracing.

Instead you should likely inject your own tracing_context into the request object itself, and now that will be available anywhere the request is.

As for integrations for inspiration, there are a couple that I know exist, they are of varying quality but should hopefully provide some guidance on how others have built their integration:

https://github.com/opentracing-contrib/python-pyramid https://github.com/Yelp/pyramid_zipkin (https://engineeringblog.yelp.com/2016/04/distributed-tracing-at-yelp.html) https://github.com/getsentry/sentry-python/blob/master/sentry_sdk/integrations/pyramid.py