Recommend JSON object encoding of an attribute for non-OTLP - #5028
Conversation
Can you clarify what this applies to? I can think of a couple of potential candidates:
Is this what you had in mind? Any other contexts you know of at the moment? |
|
@jack-berg, mostly
I thought that maybe some exporters may need a representation of a single attribute (we already have a defintion how to encode a map of AnyValue which is equivalent to a collection of attributes). This is why I decided to also include "For non-OTLP protocols". |
|
I am not sure I understand the purpose of this PR. Where is this representation is supposed to be used? For Zipkip for example we have specific guideline on how to represent attributes: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#attribute If this is just for debugging purposes I why don't we use canonical OTLP JSON representation? |
|
@tigrannajaryan, thanks, that is a fair question. I do not think this PR should be understood as redefining Zipkin, Prometheus, or other protocol-specific mappings, and I also do not think it should require every debugging exporter to use this exact output.
The narrower purpose is to fill a gap in the existing non-OTLP string representation guidance. The specification already has stable guidance for representing
For protocols with their own mapping, that mapping remains authoritative. Zipkin is a good example: the Zipkin exporter spec already says that OpenTelemetry span attributes are reported as Zipkin
On "why not canonical OTLP JSON": OTLP JSON is the right answer when the goal is to emit OTLP. For example, the File Exporter spec explicitly serializes telemetry data using OTLP JSON. However, the existing and stable non-OTLP I think it is worth noting that, PR #4848 added that guidance, and the stable Prometheus/OpenMetrics I updated the PR description. |
We have this in spec precisely because conversion to formats like Prometheus need well-defined conversion rules that are followed by all implementations. This is important for interoperability and definitely belongs in the spec. Debugging output format on the other hand has no such interoperability requirements, since it is for humans to read and it doesn't have the same need for strict normative specification as the data formats or network protocols have. If we want to have a centralized Otel-wide format for debugging output I would put it in a separate non-normative document, as a guidance to implementations, but not as strict requirement. If there are other use cases besides debugging output please list them. |
Yes, I agree with that, and that was the distinction I was trying to make earlier. Also, based on the earlier comments about lossy representation and type information, should we be concerned about the existing Prometheus/OpenMetrics interoperability guidance that already depends on the stable non-OTLP
As OTel Go maintainers, we want to use an OTel-wide representation for debugging output. I agree that debugging guidance itself can live in a separate non-normative document and this is not the concern of this PR (thus #5028 (comment)). However, the document should refer to a stable representation defined by the spec, rather than define the representation itself. In other words, I would prefer this layering:
So the immediate use case is OTel Go debugging/logging output, but the reason for this PR is to define the representation that such guidance can point to. |
|
@trask do you see anything blocking in here? |
| **Status**: [Development](../document-status.md) | ||
|
|
||
| For non-OTLP protocols that need to | ||
| represent a single `Attribute` as a string, the RECOMMENDED form is a |
There was a problem hiding this comment.
To me this seems duplicative of the "#### Maps" section on line 168. It already describes how to encode using what I believe is the same output.
An attribute set is analogous to a map. A single attribute is analogous to a single entry map.
Rather than repeating that section, could we make the (obvious) analogy between attributes and maps, and link over to the existing maps section for how it should be represented?
There was a problem hiding this comment.
A single attribute is analogous to a single entry map.
Indeed. And this is what this section is specifying.
Rather than repeating that section, could we make the (obvious) analogy between attributes and maps, and link over to the existing maps section for how it should be represented?
I am not repeating the whole section. Note that this PR is already linking to the maps section.
Also note that e.g. https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#maps does not tell anything about the attribute name/value. I did my best to refer to the existing specification while also adding the missing parts. This is why this PR has e.g.
The attribute key SHOULD be represented as a JSON object member
name.
Other reviewers also asked to have more examples and have this section to be more clear (even it if is more verbose).
An attribute set is analogous to a map.
It is not specified anywhere. It a subject for a separate PR as we would need it for open-telemetry/opentelemetry-go#8209. I do not want to open large PRs and I plan to create it once this PR lands.
…try#5053) ### Why Per open-telemetry#5028 (comment) The previous wording only called out a few special nested cases, such as byte arrays, empty values, and special floating point values. This made the array and map sections less explicit than the top-level `AnyValue` representation guidance. The updated wording keeps the same intended representation, but makes the nested rules clearer and avoids duplicating full guidance between arrays and maps. ### What Clarifies how nested `AnyValue` values are represented in the non-OTLP representation guidance for arrays and maps. This PR: - makes the array representation explicitly link to JSON arrays; - documents how each nested `AnyValue` type should be represented as a JSON value; - clarifies that map keys are JSON string member names; - clarifies that map values follow the same representation rules as array elements;
…emetry#5028) ### What Adds in-development guidance for representing a single OpenTelemetry attribute as a string in non-OTLP protocols. The recommended form is a single-entry JSON object, for example: ```json {"http.request.method":"GET"} ``` **Implementation:** - open-telemetry/opentelemetry-go#8205 in OTel Go we are using non-OTLP string represnetation for debugging ### Why - open-telemetry/opentelemetry-go#7810 The narrower purpose is to fill a gap in the existing non-OTLP string representation guidance. The specification already has stable guidance for representing `AnyValue` values as strings when a non-OTLP protocol cannot represent some `AnyValue` types natively. What is missing is the corresponding representation for a single `Attribute`, which is a key plus an `AnyValue`. This PR proposes that the single attribute is represented as a single-member JSON object, with the member value following the existing non-OTLP `AnyValue` representation rules. I originally thought about `key:value` (and even `key=value` beforehand), but it is ambiguous when `:` appears in keys or string values and is not a robust debugging representation. Using a JSON object improves clarity for debugging output by: - aligning with existing non-OTLP, JSON-based guidance for complex `AnyValue` types - making the representation easier to read and copy when needed This also is consistent with how OTel Java, .NET, Python, Go are representing attributes in standard output exporters. Therefore, I think it would be most familiar representation for the users. Why not OTLP/JSON? For the OTel Go use case, the compact (lossy) non-OTLP representation is practical for human-readable output such as test failures. `String()` is intended for debugging and logging, and its output is not expected to be lossless nor suitable for deserialization. A canonical OTLP JSON `KeyValue` would need wrapper fields such as `{"key":"http.request.method","value":{"stringValue":"GET"}}`, which is much more verbose than `{"http.request.method":"GET"}` and harder to scan in failure messages. Moreover, OTel Go `attribute.Value.String()` already follows the non-OTLP `AnyValue` rules: <open-telemetry/opentelemetry-go#8142> (as OTel Go maintainers, we want to use an OTel-wide representation even for debugging output). We want `attribute.KeyValue.String()` to follow the same rules, but the string representation of a single attribute is not yet specified; this PR fills that gap and is currently blocking <open-telemetry/opentelemetry-go#8205>.
…try#5053) ### Why Per open-telemetry#5028 (comment) The previous wording only called out a few special nested cases, such as byte arrays, empty values, and special floating point values. This made the array and map sections less explicit than the top-level `AnyValue` representation guidance. The updated wording keeps the same intended representation, but makes the nested rules clearer and avoids duplicating full guidance between arrays and maps. ### What Clarifies how nested `AnyValue` values are represented in the non-OTLP representation guidance for arrays and maps. This PR: - makes the array representation explicitly link to JSON arrays; - documents how each nested `AnyValue` type should be represented as a JSON value; - clarifies that map keys are JSON string member names; - clarifies that map values follow the same representation rules as array elements;
open-telemetry#5110) Follows open-telemetry#5028 Per open-telemetry#5028 (comment): >> An attribute set is analogous to a map. > > It is not specified anywhere. It a subject for a separate PR as we would need it for open-telemetry/opentelemetry-go#8209. I do not want to open large PRs and I plan to create it once this PR lands. It will also unblock open-telemetry/opentelemetry-go#8209 (after being stable).
open-telemetry#5110) Follows open-telemetry#5028 Per open-telemetry#5028 (comment): >> An attribute set is analogous to a map. > > It is not specified anywhere. It a subject for a separate PR as we would need it for open-telemetry/opentelemetry-go#8209. I do not want to open large PRs and I plan to create it once this PR lands. It will also unblock open-telemetry/opentelemetry-go#8209 (after being stable).
### Context - Clarify that environment variable propagation carriers normalize requested keys, carrier keys, and returned keys. ([open-telemetry#5102](open-telemetry#5102)) - Specify that environment variable propagation carriers only read and return normalized environment variable names. ([open-telemetry#5144](open-telemetry#5144)) - Specify that an empty environment variable propagation name is non-normalized and normalizes to `_`. ([open-telemetry#5163](open-telemetry#5163)) ### Profiles - Remove duplicate information from and extend Profiles documentation (README.md, pprof.md). ([open-telemetry#4932](open-telemetry#4932)) ### Entities - Add specification for communicating entity information as structured log events. ([open-telemetry#4836](open-telemetry#4836)) ### Common - Add an in-development [SDK self-observability](specification/self-observability.md) section, referenced from the Tracing, Metrics, and Logs SDK specs. ([open-telemetry#5087](open-telemetry#5087)) - Clarify non-OTLP representation guidance for nested `AnyValue` values in arrays and maps. ([open-telemetry#5053](open-telemetry#5053)) - Add in-development guidance recommending a JSON object as the string representation for an attribute in non-OTLP protocols. ([open-telemetry#5028](open-telemetry#5028)) - Add in-development guidance recommending a JSON object as the string representation for an attribute collection in non-OTLP protocols. ([open-telemetry#5110](open-telemetry#5110)) ### Compatibility - Deprecate OpenCensus compatibility requirements in the specification. ([open-telemetry#5138](open-telemetry#5138)) - Stabilize sections of Prometheus Metrics Exporter. - Clarify resource attributes configuration. ([open-telemetry#5084](open-telemetry#5084)) - Stabilize the conversion of OTLP Summaries into Prometheus Summaries. ([open-telemetry#5107](open-telemetry#5107)) - Stabilize client libs section. ([open-telemetry#5106](open-telemetry#5106)) - Stabilize Prometheus Metrics Exporter default aggregation configuration. ([open-telemetry#5113](open-telemetry#5113)) - Stabilize sections of Prometheus and OpenMetrics Compatibility. - Stabilize OpenTelemetry Histogram to Prometheus Histogram transformation. ([open-telemetry#5091](open-telemetry#5091)) - Add optional OpenTelemetry Histogram to Prometheus Native Histogram with Custom Buckets transformation. ([open-telemetry#5091](open-telemetry#5091)) ### SDK Configuration - Add link to declarative config IdGenerator type ([open-telemetry#5133](open-telemetry#5133)) ### OTEPs - Context-scoped Attributes. ([open-telemetry#4931](open-telemetry#4931)) --------- Co-authored-by: Reiley Yang <reyang@microsoft.com>
What
Adds in-development guidance for representing a single OpenTelemetry attribute as a string in non-OTLP protocols.
The recommended form is a single-entry JSON object, for example:
{"http.request.method":"GET"}Implementation:
Why
The narrower purpose is to fill a gap in the existing non-OTLP string representation guidance. The specification already has stable guidance for representing
AnyValuevalues as strings when a non-OTLP protocol cannot represent someAnyValuetypes natively. What is missing is the corresponding representation for a singleAttribute, which is a key plus anAnyValue. This PR proposes that the single attribute is represented as a single-member JSON object, with the member value following the existing non-OTLPAnyValuerepresentation rules.I originally thought about
key:value(and evenkey=valuebeforehand), but it is ambiguous when:appears in keys or string values and is not a robust debugging representation.Using a JSON object improves clarity for debugging output by:
AnyValuetypesThis also is consistent with how OTel Java, .NET, Python, Go are representing attributes in standard output exporters. Therefore, I think it would be most familiar representation for the users.
Why not OTLP/JSON?
For the OTel Go use case, the compact (lossy) non-OTLP representation is practical for human-readable output such as test failures.
String()is intended for debugging and logging, and its output is not expected to be lossless nor suitable for deserialization. A canonical OTLP JSONKeyValuewould need wrapper fields such as{"key":"http.request.method","value":{"stringValue":"GET"}}, which is much more verbose than{"http.request.method":"GET"}and harder to scan in failure messages.Moreover, OTel Go
attribute.Value.String()already follows the non-OTLPAnyValuerules: open-telemetry/opentelemetry-go#8142 (as OTel Go maintainers, we want to use an OTel-wide representation even for debugging output). We wantattribute.KeyValue.String()to follow the same rules, but the string representation of a single attribute is not yet specified; this PR fills that gap and is currently blocking open-telemetry/opentelemetry-go#8205.