instrumentation-http: Introduce a hook to allow customizing the Context
Is your feature request related to a problem? Please describe.
I'm building a caching library with deep integrations into the open telemetry API, and I would like access to additional information about user request when handling data requests.
It seems to me like the Context object is the perfect mechanism for this, however there's no way to add custom properties to the context that's initialised for each request in instrumentation-http.
Describe the solution you'd like
Introduction of a new hook to allow customising the context with additional attributes.
Describe alternatives you've considered
N/A
Additional context
N/A
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.
I would still like this to get looked at please =)
Hi @s0, I'm not sure I fully understand the use-case
and I would like access to additional information about user request when handling data requests
Does this mean that you're trying to attach something to the context object which is eventually used in the business logic of your library?
Does this mean that you're trying to attach something to the context object which is eventually used in the business logic of your library?
Hi @pichlermarc that is precisely correct.
The concrete use-case here is I would like to be able to implement a cache-busting feature that can be used to clear the cache keys used for a given http request. i.e. a privileged user with the right permissions when explicitly requesting can ensure that all caches are missed and regenerated.
Another use case is enabling a statistics / information gathering feature for a particular request, so that timing information about cache hits / misses / regenerating will be gathered and presented to the user, or stored / reported somewhere (for example in a trace).
In both cases, AsyncLocalStorage is the ideal solution to enable these features, as the cached functions "will just know" which features need to be enabled. The problem is that instrumenting http / express, etc... to initialise AsyncLocalStorage consistently across all requests is quite a complicated operation (especially when trying to use it with frameworks like Next.js), but it is something that instrumentation-http already solves quite nicely.
@s0 thank you for your reply. Unfortunately I think this use-case is outside the scope of this project (it is a very cool one, though!).
OpenTelemetry is designed in a way that it should never be used in a way that alters "how" the app works (context propagation is an exception to that, because without that distributed tracing does not work at all). For instance: an app with the instrumentation installed should behave the same as when they're used without that instrumentation - we even consider anything that deviates from that principle a bug.
That same principle should also be applied to code that is used inside the hooks of an instrumentation, which we also consider instrumentation code. It seems to me that this use-case falls outside of the scope of what we're looking to support.
cc @open-telemetry/javascript-maintainers (please let me know if you disagree with this statement)
I need this feature to accomplish stopping out-going propagation to 3rd parties. 3rd party services can be distinguished by hostname from internal domains. To achieve this, I could consider followings:
- Customize context and use the information in a propagator. (via first parameter of
injectfunction) - Set the custom attribute (e.g.
propagation.disable) to the span created by the instrumentation library, which can be accessed viagetRPCMetadata(ctx).span, and use that attribute in a propagator. (also via first parameter ofinjectfunction)
But to achieve the second one, I need to use force-type casting, which is not recommended. (https://github.com/open-telemetry/opentelemetry-specification/issues/3799#issuecomment-1919867950)
By now, I need to find every code lines creating outgoing http requests and alter the context not to propagate using the following propagator.
export class SelectivePropagator implements TextMapPropagator {
constructor(private readonly propagator: TextMapPropagator) {}
extract(ctx: Context, carrier: unknown, getter: TextMapGetter<any>): Context {
return this.propagator.extract(ctx, carrier, getter);
}
fields(): string[] {
return this.propagator.fields().slice();
}
inject(ctx: Context, carrier: unknown, setter: TextMapSetter<any>): void {
const settings = getSelectivePropagationContext(ctx);
if (settings?.stopPropagation) {
return;
}
this.propagator.inject(ctx, carrier, setter);
}
}
I need this feature to accomplish stopping out-going propagation to 3rd parties. 3rd party services can be distinguished by hostname from internal domains. To achieve this, I could consider followings:
- Customize context and use the information in a propagator. (via first parameter of
injectfunction)- Set the custom attribute (e.g.
propagation.disable) to the span created by the instrumentation library, which can be accessed viagetRPCMetadata(ctx).span, and use that attribute in a propagator. (also via first parameter ofinjectfunction)But to achieve the second one, I need to use force-type casting, which is not recommended. (open-telemetry/opentelemetry-specification#3799 (comment))
By now, I need to find every code lines creating outgoing http requests and alter the context not to propagate using the following propagator.
export class SelectivePropagator implements TextMapPropagator { constructor(private readonly propagator: TextMapPropagator) {}
extract(ctx: Context, carrier: unknown, getter: TextMapGetter
): Context { return this.propagator.extract(ctx, carrier, getter); } fields(): string[] { return this.propagator.fields().slice(); }
inject(ctx: Context, carrier: unknown, setter: TextMapSetter
): void { const settings = getSelectivePropagationContext(ctx); if (settings?.stopPropagation) { return; } this.propagator.inject(ctx, carrier, setter);} }
While I agree that is an important usecase, I think it needs to be addressed by the specification rather than being handled by only one implementation SDK. This is something that likely affects all SDKs and needs to be done in a consistent manner. I would suggest looking for an issue in the spec and if there isn't one already open please open one.