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

Initial composite/global Propagators update for OTEP 66. #440

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 88 additions & 7 deletions specification/api-propagators.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Table of Contents
</summary>

- [Overview](#overview)
- [HTTP Text Format](#http-text-format)
- [Fields](#fields)
- [Inject](#inject)
Expand All @@ -13,22 +14,34 @@ Table of Contents
- [Extract](#extract)
- [Getter argument](#getter)
- [Get](#get)
- [Composite Propagator](#composite-propagator)
- [Global Propagators](#global-propagators)

</details>

Propagators API currently consists of one format:
## Overview
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved

Cross-cutting concerns send their state to the next process using
`Propagator`s, which are defined as objects used to read and write
context data to and from messages exchanged by the applications.
Each concern creates a set of `Propagator`s for every supported `Format`.

The Propagators API is expected to be leveraged by users writing
instrumentation libraries.

Propagators API currently consists of one `Format`:

- `HTTPTextFormat` is used to inject and extract a value as text into carriers that travel
in-band across process boundaries.

Deserializing must set `IsRemote` to true on the returned `SpanContext`.

A binary format will be added in the future.
A binary `Format` will be added in the future.

## HTTP Text Format

`HTTPTextFormat` is a formatter that injects and extracts a value as text into carriers that
travel in-band across process boundaries.
`HTTPTextFormat` is a formatter that injects and extracts a cross-cutting concern
value as text into carriers that travel in-band across process boundaries.
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved

Encoding is expected to conform to the HTTP Header Field semantics. Values are often encoded as
RPC/HTTP request headers.
Expand Down Expand Up @@ -62,7 +75,7 @@ Injects the value downstream. For example, as http headers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not follow anything in the preceding Fields section. It seems very vague and random. And certainly does not belong specifically under the HTTP format.

Required arguments:

- the value to be injected, can be `SpanContext` or `DistributedContext`.
- the cross-cutting concern value to be injected, such as `SpanContext` or `DistributedContext`.
- the carrier that holds propagation fields. For example, an outgoing message or http request.
- the `Setter` invoked for each propagation key to add or remove.

Expand Down Expand Up @@ -99,7 +112,7 @@ Required arguments:
- the carrier holds propagation fields. For example, an outgoing message or http request.
- the instance of `Getter` invoked for each propagation key to get.

Returns the non-null extracted value.
Returns the non-null cross-cutting concern extracted value.

#### Getter argument

Expand All @@ -118,4 +131,72 @@ Required arguments:
- the carrier of propagation fields, such as an HTTP request.
- the key of the field.

The Get function is responsible for handling case sensitivity. If the getter is intended to work with a HTTP request object, the getter MUST be case insensitive. To improve compatibility with other text-based protocols, text format implemenations MUST ensure to always use the canonical casing for their attributes. NOTE: Cannonical casing for HTTP headers is usually title case (e.g. `Content-Type` instead of `content-type`).
The Get function is responsible for handling case sensitivity. If the getter is intended to work with a HTTP request object, the getter MUST be case insensitive. To improve compatibility with other text-based protocols, text `Format` implementions MUST ensure to always use the canonical casing for their attributes. NOTE: Cannonical casing for HTTP headers is usually title case (e.g. `Content-Type` instead of `content-type`).

## Composite Propagator

Implementations MUST offer a facility to group multiple `Propagator`s
from different cross-cutting concerns in order to leverage them as a
single entity.

The resulting composite `Propagator` will invoke the `Propagators`
in the order they were specified.

Each composite `Propagator` will be bound to a specific `Format`, such
as `HttpTextFormat`, as different `Format`s will likely operate on different
data types.
There MUST be functions to accomplish the following operations.

- Create a composite propagator
- Extract from a composite propagator
- Inject into a composite propagator

carlosalberto marked this conversation as resolved.
Show resolved Hide resolved
### Create a Composite Propagator

Required arguments:

- A list of `Propagator`s.

Returns a new composite `Propagator` with the specified `Propagator`s.

### Extract
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved

Required arguments:

- A `Context`.
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved
- The carrier that holds propagation fields.
- The instance of `Getter` invoked for each propagation key to get.

carlosalberto marked this conversation as resolved.
Show resolved Hide resolved
### Inject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to specify whether inject mutates the carrier or returns a new one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So currently the carrier that is passed is in turn passed to the Setter instance, which decides what to do with it along with the keys/values. In practice (99% of the time?) the carrier is indeed mutated, but not 'enforced'.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to mark this as resolved, but for some reason don't have the button available like in the spec repo. Are permissions different here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Carrier APIs should be defined earlier in the doc. They are generally mutable by definition, although I could see how this could irk some FP-oriented people. It is rather difficult to make individual pieces immutable and composable, because the Carrier API fundamentally implies some transformation that is unknown to the propagator, like writing every key-value pair as HTTP header is a specific implementation of HTTPRequest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(well, it's actually not THAT difficult, using some pipeline like propagators.map(_.stateAsMap(context)).flatMap(carrier.accumulate),
but it's rather inefficient).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dyladan You need to be the author who opened the PR or have write permissions to the repo (i.e., be in spec-approvers) to resolve conversations.


Required arguments:

- A `Context`.
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved
- The carrier that holds propagation fields.
- The `Setter` invoked for each propagation key to add or remove.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to separate carrier and setter?


## Global Propagators

Implementations MAY provide global `Propagator`s for
each supported `Format`.

If offered, the global `Propagator`s should default to a composite `Propagator`
containing W3C Trace Context and Correlation Context `Propagator`s,
in order to provide propagation even in the presence of no-op
OpenTelemetry implementations.
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved
carlosalberto marked this conversation as resolved.
Show resolved Hide resolved

### Get Global Propagator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want one global propagator (which may be composite), or do we want to add the propagator to the TracerProvider? Reason being that if you have multiple apps in a single runtime, each of which has its own tracer provider, you may want to use different propagation formats between apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me doing that sounds like an overkill for now - in practice, it looks like most people want a single place/component to do the entire injection/extraction.

We could add in the future an additional Propagator component to items such as TracerProvider and similar components (defaulting to null) in case users want to (optionally) override the global one, though 😃

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should we be calling the propagator?

  1. api.propagator.extract(...)
  2. tracer.extract(...)
  3. tracer_provider.extract(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be using 1) as this will be the 'default' thing - we might (but we might not) add 3) as a custom, override thing, in which case we could call that one as well.

The second option is due to be removed ;)


This method MUST exist for each supported `Format`.

Returns a global `Propagator`. This usually will be composite instance.

### Set Global Propagator

This method MUST exist for each supported `Format`.

Sets the global `Propagator` instance.

Required parameters:

- A `Propagator`. This usually will be a composite instance.
5 changes: 3 additions & 2 deletions specification/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,9 @@ for an example.

## Propagators

OpenTelemetry uses `Propagators` to serialize and deserialize `SpanContext` and `DistributedContext`
into text format. Currently there is one type of propagator:
OpenTelemetry uses `Propagators` to serialize and deserialize cross-cutting concern values
such as `SpanContext` and `DistributedContext` into a `Format`. Currently there is one
type of propagator:

- `HTTPTextFormat` which is used to inject and extract a value as text into carriers that travel
in-band across process boundaries.
Expand Down