opentelemetry-js icon indicating copy to clipboard operation
opentelemetry-js copied to clipboard

How to handle common / global attributes?

Open martinkuba opened this issue 2 years ago • 5 comments

  • [ ] This only affects the JavaScript OpenTelemetry library
  • [x] This may affect other libraries, but I would like to get opinions here first

The intent of this issue is to open a discussion about how to implement handling of common/shared attributes. Common attributes are similar to resource attributes in that they apply to all spans and logs, but they are different in that they are mutable.

Background

Client instrumentation has several use cases for attributes that should be associated with all spans and logs generated within a given time period. The primary use case is to represent a session (the session.id attribute has recently been added to semantic conventions), but there are other use cases including:

  • visitor id
  • page impression id
  • page url
  • geo location data
  • network information
  • language preference
  • screen width and height

Compared with Resource attributes

We originally wanted to send these as resource attributes (see this OTEP). However, the challenge with these attributes is that their values can change during the lifetime of the SDK. Therefore, the direction we have received from the TC is to add them to each signal instead.

Compared with Context attributes

A distinction also needs to be made with trace-context attributes, as proposed in this OTEP. Client attributes, like session ID, need to be applied to many traces and standalone events generated by a client application, not just a single trace.

Questions

Should there be a standard mechanism in the SDK to handle these attributes?

Should this be standardized across all SDKs and documented in the specification?

Discussion

The options that I can think of:

Option 1: Each attribute is added by a separate span/log processor

The implementation would be straight-forward. However, adding many attributes means managing many processors: two processors (span and log) for each attribute. This may be difficult to configure and manage.

Setting and retrieving the attribute values could be handled on a case-by-case basis. A processor, for example, could itself include the detection of the value. Or, it could expose methods to set the value from an external code.

Example:

tracerProvider.addSpanProcessor(new SessionIdSpanProcessor());
tracerProvider.addSpanProcessor(new VisitorIdSpanProcessor());
tracerProvider.addSpanProcessor(new GeoLocationSpanProcessor());

loggerProvider.addLogProcessor(new SessionIdLogProcessor());
loggerProvider.addLogProcessor(new VisitorIdLogProcessor());
loggerProvider.addLogProcessor(new GeoLocationLogProcessor());

Option 2: There is a single span/log processor used to add all common attributes

There would be two processors only: one for spans and one for logs. The question is how would attributes be added and updated. The processor could accept a collection of attribute providers, or it could retrieve the attributes from a global attribute store (provided by the SDK). A prototype of the latter is available here.

For the first option, an interface would need to be defined for the attribute providers. These providers would need to be configured and passed to the processor during initialization.

const providers = [
	new SessionIdAttributeProvider(),
	new VisitorIdAttributeProvider()
	new GeoLocationAttributeProvider(),
]

tracerProvider.addSpanProcessor(new GlobalAttributesSpanProcessor(providers));
loggerProvider.addLogProcessor(new GlobalAttributesLogProcessor(providers));

Option 3: The mechanism to add these attributes is built-in to the SDK

With this option, there is no span/log processor. The SDK provides an API to set/update global attributes, and it automatically adds them when a span or log is created.

For example:

tracerProvider.setGlobalAttribute(name, value);
loggerProvider.setGlobalAttribute(name, value);

or perhaps:

new TracerProvicer({
	globalAttributeProviders: [
		new SessionIdAttributeProvider(),
		new VisitorIdAttributeProvider()
		new GeoLocationAttributeProvider(),
	]
})

Existing implementations

The Android SDK currently has a SessionIdSpanAppender span processor that adds the session ID attribute to all spans. It also has the GlobalAttributesSpanAppender span processor that can add multiple attributes at the same time.

We have also implemented a prototype in JavaScript that introduces a GlobalAttributesSpanProcessor and GlobalAttributesLogRecordProcessor processors. The attributes are retrieved from a global store, which allows for any component to add global attributes.

martinkuba avatar Nov 09 '23 05:11 martinkuba

If this ends up in SDK I would expect it to be similar across techs. If this happens it likely needs to be an optional component/api there because techs are already different in some areas (like automatic context propagation).

Is this only applicable to browsers or also to node.js/deno/... If only for browsers it should be likely at WebSDK or in web specific instrumentations. If it is on API level or generic SDK we need to clearly specify how it behaves in non web setups.

Which component is responsible for such global changes?

  • Some blessed instrumentation?
  • Actual application code which did the SDK setup?
  • What about automatic setups in this case where monitoring is "just added"?
  • Is there a specific "owner" of this state?

Is it allowed to have more parallel sessions running? If yes, how is it ensured that all (mostly independent) instrumentations are aware of this and select the "correct" session?

Is it allowed that spans on the same trace are on different sessions? Current proposals seem to allow this as span creation is unrelated to global state change and several traces may run when state is changed.

Flarna avatar Nov 09 '23 09:11 Flarna

@martinkuba

  1. I suggest skipping option 3 as it touches TracerProvider and will require more discussion / approvals.
  2. Option 2 looks very similar to MultiSpanProcessor in opentelemetry-java, so you can introduce the same in opentelemetry-js too.
  3. I think we don't need the term "Global" in GlobalAttributeProcessor as the processors are global already - that is, they are applied to all the signals emitted by instrumentations configured with tracer provider. We can stop here and implement separate processors for each concern and use the MultiSpanProcessor to configure them all in one place.
  4. Optionally, if the term Processor sounds generic and your intent is to convey that the new interface is to inject an attribute then the interface could take the attribute name to be injected as a parameter, for eg., session.id, and a generator class that generates only a value for this attribute. For eg., AttributeInjector(attributeName, ValueGenerator)

scheler avatar Nov 23 '23 23:11 scheler

@scheler:

Option 2 looks very similar to MultiSpanProcessor in opentelemetry-java, so you can introduce the same in opentelemetry-js too.

I agree that it is similar, but there is a subtle difference. The MultiSpanProcessor is more generic; it can take any SpanProcessor and invoke its processing, which could be doing anything. My proposal with the GlobalAttributesSpanProcessor is that this processor adds the attributes - it's a generic attributes "adder". The providers are there only to supply the attributes to be added.

I think we don't need the term "Global" in GlobalAttributeProcessor as the processors are global already

Having "GlobalAttribute" in the name denotes that this processor adds attributes that are considered global. In other words, this just describes what this processor does.

martinkuba avatar Nov 27 '23 19:11 martinkuba

@Flarna

If only for browsers it should be likely at WebSDK or in web specific instrumentations.

The use cases I am aware of are for client applications. They are not only for web JS though; Android and Swift SDKs have the same use cases. There might be other use cases for non-client applications that I am not aware of; perhaps I should move this discussion to the spec repo.

Which component is responsible for such global changes?

It would not be an instrumentation. I envision this to be similar to resource providers.

Is it allowed to have more parallel sessions running?

I don't think there would be multiple parallel sessions, at least not represented using the same attribute. But I think this can be a separate conversation independent of how global attributes are handled in general.

Is it allowed that spans on the same trace are on different sessions?

Good question, I would say no, but it needs to be defined.

martinkuba avatar Nov 27 '23 19:11 martinkuba

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.

github-actions[bot] avatar Jan 29 '24 06:01 github-actions[bot]

This issue was closed because it has been stale for 14 days with no activity.

github-actions[bot] avatar Mar 04 '24 06:03 github-actions[bot]