GEP: Standardized Telemetry API
- Issue: #4768
- Status: Experimental
TLDR
This proposal introduces a standardized, provider-agnostic Telemetry API to configure observability signals for North/South (Gateway) traffic, addressing the fragmentation caused by vendor-specific CRDs. While a comprehensive telemetry API includes metrics, access logs, and traces, this iteration of the GEP focuses exclusively on Tracing. Future iterations will add (via this GEP or via additional GEPs) support for Logging and Metrics.
Goals
- Establish a standardized model to configure provider-agnostic tracing for Gateways.
Non-Goals
- Defining how the telemetry is exported (sinks/shippers) beyond specifying the provider endpoint and relevant connectivity parameters.
- Replacing the underlying telemetry infrastructure (OTLP collectors, Prometheus, etc.).
- Defining the API for Metrics and Access Logs configuration. These signals are out of scope for this initial tracing-focused iteration, but will be added in future iterations or separate GEPs.
Introduction / Overview
This GEP proposes the addition of a standardized, provider-agnostic Telemetry API to the Gateway API project. The proposal aims to define a unified configuration model for the generation and propagation of telemetry signals for North/South (Gateway) traffic.
The API focuses on providing a consistent way to express observability intent, such as sampling rates for tracing, regardless of the underlying data plane implementation. While a comprehensive telemetry API must account for metrics, access logs, and distributed traces, this iteration of the GEP focuses exclusively on distributed traces. Future iterations will expand this API (via this GEP or via additional GEPs) to include support for metrics and access logs.
Purpose (Why and Who)
The Fragmentation of Observability
In the current Kubernetes landscape, the “Who, What, Where, and How Long” of network traffic is answered differently depending on the underlying proxy technology. While the Gateway API specification has unified how traffic is routed via HTTPRoute and Gateway, it has deferred the standardization of how that traffic is observed. This deferral has led to “Observability Lock-in”. Platform Engineering teams are forced to learn and manage distinct APIs for each environment. A standardized telemetry API is necessary to decouple the intent of observability from the implementation. Without such standardization it is difficult for platform owners to:
- Enforce consistent auditing and observability standards across different infrastructure providers.
- Support emerging workloads like AI Agents, which elevate the criticality of observability due to their autonomous, non-deterministic nature and requirements for specialized signals.
Who
- Platform Operators: Need to ensure uniform observability across all networking infrastructure.
- Observability Teams: Responsible for the governance of telemetry data. They need to define and enforce standardized schemas and collection policies across the entire organization.
- Security/Auditing Teams: Require a standardized audit trail for all traffic, an increasingly important need with the emergence of autonomous agent actions.
- Application Developers: Benefit from consistent metrics and traces for debugging without worrying about the underlying gateway technology.
API
Policy Attachment vs. Inline Configuration
A key area of discussion for this GEP is whether this should be a standalone Policy Attachment (e.g., TelemetryPolicy) or inline configuration within Gateway or HTTPRoute resources.
This proposal argues that the Policy Attachment model is the most effective approach to meet the stated goals, primarily for two reasons:
- Separation of Concerns: It allows different personas to manage Gateway infrastructure independently from the configuration of telemetry signals. Telemetry is typically configured by platform, observability, or security engineers rather than application developers. This also implies that HTTPRoute is not the ideal resource to target for the initial API implementation.
- Uniformity: It enables a single policy to be applied uniformly across a set of Gateways, eliminating the need to duplicate complex telemetry configurations across individual resources.
To mitigate the challenge of complex merging semantics, this GEP restricts configuration such that only a single TelemetryPolicy can target a specific Gateway at any given time. If multiple TelemetryPolicy resources target the same object, precedence is determined based on the creation timestamp. This will allow us to start with simple config and iterate based on feedback whether multiple TelemetryPolicies on the same target are needed.
High-level Considerations:
- Tracing: Configuration for OTLP endpoints, sampling rates (probabilistic and parent-based), and custom resource/span attributes.
- Export Configuration: Supporting TLS connections to telemetry collectors and the ability to inject custom headers (e.g.,
Authorization) into telemetry requests.
Future Considerations:
- Metrics: Ability to enable/disable specific metric families and customize dimensions (labels/attributes).
- Access Logs: Filtering for smart logging (e.g., only log 5xx errors or high latency), multi-protocol support, and log format customization (including field selection).
Request Flow
- A platform operator creates a
TelemetryPolicyresource targeting aGateway. - The Gateway API implementation reconciles this resource and configures the underlying data plane.
- The data plane extracts the specified signals and exports them to the telemetry infrastructure.
The TelemetryPolicy Specification
We propose the TelemetryPolicy as a direct policy attachment in the gateway.networking.k8s.io API group. See GEP-713 for more information on direct attachment.
The following is an example that demonstrates the structure of the TelemetryPolicy.
apiVersion: gateway.networking.x-k8s.io/v1alpha1
kind: TelemetryPolicy
metadata:
name: standard-telemetry
namespace: prod-ns
spec:
# GEP-713 Attachment
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: my-gateway
# Tracing Configuration
tracing:
mode: "Enabled"
provider:
backendRef:
group: ""
kind: Service
name: otel-collector
namespace: monitoring
port: 4317
samplingRate:
numerator: 5 # Represents 5/100 (5%) because denominator defaults to 100
parentBasedSampling:
mode: "Enabled"
samplingRate:
numerator: 50 # Represents 50/100 (50%)
attributes:
- name: "env"
type: Literal
literalValue: "production"
- name: "mcp_tool_name"
type: Attribute
attributeKey: "gen_ai.tool.name"
Detailed Resource Description
The following are the Go structs modeling the proposed specification.
// TelemetryPolicy defines a Direct Attached Policy to configure
// telemetry/observability signals for Gateways.
//
// By applying a TelemetryPolicy, platform operators and developers can ensure
// consistent collection, formatting, and export of observability signals.
//
// <gateway:util:excludeFromCRD>
// Notes for implementors:
//
// TelemetryPolicy is a Direct Attached Policy. Implementing controllers MUST
// adhere to the Policy Attachment guidelines (GEP-713).
//
// Precedence and Conflict Resolution:
// * To prevent complex merging semantics, only a single TelemetryPolicy is
// permitted to target a specific Gateway resource at any given time.
// * If multiple TelemetryPolicy resources target the same Gateway, precedence
// MUST be determined using the following criteria, continuing on ties:
// 1. The older policy by creation timestamp takes precedence.
// 2. The policy appearing first in alphabetical order by {namespace}/{name}.
// * For any TelemetryPolicy that does not take precedence, the controller
// MUST set the `Accepted` condition on the policy status to `status: False` with
// Reason `Conflicted`.
//
// Conformance:
// Implementations MUST support the core resource structure and `targetRefs`.
// Support for the tracing block is Extended, but if supported,
// its respective conformance profile must be met.
// </gateway:util:excludeFromCRD>
//
// Support: Core (Resource shell and targetRefs), Extended (Signals)
type TelemetryPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of TelemetryPolicy.
//
// +required
Spec TelemetryPolicySpec `json:"spec"`
// Status defines the observed state of TelemetryPolicy.
//
// +optional
Status TelemetryPolicyStatus `json:"status,omitempty"`
}
// TelemetryPolicySpec defines the desired state and target of TelemetryPolicy.
//
// Specifying at least one target resource in `targetRefs` is required.
// Tracing behavior can be configured via the `tracing` field.
type TelemetryPolicySpec struct {
// TargetRefs identifies the gateways to which this policy applies (GEP-713).
//
// When configured, the telemetry settings defined in this policy are applied
// uniformly to the referenced resources. In the absence of targetRefs, the policy is
// invalid and will not be accepted.
//
// TargetRefs must be distinct.
//
// Support: Core for Gateway
//
// +required
// +kubebuilder:validation:MinItems=1
TargetRefs []NamespacedPolicyTargetReference `json:"targetRefs"`
// Tracing defines the configuration for distributed tracing.
//
// When configured, distributed tracing spans are generated and exported. In the
// absence of this configuration, tracing behavior is determined by implementation
// defaults.
//
// Support: Extended
//
// +optional
Tracing *TracingConfig `json:"tracing,omitempty"`
}
// TracingMode defines the enablement state of tracing.
type TracingMode string
const (
// TracingModeEnabled explicitly enables tracing.
TracingModeEnabled TracingMode = "Enabled"
// TracingModeDisabled explicitly disables tracing.
TracingModeDisabled TracingMode = "Disabled"
// TracingModeImplementationDefault means that the code should
// use the implementation's default behavior for tracing.
TracingModeImplementationDefault TracingMode = "ImplementationDefault"
)
// AttributeName defines the key of a span attribute or tag.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_.:/-]+$`
type AttributeName string
// AttributeSourceType defines the source from which a telemetry attribute
// value is retrieved.
//
// Support: Core
type AttributeSourceType string
const (
// AttributeSourceHeader indicates that the attribute value should be
// extracted from a specific HTTP header in the request or response.
//
// Support: Core
AttributeSourceHeader AttributeSourceType = "Header"
// AttributeSourceLiteral indicates that the attribute value is a static
// string provided directly in the policy configuration.
//
// Support: Core
AttributeSourceLiteral AttributeSourceType = "Literal"
// AttributeSourceAttribute extracts the value from a proxy-builtin reference variable
// mapped to OpenTelemetry Semantic Conventions (e.g., "http.request.method").
// See: https://opentelemetry.io/docs/specs/semconv/
//
// Support: Extended
AttributeSourceAttribute AttributeSourceType = "Attribute"
)
// Attribute defines a single flat key-value pair to attach to traces.
//
// This allows users to enrich spans with context like HTTP headers
// (e.g., "X-User-ID"), static tags, or built-in variables.
//
// Support: Core
//
// +union
// +kubebuilder:validation:XValidation:rule="self.type == 'Header' ? has(self.headerName) : !has(self.headerName)",message="headerName is required when type is Header, and must be empty otherwise"
// +kubebuilder:validation:XValidation:rule="self.type == 'Literal' ? has(self.literalValue) : !has(self.literalValue)",message="literalValue is required when type is Literal, and must be empty otherwise"
// +kubebuilder:validation:XValidation:rule="self.type == 'Attribute' ? has(self.attributeKey) : !has(self.attributeKey)",message="attributeKey is required when type is Attribute, and must be empty otherwise"
type Attribute struct {
// Name is the key of the attribute as it will appear in the output
// (i.e., as a span tag).
//
// +required
Name AttributeName `json:"name"`
// Type specifies where the attribute value comes from.
// Valid values are "Header", "Literal", or "Attribute".
//
// +unionDiscriminator
// +required
// +kubebuilder:validation:Enum=Header;Literal;Attribute
Type AttributeSourceType `json:"type"`
// HeaderName specifies the HTTP header to extract the value from.
// This is required if Type is "Header".
//
// +optional
HeaderName *v1.HTTPHeaderName `json:"headerName,omitempty"`
// LiteralValue specifies a static string value to attach.
// This is required if Type is "Literal".
//
// +optional
// +kubebuilder:validation:MaxLength=1024
LiteralValue *string `json:"literalValue,omitempty"`
// AttributeKey refers to a standard OpenTelemetry attribute.
// For example: "http.response.status_code" or "http.request.method".
// This is required if Type is "Attribute".
// See: https://opentelemetry.io/docs/specs/semconv/
//
// +optional
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
// +kubebuilder:validation:Pattern=`^[a-z0-9_.-]+$`
AttributeKey *string `json:"attributeKey,omitempty"`
}
// TracingConfig defines the configuration for distributed tracing.
//
// Distributed tracing tracks the lifecycle of an individual request as it propagates through
// the Gateway and downstream services. Each service records a segment of the request's path
// as a "span". This configuration allows platform operators to enable tracing, select the
// destination backend, control the portion of traffic sampled, and inject custom values as
// span attributes.
//
// Users get granular visibility into request latency, system bottlenecks, and execution flows
// across complex distributed systems.
//
// Support: Extended
// +kubebuilder:validation:XValidation:rule="!has(self.mode) || self.mode != 'Enabled' || has(self.provider)",message="provider must be specified when mode is Enabled"
// +kubebuilder:validation:XValidation:rule="!has(self.mode) || self.mode != 'Disabled' || !has(self.provider)",message="provider must be empty when mode is Disabled"
// +kubebuilder:validation:XValidation:rule="self.mode == 'Enabled' ? has(self.provider) : true",message="provider must be specified when mode is Enabled"
// +kubebuilder:validation:XValidation:rule="self.mode == 'Disabled' ? !has(self.provider) : true",message="provider must be empty when mode is Disabled"
type TracingConfig struct {
// Mode explicitly controls if tracing is enabled. Valid values are "Enabled", "Disabled",
// "ImplementationDefault".
//
// In the absence of this field, it defaults to "ImplementationDefault".
//
// Support: Core (within TelemetryPolicy feature)
//
// +kubebuilder:validation:Enum=Enabled;Disabled;ImplementationDefault
// +kubebuilder:default=ImplementationDefault
Mode TracingMode `json:"mode,omitempty"`
// Provider specifies the tracing collector or backend endpoint receiving OTLP spans.
//
// When configured, spans generated by the Gateway proxy are exported to this destination.
// In the absence of this field, spans are exported to an implementation-defined default sink.
//
// Support: Core (within Tracing feature)
//
// +optional
Provider *TracingProvider `json:"provider,omitempty"`
// SamplingRate specifies the base probability of sampling new traces.
//
// The sampling probability is represented as a fraction.
//
// For example, a Numerator of 5 and Denominator of 100 represents a 5% sampling rate.
// * If configured, only the specified percentage of new traces will be initiated.
// * In the absence of this field, an implementation-defined default is used.
//
// <gateway:util:excludeFromCRD>
// Notes for implementors:
//
// Permutations of numerator > denominator are invalid and MUST be rejected via validation.
// </gateway:util:excludeFromCRD>
//
// Support: Extended
//
// +optional
SamplingRate *Fraction `json:"samplingRate,omitempty"`
// ParentBasedSampling configures whether to respect the sampling decision of the parent span.
//
// * When Mode is "Enabled", the proxy will respect the upstream trace parent's sampling
// decision.
// * When Mode is "Disabled" or absent, the proxy applies its own local sampling rate
// decision.
//
// Support: Extended
//
// +optional
ParentBasedSampling *ParentBasedSampling `json:"parentBasedSampling,omitempty"`
// ServiceName is the "service.name" attribute of the OpenTelemetry resource.
// If absent, the implementation's default service name will be used.
//
// Support: Extended
//
// +optional
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
ServiceName *string `json:"serviceName,omitempty"`
// SpanName defines a custom name for the OTel span. By default, the name
// is implementation-specific.
//
// Support: Extended
//
// +optional
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
SpanName *string `json:"spanName,omitempty"`
// Attributes is a list of custom key-value pairs (or variables) attached to every span.
//
// When configured, these attributes are injected into every generated tracing span.
// In the absence of attributes, only standard proxy-defined attributes are emitted.
//
// Support: Extended
//
// +listType=map
// +listMapKey=name
// +optional
Attributes []Attribute `json:"attributes,omitempty"`
}
// TracingProvider identifies the tracing backend that receives generated spans.
//
// Support: Core for Service
//
// Support: Implementation-specific for any other resource
type TracingProvider struct {
// BackendRef is a reference to a Kubernetes Service or other supported
// backend that receives OTLP traces.
//
// When configured, tracing data is exported to the referenced backend. If the reference
// is invalid (e.g., the Service does not exist), the implementation should update the
// policy's status conditions to indicate an unresolved reference.
//
// TLS configuration for the connection to the backend is managed by the referenced
// object. For example, if the BackendRef points to a Service, a BackendTLSPolicy
// can be attached to configure TLS. Alternatively, the referenced backend could be a
// custom resource (e.g., XBackend) that natively manages TLS.
//
// Support: Core
//
// +required
BackendRef BackendObjectReference `json:"backendRef"`
// Headers specifies a list of custom headers to be added to the telemetry
// export requests (e.g., for authentication).
//
// Support: Extended
//
// +optional
// +kubebuilder:validation:MaxItems=16
Headers []v1.HTTPHeader `json:"headers,omitempty"`
}
// ParentBasedSamplingMode defines the enablement mode for parent-based sampling.
type ParentBasedSamplingMode string
const (
// ParentBasedSamplingModeEnabled explicitly enables parent-based sampling.
ParentBasedSamplingModeEnabled ParentBasedSamplingMode = "Enabled"
// ParentBasedSamplingModeDisabled explicitly disables parent-based sampling.
ParentBasedSamplingModeDisabled ParentBasedSamplingMode = "Disabled"
// ParentBasedSamplingModeImplementationDefault means that the code should
// use the implementation's default behavior for parent-based sampling.
ParentBasedSamplingModeImplementationDefault ParentBasedSamplingMode = "ImplementationDefault"
)
// ParentBasedSampling defines the sampling behavior when a request has a pre-existing upstream
// trace parent.
//
// Support: Extended
type ParentBasedSampling struct {
// Mode explicitly controls if parent-based sampling is enabled. Valid values are "Enabled",
// "Disabled", "ImplementationDefault".
//
// In the absence of this field, it defaults to "ImplementationDefault".
//
// Support: Extended
//
// +kubebuilder:validation:Enum=Enabled;Disabled;ImplementationDefault
// +kubebuilder:default=ImplementationDefault
Mode ParentBasedSamplingMode `json:"mode,omitempty"`
// SamplingRate is the sampling rate to apply when parent-based sampling is active.
//
// This acts as a downsampling governor. It allows an operator to say: "I want to
// respect the parent's decision, but only for 50% of those requests". Even if a
// parent is already marked as "Sampled", this allows the Gateway to apply a secondary
// filter so that it can respect the parent's intent while still controlling the volume
// of spans reported.
//
// In the absence of this field, it defaults to 100% ({numerator: 100}).
//
// Support: Extended
//
// +optional
// +kubebuilder:default={numerator: 100}
SamplingRate *Fraction `json:"samplingRate,omitempty"`
}
// TelemetryPolicyStatus defines the observed state of TelemetryPolicy.
type TelemetryPolicyStatus struct {
// For Policy Status API conventions, see:
// https://gateway-api.sigs.k8s.io/geps/gep-713/#the-status-stanza-of-policy-objects
//
// Ancestors is a list of ancestor resources (specifically Gateway resources)
// that are associated with the policy, and the status of the policy with
// respect to each ancestor. When this policy attaches to a parent, the
// controller that manages the parent and the ancestors MUST add an entry
// to this list when the controller first sees the policy and SHOULD update
// the entry as appropriate when the relevant ancestor is modified.
//
// For TelemetryPolicy, the ancestor MUST be the Gateway resource
// referenced in spec.targetRefs.
//
// Note also that implementations MUST ONLY populate ancestor status for
// the Ancestor resources they are responsible for. Implementations MUST
// use the ControllerName field to uniquely identify the entries in this list
// that they are responsible for.
//
// Note that to achieve this, the list of PolicyAncestorStatus structs
// MUST be treated as a map with a composite key, made up of the AncestorRef
// and ControllerName fields combined.
//
// A maximum of 16 ancestors will be represented in this list. An empty list
// means the Policy is not relevant for any ancestors.
//
// If this slice is full, implementations MUST NOT add further entries.
// Instead they MUST consider the policy unimplementable and signal that
// on any related resources such as the ancestor that would be referenced
// here.
//
// +required
// +listType=atomic
// +kubebuilder:validation:MaxItems=16
Ancestors []PolicyAncestorStatus `json:"ancestors"`
}
Attributes and Portability
To ensure portability and avoid implementation-specific lock-in, the Attribute attribute source type relies exclusively on standard OpenTelemetry Semantic Conventions.
When users specify an attributeKey, they must use these standardized keys (e.g., http.request.method, http.response.status_code). The underlying Gateway API implementations are responsible for mapping these standard OpenTelemetry keys to their proxy-specific internal variables.
Implementations MUST NOT expose internal, proxy-specific variables through the Attribute type. If an implementation does not support mapping a specific standard attribute, it SHOULD gracefully omit it or signal the limitation via policy status conditions.
Conformance Tests
To ensure consistent implementation of the TelemetryPolicy API, the following basic conformance test scenarios are proposed for the initial Tracing support.
1. Enabling TelemetryPolicy (Core)
- Description: Verify that applying a
TelemetryPolicywith tracing enabled successfully configures the Gateway to generate and export traces. - Test:
- Apply a
TelemetryPolicytargeting aGateway, withtracing.modeset toEnabledand a validBackendRefpointing to a mock OTLP collector. - Send a request through the Gateway.
- Assertion: The configured OTLP collector receives the tracing spans for the request.
- Apply a
2. Setting Custom Values (Literal and Header) (Core)
- Description: Verify that the Gateway proxy can inject static literal values and dynamically extract HTTP headers into span attributes.
- Test:
- Configure a
TelemetryPolicywith anAttributeof typeLiteral(e.g.,literalValue: "test-env") and anAttributeof typeHeader(e.g.,headerName: "X-Test-Header"). - Send a request through the Gateway with the HTTP header
X-Test-Header: my-value. - Assertion: The exported span includes both the literal attribute and the extracted header attribute.
- Configure a
3. Setting OpenTelemetry Attributes (Extended)
- Description: Verify that the Gateway proxy can map standard OpenTelemetry semantic conventions to its spans.
- Test:
- Configure a
TelemetryPolicywith anAttributeof typeAttribute(e.g.,attributeKey: "http.request.method"). - Send a request through the Gateway.
- Assertion: The exported span includes the requested standard attribute.
- Configure a
4. Conflict Resolution for Multiple Policies (Core)
- Description: Verify that when multiple
TelemetryPolicyresources target the sameGateway, only the oldest policy is accepted, while subsequent conflicting policies are rejected withAccepted: FalseandReason: Conflicted. - Test:
- Create
TelemetryPolicyA targetingGatewayG. - Create
TelemetryPolicyB targeting the sameGatewayG at a later creation timestamp. - Send a request through the
Gateway.
- Create
- Assertion:
TelemetryPolicyA status showsAccepted: True.TelemetryPolicyB status showsAccepted: FalsewithReason: Conflicted.- The proxy applies the configuration from
TelemetryPolicyA only.
5. Target Gateway Not Found (Core)
- Description: Verify policy status behavior when a
TelemetryPolicyreferences a non-existentGateway. - Test:
- Apply a
TelemetryPolicywithtargetRefspointing to aGatewayname that does not exist in the cluster.
- Apply a
- Assertion:
- The policy’s
status.ancestorsreflectsAccepted: FalsewithReason: TargetNotFound.
- The policy’s
6. Disabling Tracing (Core)
- Description: Verify that updating
tracing.modetoDisabledstops span emission. - Test:
- Apply a valid
TelemetryPolicywithtracing.mode: Enabledand send traffic to confirm spans are received. - Update the policy setting
tracing.mode: Disabled. - Send subsequent requests through the Gateway.
- Apply a valid
- Assertion:
- The OTLP collector stops receiving new tracing spans after configuration propagation.
7. Sampling Rate Validation (Extended)
- Description: Verify both valid sampling rates (0% and 100%) and invalid fraction validation.
- Test:
- Apply a
TelemetryPolicywithsamplingRateset tonumerator: 0(0% sampling) and verify 0 spans are emitted. - Apply a
TelemetryPolicywithsamplingRateset tonumerator: 100,denominator: 100(100% sampling) and verify all requests produce spans. - Attempt to apply a policy where
numerator>denominator(e.g.,numerator: 150,denominator: 100).
- Apply a
- Assertion:
- 0% sampling emits no spans; 100% sampling emits spans for every request.
- Invalid fraction configuration (
numerator > denominator) is rejected by schema validation or flagged asAccepted: FalsewithReason: Invalid.
Open Questions
- Standard OpenTelemetry Attribute Dictionary:
What is the minimal mandated set of standard OpenTelemetry semantic convention attributes that implementations MUST support when
type: Attributeis used? While implementations MAY support additional attributes, the exact dictionary of required attributes and how conformance will be tested needs to be determined.
Potential Future Additions
- Explicit Attribute Type Configuration (
valueType): ForAttributesources of typeHeaderorLiteral, values currently default to strings, whileAttributesources rely on OpenTelemetry Semantic Conventions for type mapping. If a need arises to emit non-string types for headers or static literals, an optionalvalueTypefield can be added to theAttributestruct in a future iteration.
Alternatives Considered
Implementation-Specific OpenTelemetry Enablement
During the initial proposal in the kube-agentic-networking subproject, an alternative was suggested to avoid defining a new API standard. The idea was that implementations should natively implement the OpenTelemetry specification for traces, metrics, and logs, and simply provide their own implementation-specific mechanisms to enable or disable these features.
Reason for Rejection: While this works as a baseline, it falls short when users need to customize their telemetry (which is fairly common). Customizations like adding specific attributes or conditional log filtering would require users to rely on vendor-specific APIs increasing the risk of lock-in.
Inline Gateway Configuration
Another alternative considered was adding the telemetry configuration directly as a top-level struct on the Gateway resource instead of introducing a new Policy object.
Reason for Rejection:
While inline configuration works well for a 1:1 mapping on a single Gateway, a separate Policy attachment model provides a decoupled, reusable configuration. A single TelemetryPolicy can be applied uniformly to multiple gateways, meaning platform operators and developers can ensure consistent telemetry signals across their infrastructure. This approach prevents configuration drift and avoids bloating the core Gateway API specification.
Comparison with Prior Art
Istio
Istio’s Telemetry API is the most direct prior art that inspired this proposal. It allows configuring observability at the mesh, namespace, and workload level.
- Metrics: Istio allows users to enable/disable specific metrics, add custom dimensions, and configure providers.
- Logs: Istio supports access logging configurations with CEL-like expressions for advanced filtering.
- Traces: Istio supports probabilistic sampling, context propagation, and custom span tags.
- Customization: For advanced telemetry use-cases not natively covered by the
TelemetryAPI, Istio users can fall back to usingEnvoyFilterresources. While highly flexible,EnvoyFilterrequires deep knowledge of Envoy’s internal xDS API. This is tightly coupled to the data plane implementation and can be brittle across version upgrades. - Comparison: The proposed
TelemetryPolicyadapts Istio’s powerful intent-based capabilities to the standardized Gateway API attachment model.
Envoy Gateway
Envoy Gateway configures observability through two distinct custom resources: EnvoyGateway for the control plane and EnvoyProxy for the underlying data plane proxies.
- Metrics: Envoy Gateway allows configuring Prometheus and OpenTelemetry sinks for both the control plane (using
EnvoyGatewayCRD) and the data plane proxies (using theEnvoyProxyCRD). - Logs: Proxy access logs are configured via the
EnvoyProxyresource. It supports exporting to file, OTLP, or gRPC Access Log Service (ALS) sinks. It uses CEL expressions for smart filtering (e.g., matching specific headers), and allows applying log configurations at the Route or Listener level. - Tracing: Tracing is configured in the
EnvoyProxyresource. It allows configuring sampling and supports appending custom tags derived from literals, environment variables, or request headers. - Customization: For advanced telemetry use-cases not covered natively, users can fall back to the
EnvoyPatchPolicyAPI to mutate the underlying xDS configuration using JSON Patch semantics. This is similar to Istio’sEnvoyFilter. - Comparison: While Envoy Gateway provides a robust, native telemetry configuration, it is tightly coupled to infrastructure-oriented CRDs. The proposed
TelemetryPolicyallows users to configure telemetry behaviors using a portabletargetRefmodel, without binding their observability intent to an Envoy-specific schema.
Kuadrant
Kuadrant provides observability for API management features like rate limiting and authentication. It is configured through a mix of its own custom resources and the underlying gateway’s APIs.
- Metrics: Kuadrant enables metrics via the
KuadrantCR. It also introduces its ownTelemetryPolicyAPI (extensions.kuadrant.io/v1alpha1) to add custom dimensions to metrics. - Logs: For proxy access logging, Kuadrant relies on the underlying gateway provider (e.g., Istio’s Telemetry API). However, it configures request correlation across its own components (Authorino, Limitador, and Wasm-shim) by specifying HTTP header identifiers in the
KuadrantCR. - Tracing: Tracing is configured centrally via the
KuadrantCR. It exports OpenTelemetry spans for both the control plane and data plane components. It supports global trace filtering levels to control the verbosity of exported spans. - Customization: To make low-level, custom modifications to the data plane configuration that are not supported by Kuadrant’s native APIs, users can bypass Kuadrant and directly use the underlying gateway’s mechanisms.
- Comparison: While Kuadrant provides powerful, identity-aware telemetry (like token tracking per user), its configuration is fragmented across the
KuadrantCR, components specific CRDs, its custom extensionTelemetryPolicy, and the underlying gateway’s native APIs. The proposedTelemetryPolicyaims to unify these intent-based capabilities into a single, provider-agnostic resource.
Airlock Microgateway
Airlock Microgateway defines a Telemetry CRD to configure logging, metrics, and tracing.
- Metrics: While the
TelemetryCRD broadly targets telemetry, metric generation is largely handled by default configurations rather than via customization within the CRD itself. - Logs: Configures access logs with customizable JSON and ECS formats, relying on Envoy-specific log variables and dynamic metadata extraction.
- Tracing: Supports configuring an OpenTelemetry provider with deep exporter settings (e.g., gRPC/HTTP endpoints and custom TLS certificate pinning) and sampling strategies (ratio or parent-based).
- Customization: Explicitly supports defining mechanisms to extract and propagate correlation identifiers from request headers directly within the telemetry configuration.
- Comparison: While Airlock utilizes a unified
Telemetrycustom resource, its specification includes implementation-specific details (like TLS pinning strategies and Envoy string formatting). The proposedTelemetryPolicyabstracts these into a more portable, generalized resource.
NGINX Gateway Fabric
NGINX Gateway Fabric splits its telemetry configuration across its NginxProxy and ObservabilityPolicy custom resources.
- Metrics: Global data plane observability, such as Prometheus metrics scraping, is managed via the
NginxProxyresource which can be referenced from aGatewayClassorGateway. - Logs: Access log formatting and enablement are also managed centrally via the
NginxProxyresource. - Tracing: Distributed tracing is configured using the
ObservabilityPolicy, which is a Direct Attached Policy that specifically targetsHTTPRouteorGRPCRoute. It supports configuring OpenTelemetry sampling strategies (ratio or parent-based), context propagation, custom span names, and span attributes. - Customization: For advanced proxy configurations not natively covered by the standard policies, users can inject raw NGINX configuration using the
SnippetsPolicyat the Gateway level or theSnippetsFilterat the Route level. - Comparison: NGINX Gateway Fabric separates its telemetry intents across multiple layers, splitting infrastructure-level metrics and logs from route-level tracing configurations. The proposed
TelemetryPolicyconsolidates these observability signals into a single Direct Attached Policy targeting theGateway.