Skip to content

Allow supplying a callback for adding new attributes when spans start #1724

Description

@zionsofer

Adding attributes to spans is a crucial part of OpenTelemtry to allow extra information passing.
The instrumentation libraries create their spans in a very specific part of the flow, "wrapping" existing functionality to allow span creation correctly.

Sometimes, as part of our recurring flows in the system (for example, server http requests), we want to add extra attributes to the existing spans, in a single place for all relevant flows (for example, adding information about the user that made the request as attributes).
Unfortunately, that span only exists and created in a specific point in time, and any attempt to add any attribute to it before that, as expected, results in nothing.

My proposition is to be able to configure the tracer with some kind of callback that will be applied each time a span is created to allow extra configuration, for example settings additional attributes, without having to explicitly do it in code which is supposed to be agnostic to opentelemetry instrumentation and does not rely on it running.

For example, assume I have a python service based on flask.
This service uses instrumentation:

otlp_exporter = OTLPSpanExporter()
span_processor = BatchSpanProcessor(otlp_exporter)
trace.set_tracer_provider(
    TracerProvider(
        active_span_processor=span_processor,
        id_generator=AwsXRayIdGenerator(),
    )
)

app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)

@app.route("/ping")
def ping():
    span = trace.get_current_span()
    span.set_attribute("name", "flask_otel")
    return {"message": "ok"}

In this case, the setting of the attribute would work because this is inside the route itself, which means it's being done inside the created span of OpenTelemetry.
But, let's assume I have a flask middleware which adds information for all requests:

@app.before_request
def add_otel_attribute():
    span = trace.get_current_span()
    span.set_attribute("name", "flask_otel")

So, this would be done once for all endpoints instead of setting it at each route.
This, unfortunately, won't work, because at this point the span created by OpenTelemetry does not exist yet, and thus will be an invalid (noop) span.

If we had, however, some way to supply a callback to add attributes to each span creation, we would not rely on when and how the spans are created, and won't need to "manoeuvre" our way to find when we can add our attributes.
This callback can return an object (key-value store) of attributes, just like we would have added manually.

Something like this:

def add_otel_flask_attributes():
    return {"name": "flask_otel"}

otlp_exporter = OTLPSpanExporter()
span_processor = BatchSpanProcessor(otlp_exporter)
trace.set_tracer_provider(
    TracerProvider(
        active_span_processor=span_processor,
        id_generator=AwsXRayIdGenerator()
    )
)

app = Flask(__name__)
FlaskInstrumentor(span_attr_callback=add_otel_flask_attributes).instrument_app(app)

Then, this callback will be applied inside the span creation to add attributes the moment the span is created.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiCross language API specification issueenhancementNew feature or requestspec:traceRelated to the specification/trace directory

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions