From dff8df9c05cdee96bc19cb9642ccd1d4a6667ec5 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sun, 8 Sep 2019 10:01:10 -0700 Subject: [PATCH 01/77] Proposal to separate context propagation from observability --- text/0000-separate-context-propagation.md | 195 +++++++++++++++++++ text/img/context_propagation_details.png | Bin 0 -> 83684 bytes text/img/context_propagation_explanation.png | Bin 0 -> 43406 bytes 3 files changed, 195 insertions(+) create mode 100644 text/0000-separate-context-propagation.md create mode 100644 text/img/context_propagation_details.png create mode 100644 text/img/context_propagation_explanation.png diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md new file mode 100644 index 000000000..08e10911f --- /dev/null +++ b/text/0000-separate-context-propagation.md @@ -0,0 +1,195 @@ +# Proposal: Separate Layer for Context Propagation + +Status: `proposed` + +Design OpenTelemetry as a set of separate applications which operate on a shared context propagation mechanism. + + +## Motivation + +Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling, and may have different requirements from other systems which require non-local information to be sent downstream. +* Separation of concerns + * Remove dependecy the Tracer dependency from context propagation mechanisms. + * Separate distributed context into Baggage and Correlations +* Extensibility + * Allow users to create new applications for context propagation. + * For example: A/B testing, encrypted or authenticated data, and new, experimental forms of observability. + +## Explanation + +# OpenTelemetry Layered Architecture + +![drawing](img/context_propagation_explanation.png) + +OpenTelemetry is a distributed program, which requires non-local, transaction-level context in order to execute correctly. Transaction-level context can also be used to build other distributed programs, such as security, versioning, and network switching programs. + +To allow for this extensibility, OpenTelemetry is separated into **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - such as the observability and baggage systems provided by OpenTelemetry - simultaneously share the same underlying context propagation system in order to execute their programs. + + +# Application Layer + +## Observability API + +OpenTelemetry currently contains two observability systems - Tracing and Metrics – and may be extended over time. These separate systems are bound into a unified Observability API through sharing labels – a mechanism for correlating independent observations – and through sharing propagators. + +**Observe( context, labels…, observations...) context** +The general form for all observability APIs is a function which takes a Context, label keys, and observations as input, and returns an updated Context. + +**Correlate( context, label, value, hoplimit) context** +To set the label values used by all observations in the current transaction, the Observability API provides a function which takes a context, a label key, a value, and a hoplimit, and returns an updated context. If the hoplimit is set to NO_PROPAGATION, the label will only be available to observability functions in the same process. If the hoplimit is set to UNLIMITED_PROPAGATION, it will be available to all downstream services. + +**GetPropagator( type) inject, extract** +To register with the propagation system, the Observability API provides a set of propagation functions for every propagation type. + + +## Baggage API + +In addition to observability, OpenTelemetry provides a simple mechanism for propagating arbitrary data, called Baggage. This allows new distributed applications to be implemented without having to create new propagators. + +To manage the state of a distributed application, the Baggage API provides a set of functions which read, write, and remove data. + +**SetBaggage(context, key, value) context** +To record the distributed state of an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. + +**GetBaggage( context, key) value** +To access the distributed state of an application, the Baggage API provides a function which takes a context and a key as input, and returns a value. + +**RemoveBaggage( context, key) context** +To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. + +**CleanBaggage( context) context** +To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, + +**GetPropagator( type) inject, extract** +To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. + + +## Additional APIs + +Because the application and context propagation layers are separated, it is possible to create new distributed applications which do not depend on either the Observability or Baggage APIs. + +**GetPropagator(type) inject, extract** +To register with the propagation system, additional APIs provide a set of propagation functions for every propagation type. + + +# Context Propagation Layer + +## Context API + +Distributed applications access data in-process using a shared context object. Each distributed application sets a single key in the context, containing all of the data for that system. + +**SetValue( context, key, value) context** +To record the local state of an application, the Context API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. + +**GetValue( context, key) value** +To access the local state of an application, the Context API provides a function which takes a context and a key as input, and returns a value. + +**Optional: Automated Context Management** +When possible, context should automatically be associated with program execution . Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. + +**Optional: SetCurrentContext( context)** +To associate a context with program execution, the Context API provides a function which takes a Context. + +**Optional: GetCurrentContext() context** +To access the context associated with program execution, the Context API provides a function which takes no arguments and returns a Context. + + +## Propagation API + +Distributed applications send data to downstream processes via propagators, functions which read and write application context into RPC requests. Each distributed application creates a set of propagators for every type of supported medium - currently HTTP and Binary. + +**Inject( context, request)** +To send the data for all distributed applications downstream to the next process, the Propagation API provides a function which takes a context and a request, and mutates the request to include the encoded context. The canonical representation of a request is as a map. + +**Extract( context, request) context** +To receive the data from all distributed applications set by prior upstream processes, the Propagation API provides a function which takes a context and a request, and mutates the request to include the encoded context. The canonical representation of a request is as a map. + +**RegisterPropagator( type, inject, extract)** +In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. + +OpenTelemetry currently contains two types of Propagators: + +* **HTTP** - context is written into and read from a map of HTTP headers. +* **Binary** - context is serialized into and deserialized from a stream of bytes. + +# Internal details + +![drawing](img/context_propagation_details.png) + +## Context details +OpenTelemetry currently implements three context types of context propagation. + +**Span Context -** Observability data used by the tracing system. The readable attributes are defined to match those found in the W3C **traceparent** header. Span Context is used as labels for metrics and traces. This can quickly add overhead when propagated in-band. But, because this data is write-only, how this information is transmitted remains undefined. + +**Correlation Context -** Transaction-level observability data, which can be applied as labels to spans and metrics. + +**Baggage Context -** Transaction-level application data, meant to be shared with downstream components. + +Note that when possible, OpenTelemetry APIs calls are given access to the entire context object, and not a specific context type. + + +## Context Management and in-process propagation + +In order for Context to function, it must always remain bound to the execution of code it represents. By default, this means that the programmer must pass a Context down the call stack as a function parameter. However, many languages provide automated context management facilities, such as thread locals. OpenTelemetry should leverage these facilities when available, in order to provide automatic context management. + +## Pre-existing Context implementations + +In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. In the cases where there is not an extremely clear pre-existing option available, OpenTelemetry should provide its own Context implementation. + +While the above explanation represents the default OpenTelemetry approach to context propagation, it is important to note that some languages may already contain a form of context propagation. For example, Go has a the context.Context object, and widespread conventions for how to pass. + +Span data is used as labels for metrics and traces. This can quickly add overhead when propagated in-band. But, because this data is write-only, how this information is transmitted remains undefined. + +## Default Propagators + +When available, OpenTelemetry defaults to propagating via HTTP header definitions which have been standardized by the W3C. + + +# Trade-offs and mitigations + +## Why separate Baggage from Correlations? + +Since Baggage Context and Correlation Context appear very similar, why have two? + +First and foremost, the intended uses for Baggage and Correlations are completely different. Secondly, the propagation requirements diverge significantly. + +Correlations values are solely to be used as labels for metrics and traces. By making Correlation Context data write-only, how and when it is transmitted remains undefined. This leaves the door open to optimizations, such as propagating some data out-of-band, and situations where sampling decisions may cease the need to propagate correlation context any further. + +Baggage values, on the other hand, are explicitly added in order to be accessed by downstream by other application code. Therefore, Baggage Context must be readable, and reliably propagated in-band in order to accomplish this goal. + +There may be cases where a key-value pair is propagated as TagMap for observability and as a Baggage for application specific use. AB testing is one example of such use case. There is potential duplication here at call site where a pair is created and also at propagation. + +Solving this issue is not worth having semantic confusion with dual purpose. However, because all observability functions take the complete context as input, it may still be possible to use baggage values as labels. + + +## What about complex propagation behavior? + +Some OpenTelemetry proposals have called for more complex propagation behavior. For example, having a fallback to extracting B3 headersif Trace-Context headers are not found. Chained propagators and other complex behavior can be modeled as implementation details behind the Propagator interface. Therefore, the propagation system itself does not need to provide chained propagators or other additional facilities. + + +## Did you add a context parameter to every API call because Go has infected your brain? + +No. The concept of an explicit context is fundamental to a model where independent distributed applications share the same context propagation layer. How this context appears or is expressed is language specific, but it must be present in some form. + + +# Prior art and alternatives + +Prior art: +* OpenTelemetry distributed context +* OpenCensus propagators +* OpenTracing spans +* gRPC context + +# Open questions + +Related work on HTTP propagators has not been completed yet. + +* [W3C Trace-Context](https://www.w3.org/TR/trace-context/) candidate is not yet accepted +* Work on [W3C Correlation-Context](https://w3c.github.io/correlation-context/) has begun, but was halted to focus on Trace-Context. +* No work has begun on a theoretical W3C Baggage-Context. + +Given that we must ship with working propagators, and the W3C specifications are not yet complete, how should we move forwards with implementing context propagation? + +# Future possibilities + +Cleanly splitting OpenTelemetry into an Application and Context Propagation layer may allow us to move the Context Propagation layer into its own, stand-alone project. This may facilitate adoption, by allowing us to share Context Propagation with gRPC and other projects. diff --git a/text/img/context_propagation_details.png b/text/img/context_propagation_details.png new file mode 100644 index 0000000000000000000000000000000000000000..459c847aa063c726deceee6522be6f28e318813e GIT binary patch literal 83684 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 literal 0 HcmV?d00001 From 5ad7d1c3650712b7b5ad1f7aff3b9dab02a00550 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 9 Sep 2019 22:09:44 -0700 Subject: [PATCH 02/77] cleanup description for Extract --- text/0000-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 08e10911f..08c8ba655 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -9,7 +9,7 @@ Design OpenTelemetry as a set of separate applications which operate on a shared Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling, and may have different requirements from other systems which require non-local information to be sent downstream. * Separation of concerns - * Remove dependecy the Tracer dependency from context propagation mechanisms. + * Remove the Tracer dependency from context propagation mechanisms. * Separate distributed context into Baggage and Correlations * Extensibility * Allow users to create new applications for context propagation. @@ -102,7 +102,7 @@ Distributed applications send data to downstream processes via propagators, func To send the data for all distributed applications downstream to the next process, the Propagation API provides a function which takes a context and a request, and mutates the request to include the encoded context. The canonical representation of a request is as a map. **Extract( context, request) context** -To receive the data from all distributed applications set by prior upstream processes, the Propagation API provides a function which takes a context and a request, and mutates the request to include the encoded context. The canonical representation of a request is as a map. +To receive data injected by prior upstream processes, the Propagation API provides a function which takes a context and a request, and returns an updated context. **RegisterPropagator( type, inject, extract)** In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. From 1dc3c7b6f841cca042f5f9ad6093570eae01facf Mon Sep 17 00:00:00 2001 From: Ted Young Date: Tue, 10 Sep 2019 08:01:37 -0700 Subject: [PATCH 03/77] commas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Christian Neumüller --- text/0000-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 08c8ba655..5dd9dca38 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -58,7 +58,7 @@ To access the distributed state of an application, the Baggage API provides a fu To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. **CleanBaggage( context) context** -To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, +To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context. **GetPropagator( type) inject, extract** To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. From 58248e6e0fdc3beab7873c36fa019fa4938690a4 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Tue, 10 Sep 2019 13:39:59 -0700 Subject: [PATCH 04/77] Update text/0000-separate-context-propagation.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Christian Neumüller --- text/0000-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 5dd9dca38..bf6cce277 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -164,7 +164,7 @@ Solving this issue is not worth having semantic confusion with dual purpose. How ## What about complex propagation behavior? -Some OpenTelemetry proposals have called for more complex propagation behavior. For example, having a fallback to extracting B3 headersif Trace-Context headers are not found. Chained propagators and other complex behavior can be modeled as implementation details behind the Propagator interface. Therefore, the propagation system itself does not need to provide chained propagators or other additional facilities. +Some OpenTelemetry proposals have called for more complex propagation behavior. For example, falling back to extracting B3 headers if W3C Trace-Context headers are not found. Chained propagators and other complex behavior can be modeled as implementation details behind the Propagator interface. Therefore, the propagation system itself does not need to provide chained propagators or other additional facilities. ## Did you add a context parameter to every API call because Go has infected your brain? From 68cb0ba458f434382115444e24bec58e75cc0533 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Mon, 12 Aug 2019 19:48:43 -0700 Subject: [PATCH 05/77] RFC proposal: A layered approach to data formats --- text/0000-data-formats.md | 151 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 text/0000-data-formats.md diff --git a/text/0000-data-formats.md b/text/0000-data-formats.md new file mode 100644 index 000000000..21546f075 --- /dev/null +++ b/text/0000-data-formats.md @@ -0,0 +1,151 @@ +# RFC: Data Formats for OpenTelemetry + +# Overview +OpenTelemetry is an observability system, which generates observations, and then sends that data to remote systems for analysis. Describing how this observability data is represented and transmitted is critical part of the OpenTelemetry specification. + +**Proposed Layers** +* Metadata and Semantic Conventions +* Logical Structure +* Encodings +* Exchange Protocols + + +# Metadata and Semantic Conventions + +Spans represent specific operations in and between systems. Some of these operations represent calls that use well-known protocols like HTTP or database calls. Depending on the protocol and the type of operation, additional information is needed to represent and analyze a span correctly in monitoring systems. It is also important to unify how this attribution is made in different languages. This way, the operator will not need to learn specifics of a language and telemetry collected from multi-language micro-service can still be easily correlated and cross-analyzed. + +## Requirements + + +### Handle complex data + +Semantic information is often complex and multi-valued. How we represent semantic data is related to how attributes are defined at the logical layer. + +* At minimum, nested lists of key/value pairs are necessary to represent them. +* Is it advantageous to also have strongly typed data structures? +* Are there issues with having strongly typed data structures in certain environment, such as dynamic languages? + + +### Compose Multiple Semantics + +Objects in OpenTelemetry may describe more than one type of operation. For example, a span may simultaneously represent a MySQL transaction and a TCP/IP request. Pieces of metadata must compose well, without losing the structure integrity of each piece or risking a conflict in meaning. + + +### Easily Extensible + +Not all semantics conventions and common operations are known at this time. It must be possible to add more semantic definitions without creating to versioning or compatibility issues with OpenTelemetry implementations. + + +## Examples of Common Operations + +#### RPC calls + +Modeling distributed transactions is a core usecase, it is common to analyze errors and latency for RPC calls. Because multiple individual network requests may be involved in a single logical RPC operation, it must be possible to describe RPC calls as logical operations, independent from the descriptions of transport and application protocols which they contain. + +Examples: +* A client sending a request to a remote server. +* A server receiving a request from a remote client. +* Logical vs physical networking: a request for “[https://www.wikipedia.org/wiki/Rabbit](https://www.wikipedia.org/wiki/Rabbit)” results in two physical HTTP requests - one 301 redirection, and another 200 okay. The logical RPC operation represents both requests together. + +#### Network protocols + +The internet suite of network protocols is thoroughly standardized, and the details of network interactions calls are critical to analysing the behavior of distributed systems. + +Examples: +* Application Protocols: HTTP/1.1, HTTP/2, gRPC +* Transport Protocols: TCP, UDP +* Internet Protocols: IPv4, IPv6 + +#### Database protocols + +Database requests often contain information which is key to root causing errors and latency. + +Examples: +* SQL: MySQL, Postrgres +* NoSQL: MongoDB + + +# Logical Structure + +OpenTelemetry observability model, defined as data structures instead of API calls. A logical layer describes the default data structures expected to be produced by OpenTelemetry APIs for transport. + +## Requirements + +### Support Metadata + +The logical structure must be able to support all of the requirements for the metadata layer. + + +### Support both an API and a Streaming Representation + +OpenTelemetry data structures must work well with the APIs and SDKs which produce them. It must be possible to stream data from services without resorting to an encoding or exchange protocol which bears little resemblance to the API. + + +# Encoding Formats + +While there is a single set of OpenTelemetry data structures, they can be encoded in a variety of serialization formats. + +## Requirements + +### Prefer pre-existing formats + +Codecs must be commonly available in all major languages. It is outside the bounds of the OpenTelemetry project to define new serialization formats. + +**Examples:** +* Protobuf +* JSON + + +# Exchange Protocols + +Exchange protocols describe how to transmit serialized OpenTelemetry data. Beyond just defining which application and transport protocol should be used, the exchange protocol defines transmission qualities such as streaming, retry, acks, backpressure, and throttling. + +## Requirements + +### Support Common Topologies + +Be suitable for use between all of the following node types: instrumented applications, telemetry backends, local agents, stand-alone collectors/forwarders. + +* Be load-balancer friendly (do not hinder re-balancing). +* Allow backpressure signalling. + + +### Be Reliable and Observable + +* Prefer transports which have high reliability of data delivery. +* When data must be dropped, have visibility into what was not delivered. + + +### Be Efficient + +* Have low CPU usage for serialization and deserialization. +* Impose minimal pressure on memory manager, including pass-through scenarios, where deserialized data is short-lived and must be serialized as-is shortly after and where such short-lived data is created and discarded at high frequency (think telemetry data forwarders). +* Support ability to efficiently modify deserialized data and serialize again to pass further. This is related but slightly different from the previous requirement. +* Ensure high throughput (within the available bandwidth) in high latency networks (e.g. scenarios where telemetry source and the backend are separated by high latency network). + +--- + +# Appendix + + +## Currently Open Issues and PRs + +* Exchange Protocol: [https://github.com/open-telemetry/opentelemetry-specification/pull/193](https://github.com/open-telemetry/opentelemetry-specification/pull/193) + + +## Prior art + +* [https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md) +* [https://github.com/open-telemetry/opentelemetry-proto/](https://github.com/open-telemetry/opentelemetry-proto/pull/21) +* [https://github.com/open-telemetry/opentelemetry-specification/tree/master/work_in_progress/typedspans](https://github.com/open-telemetry/opentelemetry-specification/tree/master/work_in_progress/typedspans) +* [https://github.com/dynatrace-innovationlab/TracingApiDatamodel](https://github.com/dynatrace-innovationlab/TracingApiDatamodel) + + +## Questions, TODOs + +* Should “span status” be at the logical or semantic layer? + * Already overlaps with some semantics, like `http.status` + * Separate PR for this +* Are transports separate from the exchange protocol? + * Supported Transport protocols, such as HTTP and UDP, may be part of the exchange protocol, or they may be a separate layer. + * [https://github.com/open-telemetry/opentelemetry-specification/pull/193#issuecomment-516325059](https://github.com/open-telemetry/opentelemetry-specification/pull/193#issuecomment-516325059) From 3dc6a7633c623bf6f19105e15fa939c0134dd728 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Thu, 22 Aug 2019 08:59:05 -0700 Subject: [PATCH 06/77] whitespace Co-Authored-By: Reiley Yang --- text/0000-data-formats.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-data-formats.md b/text/0000-data-formats.md index 21546f075..1dd91bde5 100644 --- a/text/0000-data-formats.md +++ b/text/0000-data-formats.md @@ -1,7 +1,7 @@ # RFC: Data Formats for OpenTelemetry # Overview -OpenTelemetry is an observability system, which generates observations, and then sends that data to remote systems for analysis. Describing how this observability data is represented and transmitted is critical part of the OpenTelemetry specification. +OpenTelemetry is an observability system, which generates observations, and then sends that data to remote systems for analysis. Describing how this observability data is represented and transmitted is critical part of the OpenTelemetry specification. **Proposed Layers** * Metadata and Semantic Conventions From 459435e850e8467ad9eb991a437c688f37714278 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Thu, 22 Aug 2019 08:59:24 -0700 Subject: [PATCH 07/77] Capitalization Co-Authored-By: Reiley Yang --- text/0000-data-formats.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-data-formats.md b/text/0000-data-formats.md index 1dd91bde5..0143bd9cf 100644 --- a/text/0000-data-formats.md +++ b/text/0000-data-formats.md @@ -17,7 +17,7 @@ Spans represent specific operations in and between systems. Some of these operat ## Requirements -### Handle complex data +### Handle Complex Data Semantic information is often complex and multi-valued. How we represent semantic data is related to how attributes are defined at the logical layer. From c9c64f46d8fc7c75fe75bf7e8112a606f308a2ec Mon Sep 17 00:00:00 2001 From: Ted Young Date: Thu, 22 Aug 2019 08:59:38 -0700 Subject: [PATCH 08/77] whitespace Co-Authored-By: Reiley Yang --- text/0000-data-formats.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-data-formats.md b/text/0000-data-formats.md index 0143bd9cf..c64451af0 100644 --- a/text/0000-data-formats.md +++ b/text/0000-data-formats.md @@ -40,7 +40,7 @@ Not all semantics conventions and common operations are known at this time. It m #### RPC calls -Modeling distributed transactions is a core usecase, it is common to analyze errors and latency for RPC calls. Because multiple individual network requests may be involved in a single logical RPC operation, it must be possible to describe RPC calls as logical operations, independent from the descriptions of transport and application protocols which they contain. +Modeling distributed transactions is a core usecase, it is common to analyze errors and latency for RPC calls. Because multiple individual network requests may be involved in a single logical RPC operation, it must be possible to describe RPC calls as logical operations, independent from the descriptions of transport and application protocols which they contain. Examples: * A client sending a request to a remote server. From c3c7c24e86a2ed4fecdb3ff12f75da759d9ffc0b Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 13:19:09 -0700 Subject: [PATCH 09/77] CleanBaggage -> ClearBaggage --- text/0000-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index bf6cce277..b23d5bb0f 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -57,8 +57,8 @@ To access the distributed state of an application, the Baggage API provides a fu **RemoveBaggage( context, key) context** To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**CleanBaggage( context) context** -To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context. +**ClearBaggage( context) context** +To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context. **GetPropagator( type) inject, extract** To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. From 45880966ba64cf51059a603a50af3afbca3fc1c9 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 13:22:25 -0700 Subject: [PATCH 10/77] move function descriptions to new line --- text/0000-separate-context-propagation.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index b23d5bb0f..9c822194d 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -32,13 +32,13 @@ To allow for this extensibility, OpenTelemetry is separated into **application l OpenTelemetry currently contains two observability systems - Tracing and Metrics – and may be extended over time. These separate systems are bound into a unified Observability API through sharing labels – a mechanism for correlating independent observations – and through sharing propagators. -**Observe( context, labels…, observations...) context** +**Observe( context, labels…, observations...) context** The general form for all observability APIs is a function which takes a Context, label keys, and observations as input, and returns an updated Context. -**Correlate( context, label, value, hoplimit) context** +**Correlate( context, label, value, hoplimit) context** To set the label values used by all observations in the current transaction, the Observability API provides a function which takes a context, a label key, a value, and a hoplimit, and returns an updated context. If the hoplimit is set to NO_PROPAGATION, the label will only be available to observability functions in the same process. If the hoplimit is set to UNLIMITED_PROPAGATION, it will be available to all downstream services. -**GetPropagator( type) inject, extract** +**GetPropagator( type) inject, extract** To register with the propagation system, the Observability API provides a set of propagation functions for every propagation type. @@ -48,19 +48,24 @@ In addition to observability, OpenTelemetry provides a simple mechanism for prop To manage the state of a distributed application, the Baggage API provides a set of functions which read, write, and remove data. -**SetBaggage(context, key, value) context** +**SetBaggage(context, key, value) context** To record the distributed state of an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**GetBaggage( context, key) value** +**GetBaggage( context, key) value** To access the distributed state of an application, the Baggage API provides a function which takes a context and a key as input, and returns a value. -**RemoveBaggage( context, key) context** +**RemoveBaggage( context, key) context** To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. +<<<<<<< HEAD **ClearBaggage( context) context** To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context. +======= +**ClearBaggage( context) context** +To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, +>>>>>>> move function descriptions to new line -**GetPropagator( type) inject, extract** +**GetPropagator( type) inject, extract** To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. @@ -68,7 +73,7 @@ To register with the propagation system, the Baggage API provides a set of propa Because the application and context propagation layers are separated, it is possible to create new distributed applications which do not depend on either the Observability or Baggage APIs. -**GetPropagator(type) inject, extract** +**GetPropagator(type) inject, extract** To register with the propagation system, additional APIs provide a set of propagation functions for every propagation type. From 2d80daebbb74a850097d582fbcb59f2c47f6acbb Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 13:30:35 -0700 Subject: [PATCH 11/77] Add Optional subheader --- text/0000-separate-context-propagation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 9c822194d..da3385b3a 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -89,13 +89,13 @@ To record the local state of an application, the Context API provides a function **GetValue( context, key) value** To access the local state of an application, the Context API provides a function which takes a context and a key as input, and returns a value. -**Optional: Automated Context Management** +### Optional: Automated Context Management When possible, context should automatically be associated with program execution . Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. -**Optional: SetCurrentContext( context)** +**SetCurrentContext( context)** To associate a context with program execution, the Context API provides a function which takes a Context. -**Optional: GetCurrentContext() context** +**GetCurrentContext() context** To access the context associated with program execution, the Context API provides a function which takes no arguments and returns a Context. From 7a73210fb0d7a6374eb7dbf82d21abbffc77e060 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 13:35:19 -0700 Subject: [PATCH 12/77] cleanup rough edits --- text/0000-separate-context-propagation.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index da3385b3a..c18d96769 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -141,9 +141,7 @@ In order for Context to function, it must always remain bound to the execution o In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. In the cases where there is not an extremely clear pre-existing option available, OpenTelemetry should provide its own Context implementation. -While the above explanation represents the default OpenTelemetry approach to context propagation, it is important to note that some languages may already contain a form of context propagation. For example, Go has a the context.Context object, and widespread conventions for how to pass. - -Span data is used as labels for metrics and traces. This can quickly add overhead when propagated in-band. But, because this data is write-only, how this information is transmitted remains undefined. +While the specification defines the default OpenTelemetry approach to context propagation, it is important to note that in some languages, a form of context propagation may already exist. For example, Go has a the context.Context object, and widespread conventions for how to pass it down the call stack. ## Default Propagators @@ -158,7 +156,7 @@ Since Baggage Context and Correlation Context appear very similar, why have two? First and foremost, the intended uses for Baggage and Correlations are completely different. Secondly, the propagation requirements diverge significantly. -Correlations values are solely to be used as labels for metrics and traces. By making Correlation Context data write-only, how and when it is transmitted remains undefined. This leaves the door open to optimizations, such as propagating some data out-of-band, and situations where sampling decisions may cease the need to propagate correlation context any further. +Correlation values are solely to be used as labels for metrics and traces. By making Correlation data write-only, how and when it is transmitted remains undefined. This leaves the door open to optimizations, such as propagating some data out-of-band, and situations where sampling decisions may cease the need to propagate correlation context any further. Baggage values, on the other hand, are explicitly added in order to be accessed by downstream by other application code. Therefore, Baggage Context must be readable, and reliably propagated in-band in order to accomplish this goal. From 0d8e41be26baba8b3af4e46e1d708599db3518a0 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 14:04:40 -0700 Subject: [PATCH 13/77] clean up advice on pre-existing context implementations --- text/0000-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index c18d96769..96ab66535 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -139,9 +139,9 @@ In order for Context to function, it must always remain bound to the execution o ## Pre-existing Context implementations -In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. In the cases where there is not an extremely clear pre-existing option available, OpenTelemetry should provide its own Context implementation. +In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. For example, Go has a the context.Context object, and widespread conventions for how to pass it down the call stack. -While the specification defines the default OpenTelemetry approach to context propagation, it is important to note that in some languages, a form of context propagation may already exist. For example, Go has a the context.Context object, and widespread conventions for how to pass it down the call stack. +In the cases where an extremely clear, pre-existing option is not available, OpenTelemetry should provide its own Context implementation. ## Default Propagators From aad5605de488a4873904f33d1ed283ea47a16e72 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 14:16:00 -0700 Subject: [PATCH 14/77] Better context descriptions --- text/0000-separate-context-propagation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 96ab66535..3c2f054cc 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -124,11 +124,11 @@ OpenTelemetry currently contains two types of Propagators: ## Context details OpenTelemetry currently implements three context types of context propagation. -**Span Context -** Observability data used by the tracing system. The readable attributes are defined to match those found in the W3C **traceparent** header. Span Context is used as labels for metrics and traces. This can quickly add overhead when propagated in-band. But, because this data is write-only, how this information is transmitted remains undefined. +**Span Context -** The serializable portion of a span, which is injected and extracted. The readable attributes are defined to match those found in the W3C **traceparent** header. -**Correlation Context -** Transaction-level observability data, which can be applied as labels to spans and metrics. +**Correlation Context -** Correlation Context contains a map of labels and values, to be shared between metrics and traces. This allows observability data to be indexed and dimensionalized in a variety of ways. Note that correlations can quickly add overhead when propagated in-band. But because this data is write-only, it may be possible to optimize how it is transmitted. -**Baggage Context -** Transaction-level application data, meant to be shared with downstream components. +**Baggage Context -** Transaction-level application data, meant to be shared with downstream components. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size of RPC requests. Note that when possible, OpenTelemetry APIs calls are given access to the entire context object, and not a specific context type. From 4a930ebb612ec286c426b1bd8c1c6060a1908d33 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 19:54:56 -0700 Subject: [PATCH 15/77] remove data format file --- text/0000-data-formats.md | 151 -------------------------------------- 1 file changed, 151 deletions(-) delete mode 100644 text/0000-data-formats.md diff --git a/text/0000-data-formats.md b/text/0000-data-formats.md deleted file mode 100644 index c64451af0..000000000 --- a/text/0000-data-formats.md +++ /dev/null @@ -1,151 +0,0 @@ -# RFC: Data Formats for OpenTelemetry - -# Overview -OpenTelemetry is an observability system, which generates observations, and then sends that data to remote systems for analysis. Describing how this observability data is represented and transmitted is critical part of the OpenTelemetry specification. - -**Proposed Layers** -* Metadata and Semantic Conventions -* Logical Structure -* Encodings -* Exchange Protocols - - -# Metadata and Semantic Conventions - -Spans represent specific operations in and between systems. Some of these operations represent calls that use well-known protocols like HTTP or database calls. Depending on the protocol and the type of operation, additional information is needed to represent and analyze a span correctly in monitoring systems. It is also important to unify how this attribution is made in different languages. This way, the operator will not need to learn specifics of a language and telemetry collected from multi-language micro-service can still be easily correlated and cross-analyzed. - -## Requirements - - -### Handle Complex Data - -Semantic information is often complex and multi-valued. How we represent semantic data is related to how attributes are defined at the logical layer. - -* At minimum, nested lists of key/value pairs are necessary to represent them. -* Is it advantageous to also have strongly typed data structures? -* Are there issues with having strongly typed data structures in certain environment, such as dynamic languages? - - -### Compose Multiple Semantics - -Objects in OpenTelemetry may describe more than one type of operation. For example, a span may simultaneously represent a MySQL transaction and a TCP/IP request. Pieces of metadata must compose well, without losing the structure integrity of each piece or risking a conflict in meaning. - - -### Easily Extensible - -Not all semantics conventions and common operations are known at this time. It must be possible to add more semantic definitions without creating to versioning or compatibility issues with OpenTelemetry implementations. - - -## Examples of Common Operations - -#### RPC calls - -Modeling distributed transactions is a core usecase, it is common to analyze errors and latency for RPC calls. Because multiple individual network requests may be involved in a single logical RPC operation, it must be possible to describe RPC calls as logical operations, independent from the descriptions of transport and application protocols which they contain. - -Examples: -* A client sending a request to a remote server. -* A server receiving a request from a remote client. -* Logical vs physical networking: a request for “[https://www.wikipedia.org/wiki/Rabbit](https://www.wikipedia.org/wiki/Rabbit)” results in two physical HTTP requests - one 301 redirection, and another 200 okay. The logical RPC operation represents both requests together. - -#### Network protocols - -The internet suite of network protocols is thoroughly standardized, and the details of network interactions calls are critical to analysing the behavior of distributed systems. - -Examples: -* Application Protocols: HTTP/1.1, HTTP/2, gRPC -* Transport Protocols: TCP, UDP -* Internet Protocols: IPv4, IPv6 - -#### Database protocols - -Database requests often contain information which is key to root causing errors and latency. - -Examples: -* SQL: MySQL, Postrgres -* NoSQL: MongoDB - - -# Logical Structure - -OpenTelemetry observability model, defined as data structures instead of API calls. A logical layer describes the default data structures expected to be produced by OpenTelemetry APIs for transport. - -## Requirements - -### Support Metadata - -The logical structure must be able to support all of the requirements for the metadata layer. - - -### Support both an API and a Streaming Representation - -OpenTelemetry data structures must work well with the APIs and SDKs which produce them. It must be possible to stream data from services without resorting to an encoding or exchange protocol which bears little resemblance to the API. - - -# Encoding Formats - -While there is a single set of OpenTelemetry data structures, they can be encoded in a variety of serialization formats. - -## Requirements - -### Prefer pre-existing formats - -Codecs must be commonly available in all major languages. It is outside the bounds of the OpenTelemetry project to define new serialization formats. - -**Examples:** -* Protobuf -* JSON - - -# Exchange Protocols - -Exchange protocols describe how to transmit serialized OpenTelemetry data. Beyond just defining which application and transport protocol should be used, the exchange protocol defines transmission qualities such as streaming, retry, acks, backpressure, and throttling. - -## Requirements - -### Support Common Topologies - -Be suitable for use between all of the following node types: instrumented applications, telemetry backends, local agents, stand-alone collectors/forwarders. - -* Be load-balancer friendly (do not hinder re-balancing). -* Allow backpressure signalling. - - -### Be Reliable and Observable - -* Prefer transports which have high reliability of data delivery. -* When data must be dropped, have visibility into what was not delivered. - - -### Be Efficient - -* Have low CPU usage for serialization and deserialization. -* Impose minimal pressure on memory manager, including pass-through scenarios, where deserialized data is short-lived and must be serialized as-is shortly after and where such short-lived data is created and discarded at high frequency (think telemetry data forwarders). -* Support ability to efficiently modify deserialized data and serialize again to pass further. This is related but slightly different from the previous requirement. -* Ensure high throughput (within the available bandwidth) in high latency networks (e.g. scenarios where telemetry source and the backend are separated by high latency network). - ---- - -# Appendix - - -## Currently Open Issues and PRs - -* Exchange Protocol: [https://github.com/open-telemetry/opentelemetry-specification/pull/193](https://github.com/open-telemetry/opentelemetry-specification/pull/193) - - -## Prior art - -* [https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md) -* [https://github.com/open-telemetry/opentelemetry-proto/](https://github.com/open-telemetry/opentelemetry-proto/pull/21) -* [https://github.com/open-telemetry/opentelemetry-specification/tree/master/work_in_progress/typedspans](https://github.com/open-telemetry/opentelemetry-specification/tree/master/work_in_progress/typedspans) -* [https://github.com/dynatrace-innovationlab/TracingApiDatamodel](https://github.com/dynatrace-innovationlab/TracingApiDatamodel) - - -## Questions, TODOs - -* Should “span status” be at the logical or semantic layer? - * Already overlaps with some semantics, like `http.status` - * Separate PR for this -* Are transports separate from the exchange protocol? - * Supported Transport protocols, such as HTTP and UDP, may be part of the exchange protocol, or they may be a separate layer. - * [https://github.com/open-telemetry/opentelemetry-specification/pull/193#issuecomment-516325059](https://github.com/open-telemetry/opentelemetry-specification/pull/193#issuecomment-516325059) From e1ef61fb074fa1dfe1a3b836e81180c547605a4f Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 19:56:53 -0700 Subject: [PATCH 16/77] remove git diff message --- text/0000-separate-context-propagation.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 3c2f054cc..e6857210a 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -57,13 +57,8 @@ To access the distributed state of an application, the Baggage API provides a fu **RemoveBaggage( context, key) context** To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -<<<<<<< HEAD -**ClearBaggage( context) context** -To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context. -======= **ClearBaggage( context) context** To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, ->>>>>>> move function descriptions to new line **GetPropagator( type) inject, extract** To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. From f94943501d91b261d10d252cde33fa81abd2f70d Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 19:58:57 -0700 Subject: [PATCH 17/77] improved code sytnax --- text/0000-separate-context-propagation.md | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index e6857210a..ff09ec8d2 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -32,13 +32,13 @@ To allow for this extensibility, OpenTelemetry is separated into **application l OpenTelemetry currently contains two observability systems - Tracing and Metrics – and may be extended over time. These separate systems are bound into a unified Observability API through sharing labels – a mechanism for correlating independent observations – and through sharing propagators. -**Observe( context, labels…, observations...) context** +**Observe(context, labels…, observations...) -> context** The general form for all observability APIs is a function which takes a Context, label keys, and observations as input, and returns an updated Context. -**Correlate( context, label, value, hoplimit) context** +**Correlate(context, label, value, hoplimit) -> context** To set the label values used by all observations in the current transaction, the Observability API provides a function which takes a context, a label key, a value, and a hoplimit, and returns an updated context. If the hoplimit is set to NO_PROPAGATION, the label will only be available to observability functions in the same process. If the hoplimit is set to UNLIMITED_PROPAGATION, it will be available to all downstream services. -**GetPropagator( type) inject, extract** +**GetPropagator(type) -> (inject, extract)** To register with the propagation system, the Observability API provides a set of propagation functions for every propagation type. @@ -48,19 +48,19 @@ In addition to observability, OpenTelemetry provides a simple mechanism for prop To manage the state of a distributed application, the Baggage API provides a set of functions which read, write, and remove data. -**SetBaggage(context, key, value) context** +**SetBaggage(context, key, value) -> context** To record the distributed state of an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**GetBaggage( context, key) value** +**GetBaggage(context, key) -> value** To access the distributed state of an application, the Baggage API provides a function which takes a context and a key as input, and returns a value. -**RemoveBaggage( context, key) context** +**RemoveBaggage(context, key) -> context** To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**ClearBaggage( context) context** +**ClearBaggage(context) -> context** To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, -**GetPropagator( type) inject, extract** +**GetPropagator(type) -> (inject, extract)** To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. @@ -68,7 +68,7 @@ To register with the propagation system, the Baggage API provides a set of propa Because the application and context propagation layers are separated, it is possible to create new distributed applications which do not depend on either the Observability or Baggage APIs. -**GetPropagator(type) inject, extract** +**GetPropagator(type) -> (inject, extract)** To register with the propagation system, additional APIs provide a set of propagation functions for every propagation type. @@ -78,19 +78,19 @@ To register with the propagation system, additional APIs provide a set of propag Distributed applications access data in-process using a shared context object. Each distributed application sets a single key in the context, containing all of the data for that system. -**SetValue( context, key, value) context** +**SetValue(context, key, value) -> context** To record the local state of an application, the Context API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**GetValue( context, key) value** +**GetValue(context, key) -> value** To access the local state of an application, the Context API provides a function which takes a context and a key as input, and returns a value. ### Optional: Automated Context Management When possible, context should automatically be associated with program execution . Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. -**SetCurrentContext( context)** +**SetCurrentContext(context)** To associate a context with program execution, the Context API provides a function which takes a Context. -**GetCurrentContext() context** +**GetCurrentContext() -> context** To access the context associated with program execution, the Context API provides a function which takes no arguments and returns a Context. @@ -98,13 +98,13 @@ To access the context associated with program execution, the Context API provide Distributed applications send data to downstream processes via propagators, functions which read and write application context into RPC requests. Each distributed application creates a set of propagators for every type of supported medium - currently HTTP and Binary. -**Inject( context, request)** +**Inject(context, request)** To send the data for all distributed applications downstream to the next process, the Propagation API provides a function which takes a context and a request, and mutates the request to include the encoded context. The canonical representation of a request is as a map. -**Extract( context, request) context** +**Extract(context, request) -> context** To receive data injected by prior upstream processes, the Propagation API provides a function which takes a context and a request, and returns an updated context. -**RegisterPropagator( type, inject, extract)** +**RegisterPropagator(type, inject, extract)** In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. OpenTelemetry currently contains two types of Propagators: From 1cb155efd784a52d50f3ae5b0fe00983006dd654 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 20:00:40 -0700 Subject: [PATCH 18/77] stop stuttering --- text/0000-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index ff09ec8d2..3bffe9664 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -87,10 +87,10 @@ To access the local state of an application, the Context API provides a function ### Optional: Automated Context Management When possible, context should automatically be associated with program execution . Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. -**SetCurrentContext(context)** +**SetCurrent(context)** To associate a context with program execution, the Context API provides a function which takes a Context. -**GetCurrentContext() -> context** +**GetCurrent() -> context** To access the context associated with program execution, the Context API provides a function which takes no arguments and returns a Context. From 7b9e86188827c74876cd7b07a2b191acb606a7f2 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Tue, 10 Sep 2019 20:02:48 -0700 Subject: [PATCH 19/77] Update text/0000-separate-context-propagation.md Co-Authored-By: Reiley Yang --- text/0000-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 3bffe9664..0fcff1c7f 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -190,4 +190,4 @@ Given that we must ship with working propagators, and the W3C specifications are # Future possibilities -Cleanly splitting OpenTelemetry into an Application and Context Propagation layer may allow us to move the Context Propagation layer into its own, stand-alone project. This may facilitate adoption, by allowing us to share Context Propagation with gRPC and other projects. +Cleanly splitting OpenTelemetry into an Application and Context Propagation layer may allow us to move the Context Propagation layer into its own, stand-alone project. This may facilitate adoption, by allowing us to share Context Propagation with gRPC and other projects. From 07eb397257f1de112ae0b05a02f406d7de5722c8 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 10 Sep 2019 20:04:33 -0700 Subject: [PATCH 20/77] spacing --- text/0000-separate-context-propagation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 0fcff1c7f..6a29f00ae 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -7,7 +7,7 @@ Design OpenTelemetry as a set of separate applications which operate on a shared ## Motivation -Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling, and may have different requirements from other systems which require non-local information to be sent downstream. +Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling, and may have different requirements from other systems which require non-local information to be sent downstream. * Separation of concerns * Remove the Tracer dependency from context propagation mechanisms. * Separate distributed context into Baggage and Correlations @@ -23,7 +23,7 @@ Based on prior art, we know that fusing the observability system and the context OpenTelemetry is a distributed program, which requires non-local, transaction-level context in order to execute correctly. Transaction-level context can also be used to build other distributed programs, such as security, versioning, and network switching programs. -To allow for this extensibility, OpenTelemetry is separated into **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - such as the observability and baggage systems provided by OpenTelemetry - simultaneously share the same underlying context propagation system in order to execute their programs. +To allow for this extensibility, OpenTelemetry is separated into **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - such as the observability and baggage systems provided by OpenTelemetry - simultaneously share the same underlying context propagation system in order to execute their programs. # Application Layer @@ -130,7 +130,7 @@ Note that when possible, OpenTelemetry APIs calls are given access to the entire ## Context Management and in-process propagation -In order for Context to function, it must always remain bound to the execution of code it represents. By default, this means that the programmer must pass a Context down the call stack as a function parameter. However, many languages provide automated context management facilities, such as thread locals. OpenTelemetry should leverage these facilities when available, in order to provide automatic context management. +In order for Context to function, it must always remain bound to the execution of code it represents. By default, this means that the programmer must pass a Context down the call stack as a function parameter. However, many languages provide automated context management facilities, such as thread locals. OpenTelemetry should leverage these facilities when available, in order to provide automatic context management. ## Pre-existing Context implementations From 0ebeb6c52451b8dde55c97d29d0d61e78fe1b002 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Wed, 25 Sep 2019 12:50:49 -0700 Subject: [PATCH 21/77] Refine propagation * Remove registry concept * Add explicit chaining --- text/0000-separate-context-propagation.md | 40 +++++++++++++---------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/text/0000-separate-context-propagation.md b/text/0000-separate-context-propagation.md index 6a29f00ae..749271632 100644 --- a/text/0000-separate-context-propagation.md +++ b/text/0000-separate-context-propagation.md @@ -23,7 +23,7 @@ Based on prior art, we know that fusing the observability system and the context OpenTelemetry is a distributed program, which requires non-local, transaction-level context in order to execute correctly. Transaction-level context can also be used to build other distributed programs, such as security, versioning, and network switching programs. -To allow for this extensibility, OpenTelemetry is separated into **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - such as the observability and baggage systems provided by OpenTelemetry - simultaneously share the same underlying context propagation system in order to execute their programs. +To allow for this extensibility, OpenTelemetry is separated into an **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - such as the observability and baggage systems provided by OpenTelemetry - simultaneously share the same underlying context propagation system in order to execute their programs. # Application Layer @@ -38,8 +38,11 @@ The general form for all observability APIs is a function which takes a Context, **Correlate(context, label, value, hoplimit) -> context** To set the label values used by all observations in the current transaction, the Observability API provides a function which takes a context, a label key, a value, and a hoplimit, and returns an updated context. If the hoplimit is set to NO_PROPAGATION, the label will only be available to observability functions in the same process. If the hoplimit is set to UNLIMITED_PROPAGATION, it will be available to all downstream services. -**GetPropagator(type) -> (inject, extract)** -To register with the propagation system, the Observability API provides a set of propagation functions for every propagation type. +**GetHTTPExtractor() -> extractor** +To deserialize the state of the observability system in the prior upstream process, the Observability API provides a function which returns a HTTPExtract function. + +**GetHTTPInjector() -> injector** +To serialize the the current state of the observability system and send it to the next downstream process, the Observability API provides a function which returns a HTTPInject function. ## Baggage API @@ -59,17 +62,22 @@ To delete distributed state from an application, the Baggage API provides a func **ClearBaggage(context) -> context** To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, +**GetHTTPExtractor() -> extractor** +To deserialize the state of the system in the prior upstream process, the Baggage API provides a function which returns a HTTPExtract function. -**GetPropagator(type) -> (inject, extract)** -To register with the propagation system, the Baggage API provides a set of propagation functions for every propagation type. +**GetHTTPInjector() -> injector** +To serialize the the current state of the system and send it to the next downstream process, the Baggage API provides a function which returns a HTTPInject function. ## Additional APIs Because the application and context propagation layers are separated, it is possible to create new distributed applications which do not depend on either the Observability or Baggage APIs. -**GetPropagator(type) -> (inject, extract)** -To register with the propagation system, additional APIs provide a set of propagation functions for every propagation type. +**GetHTTPExtractor() -> extractor** +To deserialize the state of the system in the prior upstream process, all additional APIs provide a function which returns a HTTPExtract function. + +**GetHTTPInjector() -> injector** +To serialize the the current state of the system and send it to the next downstream process, all additional APIs provide a function which returns a HTTPInject function. # Context Propagation Layer @@ -96,21 +104,19 @@ To access the context associated with program execution, the Context API provide ## Propagation API -Distributed applications send data to downstream processes via propagators, functions which read and write application context into RPC requests. Each distributed application creates a set of propagators for every type of supported medium - currently HTTP and Binary. +Distributed applications propagate their state by data to downstream processes via injectors, functions which read and write application context into RPC requests. Each distributed application creates a set of propagators for every type of supported medium - currently only HTTP requests. -**Inject(context, request)** -To send the data for all distributed applications downstream to the next process, the Propagation API provides a function which takes a context and a request, and mutates the request to include the encoded context. The canonical representation of a request is as a map. +**HTTPInject(context, request)** +To send the data for all distributed applications downstream to the next process, the Propagation API provides a function which takes a context and an HTTP request, and mutates the HTTP request to include an HTTP Header representation of the context. -**Extract(context, request) -> context** -To receive data injected by prior upstream processes, the Propagation API provides a function which takes a context and a request, and returns an updated context. +**HTTPExtract(context, request) -> context** +To receive data injected by prior upstream processes, the Propagation API provides a function which takes a context and an HTTP request, and returns context which represents the state of the upstream system. -**RegisterPropagator(type, inject, extract)** +**ChainHTTPInjector(injector, injector) injector** In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. -OpenTelemetry currently contains two types of Propagators: - -* **HTTP** - context is written into and read from a map of HTTP headers. -* **Binary** - context is serialized into and deserialized from a stream of bytes. +**ChainHTTPExtractor(extractor, extractor) extractor** +In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. # Internal details From 147d6b05e3127a0cb435fee92026d1da675e4404 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 1 Oct 2019 00:34:40 -0700 Subject: [PATCH 22/77] Add RFC ID number from PR --- ...ontext-propagation.md => 0042-separate-context-propagation.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-separate-context-propagation.md => 0042-separate-context-propagation.md} (100%) diff --git a/text/0000-separate-context-propagation.md b/text/0042-separate-context-propagation.md similarity index 100% rename from text/0000-separate-context-propagation.md rename to text/0042-separate-context-propagation.md From 72d4651d85446bad4cbba1d00c269dab9c0867fe Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 1 Oct 2019 00:35:17 -0700 Subject: [PATCH 23/77] remove RFC status line --- text/0042-separate-context-propagation.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 749271632..1c9ae12f5 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -1,6 +1,4 @@ -# Proposal: Separate Layer for Context Propagation - -Status: `proposed` +# Proposal: Separate Layer for Context Propagation Design OpenTelemetry as a set of separate applications which operate on a shared context propagation mechanism. From 14721978c2208fbdb33f951bcd48384a1b37390b Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 1 Oct 2019 00:39:13 -0700 Subject: [PATCH 24/77] slight calrification for GetHTTPExtractor --- text/0042-separate-context-propagation.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 1c9ae12f5..e309596b4 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -37,7 +37,7 @@ The general form for all observability APIs is a function which takes a Context, To set the label values used by all observations in the current transaction, the Observability API provides a function which takes a context, a label key, a value, and a hoplimit, and returns an updated context. If the hoplimit is set to NO_PROPAGATION, the label will only be available to observability functions in the same process. If the hoplimit is set to UNLIMITED_PROPAGATION, it will be available to all downstream services. **GetHTTPExtractor() -> extractor** -To deserialize the state of the observability system in the prior upstream process, the Observability API provides a function which returns a HTTPExtract function. +To deserialize the state of the system sent from the prior upstream process, the Observability API provides a function which returns a HTTPExtract function. **GetHTTPInjector() -> injector** To serialize the the current state of the observability system and send it to the next downstream process, the Observability API provides a function which returns a HTTPInject function. @@ -60,8 +60,9 @@ To delete distributed state from an application, the Baggage API provides a func **ClearBaggage(context) -> context** To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, + **GetHTTPExtractor() -> extractor** -To deserialize the state of the system in the prior upstream process, the Baggage API provides a function which returns a HTTPExtract function. +To deserialize the state of the system sent from the the prior upstream process, the Baggage API provides a function which returns a HTTPExtract function. **GetHTTPInjector() -> injector** To serialize the the current state of the system and send it to the next downstream process, the Baggage API provides a function which returns a HTTPInject function. @@ -75,7 +76,7 @@ Because the application and context propagation layers are separated, it is poss To deserialize the state of the system in the prior upstream process, all additional APIs provide a function which returns a HTTPExtract function. **GetHTTPInjector() -> injector** -To serialize the the current state of the system and send it to the next downstream process, all additional APIs provide a function which returns a HTTPInject function. +To serialize the the current state of the system and send it to the next downstream process, all additional APIs provide a function which returns a HTTPInject function. # Context Propagation Layer From 18a37d444a55c10b7a1dc7ef2f0180b8ef7372eb Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 1 Oct 2019 01:04:16 -0700 Subject: [PATCH 25/77] add global propagators --- text/0042-separate-context-propagation.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index e309596b4..e81389786 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -117,6 +117,21 @@ In order for the application layer to function correctly, Propagation choices mu **ChainHTTPExtractor(extractor, extractor) extractor** In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. +### Optional: Global Propagators +It is often convenient to create a chain of propagators during program initialization, and then access these combined propagators later in the program. To facilitate this, global injectors and extractors are optionally available. However, there is no requirement to use this feature. + +**SetHTTPInjector(injector)** +To update the global injector, the Propagation API provides a function which takes an injector. + +**GetHTTPInjector() -> injector** +To access the global injector, the Propagation API provides a function which returns an injector. + +**SetHTTPExtractor(extractor)** +To update the global extractor, the Propagation API provides a function which takes an injector. + +**GetHTTPExtractor() -> extractor** +To access the global extractor, the Propagation API provides a function which returns an extractor. + # Internal details ![drawing](img/context_propagation_details.png) From 7ea1834f5a3cf739b1414954f339d7764af0b6a7 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 19:33:34 -0700 Subject: [PATCH 26/77] Clean up motivation --- text/0042-separate-context-propagation.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index e81389786..6542aedc0 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -5,13 +5,16 @@ Design OpenTelemetry as a set of separate applications which operate on a shared ## Motivation -Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling, and may have different requirements from other systems which require non-local information to be sent downstream. -* Separation of concerns - * Remove the Tracer dependency from context propagation mechanisms. - * Separate distributed context into Baggage and Correlations -* Extensibility - * Allow users to create new applications for context propagation. - * For example: A/B testing, encrypted or authenticated data, and new, experimental forms of observability. +Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling. This can create difficulty for other systems, which may want to leverage the same context propagation mechanism, but have different rules and requirements regarding the data they are sending. The Baggage system within OpenTelemetry is one such example. + +This RFC addresses the following topics: + +**Separatation of concerns** +* Remove the Tracer dependency from context propagation mechanisms. +* Handle user data (Baggage) and observability data (Correlations) seprately. + +**Extensibility** +* Allow users to create new applications for context propagation. For example: A/B testing, encrypted or authenticated data, and new, experimental forms of observability. ## Explanation From 73177470a969ef0043b12d49e715d2074ce98e5b Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 19:48:21 -0700 Subject: [PATCH 27/77] Clean up explanbation intro --- text/0042-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 6542aedc0..6752ff4dd 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -22,9 +22,9 @@ This RFC addresses the following topics: ![drawing](img/context_propagation_explanation.png) -OpenTelemetry is a distributed program, which requires non-local, transaction-level context in order to execute correctly. Transaction-level context can also be used to build other distributed programs, such as security, versioning, and network switching programs. +Distributed tracing is an example of a cross-cutting concern, which requires non-local, transaction-level context propagation in order to execute correctly. Transaction-level context propagation can also be useful for other cross-cutting concerns, e.g., for security, versioning, and network switching. We refer to these types of cross-cutting concerns as **distributed applications**. -To allow for this extensibility, OpenTelemetry is separated into an **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - such as the observability and baggage systems provided by OpenTelemetry - simultaneously share the same underlying context propagation system in order to execute their programs. +OpenTelemetry is separated into an **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - including the observability and baggage systems provided by OpenTelemetry - share the same underlying context propagation system. # Application Layer From 43ba8fd36a1148713ab1f7954cee71898d0487d1 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 21:50:00 -0700 Subject: [PATCH 28/77] Clarify context types --- text/0042-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 6752ff4dd..3ad53424a 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -95,7 +95,7 @@ To record the local state of an application, the Context API provides a function To access the local state of an application, the Context API provides a function which takes a context and a key as input, and returns a value. ### Optional: Automated Context Management -When possible, context should automatically be associated with program execution . Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. +When possible, the OpenTelemetry context should automatically be associated with the program execution context. Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. **SetCurrent(context)** To associate a context with program execution, the Context API provides a function which takes a Context. From d7d6f1c70a525249eb262d270a0e0530b0a9953a Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 22:12:41 -0700 Subject: [PATCH 29/77] Fix ChainHTTPInjector and ChainHTTPExtractor --- text/0042-separate-context-propagation.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 3ad53424a..92c4f19e5 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -114,11 +114,12 @@ To send the data for all distributed applications downstream to the next process **HTTPExtract(context, request) -> context** To receive data injected by prior upstream processes, the Propagation API provides a function which takes a context and an HTTP request, and returns context which represents the state of the upstream system. -**ChainHTTPInjector(injector, injector) injector** -In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. +**ChainHTTPInjector(injector, injector) -> injector** +To allow multiple distributed applications to inject their context into the same request, the Propagation API provides a function which takes two injectors, and returns a single injector which calls the two original injectors in order. + +**ChainHTTPExtractor(extractor, extractor) -> extractor** +To allow multiple distributed applications to extract their context from the same request, the Propagation API provides a function which takes two extractors, and returns a single extractor which calls the two original extractors in order. -**ChainHTTPExtractor(extractor, extractor) extractor** -In order for the application layer to function correctly, Propagation choices must be syncronized between all processes in the distributed system, and multiple applications must be able to inject and extract their context into the same request. To meet these requirements, the Propagation API provides a function which registers a set of propagators, which will all be executed in order when the future calls to inject and extract are made. A canonical propagator consists of an inject and an extract function. ### Optional: Global Propagators It is often convenient to create a chain of propagators during program initialization, and then access these combined propagators later in the program. To facilitate this, global injectors and extractors are optionally available. However, there is no requirement to use this feature. From 3a817a2d1b3afe7f0215854ed7e2a6d051e1642a Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 22:25:58 -0700 Subject: [PATCH 30/77] typo --- text/0042-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 92c4f19e5..42eb447f0 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -131,7 +131,7 @@ To update the global injector, the Propagation API provides a function which tak To access the global injector, the Propagation API provides a function which returns an injector. **SetHTTPExtractor(extractor)** -To update the global extractor, the Propagation API provides a function which takes an injector. +To update the global extractor, the Propagation API provides a function which takes an extractor. **GetHTTPExtractor() -> extractor** To access the global extractor, the Propagation API provides a function which returns an extractor. From 3381e0f32a4e2468d663500a49e6729b79a3122b Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 22:27:06 -0700 Subject: [PATCH 31/77] Reference Trace-Context, not just traceparent --- text/0042-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 42eb447f0..425e83bb2 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -143,7 +143,7 @@ To access the global extractor, the Propagation API provides a function which re ## Context details OpenTelemetry currently implements three context types of context propagation. -**Span Context -** The serializable portion of a span, which is injected and extracted. The readable attributes are defined to match those found in the W3C **traceparent** header. +**Span Context -** The serializable portion of a span, which is injected and extracted. The readable attributes are defined to match those found in the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). **Correlation Context -** Correlation Context contains a map of labels and values, to be shared between metrics and traces. This allows observability data to be indexed and dimensionalized in a variety of ways. Note that correlations can quickly add overhead when propagated in-band. But because this data is write-only, it may be possible to optimize how it is transmitted. From c15a1077cb65b439c67cad5374140e3b0ccd09d5 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 22:27:58 -0700 Subject: [PATCH 32/77] Bagge context cleanup --- text/0042-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 425e83bb2..e1bcbbebe 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -147,7 +147,7 @@ OpenTelemetry currently implements three context types of context propagation. **Correlation Context -** Correlation Context contains a map of labels and values, to be shared between metrics and traces. This allows observability data to be indexed and dimensionalized in a variety of ways. Note that correlations can quickly add overhead when propagated in-band. But because this data is write-only, it may be possible to optimize how it is transmitted. -**Baggage Context -** Transaction-level application data, meant to be shared with downstream components. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size of RPC requests. +**Baggage -** Transaction-level application data, meant to be shared with downstream components. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size of all downstream requests. Note that when possible, OpenTelemetry APIs calls are given access to the entire context object, and not a specific context type. From 310e8d589eba7658b24a8b53a4ca22162e2a5a33 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 22:31:39 -0700 Subject: [PATCH 33/77] stronger language around context access --- text/0042-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index e1bcbbebe..50f4916e9 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -149,7 +149,7 @@ OpenTelemetry currently implements three context types of context propagation. **Baggage -** Transaction-level application data, meant to be shared with downstream components. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size of all downstream requests. -Note that when possible, OpenTelemetry APIs calls are given access to the entire context object, and not a specific context type. +Note that OpenTelemetry APIs calls should *always* be given access to the entire context object, and never just a subset of the context, such as the value in a single key. This allows the SDK to make improvements and leverage additional data that may be available, without changes to all of the call sites. ## Context Management and in-process propagation From f59fc27eb9083a0a1bde50662d5805b8428a223e Mon Sep 17 00:00:00 2001 From: Ted Young Date: Mon, 14 Oct 2019 22:34:02 -0700 Subject: [PATCH 34/77] Update text/0042-separate-context-propagation.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Christian Neumüller --- text/0042-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 50f4916e9..5de766132 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -158,7 +158,7 @@ In order for Context to function, it must always remain bound to the execution o ## Pre-existing Context implementations -In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. For example, Go has a the context.Context object, and widespread conventions for how to pass it down the call stack. +In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. For example, Go has a the `context.Context` object, and widespread conventions for how to pass it down the call stack. In the cases where an extremely clear, pre-existing option is not available, OpenTelemetry should provide its own Context implementation. From 153b9aadf50581ad2875dbb38b156bac63e07e0b Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 14 Oct 2019 23:32:41 -0700 Subject: [PATCH 35/77] clean up tradeoffs --- text/0042-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index 5de766132..b05557952 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -179,9 +179,9 @@ Correlation values are solely to be used as labels for metrics and traces. By ma Baggage values, on the other hand, are explicitly added in order to be accessed by downstream by other application code. Therefore, Baggage Context must be readable, and reliably propagated in-band in order to accomplish this goal. -There may be cases where a key-value pair is propagated as TagMap for observability and as a Baggage for application specific use. AB testing is one example of such use case. There is potential duplication here at call site where a pair is created and also at propagation. +There may be cases where a key-value pair is propagated as a Correlation for observability and as a Baggage item for application-specific use. AB testing is one example of such use case. This would result in extra overhead, as the same key-value pair would be present in two separate headers. -Solving this issue is not worth having semantic confusion with dual purpose. However, because all observability functions take the complete context as input, it may still be possible to use baggage values as labels. +Solving this issue is not worth having semantic confusion with dual purpose. However, because all observability functions take the complete context as input – and baggage is not sampled – it may still be possible to use baggage values as labels for observability. ## What about complex propagation behavior? From 10e7d60459a02ba0e6c0b58f0f7f2379e758d40a Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 15 Nov 2019 00:13:11 -0800 Subject: [PATCH 36/77] v2.0 of this OTEP --- text/0042-separate-context-propagation.md | 459 +++++++++++++++++----- 1 file changed, 364 insertions(+), 95 deletions(-) diff --git a/text/0042-separate-context-propagation.md b/text/0042-separate-context-propagation.md index b05557952..5cd18063b 100644 --- a/text/0042-separate-context-propagation.md +++ b/text/0042-separate-context-propagation.md @@ -1,170 +1,415 @@ # Proposal: Separate Layer for Context Propagation -Design OpenTelemetry as a set of separate applications which operate on a shared context propagation mechanism. - +Refactor OpenTelemetry into a set of separate cross-cutting concerns which +operate on a shared context propagation mechanism. ## Motivation -Based on prior art, we know that fusing the observability system and the context propagation system together creates issues. Observability systems have special rules for propagating information, such as sampling. This can create difficulty for other systems, which may want to leverage the same context propagation mechanism, but have different rules and requirements regarding the data they are sending. The Baggage system within OpenTelemetry is one such example. - This RFC addresses the following topics: **Separatation of concerns** -* Remove the Tracer dependency from context propagation mechanisms. -* Handle user data (Baggage) and observability data (Correlations) seprately. +* Remove the Tracer dependency from context propagation mechanisms. +* Handle user data (Baggage) and observability data (SpanContext, Correlations) + seperately. **Extensibility** -* Allow users to create new applications for context propagation. For example: A/B testing, encrypted or authenticated data, and new, experimental forms of observability. +* Allow developers to create new applications for context propagation. For + example: A/B testing, encrypted or authenticated data, and new, experimental + forms of observability. ## Explanation # OpenTelemetry Layered Architecture -![drawing](img/context_propagation_explanation.png) - -Distributed tracing is an example of a cross-cutting concern, which requires non-local, transaction-level context propagation in order to execute correctly. Transaction-level context propagation can also be useful for other cross-cutting concerns, e.g., for security, versioning, and network switching. We refer to these types of cross-cutting concerns as **distributed applications**. +The design of OpenTelemetry is based on the priciples of aspect-oriented +programming, adopted to the needs of distributed systems. -OpenTelemetry is separated into an **application layer** and a **context propagation layer**. In this architecture, multiple distributed applications - including the observability and baggage systems provided by OpenTelemetry - share the same underlying context propagation system. +OpenTelemetry is separated into two layers: **aspects** which crosscut and +intertwine with a program's functional concerns, and cannot be encapsulated. In +this architecture, each cross-cutting concern is modeled as an independent +subsystem. Multiple aspects - including the tracing and baggage systems provided +by OpenTelemetry - then share the same underlying **context propagation** +system, which allows these cross-cutting concerns to store and access their +data across the lifespan of a distributed transaction. - -# Application Layer +# Aspects ## Observability API +Distributed tracing is one example of an aspect. Tracing code is interleaved +with regular code, and ties together indepentent code modules which would +otherwise remain encapsulated. Tracing is also distributed, and requires +non-local, transaction-level context propagation in order to execute correctly. -OpenTelemetry currently contains two observability systems - Tracing and Metrics – and may be extended over time. These separate systems are bound into a unified Observability API through sharing labels – a mechanism for correlating independent observations – and through sharing propagators. - -**Observe(context, labels…, observations...) -> context** -The general form for all observability APIs is a function which takes a Context, label keys, and observations as input, and returns an updated Context. - -**Correlate(context, label, value, hoplimit) -> context** -To set the label values used by all observations in the current transaction, the Observability API provides a function which takes a context, a label key, a value, and a hoplimit, and returns an updated context. If the hoplimit is set to NO_PROPAGATION, the label will only be available to observability functions in the same process. If the hoplimit is set to UNLIMITED_PROPAGATION, it will be available to all downstream services. - -**GetHTTPExtractor() -> extractor** -To deserialize the state of the system sent from the prior upstream process, the Observability API provides a function which returns a HTTPExtract function. - -**GetHTTPInjector() -> injector** -To serialize the the current state of the observability system and send it to the next downstream process, the Observability API provides a function which returns a HTTPInject function. +A second observability aspect is correlations. Correlations are labels for +metrics, where the value of the label may be defined anywhere in the +transaction. Correlations are like Baggage, but are write only - the values can +only be used for observability. +The observability APIs are not described here directly. However, in this new +design, all observability APIs would be modified to make use of the generalized +context propagation mechanism described below, rather than the tracing-specific +propagation system it uses today. ## Baggage API -In addition to observability, OpenTelemetry provides a simple mechanism for propagating arbitrary data, called Baggage. This allows new distributed applications to be implemented without having to create new propagators. +In addition to observability, OpenTelemetry provides a simple mechanism for +propagating arbitrary user data, called Baggage. This mechanism is not related +to tracing or observability, but it uses the same context propagation layer. -To manage the state of a distributed application, the Baggage API provides a set of functions which read, write, and remove data. +Baggage may be used to model new aspects which would benefit from the same +transaction-level context as tracing, e.g., for identity, versioning, and +network switching. + +To manage the state of these cross-cutting concerns, the Baggage API provides a +set of functions which read, write, and propagate data. **SetBaggage(context, key, value) -> context** -To record the distributed state of an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. +To record the distributed state of an aspect, the Baggage API provides a +function which takes a context, a key, and a value as input, and returns an +updated context which contains the new value. **GetBaggage(context, key) -> value** -To access the distributed state of an application, the Baggage API provides a function which takes a context and a key as input, and returns a value. +To access the distributed state of an aspect, the Baggage API provides a +function which takes a context and a key as input, and returns a value. **RemoveBaggage(context, key) -> context** -To delete distributed state from an application, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. +To delete distributed state from an aspect, the Baggage API provides a function +which takes a context, a key, and a value as input, and returns an updated +context which contains the new value. **ClearBaggage(context) -> context** -To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function remove all baggage from a context, - -**GetHTTPExtractor() -> extractor** -To deserialize the state of the system sent from the the prior upstream process, the Baggage API provides a function which returns a HTTPExtract function. - -**GetHTTPInjector() -> injector** -To serialize the the current state of the system and send it to the next downstream process, the Baggage API provides a function which returns a HTTPInject function. +To avoid sending baggage to an untrusted downstream process, the Baggage API +provides a function remove all baggage from a context. +**GetBaggageHTTPPropagator() -> (HTTPExtract, HTTPInject)** +To deserialize the state of the system sent from the the prior upstream process, +and to serialize the the current state of the system and send it to the next +downstream process, the Baggage API provides a function which returns a +baggage-specific implementation of the HTTPExtract and HTTPInject functions. -## Additional APIs - -Because the application and context propagation layers are separated, it is possible to create new distributed applications which do not depend on either the Observability or Baggage APIs. - -**GetHTTPExtractor() -> extractor** -To deserialize the state of the system in the prior upstream process, all additional APIs provide a function which returns a HTTPExtract function. - -**GetHTTPInjector() -> injector** -To serialize the the current state of the system and send it to the next downstream process, all additional APIs provide a function which returns a HTTPInject function. - - -# Context Propagation Layer +# Context Propagation ## Context API -Distributed applications access data in-process using a shared context object. Each distributed application sets a single key in the context, containing all of the data for that system. +Aspects access data in-process using a shared context object. Each aspect uses +its own namespaced set of keys in the context, containing all of the data for +that cross-cutting concern. **SetValue(context, key, value) -> context** -To record the local state of an application, the Context API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. +To record the local state of an aspect, the Context API provides a function +which takes a context, a key, and a value as input, and returns an updated +context which contains the new value. **GetValue(context, key) -> value** -To access the local state of an application, the Context API provides a function which takes a context and a key as input, and returns a value. +To access the local state of an aspect, the Context API provides a function +which takes a context and a key as input, and returns a value. ### Optional: Automated Context Management -When possible, the OpenTelemetry context should automatically be associated with the program execution context. Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. +When possible, the OpenTelemetry context should automatically be associated +with the program execution context. Note that some languages do not provide any +facility for setting and getting a current context. In these cases, the user is +responsible for managing the current context. **SetCurrent(context)** -To associate a context with program execution, the Context API provides a function which takes a Context. +To associate a context with program execution, the Context API provides a +function which takes a Context. **GetCurrent() -> context** -To access the context associated with program execution, the Context API provides a function which takes no arguments and returns a Context. +To access the context associated with program execution, the Context API +provides a function which takes no arguments and returns a Context. ## Propagation API -Distributed applications propagate their state by data to downstream processes via injectors, functions which read and write application context into RPC requests. Each distributed application creates a set of propagators for every type of supported medium - currently only HTTP requests. - -**HTTPInject(context, request)** -To send the data for all distributed applications downstream to the next process, the Propagation API provides a function which takes a context and an HTTP request, and mutates the HTTP request to include an HTTP Header representation of the context. +Aspects send their state to downstream processes via propagators: +functions which read and write context into RPC requests. Each aspect creates a +set of propagators for every type of supported medium - currently only HTTP +requests. **HTTPExtract(context, request) -> context** -To receive data injected by prior upstream processes, the Propagation API provides a function which takes a context and an HTTP request, and returns context which represents the state of the upstream system. +To receive data injected by prior upstream processes, the Propagation API +provides a function which takes a context and an HTTP request, and returns +context which represents the state of the upstream system. + +**HTTPInject(context, request)** +To send the data for all aspects downstream to the next process, the +Propagation API provides a function which takes a context and an HTTP request, +and mutates the HTTP request to include an HTTP Header representation of the +context. **ChainHTTPInjector(injector, injector) -> injector** -To allow multiple distributed applications to inject their context into the same request, the Propagation API provides a function which takes two injectors, and returns a single injector which calls the two original injectors in order. +To allow multiple aspects to inject their context into the same request, the +Propagation API provides a function which takes two injectors, and returns a +single injector which calls the two original injectors in order. **ChainHTTPExtractor(extractor, extractor) -> extractor** -To allow multiple distributed applications to extract their context from the same request, the Propagation API provides a function which takes two extractors, and returns a single extractor which calls the two original extractors in order. - +To allow multiple aspects to extract their context from the same request, the +Propagation API provides a function which takes two extractors, and returns a +single extractor which calls the two original extractors in order. ### Optional: Global Propagators -It is often convenient to create a chain of propagators during program initialization, and then access these combined propagators later in the program. To facilitate this, global injectors and extractors are optionally available. However, there is no requirement to use this feature. - -**SetHTTPInjector(injector)** -To update the global injector, the Propagation API provides a function which takes an injector. - -**GetHTTPInjector() -> injector** -To access the global injector, the Propagation API provides a function which returns an injector. +It is often convenient to create a chain of propagators during program +initialization, and then access these combined propagators later in the program. +To facilitate this, global injectors and extractors are optionally available. +However, there is no requirement to use this feature. **SetHTTPExtractor(extractor)** -To update the global extractor, the Propagation API provides a function which takes an extractor. +To update the global extractor, the Propagation API provides a function which +takes an extractor. **GetHTTPExtractor() -> extractor** -To access the global extractor, the Propagation API provides a function which returns an extractor. +To access the global extractor, the Propagation API provides a function which +returns an extractor. + +**SetHTTPInjector(injector)** +To update the global injector, the Propagation API provides a function which +takes an injector. + +**GetHTTPInjector() -> injector** +To access the global injector, the Propagation API provides a function which +returns an injector. + +## Prototypes + +**Go:** https://github.com/open-telemetry/opentelemetry-go/pull/297 +**Java:** https://github.com/open-telemetry/opentelemetry-java/pull/655 +**Python:** https://github.com/open-telemetry/opentelemetry-python/pull/278 +**Ruby:** https://github.com/open-telemetry/opentelemetry-ruby/pull/147 + +## Examples + +It might be helpful to look at an example, written in pseudocode. Let's describe +a simple scenario, where `service A` responds to an HTTP request from a `client` +with the result of a request to `service B`. + +``` +client -> service A -> service B +``` + +Now, let's assume the `client` in the above system is version 1.0. With version +v2.0 of the `client`, `service A` must call `service C` instead of `service B` +in order to return the correct data. + +``` +client -> service A -> service C +``` + +In , we would like `service A` to decide on which backend service to call, +based on the client version. We would also like to trace the entire system, in +order to understand if requests to `service C` are slower or faster than +`service B`. What might `service A` look like? + +First, during program initialization, `service A` might set a global +extractor and injector which chains together baggage and tracing +propagation. Let's assume this tracing system is configured to use B3, +and has a specific propagator for that format. Initializating the propagators +might look like this: + +``` +func Init { + // create the propagators + bagExtract, bagInject = Baggage::HTTPPropagator() + traceExtract, traceInject = Tracer::B3Propagator() + + // chain the propagators together and make them globally available. + extract = Propagation::ChainHTTPExtractor(bagExtract,traceExtract) + inject = Propagation::ChainHTTPInjector(bagInject,traceInject) + + Propagation::SetHTTPExtractor(extract) + Propagation::SetHTTPInjector(inject) +} +``` + +These propagators can then be used in the request handler for `service A`. The +tracing and baggage aspects use the context object to handle state without +breaking the encapsulation of the functions they are embedded in. + +``` +func HandleUpstreamRequest(context, request, project) -> (context) { + // Extract the span context. Because the extractors have been chained, + // both a span context and any upstream baggage have been extracted + // from the request headers into the returned context. + extract = Propagation::GetHTTPExtractor() + context = extract(context, request.Headers) + + // Start a span, setting the parent to the span context received from + // the upstream. The new span will then be in the returned context. + context = Tracer::StartSpan(context, [span options]) + + version = Baggage::GetBaggage( context, "client-version") + + switch( version ){ + case "v1.0": + data, context = FetchDataFromServiceB(context) + case "v2.0": + data, context = FetchDataFromServiceC(context) + } + + context = request.Response(context, data) + + // End the current span + context = Tracer::EndSpan(context) + + return context +} + +func FetchDataFromServiceB(context) -> (context, data) { + request = NewRequest([request options]) + + // Inject the contexts to be propagated. Note that there is no direct + // reference to tracing or baggage. + inject = Propagation::GetHTTPInjector() + context = inject(context, request.Headers) + + // make an http request + data = request.Do() + + return data +} +``` + +In this version of pseudocode above, we assume that the context object is +explict,and is pass and returned from every function as an ordinary parameter. +This is cumbersome, and in many languages, a mechanism exists which allows +context to be propagated automatically. + +In this version of pseudocode, assume that the current context can be stored as +a thread local, and is implicitly passed to and returned from every function. + +``` +func HandleUpstreamRequest(request, project) { + extract = Propagation::GetHTTPExtractor() + extractor(request.Headers) + + Tracer::StartSpan([span options]) + + version = Baggage::GetBaggage("client-version") + + switch( version ){ + case "v1.0": + data = FetchDataFromServiceB() + case "v2.0": + data = FetchDataFromServiceC() + } + + request.Response(data) + + Tracer::EndSpan() +} + +func FetchDataFromServiceB() -> (data) { + request = newRequest([request options]) + + inject = Propagation::GetHTTPInjector() + inject(request.Headers) + + data = request.Do() + + return data +} +``` + +Digging into the details of the tracing system, what might some of the details +look like? Here is a crude example of extracting and injecting B3 headers, +using an explicit context. + +``` + B3Extractor(context, headers) -> (context) { + context = Context::SetValue( context, + "trace.parentTraceID", + headers["X-B3-TraceId"]) + context = Context::SetValue( context, + "trace.parentSpanID", + headers["X-B3-SpanId"]) + return context + } + + B3Injector(context, headers) -> (context) { + headers["X-B3-TraceId"] = Context::GetValue( context, "trace.parentTraceID") + headers["X-B3-SpanId"] = Context::GetValue( context, "trace.parentSpanID") + + return context + } +``` + +Now, have a look at a crude example of how StartSpan might then make use of the +context. Note that this code must know the internal details about the context +keys in which the propagators above store their data. For this pseudocode, let's +assume again that the context is passed implicitly in a thread local. + +``` + StartSpan(options) { + spanData = newSpanData() + + spanData.parentTraceID = Context::GetValue( "trace.parentTraceID") + spanData.parentSpanID = Context::GetValue( "trace.parentSpanID") + + spanData.traceID = newTraceID() + spanData.spanID = newSpanID() + + Context::SetValue( "trace.parentTraceID", spanData.traceID) + Context::SetValue( "trace.parentSpanID", spanData.spanID) + + // store the spanData object as well, for in-process propagation. Note that + this key will not be propagated downstream. + Context::SetValue( "trace.currentSpanData", spanData) + + return + } +``` # Internal details ![drawing](img/context_propagation_details.png) ## Context details -OpenTelemetry currently implements three context types of context propagation. +OpenTelemetry currently intends to implement three context types of context +propagation. -**Span Context -** The serializable portion of a span, which is injected and extracted. The readable attributes are defined to match those found in the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). +**Span Context -** The serializable portion of a span, which is injected and +extracted. The readable attributes are defined to match those found in the +[W3C Trace Context specification](https://www.w3.org/TR/trace-context/). -**Correlation Context -** Correlation Context contains a map of labels and values, to be shared between metrics and traces. This allows observability data to be indexed and dimensionalized in a variety of ways. Note that correlations can quickly add overhead when propagated in-band. But because this data is write-only, it may be possible to optimize how it is transmitted. +**Correlation Context -** Correlation Context contains a map of labels and +values, to be shared between metrics and traces. This allows observability data +to be indexed and dimensionalized in a variety of ways. Note that correlations +can quickly add overhead when propagated in-band. But because this data is +write-only, it may be possible to optimize how it is transmitted. -**Baggage -** Transaction-level application data, meant to be shared with downstream components. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size of all downstream requests. +**Baggage -** Transaction-level application data, meant to be shared with +downstream components. This data is readable, and must be propagated in-band. +Because of this, Baggage should be used sparingly, to avoid ballooning the size +of all downstream requests. -Note that OpenTelemetry APIs calls should *always* be given access to the entire context object, and never just a subset of the context, such as the value in a single key. This allows the SDK to make improvements and leverage additional data that may be available, without changes to all of the call sites. +Note that OpenTelemetry APIs calls should *always* be given access to the entire +context object, and never just a subset of the context, such as the value in a +single key. This allows the SDK to make improvements and leverage additional +data that may be available, without changes to all of the call sites. ## Context Management and in-process propagation -In order for Context to function, it must always remain bound to the execution of code it represents. By default, this means that the programmer must pass a Context down the call stack as a function parameter. However, many languages provide automated context management facilities, such as thread locals. OpenTelemetry should leverage these facilities when available, in order to provide automatic context management. +In order for Context to function, it must always remain bound to the execution +of code it represents. By default, this means that the programmer must pass a +Context down the call stack as a function parameter. However, many languages +provide automated context management facilities, such as thread locals. +OpenTelemetry should leverage these facilities when available, in order to +provide automatic context management. ## Pre-existing Context implementations -In some languages, a single, widely used Context implementation exists. In other languages, there many be too many implementations, or none at all. For example, Go has a the `context.Context` object, and widespread conventions for how to pass it down the call stack. +In some languages, a single, widely used Context implementation exists. In other +languages, there many be too many implementations, or none at all. For example, +Go has a the `context.Context` object, and widespread conventions for how to +pass it down the call stack. -In the cases where an extremely clear, pre-existing option is not available, OpenTelemetry should provide its own Context implementation. +In the cases where an extremely clear, pre-existing option is not available, +OpenTelemetry should provide its own Context implementation. ## Default Propagators -When available, OpenTelemetry defaults to propagating via HTTP header definitions which have been standardized by the W3C. +When available, OpenTelemetry defaults to propagating via HTTP header +definitions which have been standardized by the W3C. # Trade-offs and mitigations @@ -173,26 +418,43 @@ When available, OpenTelemetry defaults to propagating via HTTP header definition Since Baggage Context and Correlation Context appear very similar, why have two? -First and foremost, the intended uses for Baggage and Correlations are completely different. Secondly, the propagation requirements diverge significantly. +First and foremost, the intended uses for Baggage and Correlations are +completely different. Secondly, the propagation requirements diverge +significantly. -Correlation values are solely to be used as labels for metrics and traces. By making Correlation data write-only, how and when it is transmitted remains undefined. This leaves the door open to optimizations, such as propagating some data out-of-band, and situations where sampling decisions may cease the need to propagate correlation context any further. +Correlation values are solely to be used as labels for metrics and traces. By +making Correlation data write-only, how and when it is transmitted remains +undefined. This leaves the door open to optimizations, such as propagating the +correlation data out-of-band. -Baggage values, on the other hand, are explicitly added in order to be accessed by downstream by other application code. Therefore, Baggage Context must be readable, and reliably propagated in-band in order to accomplish this goal. +Baggage values, on the other hand, are explicitly added in order to be accessed +by downstream by other application code. Therefore, Baggage Context must be +readable, and reliably propagated in-band in order to accomplish this goal. -There may be cases where a key-value pair is propagated as a Correlation for observability and as a Baggage item for application-specific use. AB testing is one example of such use case. This would result in extra overhead, as the same key-value pair would be present in two separate headers. - -Solving this issue is not worth having semantic confusion with dual purpose. However, because all observability functions take the complete context as input – and baggage is not sampled – it may still be possible to use baggage values as labels for observability. +There may be cases where a key-value pair is propagated as a Correlation for +observability and as a Baggage item for application-specific use. AB testing is +one example of such use case. This would result in extra overhead, as the same +key-value pair would be present in two separate headers. +Solving this edge case is not worth having the semantic confusion of a single +implementation with a dual purpose. ## What about complex propagation behavior? -Some OpenTelemetry proposals have called for more complex propagation behavior. For example, falling back to extracting B3 headers if W3C Trace-Context headers are not found. Chained propagators and other complex behavior can be modeled as implementation details behind the Propagator interface. Therefore, the propagation system itself does not need to provide chained propagators or other additional facilities. +Some OpenTelemetry proposals have called for more complex propagation behavior. +For example, falling back to extracting B3 headers if W3C Trace-Context headers +are not found. Chained propagators and other complex behavior can be modeled as +implementation details behind the Propagator interface. Therefore, the +propagation system itself does not need to provide chained propagators or other +additional facilities. ## Did you add a context parameter to every API call because Go has infected your brain? -No. The concept of an explicit context is fundamental to a model where independent distributed applications share the same context propagation layer. How this context appears or is expressed is language specific, but it must be present in some form. - +No. The concept of an explicit context is fundamental to a model where +independent cross-cutting concerns share the same context propagation layer. +How this context appears or is expressed is language specific, but it must be +present in some form. # Prior art and alternatives @@ -206,12 +468,19 @@ Prior art: Related work on HTTP propagators has not been completed yet. -* [W3C Trace-Context](https://www.w3.org/TR/trace-context/) candidate is not yet accepted -* Work on [W3C Correlation-Context](https://w3c.github.io/correlation-context/) has begun, but was halted to focus on Trace-Context. +* [W3C Trace-Context](https://www.w3.org/TR/trace-context/) candidate is not yet + accepted. +* Work on [W3C Correlation-Context](https://w3c.github.io/correlation-context/) + has begun, but was halted to focus on Trace-Context. * No work has begun on a theoretical W3C Baggage-Context. -Given that we must ship with working propagators, and the W3C specifications are not yet complete, how should we move forwards with implementing context propagation? +Given that we must ship with working propagators, and the W3C specifications are +not yet complete, how should we move forwards with implementing context +propagation? # Future possibilities -Cleanly splitting OpenTelemetry into an Application and Context Propagation layer may allow us to move the Context Propagation layer into its own, stand-alone project. This may facilitate adoption, by allowing us to share Context Propagation with gRPC and other projects. +Cleanly splitting OpenTelemetry into Apects and Context Propagation layer may +allow us to move the Context Propagation layer into its own, stand-alone +project. This may facilitate adoption, by allowing us to share Context +Propagation with gRPC and other projects. From 40a3bd2ba306cc04367d9002082200949f32273c Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 15 Nov 2019 01:47:08 -0800 Subject: [PATCH 37/77] Update OTEP number for new submission * remove image file for unused diagram --- ....md => 0066-separate-context-propagation.md} | 0 text/img/context_propagation_explanation.png | Bin 43406 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) rename text/{0042-separate-context-propagation.md => 0066-separate-context-propagation.md} (100%) delete mode 100644 text/img/context_propagation_explanation.png diff --git a/text/0042-separate-context-propagation.md b/text/0066-separate-context-propagation.md similarity index 100% rename from text/0042-separate-context-propagation.md rename to text/0066-separate-context-propagation.md diff --git a/text/img/context_propagation_explanation.png b/text/img/context_propagation_explanation.png deleted file mode 100644 index dbba79a6c850ca0b4325db44ed341e6301578bda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43406 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 From 5151d3f341fdf51833495342b3bd2e625470ee9d Mon Sep 17 00:00:00 2001 From: Ted Young Date: Fri, 15 Nov 2019 10:39:08 -0800 Subject: [PATCH 38/77] Update text/0066-separate-context-propagation.md Co-Authored-By: alrex --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 5cd18063b..64111bf2a 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -10,7 +10,7 @@ This RFC addresses the following topics: **Separatation of concerns** * Remove the Tracer dependency from context propagation mechanisms. * Handle user data (Baggage) and observability data (SpanContext, Correlations) - seperately. + separately. **Extensibility** * Allow developers to create new applications for context propagation. For From bbbe41ebe2ce38843e4ee28422ba46e35f2bb178 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 15 Nov 2019 17:43:53 -0800 Subject: [PATCH 39/77] Link to Erlang prototype --- text/0066-separate-context-propagation.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 64111bf2a..c930a0403 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -171,6 +171,7 @@ returns an injector. ## Prototypes +**Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 **Go:** https://github.com/open-telemetry/opentelemetry-go/pull/297 **Java:** https://github.com/open-telemetry/opentelemetry-java/pull/655 **Python:** https://github.com/open-telemetry/opentelemetry-python/pull/278 @@ -205,9 +206,9 @@ propagation. Let's assume this tracing system is configured to use B3, and has a specific propagator for that format. Initializating the propagators might look like this: -``` -func Init { - // create the propagators +```php +func InitializeOpentelemetry() { + // create the propagators for tracing and baggage. bagExtract, bagInject = Baggage::HTTPPropagator() traceExtract, traceInject = Tracer::B3Propagator() @@ -224,7 +225,7 @@ These propagators can then be used in the request handler for `service A`. The tracing and baggage aspects use the context object to handle state without breaking the encapsulation of the functions they are embedded in. -``` +```php func HandleUpstreamRequest(context, request, project) -> (context) { // Extract the span context. Because the extractors have been chained, // both a span context and any upstream baggage have been extracted @@ -276,7 +277,7 @@ context to be propagated automatically. In this version of pseudocode, assume that the current context can be stored as a thread local, and is implicitly passed to and returned from every function. -``` +```php func HandleUpstreamRequest(request, project) { extract = Propagation::GetHTTPExtractor() extractor(request.Headers) @@ -313,8 +314,8 @@ Digging into the details of the tracing system, what might some of the details look like? Here is a crude example of extracting and injecting B3 headers, using an explicit context. -``` - B3Extractor(context, headers) -> (context) { +```php + func B3Extractor(context, headers) -> (context) { context = Context::SetValue( context, "trace.parentTraceID", headers["X-B3-TraceId"]) @@ -324,7 +325,7 @@ using an explicit context. return context } - B3Injector(context, headers) -> (context) { + func B3Injector(context, headers) -> (context) { headers["X-B3-TraceId"] = Context::GetValue( context, "trace.parentTraceID") headers["X-B3-SpanId"] = Context::GetValue( context, "trace.parentSpanID") @@ -337,8 +338,8 @@ context. Note that this code must know the internal details about the context keys in which the propagators above store their data. For this pseudocode, let's assume again that the context is passed implicitly in a thread local. -``` - StartSpan(options) { +```php + func StartSpan(options) { spanData = newSpanData() spanData.parentTraceID = Context::GetValue( "trace.parentTraceID") From 6693f91f31f24639c33a2f3932ad5731e292e2fa Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 15 Nov 2019 18:03:38 -0800 Subject: [PATCH 40/77] whitespace --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index c930a0403..2cb576349 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -171,7 +171,7 @@ returns an injector. ## Prototypes -**Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 +**Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 **Go:** https://github.com/open-telemetry/opentelemetry-go/pull/297 **Java:** https://github.com/open-telemetry/opentelemetry-java/pull/655 **Python:** https://github.com/open-telemetry/opentelemetry-python/pull/278 From e5c0395f0cbf79963bdca8addc2cb34882bcbe8e Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 15 Nov 2019 18:25:33 -0800 Subject: [PATCH 41/77] ToC --- text/0066-separate-context-propagation.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 2cb576349..15509c7d6 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -1,5 +1,17 @@ # Proposal: Separate Layer for Context Propagation +* [OpenTelemetry layered architecture](#openTelemetry-layered-architecture) + * [Aspects](#Aspects) + * [Observability API](#Observability-API) + * [Baggage API](#Baggage-API) + * [Context Propagation](#Context-Propagation) + * [Context API](#Context-API) + * [Propagation API](#Propagation-API) +* [Prototypes](#Prototypes) +* [Examples](#Examples) +* [Internal details](#Internal-details) +* [FAQ](#faq) + Refactor OpenTelemetry into a set of separate cross-cutting concerns which operate on a shared context propagation mechanism. @@ -19,7 +31,7 @@ This RFC addresses the following topics: ## Explanation -# OpenTelemetry Layered Architecture +# OpenTelemetry layered architecture The design of OpenTelemetry is based on the priciples of aspect-oriented programming, adopted to the needs of distributed systems. @@ -169,7 +181,7 @@ takes an injector. To access the global injector, the Propagation API provides a function which returns an injector. -## Prototypes +# Prototypes **Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 **Go:** https://github.com/open-telemetry/opentelemetry-go/pull/297 @@ -177,7 +189,7 @@ returns an injector. **Python:** https://github.com/open-telemetry/opentelemetry-python/pull/278 **Ruby:** https://github.com/open-telemetry/opentelemetry-ruby/pull/147 -## Examples +# Examples It might be helpful to look at an example, written in pseudocode. Let's describe a simple scenario, where `service A` responds to an HTTP request from a `client` @@ -413,7 +425,7 @@ When available, OpenTelemetry defaults to propagating via HTTP header definitions which have been standardized by the W3C. -# Trade-offs and mitigations +# FAQ ## Why separate Baggage from Correlations? From af86fc69ef51eb9b6c3a86e4923b7f79a0944e46 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Sat, 16 Nov 2019 13:34:13 -0800 Subject: [PATCH 42/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Tristan Sloughter --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 15509c7d6..7cf593c3c 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -292,7 +292,7 @@ a thread local, and is implicitly passed to and returned from every function. ```php func HandleUpstreamRequest(request, project) { extract = Propagation::GetHTTPExtractor() - extractor(request.Headers) + extract(request.Headers) Tracer::StartSpan([span options]) From 716956a616445f26dc341ae7b53f03518f0ff905 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sat, 16 Nov 2019 14:16:48 -0800 Subject: [PATCH 43/77] more context examples --- text/0066-separate-context-propagation.md | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 7cf593c3c..60dd8c8d6 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -371,6 +371,66 @@ assume again that the context is passed implicitly in a thread local. } ``` +Let's look at a couple other scenarios related to automatic context propagation. + +When are the values in the current contexdt available? Scope managemenent may be different in each langauge, but as long as the scope does not change (by switching threads, for example) the +current context follows the execuption of the program. This includes after a +function returns. Note that the context objects themselves are immutable, so +explict handles to prior contexts will not be updated when the current context +is changed. + +```php +func Request() { + emptyContext = Context::GetCurrent() + + Context::SetValue( "say-something", "foo") + secondContext = Context::GetCurrent() + + print(Context::GetValue("say-something")) // prints "foo" + + DoWork() + + thirdContext = Context::GetCurrent() + + print(Context::GetValue("say-something")) // prints "bar" + + print( emptyContext.GetValue("say-something") ) // prints "" + print( secondContext.GetValue("say-something") ) // prints "foo" + print( thirdContext.GetValue("say-something") ) // prints "bar" +} + +func DoWork(){ + Context::SetValue( "say-something", "bar") +} +``` + +If context propagation is automantic, does the user ever need to reference a +context object directly? Sometimes. Ehen automated context propagation is +available, there is no restriction that aspects must only ever access the +current context. + +For example, if an aspect wanted to merge the data beween two contexts, at +least one of them will not be the current context. + +```php +mergedContext = MergeBaggage( Context::GetCurrent(), otherContext) +Context::SetCurrent(mergedContext) +``` + +Sometimes, suppling an additional version of a function which uses explict +contexts is necessary, in order to handle edge cases. For example, in some cases +an extracted context is not intended to be set as current context. An +alternate extract method can be added to the API to handle this. + +```php +// Most of the time, the extract function operates on the current context. +Extract(headers) + +// When a context needs to be extracted without changing the current +// context, fall back to the explicit API. +otherContext = ExtractWithContext(Context::GetCurrent(), headers) +``` + # Internal details ![drawing](img/context_propagation_details.png) From 14d943f93792a10e22e07a336da25f19459834ce Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sun, 17 Nov 2019 15:31:31 -0800 Subject: [PATCH 44/77] typo --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 60dd8c8d6..06f373576 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -405,7 +405,7 @@ func DoWork(){ ``` If context propagation is automantic, does the user ever need to reference a -context object directly? Sometimes. Ehen automated context propagation is +context object directly? Sometimes. When automated context propagation is available, there is no restriction that aspects must only ever access the current context. From 3dfa025d9d2eeb718a0503535c3e6b47a1a262e8 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Wed, 20 Nov 2019 17:30:31 -0800 Subject: [PATCH 45/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Yusuke Tsutsumi --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 06f373576..039d1c4b6 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -48,7 +48,7 @@ data across the lifespan of a distributed transaction. ## Observability API Distributed tracing is one example of an aspect. Tracing code is interleaved -with regular code, and ties together indepentent code modules which would +with regular code, and ties together independent code modules which would otherwise remain encapsulated. Tracing is also distributed, and requires non-local, transaction-level context propagation in order to execute correctly. From fc4fd1881c843f546222cb199aec10df6de5cbca Mon Sep 17 00:00:00 2001 From: Ted Young Date: Wed, 20 Nov 2019 18:49:14 -0800 Subject: [PATCH 46/77] Apply suggestions from code review Co-Authored-By: Yuri Shkuro --- text/0066-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 039d1c4b6..6967a984d 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -91,7 +91,7 @@ context which contains the new value. **ClearBaggage(context) -> context** To avoid sending baggage to an untrusted downstream process, the Baggage API -provides a function remove all baggage from a context. +provides a function to remove all baggage from a context. **GetBaggageHTTPPropagator() -> (HTTPExtract, HTTPInject)** To deserialize the state of the system sent from the the prior upstream process, @@ -501,7 +501,7 @@ undefined. This leaves the door open to optimizations, such as propagating the correlation data out-of-band. Baggage values, on the other hand, are explicitly added in order to be accessed -by downstream by other application code. Therefore, Baggage Context must be +by other application code downstream. Therefore, Baggage Context must be readable, and reliably propagated in-band in order to accomplish this goal. There may be cases where a key-value pair is propagated as a Correlation for From ef70b798ef12b3323e63129d77072536c2b6425e Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sat, 30 Nov 2019 17:53:30 -0800 Subject: [PATCH 47/77] Renamed aspects to "cross-cutting concerns" --- text/0066-separate-context-propagation.md | 68 +++++++++++------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 6967a984d..223d35217 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -1,7 +1,7 @@ -# Proposal: Separate Layer for Context Propagation +# Context Propagation: A Layered Approach -* [OpenTelemetry layered architecture](#openTelemetry-layered-architecture) - * [Aspects](#Aspects) +* [OpenTelemetry layered architecture](#OpenTelemetry-layered-architecture) + * [Cross-Cutting Concerns](#Cross-Cutting-Concerns) * [Observability API](#Observability-API) * [Baggage API](#Baggage-API) * [Context Propagation](#Context-Propagation) @@ -36,26 +36,26 @@ This RFC addresses the following topics: The design of OpenTelemetry is based on the priciples of aspect-oriented programming, adopted to the needs of distributed systems. -OpenTelemetry is separated into two layers: **aspects** which crosscut and -intertwine with a program's functional concerns, and cannot be encapsulated. In -this architecture, each cross-cutting concern is modeled as an independent -subsystem. Multiple aspects - including the tracing and baggage systems provided +OpenTelemetry is separated into two layers: **cross-cutting concerns** which +intertwine with a program's application logic, and cannot be encapsulated. In +this architecture, each concern is modeled as an independent subsystem. +Multiple concerns - including the tracing and baggage systems provided by OpenTelemetry - then share the same underlying **context propagation** system, which allows these cross-cutting concerns to store and access their data across the lifespan of a distributed transaction. -# Aspects +# Cross-Cutting Concerns ## Observability API -Distributed tracing is one example of an aspect. Tracing code is interleaved -with regular code, and ties together independent code modules which would -otherwise remain encapsulated. Tracing is also distributed, and requires +Distributed tracing is one example of a cross-cutting concern. Tracing code is +interleaved with regular code, and ties together independent code modules which +would otherwise remain encapsulated. Tracing is also distributed, and requires non-local, transaction-level context propagation in order to execute correctly. -A second observability aspect is correlations. Correlations are labels for -metrics, where the value of the label may be defined anywhere in the -transaction. Correlations are like Baggage, but are write only - the values can -only be used for observability. +A second observability concern is **correlations**. Correlations provide values +for labels, which can then be applied to any metric occurring later in the +same transaction. The Correlations API is similar to the Baggage API, except it +is a write-only interface. The observability APIs are not described here directly. However, in this new design, all observability APIs would be modified to make use of the generalized @@ -68,7 +68,7 @@ In addition to observability, OpenTelemetry provides a simple mechanism for propagating arbitrary user data, called Baggage. This mechanism is not related to tracing or observability, but it uses the same context propagation layer. -Baggage may be used to model new aspects which would benefit from the same +Baggage may be used to model new concerns which would benefit from the same transaction-level context as tracing, e.g., for identity, versioning, and network switching. @@ -76,16 +76,16 @@ To manage the state of these cross-cutting concerns, the Baggage API provides a set of functions which read, write, and propagate data. **SetBaggage(context, key, value) -> context** -To record the distributed state of an aspect, the Baggage API provides a +To record the distributed state of a concern, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. **GetBaggage(context, key) -> value** -To access the distributed state of an aspect, the Baggage API provides a +To access the distributed state of a concern, the Baggage API provides a function which takes a context and a key as input, and returns a value. **RemoveBaggage(context, key) -> context** -To delete distributed state from an aspect, the Baggage API provides a function +To delete distributed state from a concern, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. @@ -103,17 +103,17 @@ baggage-specific implementation of the HTTPExtract and HTTPInject functions. ## Context API -Aspects access data in-process using a shared context object. Each aspect uses -its own namespaced set of keys in the context, containing all of the data for -that cross-cutting concern. +Cross-cutting concerns access data in-process using the same, shared context +object. Each concern uses its own namespaced set of keys in the context, +containing all of the data for that cross-cutting concern. **SetValue(context, key, value) -> context** -To record the local state of an aspect, the Context API provides a function -which takes a context, a key, and a value as input, and returns an updated -context which contains the new value. +To record the local state of a cross-cutting concern, the Context API provides a +function which takes a context, a key, and a value as input, and returns an +updated context which contains the new value. **GetValue(context, key) -> value** -To access the local state of an aspect, the Context API provides a function +To access the local state of an concern, the Context API provides a function which takes a context and a key as input, and returns a value. ### Optional: Automated Context Management @@ -133,8 +133,8 @@ provides a function which takes no arguments and returns a Context. ## Propagation API -Aspects send their state to downstream processes via propagators: -functions which read and write context into RPC requests. Each aspect creates a +Cross-cutting concerns send their state to downstream processes via propagators: +functions which read and write context into RPC requests. Each concern creates a set of propagators for every type of supported medium - currently only HTTP requests. @@ -144,18 +144,18 @@ provides a function which takes a context and an HTTP request, and returns context which represents the state of the upstream system. **HTTPInject(context, request)** -To send the data for all aspects downstream to the next process, the +To send the data for all concerns downstream to the next process, the Propagation API provides a function which takes a context and an HTTP request, and mutates the HTTP request to include an HTTP Header representation of the context. **ChainHTTPInjector(injector, injector) -> injector** -To allow multiple aspects to inject their context into the same request, the +To allow multiple concerns to inject their context into the same request, the Propagation API provides a function which takes two injectors, and returns a single injector which calls the two original injectors in order. **ChainHTTPExtractor(extractor, extractor) -> extractor** -To allow multiple aspects to extract their context from the same request, the +To allow multiple concerns to extract their context from the same request, the Propagation API provides a function which takes two extractors, and returns a single extractor which calls the two original extractors in order. @@ -234,7 +234,7 @@ func InitializeOpentelemetry() { ``` These propagators can then be used in the request handler for `service A`. The -tracing and baggage aspects use the context object to handle state without +tracing and baggage concerns use the context object to handle state without breaking the encapsulation of the functions they are embedded in. ```php @@ -406,10 +406,10 @@ func DoWork(){ If context propagation is automantic, does the user ever need to reference a context object directly? Sometimes. When automated context propagation is -available, there is no restriction that aspects must only ever access the +available, there is no restriction that concerns must only ever access the current context. -For example, if an aspect wanted to merge the data beween two contexts, at +For example, if a concern wanted to merge the data beween two contexts, at least one of them will not be the current context. ```php From 094817fd968c1201b9977ab71cf2053fd1b8a18f Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sat, 30 Nov 2019 18:22:34 -0800 Subject: [PATCH 48/77] injectors are a list instead of chained --- text/0066-separate-context-propagation.md | 43 ++++++++++++----------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 223d35217..f9f7f97b4 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -93,7 +93,7 @@ context which contains the new value. To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function to remove all baggage from a context. -**GetBaggageHTTPPropagator() -> (HTTPExtract, HTTPInject)** +**GetBaggagePropagator() -> (HTTP_Extractor, HTTP_Injector)** To deserialize the state of the system sent from the the prior upstream process, and to serialize the the current state of the system and send it to the next downstream process, the Baggage API provides a function which returns a @@ -138,26 +138,27 @@ functions which read and write context into RPC requests. Each concern creates a set of propagators for every type of supported medium - currently only HTTP requests. -**HTTPExtract(context, request) -> context** -To receive data injected by prior upstream processes, the Propagation API -provides a function which takes a context and an HTTP request, and returns -context which represents the state of the upstream system. +**Extract(context, []http_injector, headers) -> context** +In order to continue transmitting data injected earlier in the transaction, +the Propagation API provides a function which takes a context, a set of +HTTP_Injectors, and a set of HTTP headers, and returns a new context which +includes the state sent from the upstream system. -**HTTPInject(context, request)** +**Inject(context, []http_extractor, headers) -> headers** To send the data for all concerns downstream to the next process, the -Propagation API provides a function which takes a context and an HTTP request, -and mutates the HTTP request to include an HTTP Header representation of the -context. +Propagation API provides a function which takes a context and a set of +HTTP_Extractors, and adds the contents of the context in to HTTP headers to +include an HTTP Header representation of the context. -**ChainHTTPInjector(injector, injector) -> injector** -To allow multiple concerns to inject their context into the same request, the -Propagation API provides a function which takes two injectors, and returns a -single injector which calls the two original injectors in order. +**HTTP_Extractor(context, headers) -> context** +Each concern must implement an HTTP_Extractor, which can locate the headers +containing the http-formatted data, and then translate the contents into an +in-memory representation, set within the returned context object. -**ChainHTTPExtractor(extractor, extractor) -> extractor** -To allow multiple concerns to extract their context from the same request, the -Propagation API provides a function which takes two extractors, and returns a -single extractor which calls the two original extractors in order. +**HTTP_Injector(context, headers) -> headers** +Each concern must implement an HTTP_Injector, which can take the in-memory +representation of its data from the given context object, and add it to an +existing set of HTTP headers. ### Optional: Global Propagators It is often convenient to create a chain of propagators during program @@ -165,19 +166,19 @@ initialization, and then access these combined propagators later in the program. To facilitate this, global injectors and extractors are optionally available. However, there is no requirement to use this feature. -**SetHTTPExtractor(extractor)** +**SetExtractors([]http_extractor)** To update the global extractor, the Propagation API provides a function which takes an extractor. -**GetHTTPExtractor() -> extractor** +**GetExtractors() -> []http_extractor** To access the global extractor, the Propagation API provides a function which returns an extractor. -**SetHTTPInjector(injector)** +**SetInjectors([]http_injector)** To update the global injector, the Propagation API provides a function which takes an injector. -**GetHTTPInjector() -> injector** +**GetInjectors() -> []http_injector** To access the global injector, the Propagation API provides a function which returns an injector. From 7a912ae1ce853dac4b08c1884a98fb8357c896db Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sat, 30 Nov 2019 18:28:32 -0800 Subject: [PATCH 49/77] clean up API representation --- text/0066-separate-context-propagation.md | 62 ++++++++++++----------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index f9f7f97b4..5192075f1 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -57,7 +57,7 @@ for labels, which can then be applied to any metric occurring later in the same transaction. The Correlations API is similar to the Baggage API, except it is a write-only interface. -The observability APIs are not described here directly. However, in this new +The various observability APIs are not described here directly. However, in this new design, all observability APIs would be modified to make use of the generalized context propagation mechanism described below, rather than the tracing-specific propagation system it uses today. @@ -75,25 +75,25 @@ network switching. To manage the state of these cross-cutting concerns, the Baggage API provides a set of functions which read, write, and propagate data. -**SetBaggage(context, key, value) -> context** +**`GetBaggage(context, key) -> value`** +To access the distributed state of a concern, the Baggage API provides a +function which takes a context and a key as input, and returns a value. + +**`SetBaggage(context, key, value) -> context`** To record the distributed state of a concern, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**GetBaggage(context, key) -> value** -To access the distributed state of a concern, the Baggage API provides a -function which takes a context and a key as input, and returns a value. - -**RemoveBaggage(context, key) -> context** +**`RemoveBaggage(context, key) -> context`** To delete distributed state from a concern, the Baggage API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**ClearBaggage(context) -> context** +**`ClearBaggage(context) -> context`** To avoid sending baggage to an untrusted downstream process, the Baggage API provides a function to remove all baggage from a context. -**GetBaggagePropagator() -> (HTTP_Extractor, HTTP_Injector)** +**`GetBaggagePropagator() -> (HTTP_Extractor, HTTP_Injector)`** To deserialize the state of the system sent from the the prior upstream process, and to serialize the the current state of the system and send it to the next downstream process, the Baggage API provides a function which returns a @@ -107,14 +107,15 @@ Cross-cutting concerns access data in-process using the same, shared context object. Each concern uses its own namespaced set of keys in the context, containing all of the data for that cross-cutting concern. -**SetValue(context, key, value) -> context** +**`GetValue(context, key) -> value`** +To access the local state of an concern, the Context API provides a function +which takes a context and a key as input, and returns a value. + +**`SetValue(context, key, value) -> context`** To record the local state of a cross-cutting concern, the Context API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**GetValue(context, key) -> value** -To access the local state of an concern, the Context API provides a function -which takes a context and a key as input, and returns a value. ### Optional: Automated Context Management When possible, the OpenTelemetry context should automatically be associated @@ -122,13 +123,14 @@ with the program execution context. Note that some languages do not provide any facility for setting and getting a current context. In these cases, the user is responsible for managing the current context. -**SetCurrent(context)** +**`GetCurrent() -> context`** +To access the context associated with program execution, the Context API +provides a function which takes no arguments and returns a Context. + +**`SetCurrent(context)`** To associate a context with program execution, the Context API provides a function which takes a Context. -**GetCurrent() -> context** -To access the context associated with program execution, the Context API -provides a function which takes no arguments and returns a Context. ## Propagation API @@ -138,24 +140,24 @@ functions which read and write context into RPC requests. Each concern creates a set of propagators for every type of supported medium - currently only HTTP requests. -**Extract(context, []http_injector, headers) -> context** +**`Extract(context, []http_injector, headers) -> context`** In order to continue transmitting data injected earlier in the transaction, the Propagation API provides a function which takes a context, a set of HTTP_Injectors, and a set of HTTP headers, and returns a new context which includes the state sent from the upstream system. -**Inject(context, []http_extractor, headers) -> headers** +**`Inject(context, []http_extractor, headers) -> headers`** To send the data for all concerns downstream to the next process, the Propagation API provides a function which takes a context and a set of HTTP_Extractors, and adds the contents of the context in to HTTP headers to include an HTTP Header representation of the context. -**HTTP_Extractor(context, headers) -> context** +**`HTTP_Extractor(context, headers) -> context`** Each concern must implement an HTTP_Extractor, which can locate the headers containing the http-formatted data, and then translate the contents into an in-memory representation, set within the returned context object. -**HTTP_Injector(context, headers) -> headers** +**`HTTP_Injector(context, headers) -> headers`** Each concern must implement an HTTP_Injector, which can take the in-memory representation of its data from the given context object, and add it to an existing set of HTTP headers. @@ -166,22 +168,22 @@ initialization, and then access these combined propagators later in the program. To facilitate this, global injectors and extractors are optionally available. However, there is no requirement to use this feature. -**SetExtractors([]http_extractor)** -To update the global extractor, the Propagation API provides a function which -takes an extractor. - -**GetExtractors() -> []http_extractor** +**`GetExtractors() -> []http_extractor`** To access the global extractor, the Propagation API provides a function which returns an extractor. -**SetInjectors([]http_injector)** -To update the global injector, the Propagation API provides a function which -takes an injector. +**`SetExtractors([]http_extractor)`** +To update the global extractor, the Propagation API provides a function which +takes an extractor. -**GetInjectors() -> []http_injector** +**`GetInjectors() -> []http_injector`** To access the global injector, the Propagation API provides a function which returns an injector. +**`SetInjectors([]http_injector)`** +To update the global injector, the Propagation API provides a function which +takes an injector. + # Prototypes **Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 From 865ebc27dbae513fd22875d8edd1fa9d4b8ccb3b Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sat, 30 Nov 2019 18:59:01 -0800 Subject: [PATCH 50/77] cleanup examples --- text/0066-separate-context-propagation.md | 84 ++++++++++++++--------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 5192075f1..27822e0b8 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -1,5 +1,6 @@ # Context Propagation: A Layered Approach +* [Motivation](#Motivation) * [OpenTelemetry layered architecture](#OpenTelemetry-layered-architecture) * [Cross-Cutting Concerns](#Cross-Cutting-Concerns) * [Observability API](#Observability-API) @@ -9,13 +10,21 @@ * [Propagation API](#Propagation-API) * [Prototypes](#Prototypes) * [Examples](#Examples) + * [Global initialization](#Global-initialization) + * [Extracting and injecting from HTTP headers](#Extracting-and-injecting-from-HTTP-headers) + * [Simplfy the API with automated context propagation](#Simplfy-the-API-with-automated-context-propagation) + * [Implementing a propagator](#Implementing-a-propagator) + * [Implementing a concern](#Implementing-a-concern) + * [The scope of current context](#The-scope-of-current-context) + * [Referencing multiple contexts](#Referencing-multiple-contexts) + * [Falling back to explicit contexts](#Falling-back-to-explicit-contexts) * [Internal details](#Internal-details) * [FAQ](#faq) Refactor OpenTelemetry into a set of separate cross-cutting concerns which operate on a shared context propagation mechanism. -## Motivation +# Motivation This RFC addresses the following topics: @@ -210,11 +219,12 @@ in order to return the correct data. client -> service A -> service C ``` -In , we would like `service A` to decide on which backend service to call, -based on the client version. We would also like to trace the entire system, in -order to understand if requests to `service C` are slower or faster than -`service B`. What might `service A` look like? +In this example, we would like `service A` to decide on which backend service +to call, based on the client version. We would also like to trace the entire +system, in order to understand if requests to `service C` are slower or faster +than `service B`. What might `service A` look like? +## Global initialization First, during program initialization, `service A` might set a global extractor and injector which chains together baggage and tracing propagation. Let's assume this tracing system is configured to use B3, @@ -227,31 +237,32 @@ func InitializeOpentelemetry() { bagExtract, bagInject = Baggage::HTTPPropagator() traceExtract, traceInject = Tracer::B3Propagator() - // chain the propagators together and make them globally available. - extract = Propagation::ChainHTTPExtractor(bagExtract,traceExtract) - inject = Propagation::ChainHTTPInjector(bagInject,traceInject) - - Propagation::SetHTTPExtractor(extract) - Propagation::SetHTTPInjector(inject) + // add the propagators to the global list. + Propagation::SetExtractors(bagExtract, traceExtract) + Propagation::SetInjectors(bagInject, traceInject) } ``` - +## Extracting and injecting from HTTP headers These propagators can then be used in the request handler for `service A`. The tracing and baggage concerns use the context object to handle state without breaking the encapsulation of the functions they are embedded in. ```php func HandleUpstreamRequest(context, request, project) -> (context) { - // Extract the span context. Because the extractors have been chained, - // both a span context and any upstream baggage have been extracted - // from the request headers into the returned context. - extract = Propagation::GetHTTPExtractor() - context = extract(context, request.Headers) + // Extract the upstream context from the HTTP headers. Because the list of + // extractors includes a trace extractor and a baggage extractor, the + // contents for both systems are included in the request headers into the + // returned context. + extractors = Propagation::GetExtractors() + context = Propagation::Extract(context, extractors, request.Headers) // Start a span, setting the parent to the span context received from // the upstream. The new span will then be in the returned context. context = Tracer::StartSpan(context, [span options]) + // Determine the version of the upstream client, in order to handle the data + // migration and allow new clients access to a data source that older clients + // are unaware of. version = Baggage::GetBaggage( context, "client-version") switch( version ){ @@ -274,8 +285,8 @@ func FetchDataFromServiceB(context) -> (context, data) { // Inject the contexts to be propagated. Note that there is no direct // reference to tracing or baggage. - inject = Propagation::GetHTTPInjector() - context = inject(context, request.Headers) + injectors = Propagation::GetInjectors() + context = Propagation::Inject(context, injectors, request.Headers) // make an http request data = request.Do() @@ -284,6 +295,7 @@ func FetchDataFromServiceB(context) -> (context, data) { } ``` +## Simplfy the API with automated context propagation In this version of pseudocode above, we assume that the context object is explict,and is pass and returned from every function as an ordinary parameter. This is cumbersome, and in many languages, a mechanism exists which allows @@ -294,8 +306,8 @@ a thread local, and is implicitly passed to and returned from every function. ```php func HandleUpstreamRequest(request, project) { - extract = Propagation::GetHTTPExtractor() - extract(request.Headers) + extractors = Propagation::GetExtractors() + Propagation::Extract(extractors, request.Headers) Tracer::StartSpan([span options]) @@ -316,18 +328,18 @@ func HandleUpstreamRequest(request, project) { func FetchDataFromServiceB() -> (data) { request = newRequest([request options]) - inject = Propagation::GetHTTPInjector() - inject(request.Headers) + injectors = Propagation::GetInjectors() + Propagation::Inject(request.Headers) data = request.Do() return data } ``` - -Digging into the details of the tracing system, what might some of the details -look like? Here is a crude example of extracting and injecting B3 headers, -using an explicit context. +## Implementing a propagator +Digging into the details of the tracing system, what might the internals of a +span context propagator look like? Here is a crude example of extracting and +injecting B3 headers, using an explicit context. ```php func B3Extractor(context, headers) -> (context) { @@ -340,15 +352,16 @@ using an explicit context. return context } - func B3Injector(context, headers) -> (context) { + func B3Injector(context, headers) -> (headers) { headers["X-B3-TraceId"] = Context::GetValue( context, "trace.parentTraceID") headers["X-B3-SpanId"] = Context::GetValue( context, "trace.parentSpanID") - return context + return headers } ``` -Now, have a look at a crude example of how StartSpan might then make use of the +## Implementing a concern +Now, have a look at a crude example of how StartSpan might make use of the context. Note that this code must know the internal details about the context keys in which the propagators above store their data. For this pseudocode, let's assume again that the context is passed implicitly in a thread local. @@ -374,9 +387,10 @@ assume again that the context is passed implicitly in a thread local. } ``` +## The scope of current context Let's look at a couple other scenarios related to automatic context propagation. -When are the values in the current contexdt available? Scope managemenent may be different in each langauge, but as long as the scope does not change (by switching threads, for example) the +When are the values in the current context available? Scope managemenent may be different in each langauge, but as long as the scope does not change (by switching threads, for example) the current context follows the execuption of the program. This includes after a function returns. Note that the context objects themselves are immutable, so explict handles to prior contexts will not be updated when the current context @@ -407,10 +421,11 @@ func DoWork(){ } ``` +## Referencing multiple contexts If context propagation is automantic, does the user ever need to reference a -context object directly? Sometimes. When automated context propagation is -available, there is no restriction that concerns must only ever access the -current context. +context object directly? Sometimes. Even when automated context propagation is +an available option, there is no restriction which says that concerns must only +ever access the current context. For example, if a concern wanted to merge the data beween two contexts, at least one of them will not be the current context. @@ -420,6 +435,7 @@ mergedContext = MergeBaggage( Context::GetCurrent(), otherContext) Context::SetCurrent(mergedContext) ``` +## Falling back to explicit contexts Sometimes, suppling an additional version of a function which uses explict contexts is necessary, in order to handle edge cases. For example, in some cases an extracted context is not intended to be set as current context. An From 0c38469970eea803d79f47edcc76eb1c739c16ed Mon Sep 17 00:00:00 2001 From: tedsuo Date: Wed, 4 Dec 2019 14:49:32 -0800 Subject: [PATCH 51/77] typo --- text/0066-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 27822e0b8..5c3ff2793 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -149,13 +149,13 @@ functions which read and write context into RPC requests. Each concern creates a set of propagators for every type of supported medium - currently only HTTP requests. -**`Extract(context, []http_injector, headers) -> context`** +**`Extract(context, []http_extractor, headers) -> context`** In order to continue transmitting data injected earlier in the transaction, the Propagation API provides a function which takes a context, a set of HTTP_Injectors, and a set of HTTP headers, and returns a new context which includes the state sent from the upstream system. -**`Inject(context, []http_extractor, headers) -> headers`** +**`Inject(context, []http_injector, headers) -> headers`** To send the data for all concerns downstream to the next process, the Propagation API provides a function which takes a context and a set of HTTP_Extractors, and adds the contents of the context in to HTTP headers to From ff5d999b573f9df6aeee2cc539466e210c698dbf Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 6 Dec 2019 09:38:56 -0800 Subject: [PATCH 52/77] remove correlations from proposal --- text/0066-separate-context-propagation.md | 38 +---------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 5c3ff2793..456f13b39 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -30,7 +30,7 @@ This RFC addresses the following topics: **Separatation of concerns** * Remove the Tracer dependency from context propagation mechanisms. -* Handle user data (Baggage) and observability data (SpanContext, Correlations) +* Handle user data (Baggage) and observability data (SpanContext, etc) separately. **Extensibility** @@ -61,11 +61,6 @@ interleaved with regular code, and ties together independent code modules which would otherwise remain encapsulated. Tracing is also distributed, and requires non-local, transaction-level context propagation in order to execute correctly. -A second observability concern is **correlations**. Correlations provide values -for labels, which can then be applied to any metric occurring later in the -same transaction. The Correlations API is similar to the Baggage API, except it -is a write-only interface. - The various observability APIs are not described here directly. However, in this new design, all observability APIs would be modified to make use of the generalized context propagation mechanism described below, rather than the tracing-specific @@ -462,12 +457,6 @@ propagation. extracted. The readable attributes are defined to match those found in the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). -**Correlation Context -** Correlation Context contains a map of labels and -values, to be shared between metrics and traces. This allows observability data -to be indexed and dimensionalized in a variety of ways. Note that correlations -can quickly add overhead when propagated in-band. But because this data is -write-only, it may be possible to optimize how it is transmitted. - **Baggage -** Transaction-level application data, meant to be shared with downstream components. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size @@ -506,31 +495,6 @@ definitions which have been standardized by the W3C. # FAQ -## Why separate Baggage from Correlations? - -Since Baggage Context and Correlation Context appear very similar, why have two? - -First and foremost, the intended uses for Baggage and Correlations are -completely different. Secondly, the propagation requirements diverge -significantly. - -Correlation values are solely to be used as labels for metrics and traces. By -making Correlation data write-only, how and when it is transmitted remains -undefined. This leaves the door open to optimizations, such as propagating the -correlation data out-of-band. - -Baggage values, on the other hand, are explicitly added in order to be accessed -by other application code downstream. Therefore, Baggage Context must be -readable, and reliably propagated in-band in order to accomplish this goal. - -There may be cases where a key-value pair is propagated as a Correlation for -observability and as a Baggage item for application-specific use. AB testing is -one example of such use case. This would result in extra overhead, as the same -key-value pair would be present in two separate headers. - -Solving this edge case is not worth having the semantic confusion of a single -implementation with a dual purpose. - ## What about complex propagation behavior? Some OpenTelemetry proposals have called for more complex propagation behavior. From 5262aeba875a454482a7cf81bbd20de2c4324389 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Mon, 9 Dec 2019 10:16:45 -0800 Subject: [PATCH 53/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Yuri Shkuro --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 456f13b39..8e0a1d4f0 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -290,7 +290,7 @@ func FetchDataFromServiceB(context) -> (context, data) { } ``` -## Simplfy the API with automated context propagation +## Simplify the API with automated context propagation In this version of pseudocode above, we assume that the context object is explict,and is pass and returned from every function as an ordinary parameter. This is cumbersome, and in many languages, a mechanism exists which allows From 3d0d1fba74cca3b36ff63fc072aaab47fdeaa257 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 12 Dec 2019 13:18:56 -0800 Subject: [PATCH 54/77] remove the term "chaining" --- text/0066-separate-context-propagation.md | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 8e0a1d4f0..5d8284811 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -167,8 +167,8 @@ representation of its data from the given context object, and add it to an existing set of HTTP headers. ### Optional: Global Propagators -It is often convenient to create a chain of propagators during program -initialization, and then access these combined propagators later in the program. +It may be convenient to create a list of propagators during program +initialization, and then access these propagators later in the program. To facilitate this, global injectors and extractors are optionally available. However, there is no requirement to use this feature. @@ -220,11 +220,10 @@ system, in order to understand if requests to `service C` are slower or faster than `service B`. What might `service A` look like? ## Global initialization -First, during program initialization, `service A` might set a global -extractor and injector which chains together baggage and tracing -propagation. Let's assume this tracing system is configured to use B3, -and has a specific propagator for that format. Initializating the propagators -might look like this: +First, during program initialization, `service A` configures baggage and tracing +propagation, and include them in the global list of injectors and extractors. +Let's assume this tracing system is configured to use B3, and has a specific +propagator for that format. Initializating the propagators might look like this: ```php func InitializeOpentelemetry() { @@ -385,11 +384,12 @@ assume again that the context is passed implicitly in a thread local. ## The scope of current context Let's look at a couple other scenarios related to automatic context propagation. -When are the values in the current context available? Scope managemenent may be different in each langauge, but as long as the scope does not change (by switching threads, for example) the -current context follows the execuption of the program. This includes after a -function returns. Note that the context objects themselves are immutable, so -explict handles to prior contexts will not be updated when the current context -is changed. +When are the values in the current context available? Scope managemenent may be +different in each langauge, but as long as the scope does not change (by +switching threads, for example) the current context follows the execuption of +the program. This includes after a function returns. Note that the context +objects themselves are immutable, so explict handles to prior contexts will not +be updated when the current context is changed. ```php func Request() { @@ -499,10 +499,10 @@ definitions which have been standardized by the W3C. Some OpenTelemetry proposals have called for more complex propagation behavior. For example, falling back to extracting B3 headers if W3C Trace-Context headers -are not found. Chained propagators and other complex behavior can be modeled as +are not found. "Fallback propagators" and other complex behavior can be modeled as implementation details behind the Propagator interface. Therefore, the -propagation system itself does not need to provide chained propagators or other -additional facilities. +propagation system itself does not need to provide an mechanism for chaining +together propagators or other additional facilities. ## Did you add a context parameter to every API call because Go has infected your brain? From 2dbb2cab035ea764a1a2ba2f90f4adf7d9f07366 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 12 Dec 2019 13:30:10 -0800 Subject: [PATCH 55/77] remove the terms upstream and downstream --- text/0066-separate-context-propagation.md | 38 +++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 5d8284811..0ec4d4aa4 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -94,14 +94,14 @@ which takes a context, a key, and a value as input, and returns an updated context which contains the new value. **`ClearBaggage(context) -> context`** -To avoid sending baggage to an untrusted downstream process, the Baggage API -provides a function to remove all baggage from a context. +To avoid sending baggage to an untrusted process, the Baggage API provides a +function to remove all baggage from a context. **`GetBaggagePropagator() -> (HTTP_Extractor, HTTP_Injector)`** -To deserialize the state of the system sent from the the prior upstream process, -and to serialize the the current state of the system and send it to the next -downstream process, the Baggage API provides a function which returns a -baggage-specific implementation of the HTTPExtract and HTTPInject functions. +To deserialize the state of the system sent from the the prior process, and to +serialize the the current state of the system and send it to the next process, +the Baggage API provides a function which returns a baggage-specific +implementation of the HTTPExtract and HTTPInject functions. # Context Propagation @@ -139,7 +139,7 @@ function which takes a Context. ## Propagation API -Cross-cutting concerns send their state to downstream processes via propagators: +Cross-cutting concerns send their state to the next process via propagators: functions which read and write context into RPC requests. Each concern creates a set of propagators for every type of supported medium - currently only HTTP requests. @@ -148,10 +148,10 @@ requests. In order to continue transmitting data injected earlier in the transaction, the Propagation API provides a function which takes a context, a set of HTTP_Injectors, and a set of HTTP headers, and returns a new context which -includes the state sent from the upstream system. +includes the state sent from the prior process. **`Inject(context, []http_injector, headers) -> headers`** -To send the data for all concerns downstream to the next process, the +To send the data for all concerns to the next process in the transaction, the Propagation API provides a function which takes a context and a set of HTTP_Extractors, and adds the contents of the context in to HTTP headers to include an HTTP Header representation of the context. @@ -242,8 +242,8 @@ tracing and baggage concerns use the context object to handle state without breaking the encapsulation of the functions they are embedded in. ```php -func HandleUpstreamRequest(context, request, project) -> (context) { - // Extract the upstream context from the HTTP headers. Because the list of +func ServeRequest(context, request, project) -> (context) { + // Extract the context from the HTTP headers. Because the list of // extractors includes a trace extractor and a baggage extractor, the // contents for both systems are included in the request headers into the // returned context. @@ -251,12 +251,12 @@ func HandleUpstreamRequest(context, request, project) -> (context) { context = Propagation::Extract(context, extractors, request.Headers) // Start a span, setting the parent to the span context received from - // the upstream. The new span will then be in the returned context. + // the client process. The new span will then be in the returned context. context = Tracer::StartSpan(context, [span options]) - // Determine the version of the upstream client, in order to handle the data - // migration and allow new clients access to a data source that older clients - // are unaware of. + // Determine the version of the client, in order to handle the data + // migration and allow new clients access to a data source that older + // clients are unaware of. version = Baggage::GetBaggage( context, "client-version") switch( version ){ @@ -299,7 +299,7 @@ In this version of pseudocode, assume that the current context can be stored as a thread local, and is implicitly passed to and returned from every function. ```php -func HandleUpstreamRequest(request, project) { +func ServeRequest(request, project) { extractors = Propagation::GetExtractors() Propagation::Extract(extractors, request.Headers) @@ -374,7 +374,7 @@ assume again that the context is passed implicitly in a thread local. Context::SetValue( "trace.parentSpanID", spanData.spanID) // store the spanData object as well, for in-process propagation. Note that - this key will not be propagated downstream. + // this key will not be propagated, it is for local use only. Context::SetValue( "trace.currentSpanData", spanData) return @@ -458,9 +458,9 @@ extracted. The readable attributes are defined to match those found in the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). **Baggage -** Transaction-level application data, meant to be shared with -downstream components. This data is readable, and must be propagated in-band. +all services. This data is readable, and must be propagated in-band. Because of this, Baggage should be used sparingly, to avoid ballooning the size -of all downstream requests. +of every request. Note that OpenTelemetry APIs calls should *always* be given access to the entire context object, and never just a subset of the context, such as the value in a From 5e905b0c90f81d6fb50e5d34bd41dfb85ad0d4ab Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 12 Dec 2019 21:46:58 -0800 Subject: [PATCH 56/77] improved architecturural explanation --- text/0066-separate-context-propagation.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 0ec4d4aa4..b04f7e980 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -40,18 +40,19 @@ This RFC addresses the following topics: ## Explanation + # OpenTelemetry layered architecture -The design of OpenTelemetry is based on the priciples of aspect-oriented -programming, adopted to the needs of distributed systems. +The design of OpenTelemetry is based on the priciples of [aspect-oriented +programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming), +adopted to the needs of distributed systems. + +OpenTelemetry is separated into two layers. The top layer contains a set of +independent **cross-cutting concerns**, which intertwine with a program's +application logic, and cannot be cleanly encapsulated. All concerns share an +underlying distributed **context propagation** layer, for storing state and +accessing data across the lifespan of a distributed transaction. -OpenTelemetry is separated into two layers: **cross-cutting concerns** which -intertwine with a program's application logic, and cannot be encapsulated. In -this architecture, each concern is modeled as an independent subsystem. -Multiple concerns - including the tracing and baggage systems provided -by OpenTelemetry - then share the same underlying **context propagation** -system, which allows these cross-cutting concerns to store and access their -data across the lifespan of a distributed transaction. # Cross-Cutting Concerns @@ -445,6 +446,7 @@ Extract(headers) otherContext = ExtractWithContext(Context::GetCurrent(), headers) ``` + # Internal details ![drawing](img/context_propagation_details.png) From f25437299ba9a37c27b388e13f5259536c1fbb64 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 12 Dec 2019 21:52:04 -0800 Subject: [PATCH 57/77] whitespace --- text/0066-separate-context-propagation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index b04f7e980..221f0289d 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -237,6 +237,7 @@ func InitializeOpentelemetry() { Propagation::SetInjectors(bagInject, traceInject) } ``` + ## Extracting and injecting from HTTP headers These propagators can then be used in the request handler for `service A`. The tracing and baggage concerns use the context object to handle state without @@ -331,6 +332,7 @@ func FetchDataFromServiceB() -> (data) { return data } ``` + ## Implementing a propagator Digging into the details of the tracing system, what might the internals of a span context propagator look like? Here is a crude example of extracting and From fa23bf596528477e93b02f7651d37442c39a464f Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 12 Dec 2019 21:52:49 -0800 Subject: [PATCH 58/77] update link to Go prototype --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 221f0289d..776333f21 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -192,7 +192,7 @@ takes an injector. # Prototypes **Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 -**Go:** https://github.com/open-telemetry/opentelemetry-go/pull/297 +**Go:** https://github.com/open-telemetry/opentelemetry-go/pull/381 **Java:** https://github.com/open-telemetry/opentelemetry-java/pull/655 **Python:** https://github.com/open-telemetry/opentelemetry-python/pull/278 **Ruby:** https://github.com/open-telemetry/opentelemetry-ruby/pull/147 From 762ab0aceee005c083784b120deb24d305f16275 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Sun, 15 Dec 2019 15:42:40 -0800 Subject: [PATCH 59/77] Removed the Baggage API; replaced it with Correlations. --- text/0066-separate-context-propagation.md | 137 ++++++++++++---------- 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 776333f21..762b578f2 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -4,7 +4,7 @@ * [OpenTelemetry layered architecture](#OpenTelemetry-layered-architecture) * [Cross-Cutting Concerns](#Cross-Cutting-Concerns) * [Observability API](#Observability-API) - * [Baggage API](#Baggage-API) + * [Correlations API](#Correlations-API) * [Context Propagation](#Context-Propagation) * [Context API](#Context-API) * [Propagation API](#Propagation-API) @@ -30,7 +30,7 @@ This RFC addresses the following topics: **Separatation of concerns** * Remove the Tracer dependency from context propagation mechanisms. -* Handle user data (Baggage) and observability data (SpanContext, etc) +* Handle user data (Correlations) and observability data (SpanContext, etc) separately. **Extensibility** @@ -67,42 +67,62 @@ design, all observability APIs would be modified to make use of the generalized context propagation mechanism described below, rather than the tracing-specific propagation system it uses today. -## Baggage API +Note that OpenTelemetry APIs calls should *always* be given access to the entire +context object, and never just a subset of the context, such as the value in a +single key. This allows the SDK to make improvements and leverage additional +data that may be available, without changes to all of the call sites. -In addition to observability, OpenTelemetry provides a simple mechanism for -propagating arbitrary user data, called Baggage. This mechanism is not related -to tracing or observability, but it uses the same context propagation layer. +## Correlations API -Baggage may be used to model new concerns which would benefit from the same -transaction-level context as tracing, e.g., for identity, versioning, and -network switching. +In addition to trace propagation, OpenTelemetry provides a simple mechanism for +propagating arbitrary transaction-level data, called Correlations. Correlations +implements an extremely basic form of cross-cutting concern - a transaction-level +key-value store. Values placed in the -To manage the state of these cross-cutting concerns, the Baggage API provides a -set of functions which read, write, and propagate data. +Event correlation is a mechanism for indexing events in one service with +attributes provided by a prior service in the same transaction. This helps to +establish a causal relationship between these events. For example, determining +that a particular browser version is associated with a failure in an image +processing service. -**`GetBaggage(context, key) -> value`** -To access the distributed state of a concern, the Baggage API provides a -function which takes a context and a key as input, and returns a value. +Correlations is based on the [W3C Correlation-Context specification](https://w3c.github.io/correlation-context/), +and implements the protocol as it is defined in that working group. -**`SetBaggage(context, key, value) -> context`** -To record the distributed state of a concern, the Baggage API provides a -function which takes a context, a key, and a value as input, and returns an -updated context which contains the new value. +While Correlations can be used to prototype other cross-cutting concerns, this +mechanism is primarily intended to convey values for the OpenTelemetry +observability systems. New concerns with different criteria should be modeled +seperately, using the same underlying context propagation layer as building +blocks. + +For backwards compatibility, OpenTracing Baggage is propagated as Correlations +when using the OpenTracing bridge. + +As every correlation adds additional overhead to every request, correlations +should be used with care. + +**`GetCorrelation(context, key) -> value`** +To access the value for a label set by a prior event, the Correlations API +provides a function which takes a context and a key as input, and returns a value. -**`RemoveBaggage(context, key) -> context`** -To delete distributed state from a concern, the Baggage API provides a function +**`SetCorrelation(context, key, value) -> context`** +To record the value for a label, the Correlations API provides a function which +takes a context, a key, and a value as input, and returns an updated context +which contains the new value. + +**`RemoveCorrelation(context, key) -> context`** +To delete a label, the Correlations API provides a function which takes a context, a key, and a value as input, and returns an updated context which contains the new value. -**`ClearBaggage(context) -> context`** -To avoid sending baggage to an untrusted process, the Baggage API provides a -function to remove all baggage from a context. +**`ClearCorrelations(context) -> context`** +To avoid sending any labels to an untrusted process, the Correlation API +provides a function to remove all Correlations from a context. -**`GetBaggagePropagator() -> (HTTP_Extractor, HTTP_Injector)`** -To deserialize the state of the system sent from the the prior process, and to -serialize the the current state of the system and send it to the next process, -the Baggage API provides a function which returns a baggage-specific -implementation of the HTTPExtract and HTTPInject functions. +**`GetCorrelationPropagator() -> (HTTP_Extractor, HTTP_Injector)`** +To deserialize the previous labels set by prior processes, and to serialize the +current total set of labels and send them to the next process, the Correlations +API provides a function which returns a Correlation-specific implementation of +the `HTTPExtract` and `HTTPInject` functions found in the Propagation API. # Context Propagation @@ -221,15 +241,15 @@ system, in order to understand if requests to `service C` are slower or faster than `service B`. What might `service A` look like? ## Global initialization -First, during program initialization, `service A` configures baggage and tracing +First, during program initialization, `service A` configures correlation and tracing propagation, and include them in the global list of injectors and extractors. Let's assume this tracing system is configured to use B3, and has a specific propagator for that format. Initializating the propagators might look like this: ```php func InitializeOpentelemetry() { - // create the propagators for tracing and baggage. - bagExtract, bagInject = Baggage::HTTPPropagator() + // create the propagators for tracing and correlations. + bagExtract, bagInject = Correlations::HTTPPropagator() traceExtract, traceInject = Tracer::B3Propagator() // add the propagators to the global list. @@ -240,13 +260,13 @@ func InitializeOpentelemetry() { ## Extracting and injecting from HTTP headers These propagators can then be used in the request handler for `service A`. The -tracing and baggage concerns use the context object to handle state without +tracing and correlations concerns use the context object to handle state without breaking the encapsulation of the functions they are embedded in. ```php func ServeRequest(context, request, project) -> (context) { // Extract the context from the HTTP headers. Because the list of - // extractors includes a trace extractor and a baggage extractor, the + // extractors includes a trace extractor and a correlations extractor, the // contents for both systems are included in the request headers into the // returned context. extractors = Propagation::GetExtractors() @@ -259,7 +279,7 @@ func ServeRequest(context, request, project) -> (context) { // Determine the version of the client, in order to handle the data // migration and allow new clients access to a data source that older // clients are unaware of. - version = Baggage::GetBaggage( context, "client-version") + version = Correlations::GetCorrelation( context, "client-version") switch( version ){ case "v1.0": @@ -280,7 +300,7 @@ func FetchDataFromServiceB(context) -> (context, data) { request = NewRequest([request options]) // Inject the contexts to be propagated. Note that there is no direct - // reference to tracing or baggage. + // reference to tracing or correlations. injectors = Propagation::GetInjectors() context = Propagation::Inject(context, injectors, request.Headers) @@ -307,7 +327,7 @@ func ServeRequest(request, project) { Tracer::StartSpan([span options]) - version = Baggage::GetBaggage("client-version") + version = Correlations::GetCorrelation("client-version") switch( version ){ case "v1.0": @@ -429,7 +449,7 @@ For example, if a concern wanted to merge the data beween two contexts, at least one of them will not be the current context. ```php -mergedContext = MergeBaggage( Context::GetCurrent(), otherContext) +mergedContext = MergeCorrelations( Context::GetCurrent(), otherContext) Context::SetCurrent(mergedContext) ``` @@ -453,26 +473,17 @@ otherContext = ExtractWithContext(Context::GetCurrent(), headers) ![drawing](img/context_propagation_details.png) -## Context details -OpenTelemetry currently intends to implement three context types of context -propagation. +## Default HTTP headers +OpenTelemetry currently uses two standard header formats for context propagation. +Their properties and requirements are integrated into the OpenTelemetry APIs. -**Span Context -** The serializable portion of a span, which is injected and -extracted. The readable attributes are defined to match those found in the -[W3C Trace Context specification](https://www.w3.org/TR/trace-context/). +**Span Context -** The OpenTelemetry Span API is modeled on the `traceparent` +and `tracestate` headers defined in the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). -**Baggage -** Transaction-level application data, meant to be shared with -all services. This data is readable, and must be propagated in-band. -Because of this, Baggage should be used sparingly, to avoid ballooning the size -of every request. +**Correlation Context -** The OpenTelemetry Correlations API is modeled on the +`Correlation-Context` headers defined in the [W3C Correlation Context specification](https://w3c.github.io/correlation-context/). -Note that OpenTelemetry APIs calls should *always* be given access to the entire -context object, and never just a subset of the context, such as the value in a -single key. This allows the SDK to make improvements and leverage additional -data that may be available, without changes to all of the call sites. - - -## Context Management and in-process propagation +## Context management and in-process propagation In order for Context to function, it must always remain bound to the execution of code it represents. By default, this means that the programmer must pass a @@ -481,20 +492,17 @@ provide automated context management facilities, such as thread locals. OpenTelemetry should leverage these facilities when available, in order to provide automatic context management. -## Pre-existing Context implementations +## Pre-existing context implementations -In some languages, a single, widely used Context implementation exists. In other -languages, there many be too many implementations, or none at all. For example, +In some languages, a single, widely used context implementation exists. In other +languages, there many be too many implementations, or none at all. For example, Go has a the `context.Context` object, and widespread conventions for how to -pass it down the call stack. +pass it down the call stack. Java has MDC, along with several other context +implementations, but none are so widely used that their presence can be +guranteed or assumed. In the cases where an extremely clear, pre-existing option is not available, -OpenTelemetry should provide its own Context implementation. - -## Default Propagators - -When available, OpenTelemetry defaults to propagating via HTTP header -definitions which have been standardized by the W3C. +OpenTelemetry should provide its own context implementation. # FAQ @@ -532,7 +540,6 @@ Related work on HTTP propagators has not been completed yet. accepted. * Work on [W3C Correlation-Context](https://w3c.github.io/correlation-context/) has begun, but was halted to focus on Trace-Context. -* No work has begun on a theoretical W3C Baggage-Context. Given that we must ship with working propagators, and the W3C specifications are not yet complete, how should we move forwards with implementing context From 354a74c8ac76bc3bbc290c29f8ddb08455320998 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 16 Dec 2019 15:24:14 -0800 Subject: [PATCH 60/77] Fix the intro paragraph for Correlations * git merges ate my homework * some sentences were out of order --- text/0066-separate-context-propagation.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 762b578f2..bc920e8e0 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -75,17 +75,18 @@ data that may be available, without changes to all of the call sites. ## Correlations API In addition to trace propagation, OpenTelemetry provides a simple mechanism for -propagating arbitrary transaction-level data, called Correlations. Correlations -implements an extremely basic form of cross-cutting concern - a transaction-level -key-value store. Values placed in the - -Event correlation is a mechanism for indexing events in one service with -attributes provided by a prior service in the same transaction. This helps to -establish a causal relationship between these events. For example, determining -that a particular browser version is associated with a failure in an image -processing service. - -Correlations is based on the [W3C Correlation-Context specification](https://w3c.github.io/correlation-context/), +propagating metric labels, called the Correlations API. Correlations are +intended for indexing events in one service with attributes provided by a prior +service in the same transaction. This helps to establish a causal relationship +between these events. For example, determining that a particular browser version +is associated with a failure in an image processing service. + +Correlations are implemented as an extremely basic cross-cutting concern - a +transaction-level dictionary. All key-value pairs are stored in the context +object, and then injected by the Correlation propagator to be sent to the next +process. + +The Correlations API is based on the [W3C Correlation-Context specification](https://w3c.github.io/correlation-context/), and implements the protocol as it is defined in that working group. While Correlations can be used to prototype other cross-cutting concerns, this From 8a09d997c318dd264c5c31cf88fd4af5b5ad9656 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 16 Dec 2019 15:31:17 -0800 Subject: [PATCH 61/77] Clarify that correltions must be propagated --- text/0066-separate-context-propagation.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index bc920e8e0..781f30a31 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -99,7 +99,9 @@ For backwards compatibility, OpenTracing Baggage is propagated as Correlations when using the OpenTracing bridge. As every correlation adds additional overhead to every request, correlations -should be used with care. +should be used with care. The implementation MUST propagate these values. It is +entirely on the user to choose when to drop correlations, either by removing +them via the API, or by filtering them via a network proxy or service mesh. **`GetCorrelation(context, key) -> value`** To access the value for a label set by a prior event, the Correlations API @@ -535,12 +537,7 @@ Prior art: # Open questions -Related work on HTTP propagators has not been completed yet. - -* [W3C Trace-Context](https://www.w3.org/TR/trace-context/) candidate is not yet - accepted. -* Work on [W3C Correlation-Context](https://w3c.github.io/correlation-context/) - has begun, but was halted to focus on Trace-Context. +The correlations API is related to the [W3C Correlation-Context](https://w3c.github.io/correlation-context/) specification. Work on this specification has begun, but Given that we must ship with working propagators, and the W3C specifications are not yet complete, how should we move forwards with implementing context From e009b65754dc83f69d02df7576ced69b59131a71 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 16 Dec 2019 15:35:44 -0800 Subject: [PATCH 62/77] Clarify risks --- text/0066-separate-context-propagation.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 781f30a31..7a9f478dc 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -535,13 +535,12 @@ Prior art: * OpenTracing spans * gRPC context -# Open questions +# Risks -The correlations API is related to the [W3C Correlation-Context](https://w3c.github.io/correlation-context/) specification. Work on this specification has begun, but - -Given that we must ship with working propagators, and the W3C specifications are -not yet complete, how should we move forwards with implementing context -propagation? +The Correlations API is related to the [W3C Correlation-Context](https://w3c.github.io/correlation-context/) +specification. Work on this specification has begun, but is not complete. While +unlikely, it is possible that this W3C specification could diverge from the +design or guarantees needed by the Correlations API. # Future possibilities From b58e30d89543cf802140bd7c7c252f7b59f8346a Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 16 Dec 2019 15:54:09 -0800 Subject: [PATCH 63/77] removed extra header --- text/0066-separate-context-propagation.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 7a9f478dc..90dafcb60 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -38,8 +38,6 @@ This RFC addresses the following topics: example: A/B testing, encrypted or authenticated data, and new, experimental forms of observability. -## Explanation - # OpenTelemetry layered architecture From 3fa79e9187b65b484b324841b8354d89ebd7e083 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 16 Dec 2019 16:06:32 -0800 Subject: [PATCH 64/77] Clarify definition of aspect-oriented programming --- text/0066-separate-context-propagation.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 90dafcb60..e58ac52b3 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -43,13 +43,22 @@ This RFC addresses the following topics: The design of OpenTelemetry is based on the priciples of [aspect-oriented programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming), -adopted to the needs of distributed systems. - -OpenTelemetry is separated into two layers. The top layer contains a set of -independent **cross-cutting concerns**, which intertwine with a program's -application logic, and cannot be cleanly encapsulated. All concerns share an -underlying distributed **context propagation** layer, for storing state and -accessing data across the lifespan of a distributed transaction. +adopted to the needs of distributed systems. + +Some concerns "cut across" multiple abstractions in a program. Logging +exemplifies aspect orientation because a logging strategy necessarily affects +every logged part of the system. Logging thereby "cross-cuts" across all logged +classes and methods. Distributed tracing takes this strategy to the next level, +and cross-cuts across all classes and methods in all services in the entire +transaction. This requires a distributed form of the same aspect-oriented +programming principles in order to be implemented cleanly. + +OpenTelemetry approaches this by separating it's design into two layers. The top +layer contains a set of independent **cross-cutting concerns**, which intertwine +with a program's application logic and cannot be cleanly encapsulated. All +concerns share an underlying distributed **context propagation** layer, for +storing state and accessing data across the lifespan of a distributed +transaction. # Cross-Cutting Concerns From acc6d61f678329aa54f555f14cd5a15c897e2ef0 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 16 Dec 2019 16:27:57 -0800 Subject: [PATCH 65/77] Fix RemoveCorrelation --- text/0066-separate-context-propagation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index e58ac52b3..3199e35f2 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -121,8 +121,8 @@ which contains the new value. **`RemoveCorrelation(context, key) -> context`** To delete a label, the Correlations API provides a function -which takes a context, a key, and a value as input, and returns an updated -context which contains the new value. +which takes a context and a key as input, and returns an updated context which +no longer contains the selected key-value pair. **`ClearCorrelations(context) -> context`** To avoid sending any labels to an untrusted process, the Correlation API From 2bab4da8576675ffb924d9a13d710b97056d1737 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 17 Dec 2019 15:29:41 -0800 Subject: [PATCH 66/77] Spelling --- text/0066-separate-context-propagation.md | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 3199e35f2..e80cde44f 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -12,7 +12,7 @@ * [Examples](#Examples) * [Global initialization](#Global-initialization) * [Extracting and injecting from HTTP headers](#Extracting-and-injecting-from-HTTP-headers) - * [Simplfy the API with automated context propagation](#Simplfy-the-API-with-automated-context-propagation) + * [Simplify the API with automated context propagation](#Simplify-the-API-with-automated-context-propagation) * [Implementing a propagator](#Implementing-a-propagator) * [Implementing a concern](#Implementing-a-concern) * [The scope of current context](#The-scope-of-current-context) @@ -28,7 +28,7 @@ operate on a shared context propagation mechanism. This RFC addresses the following topics: -**Separatation of concerns** +**Separation of concerns** * Remove the Tracer dependency from context propagation mechanisms. * Handle user data (Correlations) and observability data (SpanContext, etc) separately. @@ -41,7 +41,7 @@ This RFC addresses the following topics: # OpenTelemetry layered architecture -The design of OpenTelemetry is based on the priciples of [aspect-oriented +The design of OpenTelemetry is based on the principles of [aspect-oriented programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming), adopted to the needs of distributed systems. @@ -99,7 +99,7 @@ and implements the protocol as it is defined in that working group. While Correlations can be used to prototype other cross-cutting concerns, this mechanism is primarily intended to convey values for the OpenTelemetry observability systems. New concerns with different criteria should be modeled -seperately, using the same underlying context propagation layer as building +separately, using the same underlying context propagation layer as building blocks. For backwards compatibility, OpenTracing Baggage is propagated as Correlations @@ -254,7 +254,7 @@ than `service B`. What might `service A` look like? First, during program initialization, `service A` configures correlation and tracing propagation, and include them in the global list of injectors and extractors. Let's assume this tracing system is configured to use B3, and has a specific -propagator for that format. Initializating the propagators might look like this: +propagator for that format. Initializing the propagators might look like this: ```php func InitializeOpentelemetry() { @@ -323,7 +323,7 @@ func FetchDataFromServiceB(context) -> (context, data) { ## Simplify the API with automated context propagation In this version of pseudocode above, we assume that the context object is -explict,and is pass and returned from every function as an ordinary parameter. +explicit, and is pass and returned from every function as an ordinary parameter. This is cumbersome, and in many languages, a mechanism exists which allows context to be propagated automatically. @@ -417,11 +417,11 @@ assume again that the context is passed implicitly in a thread local. ## The scope of current context Let's look at a couple other scenarios related to automatic context propagation. -When are the values in the current context available? Scope managemenent may be -different in each langauge, but as long as the scope does not change (by -switching threads, for example) the current context follows the execuption of +When are the values in the current context available? Scope management may be +different in each language, but as long as the scope does not change (by +switching threads, for example) the current context follows the execution of the program. This includes after a function returns. Note that the context -objects themselves are immutable, so explict handles to prior contexts will not +objects themselves are immutable, so explicit handles to prior contexts will not be updated when the current context is changed. ```php @@ -450,12 +450,12 @@ func DoWork(){ ``` ## Referencing multiple contexts -If context propagation is automantic, does the user ever need to reference a +If context propagation is automatic, does the user ever need to reference a context object directly? Sometimes. Even when automated context propagation is an available option, there is no restriction which says that concerns must only ever access the current context. -For example, if a concern wanted to merge the data beween two contexts, at +For example, if a concern wanted to merge the data between two contexts, at least one of them will not be the current context. ```php @@ -464,7 +464,7 @@ Context::SetCurrent(mergedContext) ``` ## Falling back to explicit contexts -Sometimes, suppling an additional version of a function which uses explict +Sometimes, suppling an additional version of a function which uses explicit contexts is necessary, in order to handle edge cases. For example, in some cases an extracted context is not intended to be set as current context. An alternate extract method can be added to the API to handle this. @@ -509,7 +509,7 @@ languages, there many be too many implementations, or none at all. For example, Go has a the `context.Context` object, and widespread conventions for how to pass it down the call stack. Java has MDC, along with several other context implementations, but none are so widely used that their presence can be -guranteed or assumed. +guaranteed or assumed. In the cases where an extremely clear, pre-existing option is not available, OpenTelemetry should provide its own context implementation. @@ -551,7 +551,7 @@ design or guarantees needed by the Correlations API. # Future possibilities -Cleanly splitting OpenTelemetry into Apects and Context Propagation layer may +Cleanly splitting OpenTelemetry into Aspects and Context Propagation layer may allow us to move the Context Propagation layer into its own, stand-alone project. This may facilitate adoption, by allowing us to share Context Propagation with gRPC and other projects. From 2386f069d736f2867561a97273aba9e38120928c Mon Sep 17 00:00:00 2001 From: Ted Young Date: Wed, 18 Dec 2019 16:11:07 -0800 Subject: [PATCH 67/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Sergey Kanzhelev --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index e80cde44f..b41926c88 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -67,7 +67,7 @@ transaction. Distributed tracing is one example of a cross-cutting concern. Tracing code is interleaved with regular code, and ties together independent code modules which would otherwise remain encapsulated. Tracing is also distributed, and requires -non-local, transaction-level context propagation in order to execute correctly. +transaction-level context propagation in order to execute correctly. The various observability APIs are not described here directly. However, in this new design, all observability APIs would be modified to make use of the generalized From 286fb0a8e6c6d488ca4a6a4c997a7d43524d20b2 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Mon, 13 Jan 2020 23:08:47 -0800 Subject: [PATCH 68/77] Clarifying details --- text/0066-separate-context-propagation.md | 67 +++++++++++++++++++--- text/img/context_propagation_overview.png | Bin 0 -> 35090 bytes 2 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 text/img/context_propagation_overview.png diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index b41926c88..1d97dcb3f 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -21,7 +21,9 @@ * [Internal details](#Internal-details) * [FAQ](#faq) -Refactor OpenTelemetry into a set of separate cross-cutting concerns which +![drawing](img/context_propagation_overview.png) + +A proposal to refactor OpenTelemetry into a set of separate cross-cutting concerns which operate on a shared context propagation mechanism. # Motivation @@ -29,14 +31,21 @@ operate on a shared context propagation mechanism. This RFC addresses the following topics: **Separation of concerns** -* Remove the Tracer dependency from context propagation mechanisms. -* Handle user data (Correlations) and observability data (SpanContext, etc) - separately. +* Cleaner package layout results in an easier to learn system. It is possible to + understand Context Propagation without needing to understand Observability. +* Allow for multiple types of context propagation, each self contained with + different rules. For example, TraceContext may be sampled, while + CorrelationContext never is. +* Allow the Observability and Contet Propagation to have different defaults. + The Observability systems ships with a no-op implementation and a pluggable SDK, + the context propagation system ships with a canonical, working implementation. **Extensibility** +* A clean separation allows the context propagation mechanisms to be used on + their own, so they may be consumed by other systems which do not want to + depend on an observability tool for their non-observability concerns. * Allow developers to create new applications for context propagation. For - example: A/B testing, encrypted or authenticated data, and new, experimental - forms of observability. + example: A/B testing, authentication, and network switching. # OpenTelemetry layered architecture @@ -166,8 +175,6 @@ provides a function which takes no arguments and returns a Context. To associate a context with program execution, the Context API provides a function which takes a Context. - - ## Propagation API Cross-cutting concerns send their state to the next process via propagators: @@ -301,7 +308,7 @@ func ServeRequest(context, request, project) -> (context) { context = request.Response(context, data) // End the current span - context = Tracer::EndSpan(context) + Tracer::EndSpan(context) return context } @@ -483,6 +490,48 @@ otherContext = ExtractWithContext(Context::GetCurrent(), headers) ![drawing](img/context_propagation_details.png) +## Example Package Layout +``` + Context + ContextAPI + Observability + Correlations + CorrelationAPI + HttpInjector + HttpExtractor + Metrics + MetricAPI + Trace + TracerAPI + HttpInjector + HttpExtractor + Propagation + Registry + HttpInjectorInterface + HttpExtractorInterface +``` + +## Edge Cases +There are some complications that can arise when managing a span context extracted off the wire and in-process spans for tracer operations that operate on an implicit parent. In order to ensure that a context key references an object of the expected type and that the proper implicit parent is used, the following conventions have been established: + + +### Extract +When extracting a remote context, the extracted span context MUST be stored separately from the current span. + +### Default Span Parentage +When a new span is created from a context, a default parent for the span can be assigned. The order is of assignment is as follows: + +* The current span. +* The extracted span. +* The root span. + +### Inject +When injecting a span to send over the wire, the default order is of +assignment is as follows: + +* The current span. +* The extracted span. + ## Default HTTP headers OpenTelemetry currently uses two standard header formats for context propagation. Their properties and requirements are integrated into the OpenTelemetry APIs. diff --git a/text/img/context_propagation_overview.png b/text/img/context_propagation_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..70afb512f12075b6523d3d21d5621f62d1577e14 GIT binary patch literal 35090 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 Gzxi)A3Ud+w literal 0 HcmV?d00001 From 3f5ef37f28a0a38a252c3c6b669f42c2dcb7ef79 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Tue, 14 Jan 2020 07:59:26 -0800 Subject: [PATCH 69/77] Update python prototype --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 1d97dcb3f..1571365ec 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -231,7 +231,7 @@ takes an injector. **Erlang:** https://github.com/open-telemetry/opentelemetry-erlang-api/pull/4 **Go:** https://github.com/open-telemetry/opentelemetry-go/pull/381 **Java:** https://github.com/open-telemetry/opentelemetry-java/pull/655 -**Python:** https://github.com/open-telemetry/opentelemetry-python/pull/278 +**Python:** https://github.com/open-telemetry/opentelemetry-python/pull/325 **Ruby:** https://github.com/open-telemetry/opentelemetry-ruby/pull/147 # Examples From 25a09ef63d11a169ab63e1c9946138ff4aec1834 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Wed, 15 Jan 2020 10:28:29 -0800 Subject: [PATCH 70/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Tyler Yahn --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 1571365ec..c45ed5e1b 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -185,7 +185,7 @@ requests. **`Extract(context, []http_extractor, headers) -> context`** In order to continue transmitting data injected earlier in the transaction, the Propagation API provides a function which takes a context, a set of -HTTP_Injectors, and a set of HTTP headers, and returns a new context which +HTTP_Extractors, and a set of HTTP headers, and returns a new context which includes the state sent from the prior process. **`Inject(context, []http_injector, headers) -> headers`** From a870519a2600b11cab2706b2e9aa724b8b06e132 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Wed, 15 Jan 2020 10:28:58 -0800 Subject: [PATCH 71/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Tyler Yahn --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index c45ed5e1b..1a74ffe23 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -191,7 +191,7 @@ includes the state sent from the prior process. **`Inject(context, []http_injector, headers) -> headers`** To send the data for all concerns to the next process in the transaction, the Propagation API provides a function which takes a context and a set of -HTTP_Extractors, and adds the contents of the context in to HTTP headers to +HTTP_Injectors, and adds the contents of the context in to HTTP headers to include an HTTP Header representation of the context. **`HTTP_Extractor(context, headers) -> context`** From 036705b41112c0794d25140feb4967feb23c52de Mon Sep 17 00:00:00 2001 From: Ted Young Date: Wed, 15 Jan 2020 10:29:14 -0800 Subject: [PATCH 72/77] Update text/0066-separate-context-propagation.md Co-Authored-By: Tyler Yahn --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 1a74ffe23..f15cbb634 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -190,7 +190,7 @@ includes the state sent from the prior process. **`Inject(context, []http_injector, headers) -> headers`** To send the data for all concerns to the next process in the transaction, the -Propagation API provides a function which takes a context and a set of +Propagation API provides a function which takes a context, a set of HTTP_Injectors, and adds the contents of the context in to HTTP headers to include an HTTP Header representation of the context. From a12d2456dac71d59f19051ab855d64146c2c07fc Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 16 Jan 2020 01:28:15 -0800 Subject: [PATCH 73/77] Clarify that the APIs and example code are not meant as final. Add C# prototype --- text/0066-separate-context-propagation.md | 76 ++++++++++++++++------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index f15cbb634..fd9ac1399 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -88,40 +88,47 @@ context object, and never just a subset of the context, such as the value in a single key. This allows the SDK to make improvements and leverage additional data that may be available, without changes to all of the call sites. +The following are notes on the API, and not meant as final. + +**`StartSpan(context, options) -> context`** +When a span is started, a new context is returned, with the new span set as the +current span. + +**`GetSpanPropagator() -> (HTTP_Extractor, HTTP_Injector)`** +When a span is extracted, the extracted value is stored in the context seprately +from the current span. + + ## Correlations API In addition to trace propagation, OpenTelemetry provides a simple mechanism for -propagating metric labels, called the Correlations API. Correlations are -intended for indexing events in one service with attributes provided by a prior -service in the same transaction. This helps to establish a causal relationship -between these events. For example, determining that a particular browser version -is associated with a failure in an image processing service. - -Correlations are implemented as an extremely basic cross-cutting concern - a -transaction-level dictionary. All key-value pairs are stored in the context -object, and then injected by the Correlation propagator to be sent to the next -process. +propagating indexes, called the Correlations API. Correlations are +intended for indexing observability events in one service with attributes +provided by a prior service in the same transaction. This helps to establish a +causal relationship between these events. For example, determining that a +particular browser version is associated with a failure in an image processing +service. The Correlations API is based on the [W3C Correlation-Context specification](https://w3c.github.io/correlation-context/), -and implements the protocol as it is defined in that working group. +and implements the protocol as it is defined in that working group. There are +few details provided here as it is outside the scope of this OTEP to finalize +this API. While Correlations can be used to prototype other cross-cutting concerns, this mechanism is primarily intended to convey values for the OpenTelemetry -observability systems. New concerns with different criteria should be modeled -separately, using the same underlying context propagation layer as building -blocks. +observability systems. For backwards compatibility, OpenTracing Baggage is propagated as Correlations -when using the OpenTracing bridge. +when using the OpenTracing bridge. New concerns with different criteria should +be modeled separately, using the same underlying context propagation layer as +building blocks. -As every correlation adds additional overhead to every request, correlations -should be used with care. The implementation MUST propagate these values. It is -entirely on the user to choose when to drop correlations, either by removing -them via the API, or by filtering them via a network proxy or service mesh. +The following is an example API, and not meant as final. **`GetCorrelation(context, key) -> value`** To access the value for a label set by a prior event, the Correlations API -provides a function which takes a context and a key as input, and returns a value. +provides a function which takes a context and a key as input, and returns a +value. **`SetCorrelation(context, key, value) -> context`** To record the value for a label, the Correlations API provides a function which @@ -151,14 +158,27 @@ Cross-cutting concerns access data in-process using the same, shared context object. Each concern uses its own namespaced set of keys in the context, containing all of the data for that cross-cutting concern. +The following is an example API, and not meant as final. + +**`CreateKey(name) -> key`** +To allow concerns to control access to their data, the Context API uses keys +which cannot be guessed by third parties which have not been explictly handed +the key. It is recommended that concerns mediate data access via an API, rather +than provide direct public access to their keys. + **`GetValue(context, key) -> value`** To access the local state of an concern, the Context API provides a function which takes a context and a key as input, and returns a value. **`SetValue(context, key, value) -> context`** To record the local state of a cross-cutting concern, the Context API provides a -function which takes a context, a key, and a value as input, and returns an -updated context which contains the new value. +function which takes a context, a key, and a value as input, and returns a +new context which contains the new value. Note that the new value is not present +in the old context. + +**`RemoveValue(context, key) -> context`** +RemoveValue returns a new context with the key cleared. Note that the removed +value still remains present in the old context. ### Optional: Automated Context Management @@ -182,6 +202,8 @@ functions which read and write context into RPC requests. Each concern creates a set of propagators for every type of supported medium - currently only HTTP requests. +The following is an example API, and not meant as final. + **`Extract(context, []http_extractor, headers) -> context`** In order to continue transmitting data injected earlier in the transaction, the Propagation API provides a function which takes a context, a set of @@ -232,11 +254,17 @@ takes an injector. **Go:** https://github.com/open-telemetry/opentelemetry-go/pull/381 **Java:** https://github.com/open-telemetry/opentelemetry-java/pull/655 **Python:** https://github.com/open-telemetry/opentelemetry-python/pull/325 -**Ruby:** https://github.com/open-telemetry/opentelemetry-ruby/pull/147 +**Ruby:** https://github.com/open-telemetry/opentelemetry-ruby/pull/147 +**C#/.NET:** https://github.com/open-telemetry/opentelemetry-dotnet/pull/399 # Examples -It might be helpful to look at an example, written in pseudocode. Let's describe +It might be helpful to look at some examples, written in pseudocode. Note that +the pseudocode only uses simple functions and immutable values. Most mutable, +object-orient languages will use objects, such as a Span object, to encapsulate +the context object and hide it from the user in most cases. + +Let's describe a simple scenario, where `service A` responds to an HTTP request from a `client` with the result of a request to `service B`. From f30681006cd700aa2595983933a3d631f4b179f7 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 16 Jan 2020 16:49:27 -0800 Subject: [PATCH 74/77] Inject returns headers, not context --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index fd9ac1399..3084945ce 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -347,7 +347,7 @@ func FetchDataFromServiceB(context) -> (context, data) { // Inject the contexts to be propagated. Note that there is no direct // reference to tracing or correlations. injectors = Propagation::GetInjectors() - context = Propagation::Inject(context, injectors, request.Headers) + request.Headers = Propagation::Inject(context, injectors, request.Headers) // make an http request data = request.Do() From b7efba5b7eb4c279648b92fef659ababe4f82d39 Mon Sep 17 00:00:00 2001 From: Ted Young Date: Thu, 16 Jan 2020 23:04:17 -0800 Subject: [PATCH 75/77] Update text/0066-separate-context-propagation.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mauricio Vásquez --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index 3084945ce..d1c916e5c 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -36,7 +36,7 @@ This RFC addresses the following topics: * Allow for multiple types of context propagation, each self contained with different rules. For example, TraceContext may be sampled, while CorrelationContext never is. -* Allow the Observability and Contet Propagation to have different defaults. +* Allow the Observability and Context Propagation to have different defaults. The Observability systems ships with a no-op implementation and a pluggable SDK, the context propagation system ships with a canonical, working implementation. From 630d9546901f4b90efc0454a74b6097ea8bbc723 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Thu, 16 Jan 2020 23:05:25 -0800 Subject: [PATCH 76/77] spelling --- text/0066-separate-context-propagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index d1c916e5c..b56241261 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -162,7 +162,7 @@ The following is an example API, and not meant as final. **`CreateKey(name) -> key`** To allow concerns to control access to their data, the Context API uses keys -which cannot be guessed by third parties which have not been explictly handed +which cannot be guessed by third parties which have not been explicitly handed the key. It is recommended that concerns mediate data access via an API, rather than provide direct public access to their keys. From a43b5d790596c64db0e323d33526bbf1faa24ef3 Mon Sep 17 00:00:00 2001 From: tedsuo Date: Fri, 17 Jan 2020 15:24:18 -0800 Subject: [PATCH 77/77] remove spurious go comment --- text/0066-separate-context-propagation.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/text/0066-separate-context-propagation.md b/text/0066-separate-context-propagation.md index b56241261..46b05ce45 100644 --- a/text/0066-separate-context-propagation.md +++ b/text/0066-separate-context-propagation.md @@ -603,14 +603,6 @@ implementation details behind the Propagator interface. Therefore, the propagation system itself does not need to provide an mechanism for chaining together propagators or other additional facilities. - -## Did you add a context parameter to every API call because Go has infected your brain? - -No. The concept of an explicit context is fundamental to a model where -independent cross-cutting concerns share the same context propagation layer. -How this context appears or is expressed is language specific, but it must be -present in some form. - # Prior art and alternatives Prior art: