diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go
index 75b95b0276..d33f6544d3 100644
--- a/api/v1alpha1/shared_types.go
+++ b/api/v1alpha1/shared_types.go
@@ -947,6 +947,18 @@ type Tracing struct {
//
// +optional
SamplingFraction *gwapiv1.Fraction `json:"samplingFraction,omitempty"`
+ // ClientSamplingFraction represents the fraction of requests that should be
+ // selected for tracing when requested by the client.
+ // If unspecified, client-forced tracing is disabled by default and users must
+ // set this field to opt in.
+ //
+ // +optional
+ ClientSamplingFraction *gwapiv1.Fraction `json:"clientSamplingFraction,omitempty"`
+ // OverallSamplingFraction represents the fraction of requests that should be
+ // selected for tracing after all other sampling checks have been applied.
+ //
+ // +optional
+ OverallSamplingFraction *gwapiv1.Fraction `json:"overallSamplingFraction,omitempty"`
// CustomTags defines the custom tags to add to each span.
// If provider is kubernetes, pod name and namespace are added by default.
//
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 2195616a06..4551b1a7c3 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -8473,6 +8473,16 @@ func (in *Tracing) DeepCopyInto(out *Tracing) {
*out = new(v1.Fraction)
(*in).DeepCopyInto(*out)
}
+ if in.ClientSamplingFraction != nil {
+ in, out := &in.ClientSamplingFraction, &out.ClientSamplingFraction
+ *out = new(v1.Fraction)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.OverallSamplingFraction != nil {
+ in, out := &in.OverallSamplingFraction, &out.OverallSamplingFraction
+ *out = new(v1.Fraction)
+ (*in).DeepCopyInto(*out)
+ }
if in.CustomTags != nil {
in, out := &in.CustomTags, &out.CustomTags
*out = make(map[string]CustomTag, len(*in))
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 72e4b69d56..575c1136c5 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -3152,6 +3152,28 @@ spec:
This takes precedence over EnvoyProxy tracing when set.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -3216,6 +3238,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
samplingFraction:
description: |-
SamplingFraction represents the fraction of requests that should be
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
index f09493a2f5..d8a048a90b 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -16329,6 +16329,28 @@ spec:
Tracing defines tracing configuration for managed proxies.
If unspecified, will not send tracing data.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -16393,6 +16415,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
provider:
description: Provider defines the tracing provider.
properties:
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index a79ec46e60..7572080368 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -3151,6 +3151,28 @@ spec:
This takes precedence over EnvoyProxy tracing when set.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -3215,6 +3237,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
samplingFraction:
description: |-
SamplingFraction represents the fraction of requests that should be
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
index 9211cd86da..5736db43cd 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -16328,6 +16328,28 @@ spec:
Tracing defines tracing configuration for managed proxies.
If unspecified, will not send tracing data.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -16392,6 +16414,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
provider:
description: Provider defines the tracing provider.
properties:
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index a520dbc212..8ee139d7e9 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -1191,10 +1191,12 @@ func buildBackendTracing(tracing *egv1a1.Tracing) *ir.BackendTracing {
return nil
}
return &ir.BackendTracing{
- SamplingFraction: tracing.SamplingFraction,
- CustomTags: ir.CustomTagMapToSlice(tracing.CustomTags),
- Tags: ir.MapToSlice(tracing.Tags),
- SpanName: tracing.SpanName,
+ SamplingFraction: tracing.SamplingFraction,
+ ClientSamplingFraction: tracing.ClientSamplingFraction,
+ OverallSamplingFraction: tracing.OverallSamplingFraction,
+ CustomTags: ir.CustomTagMapToSlice(tracing.CustomTags),
+ Tags: ir.MapToSlice(tracing.Tags),
+ SpanName: tracing.SpanName,
}
}
diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go
index a9dd8270f3..de318a8034 100644
--- a/internal/gatewayapi/listener.go
+++ b/internal/gatewayapi/listener.go
@@ -1015,12 +1015,14 @@ func (t *Translator) processTracing(gwCtx *GatewayContext, envoyproxy *egv1a1.En
}
return &ir.Tracing{
- Authority: authority,
- ServiceName: serviceName,
- SamplingRate: proxySamplingRate(tracing),
- CustomTags: ir.CustomTagMapToSlice(tracing.CustomTags),
- Tags: ir.MapToSlice(tracing.Tags),
- ResourceAttributes: ir.MapToSlice(getOpenTelemetryTracingResourceAttributes(&tracing.Provider)),
+ Authority: authority,
+ ServiceName: serviceName,
+ SamplingRate: proxySamplingRate(tracing.SamplingRate, tracing.SamplingFraction),
+ ClientSamplingRate: proxySamplingFractionPtr(tracing.ClientSamplingFraction),
+ OverallSamplingRate: proxySamplingFractionPtr(tracing.OverallSamplingFraction),
+ CustomTags: ir.CustomTagMapToSlice(tracing.CustomTags),
+ Tags: ir.MapToSlice(tracing.Tags),
+ ResourceAttributes: ir.MapToSlice(getOpenTelemetryTracingResourceAttributes(&tracing.Provider)),
Destination: ir.RouteDestination{
Name: destName,
Settings: ds,
@@ -1033,20 +1035,29 @@ func (t *Translator) processTracing(gwCtx *GatewayContext, envoyproxy *egv1a1.En
}, nil
}
-func proxySamplingRate(tracing *egv1a1.ProxyTracing) float64 {
- rate := 100.0
- if tracing.SamplingRate != nil {
- rate = float64(*tracing.SamplingRate)
- } else if tracing.SamplingFraction != nil {
- numerator := float64(tracing.SamplingFraction.Numerator)
- denominator := ptr.Deref(tracing.SamplingFraction.Denominator, 100)
-
- rate = numerator * 100 / float64(denominator)
- // Identifies a percentage, in the range [0.0, 100.0]
- rate = math.Max(0, rate)
- rate = math.Min(100, rate)
+func proxySamplingFractionPtr(fraction *gwapiv1.Fraction) *float64 {
+ if fraction == nil {
+ return nil
+ }
+ return new(proxySamplingRate(nil, fraction))
+}
+
+func proxySamplingRate(rate *uint32, fraction *gwapiv1.Fraction) float64 {
+ if rate != nil {
+ return float64(*rate)
}
- return rate
+ if fraction == nil {
+ return 100.0
+ }
+
+ numerator := float64(fraction.Numerator)
+ denominator := ptr.Deref(fraction.Denominator, 100)
+
+ value := numerator * 100 / float64(denominator)
+ // Identifies a percentage, in the range [0.0, 100.0]
+ value = math.Max(0, value)
+ value = math.Min(100, value)
+ return value
}
// getAuthorityFromDestination extracts the gRPC authority from a destination setting.
diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go
index 2edeef9f71..3905930964 100644
--- a/internal/gatewayapi/listener_test.go
+++ b/internal/gatewayapi/listener_test.go
@@ -34,23 +34,47 @@ type expectedListenerStatus struct {
message string
}
-func TestProxySamplingRate(t *testing.T) {
+func TestProxySamplingRates(t *testing.T) {
cases := []struct {
- name string
- tracing *egv1a1.ProxyTracing
- expected float64
+ name string
+ tracing *egv1a1.ProxyTracing
+ expectedRandom float64
+ expectedClient float64
+ expectedOverall float64
}{
{
- name: "default",
- tracing: &egv1a1.ProxyTracing{},
- expected: 100.0,
+ name: "default",
+ tracing: &egv1a1.ProxyTracing{},
+ expectedRandom: 100.0,
+ expectedClient: 100.0,
+ expectedOverall: 100.0,
},
{
name: "rate",
tracing: &egv1a1.ProxyTracing{
SamplingRate: new(uint32(10)),
},
- expected: 10.0,
+ expectedRandom: 10.0,
+ expectedClient: 100.0,
+ expectedOverall: 100.0,
+ },
+ {
+ name: "client and overall fraction",
+ tracing: &egv1a1.ProxyTracing{
+ Tracing: egv1a1.Tracing{
+ ClientSamplingFraction: &gwapiv1.Fraction{
+ Numerator: 1,
+ Denominator: new(int32(4)),
+ },
+ OverallSamplingFraction: &gwapiv1.Fraction{
+ Numerator: 3,
+ Denominator: new(int32(4)),
+ },
+ },
+ },
+ expectedRandom: 100.0,
+ expectedClient: 25.0,
+ expectedOverall: 75.0,
},
{
name: "fraction numerator only",
@@ -61,7 +85,9 @@ func TestProxySamplingRate(t *testing.T) {
},
},
},
- expected: 100,
+ expectedRandom: 100,
+ expectedClient: 100,
+ expectedOverall: 100,
},
{
name: "fraction",
@@ -73,7 +99,9 @@ func TestProxySamplingRate(t *testing.T) {
},
},
},
- expected: 10,
+ expectedRandom: 10,
+ expectedClient: 100,
+ expectedOverall: 100,
},
{
name: "less than zero",
@@ -85,7 +113,9 @@ func TestProxySamplingRate(t *testing.T) {
},
},
},
- expected: 0,
+ expectedRandom: 0,
+ expectedClient: 100,
+ expectedOverall: 100,
},
{
name: "greater than 100",
@@ -97,7 +127,9 @@ func TestProxySamplingRate(t *testing.T) {
},
},
},
- expected: 100,
+ expectedRandom: 100,
+ expectedClient: 100,
+ expectedOverall: 100,
},
{
name: "less than 1",
@@ -109,16 +141,20 @@ func TestProxySamplingRate(t *testing.T) {
},
},
},
- expected: 0.1,
+ expectedRandom: 0.1,
+ expectedClient: 100,
+ expectedOverall: 100,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
- actual := proxySamplingRate(tc.tracing)
- if actual != tc.expected {
- t.Errorf("expected %v, got %v", tc.expected, actual)
- }
+ random := proxySamplingRate(tc.tracing.SamplingRate, tc.tracing.SamplingFraction)
+ client := proxySamplingRate(nil, tc.tracing.ClientSamplingFraction)
+ overall := proxySamplingRate(nil, tc.tracing.OverallSamplingFraction)
+ assert.Equal(t, tc.expectedRandom, random)
+ assert.Equal(t, tc.expectedClient, client)
+ assert.Equal(t, tc.expectedOverall, overall)
})
}
}
diff --git a/internal/gatewayapi/testdata/tracing-sampling-fraction.in.yaml b/internal/gatewayapi/testdata/tracing-sampling-fraction.in.yaml
index b5ce63a9f9..836a9ee812 100644
--- a/internal/gatewayapi/testdata/tracing-sampling-fraction.in.yaml
+++ b/internal/gatewayapi/testdata/tracing-sampling-fraction.in.yaml
@@ -11,6 +11,12 @@ envoyProxyForGatewayClass:
samplingFraction:
numerator: 1
denominator: 100
+ clientSamplingFraction:
+ numerator: 20
+ denominator: 100
+ overallSamplingFraction:
+ numerator: 30
+ denominator: 100
provider:
host: otel-collector.monitoring.svc.cluster.local
port: 4317
diff --git a/internal/gatewayapi/testdata/tracing-sampling-fraction.out.yaml b/internal/gatewayapi/testdata/tracing-sampling-fraction.out.yaml
index 1a3b551dd2..8e4ac2b447 100644
--- a/internal/gatewayapi/testdata/tracing-sampling-fraction.out.yaml
+++ b/internal/gatewayapi/testdata/tracing-sampling-fraction.out.yaml
@@ -194,6 +194,12 @@ infraIR:
mergeGateways: true
telemetry:
tracing:
+ clientSamplingFraction:
+ denominator: 100
+ numerator: 20
+ overallSamplingFraction:
+ denominator: 100
+ numerator: 30
provider:
host: otel-collector.monitoring.svc.cluster.local
port: 4317
@@ -361,6 +367,7 @@ xdsIR:
port: 19003
tracing:
authority: otel-collector.monitoring.svc.cluster.local
+ clientSamplingRate: 20
destination:
metadata:
kind: EnvoyProxy
@@ -375,6 +382,7 @@ xdsIR:
name: tracing/backend/-1
protocol: GRPC
weight: 1
+ overallSamplingRate: 30
provider:
host: otel-collector.monitoring.svc.cluster.local
port: 4317
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index d8ff851451..ddd94ea293 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -1086,10 +1086,12 @@ type BackendTelemetry struct {
// BackendTracing defines the tracing configuration for the backend.
// +k8s:deepcopy-gen=true
type BackendTracing struct {
- SamplingFraction *gwapiv1.Fraction `json:"samplingFraction,omitempty" yaml:"samplingFraction,omitempty"`
- CustomTags []CustomTagMapEntry `json:"customTags,omitempty" yaml:"customTags,omitempty"`
- Tags []MapEntry `json:"tags,omitempty" yaml:"tags,omitempty"`
- SpanName *egv1a1.TracingSpanName `json:"spanName,omitempty" yaml:"spanName,omitempty"`
+ SamplingFraction *gwapiv1.Fraction `json:"samplingFraction,omitempty" yaml:"samplingFraction,omitempty"`
+ ClientSamplingFraction *gwapiv1.Fraction `json:"clientSamplingFraction,omitempty" yaml:"clientSamplingFraction,omitempty"`
+ OverallSamplingFraction *gwapiv1.Fraction `json:"overallSamplingFraction,omitempty" yaml:"overallSamplingFraction,omitempty"`
+ CustomTags []CustomTagMapEntry `json:"customTags,omitempty" yaml:"customTags,omitempty"`
+ Tags []MapEntry `json:"tags,omitempty" yaml:"tags,omitempty"`
+ SpanName *egv1a1.TracingSpanName `json:"spanName,omitempty" yaml:"spanName,omitempty"`
}
// BackendMetrics defines the metrics configuration for the backend.
@@ -2995,17 +2997,19 @@ func (o *JSONPatchOperation) Validate() error {
// Tracing defines the configuration for tracing a Envoy xDS Resource
// +k8s:deepcopy-gen=true
type Tracing struct {
- ServiceName string `json:"serviceName"`
- Authority string `json:"authority,omitempty"`
- SamplingRate float64 `json:"samplingRate,omitempty"`
- CustomTags []CustomTagMapEntry `json:"customTags,omitempty"`
- Tags []MapEntry `json:"tags,omitempty"`
- ResourceAttributes []MapEntry `json:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty"`
- Destination RouteDestination `json:"destination,omitempty"`
- Traffic *TrafficFeatures `json:"traffic,omitempty"`
- Provider egv1a1.TracingProvider `json:"provider"`
- Headers []gwapiv1.HTTPHeader `json:"headers,omitempty" yaml:"headers,omitempty"`
- SpanName *egv1a1.TracingSpanName `json:"spanName,omitempty"`
+ ServiceName string `json:"serviceName"`
+ Authority string `json:"authority,omitempty"`
+ SamplingRate float64 `json:"samplingRate,omitempty"`
+ ClientSamplingRate *float64 `json:"clientSamplingRate,omitempty"`
+ OverallSamplingRate *float64 `json:"overallSamplingRate,omitempty"`
+ CustomTags []CustomTagMapEntry `json:"customTags,omitempty"`
+ Tags []MapEntry `json:"tags,omitempty"`
+ ResourceAttributes []MapEntry `json:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty"`
+ Destination RouteDestination `json:"destination,omitempty"`
+ Traffic *TrafficFeatures `json:"traffic,omitempty"`
+ Provider egv1a1.TracingProvider `json:"provider"`
+ Headers []gwapiv1.HTTPHeader `json:"headers,omitempty" yaml:"headers,omitempty"`
+ SpanName *egv1a1.TracingSpanName `json:"spanName,omitempty"`
}
// Metrics defines the configuration for metrics generated by Envoy
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index f4e3a81b02..a4f4df9bee 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -524,6 +524,16 @@ func (in *BackendTracing) DeepCopyInto(out *BackendTracing) {
*out = new(v1.Fraction)
(*in).DeepCopyInto(*out)
}
+ if in.ClientSamplingFraction != nil {
+ in, out := &in.ClientSamplingFraction, &out.ClientSamplingFraction
+ *out = new(v1.Fraction)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.OverallSamplingFraction != nil {
+ in, out := &in.OverallSamplingFraction, &out.OverallSamplingFraction
+ *out = new(v1.Fraction)
+ (*in).DeepCopyInto(*out)
+ }
if in.CustomTags != nil {
in, out := &in.CustomTags, &out.CustomTags
*out = make([]CustomTagMapEntry, len(*in))
@@ -5025,6 +5035,16 @@ func (in *Timeout) DeepCopy() *Timeout {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Tracing) DeepCopyInto(out *Tracing) {
*out = *in
+ if in.ClientSamplingRate != nil {
+ in, out := &in.ClientSamplingRate, &out.ClientSamplingRate
+ *out = new(float64)
+ **out = **in
+ }
+ if in.OverallSamplingRate != nil {
+ in, out := &in.OverallSamplingRate, &out.OverallSamplingRate
+ *out = new(float64)
+ **out = **in
+ }
if in.CustomTags != nil {
in, out := &in.CustomTags, &out.CustomTags
*out = make([]CustomTagMapEntry, len(*in))
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index 4426dc2efe..21eb69951c 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -877,7 +877,9 @@ func buildRouteTracing(httpRoute *ir.HTTPRoute) (*routev3.Tracing, error) {
op, upstreamOp := buildTracingOperation(tracing.SpanName)
return &routev3.Tracing{
+ ClientSampling: fractionalpercent.FromFractionOrZero(tracing.ClientSamplingFraction),
RandomSampling: fractionalpercent.FromFraction(tracing.SamplingFraction),
+ OverallSampling: fractionalpercent.FromFraction(tracing.OverallSamplingFraction),
CustomTags: tags,
Operation: op,
UpstreamOperation: upstreamOp,
diff --git a/internal/xds/translator/route_test.go b/internal/xds/translator/route_test.go
index ab3a0bdcc3..af72994b03 100644
--- a/internal/xds/translator/route_test.go
+++ b/internal/xds/translator/route_test.go
@@ -10,11 +10,66 @@ import (
"testing"
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
+ xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3"
+ "github.com/stretchr/testify/require"
+ gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/ir"
)
+func TestBuildRouteTracingSampling(t *testing.T) {
+ tests := []struct {
+ name string
+ clientSampling *gwapiv1.Fraction
+ wantClientSampling *xdstype.FractionalPercent
+ }{
+ {
+ name: "client sampling configured",
+ clientSampling: &gwapiv1.Fraction{
+ Numerator: 2,
+ Denominator: new(int32(100)),
+ },
+ wantClientSampling: &xdstype.FractionalPercent{Numerator: 2},
+ },
+ {
+ name: "client sampling defaults to zero",
+ wantClientSampling: &xdstype.FractionalPercent{
+ Numerator: 0,
+ Denominator: xdstype.FractionalPercent_HUNDRED,
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ httpRoute := &ir.HTTPRoute{
+ Traffic: &ir.TrafficFeatures{
+ Telemetry: &ir.BackendTelemetry{
+ Tracing: &ir.BackendTracing{
+ SamplingFraction: &gwapiv1.Fraction{
+ Numerator: 1,
+ Denominator: new(int32(100)),
+ },
+ ClientSamplingFraction: tc.clientSampling,
+ OverallSamplingFraction: &gwapiv1.Fraction{
+ Numerator: 3,
+ Denominator: new(int32(100)),
+ },
+ },
+ },
+ },
+ }
+
+ got, err := buildRouteTracing(httpRoute)
+ require.NoError(t, err)
+ require.Equal(t, &xdstype.FractionalPercent{Numerator: 1}, got.RandomSampling)
+ require.Equal(t, tc.wantClientSampling, got.ClientSampling)
+ require.Equal(t, &xdstype.FractionalPercent{Numerator: 3}, got.OverallSampling)
+ })
+ }
+}
+
func TestBuildHashPolicy(t *testing.T) {
tests := []struct {
name string
diff --git a/internal/xds/translator/testdata/in/xds-ir/btp-telemetry.yaml b/internal/xds/translator/testdata/in/xds-ir/btp-telemetry.yaml
index f979b7cd15..f86f90e153 100644
--- a/internal/xds/translator/testdata/in/xds-ir/btp-telemetry.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/btp-telemetry.yaml
@@ -58,3 +58,9 @@ http:
samplingFraction:
denominator: 100
numerator: 1
+ clientSamplingFraction:
+ denominator: 100
+ numerator: 2
+ overallSamplingFraction:
+ denominator: 100
+ numerator: 3
diff --git a/internal/xds/translator/testdata/in/xds-ir/tracing.yaml b/internal/xds/translator/testdata/in/xds-ir/tracing.yaml
index eae619a45d..8912dca2aa 100644
--- a/internal/xds/translator/testdata/in/xds-ir/tracing.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/tracing.yaml
@@ -1,6 +1,8 @@
tracing:
serviceName: "fake-name.fake-ns"
samplingRate: 90
+ clientSamplingRate: 20
+ overallSamplingRate: 30
customTags:
- key: "env1"
value:
diff --git a/internal/xds/translator/testdata/out/xds-ir/btp-telemetry.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/btp-telemetry.routes.yaml
index 9f0ed45ef9..b9fa4a98a2 100644
--- a/internal/xds/translator/testdata/out/xds-ir/btp-telemetry.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/btp-telemetry.routes.yaml
@@ -28,6 +28,8 @@
upgradeConfigs:
- upgradeType: websocket
tracing:
+ clientSampling:
+ numerator: 2
customTags:
- environment:
defaultValue: '-'
@@ -40,5 +42,7 @@
defaultValue: '-'
name: X-Request-Id
tag: req1
+ overallSampling:
+ numerator: 3
randomSampling:
numerator: 1
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-datadog-uds.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-datadog-uds.listeners.yaml
index f5d891adb1..820c707840 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-datadog-uds.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-datadog-uds.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
customTags:
- environment:
defaultValue: '-'
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-datadog.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-datadog.listeners.yaml
index f5d891adb1..820c707840 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-datadog.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-datadog.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
customTags:
- environment:
defaultValue: '-'
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml
index c685e2ac55..ce39b69418 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
customTags:
- environment:
defaultValue: '-'
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-otel-headers.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-otel-headers.listeners.yaml
index 593a345e55..928ac8e2d7 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-otel-headers.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-otel-headers.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
overallSampling:
value: 100
provider:
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler-always-on.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler-always-on.listeners.yaml
index bb942f6128..8c3c66fc03 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler-always-on.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler-always-on.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
overallSampling:
value: 100
provider:
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler.listeners.yaml
index 5486bf5860..d4712ec0e0 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-otel-sampler.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
overallSampling:
value: 100
provider:
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.listeners.yaml
index 5c972f24ee..879726f4e5 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
operation: '%REQ(X-ENVOY-ORIGINAL-HOST)%%PATH(NQ:ORIG)%'
overallSampling:
value: 100
@@ -83,8 +82,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-8888
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
operation: '%REQ(X-ENVOY-ORIGINAL-HOST)%%PATH(NQ:ORIG)%'
overallSampling:
value: 100
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.routes.yaml
index 21a2ca5eea..37f0593833 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-span-name.routes.yaml
@@ -28,6 +28,7 @@
upgradeConfigs:
- upgradeType: websocket
tracing:
+ clientSampling: {}
operation: custom-client-span-%REQ(X-CUSTOM-HEADER)%
upstreamOperation: custom-server-span-%UPSTREAM_HOST%
- ignorePortInHostMatching: true
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-zipkin.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-zipkin.listeners.yaml
index 362f098ff2..ecbf2155ae 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing-zipkin.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing-zipkin.listeners.yaml
@@ -29,8 +29,7 @@
serverHeaderTransformation: PASS_THROUGH
statPrefix: http-10080
tracing:
- clientSampling:
- value: 100
+ clientSampling: {}
customTags:
- environment:
defaultValue: '-'
diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml
index b7497f7924..0f9da01cbe 100644
--- a/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml
@@ -30,7 +30,7 @@
statPrefix: http-10080
tracing:
clientSampling:
- value: 100
+ value: 20
customTags:
- environment:
defaultValue: '-'
@@ -44,7 +44,7 @@
name: X-Request-Id
tag: req1
overallSampling:
- value: 100
+ value: 30
provider:
name: envoy.tracers.opentelemetry
typedConfig:
diff --git a/internal/xds/translator/tracing.go b/internal/xds/translator/tracing.go
index 9dc9c0c732..2473729a1c 100644
--- a/internal/xds/translator/tracing.go
+++ b/internal/xds/translator/tracing.go
@@ -116,10 +116,12 @@ func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, e
return &hcm.HttpConnectionManager_Tracing{
ClientSampling: &xdstype.Percent{
- Value: 100.0,
+ // Default to 0 so Envoy Gateway does not honor client-forced tracing
+ // unless users explicitly opt in with clientSamplingFraction.
+ Value: ptr.Deref(tracing.ClientSamplingRate, 0),
},
OverallSampling: &xdstype.Percent{
- Value: 100.0,
+ Value: ptr.Deref(tracing.OverallSamplingRate, 100),
},
RandomSampling: &xdstype.Percent{
Value: randomSamplingValue(tracing),
diff --git a/internal/xds/translator/tracing_test.go b/internal/xds/translator/tracing_test.go
index 13c6c3b1d9..932a8d0263 100644
--- a/internal/xds/translator/tracing_test.go
+++ b/internal/xds/translator/tracing_test.go
@@ -15,6 +15,61 @@ import (
"github.com/envoyproxy/gateway/internal/ir"
)
+func TestBuildHCMTracingSampling(t *testing.T) {
+ testCases := []struct {
+ name string
+ tracing *ir.Tracing
+ expectedRandomSampling float64
+ expectedClientSampling float64
+ expectedOverallSampling float64
+ }{
+ {
+ name: "explicit sampling values",
+ tracing: &ir.Tracing{
+ ServiceName: "test-service",
+ SamplingRate: 10,
+ ClientSamplingRate: new(20.0),
+ OverallSamplingRate: new(30.0),
+ Destination: ir.RouteDestination{
+ Name: "tracing",
+ },
+ Provider: egv1a1.TracingProvider{
+ Type: egv1a1.TracingProviderTypeOpenTelemetry,
+ },
+ },
+ expectedRandomSampling: 10.0,
+ expectedClientSampling: 20.0,
+ expectedOverallSampling: 30.0,
+ },
+ {
+ name: "unset client sampling defaults to zero",
+ tracing: &ir.Tracing{
+ ServiceName: "test-service",
+ SamplingRate: 10,
+ Destination: ir.RouteDestination{
+ Name: "tracing",
+ },
+ Provider: egv1a1.TracingProvider{
+ Type: egv1a1.TracingProviderTypeOpenTelemetry,
+ },
+ },
+ expectedRandomSampling: 10.0,
+ expectedClientSampling: 0.0,
+ expectedOverallSampling: 100.0,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ got, err := buildHCMTracing(tc.tracing)
+ require.NoError(t, err)
+ require.Equal(t, tc.expectedRandomSampling, got.RandomSampling.Value)
+ require.Equal(t, tc.expectedClientSampling, got.ClientSampling.Value)
+ require.Equal(t, tc.expectedOverallSampling, got.OverallSampling.Value)
+ })
+ }
+}
+
func TestBuildSampler(t *testing.T) {
testCases := []struct {
name string
diff --git a/internal/xds/utils/fractionalpercent/fractional_percent.go b/internal/xds/utils/fractionalpercent/fractional_percent.go
index 3d18ba0a3f..717bfbb80d 100644
--- a/internal/xds/utils/fractionalpercent/fractional_percent.go
+++ b/internal/xds/utils/fractionalpercent/fractional_percent.go
@@ -89,6 +89,20 @@ func FromFraction(fraction *gwapiv1.Fraction) *xdstype.FractionalPercent {
}
}
+// FromFractionOrZero translates a gwapiv1.Fraction instance to envoy.type.FractionalPercent.
+// If fraction is nil, it returns an explicit zero percent value.
+func FromFractionOrZero(fraction *gwapiv1.Fraction) *xdstype.FractionalPercent {
+ fp := FromFraction(fraction)
+ if fp != nil {
+ return fp
+ }
+
+ return &xdstype.FractionalPercent{
+ Numerator: 0,
+ Denominator: xdstype.FractionalPercent_HUNDRED,
+ }
+}
+
func ToPercent(fp *gwapiv1.Fraction) float64 {
if fp == nil {
return 0
diff --git a/internal/xds/utils/fractionalpercent/fractional_percent_test.go b/internal/xds/utils/fractionalpercent/fractional_percent_test.go
index 4206d6bca6..a8f2ccfbc4 100644
--- a/internal/xds/utils/fractionalpercent/fractional_percent_test.go
+++ b/internal/xds/utils/fractionalpercent/fractional_percent_test.go
@@ -188,3 +188,40 @@ func TestFromFraction(t *testing.T) {
})
}
}
+
+func TestFromFractionOrZero(t *testing.T) {
+ cases := []struct {
+ name string
+ input *gwapiv1.Fraction
+ expected *xdstype.FractionalPercent
+ }{
+ {
+ name: "configured fraction",
+ input: &gwapiv1.Fraction{
+ Numerator: 2,
+ Denominator: new(int32(100)),
+ },
+ expected: &xdstype.FractionalPercent{
+ Numerator: 2,
+ Denominator: xdstype.FractionalPercent_HUNDRED,
+ },
+ },
+ {
+ name: "nil fraction defaults to zero",
+ input: nil,
+ expected: &xdstype.FractionalPercent{
+ Numerator: 0,
+ Denominator: xdstype.FractionalPercent_HUNDRED,
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ result := FromFractionOrZero(tc.input)
+ if result.Numerator != tc.expected.Numerator || result.Denominator != tc.expected.Denominator {
+ t.Errorf("expected %v, got %v", tc.expected, result)
+ }
+ })
+ }
+}
diff --git a/release-notes/current/breaking_changes/9372-tracing-client-sampling-default.md b/release-notes/current/breaking_changes/9372-tracing-client-sampling-default.md
new file mode 100644
index 0000000000..efca4ac4cf
--- /dev/null
+++ b/release-notes/current/breaking_changes/9372-tracing-client-sampling-default.md
@@ -0,0 +1 @@
+Tracing client sampling now defaults to 0% instead of 100%, so Envoy Gateway no longer honors client-forced tracing unless users explicitly opt in by setting `clientSamplingFraction`.
diff --git a/release-notes/current/new_features/9372-tracing-client-overall-sampling.md b/release-notes/current/new_features/9372-tracing-client-overall-sampling.md
new file mode 100644
index 0000000000..c6a9e56b58
--- /dev/null
+++ b/release-notes/current/new_features/9372-tracing-client-overall-sampling.md
@@ -0,0 +1 @@
+Added support for configuring Envoy tracing client and overall sampling fractions on EnvoyProxy and BackendTrafficPolicy tracing settings.
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 7d78e634ab..047cfd9ea8 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -5019,6 +5019,8 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `samplingFraction` | _[Fraction](https://gateway-api.sigs.k8s.io/reference/api-spec/1.5/spec/#fraction)_ | false | | SamplingFraction represents the fraction of requests that should be
selected for tracing if no prior sampling decision has been made. |
+| `clientSamplingFraction` | _[Fraction](https://gateway-api.sigs.k8s.io/reference/api-spec/1.5/spec/#fraction)_ | false | | ClientSamplingFraction represents the fraction of requests that should be
selected for tracing when requested by the client.
If unspecified, client-forced tracing is disabled by default and users must
set this field to opt in. |
+| `overallSamplingFraction` | _[Fraction](https://gateway-api.sigs.k8s.io/reference/api-spec/1.5/spec/#fraction)_ | false | | OverallSamplingFraction represents the fraction of requests that should be
selected for tracing after all other sampling checks have been applied. |
| `customTags` | _object (keys:string, values:[CustomTag](#customtag))_ | false | | CustomTags defines the custom tags to add to each span.
If provider is kubernetes, pod name and namespace are added by default.
Deprecated: Use Tags instead. |
| `tags` | _object (keys:string, values:string)_ | false | | Tags defines the custom tags to add to each span.
Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the value.
The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information.
If provider is kubernetes, pod name and namespace are added by default.
Same keys take precedence over CustomTags. |
| `spanName` | _[TracingSpanName](#tracingspanname)_ | false | | SpanName defines the name of the span which will be used for tracing.
Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the value.
The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information.
If not set, the span name is provider specific.
e.g. Datadog use `ingress` as the default client span name,
and `router egress` as the server span name. |
@@ -6315,6 +6317,8 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `samplingFraction` | _[Fraction](https://gateway-api.sigs.k8s.io/reference/api-spec/1.5/spec/#fraction)_ | false | | SamplingFraction represents the fraction of requests that should be
selected for tracing if no prior sampling decision has been made. |
+| `clientSamplingFraction` | _[Fraction](https://gateway-api.sigs.k8s.io/reference/api-spec/1.5/spec/#fraction)_ | false | | ClientSamplingFraction represents the fraction of requests that should be
selected for tracing when requested by the client.
If unspecified, client-forced tracing is disabled by default and users must
set this field to opt in. |
+| `overallSamplingFraction` | _[Fraction](https://gateway-api.sigs.k8s.io/reference/api-spec/1.5/spec/#fraction)_ | false | | OverallSamplingFraction represents the fraction of requests that should be
selected for tracing after all other sampling checks have been applied. |
| `customTags` | _object (keys:string, values:[CustomTag](#customtag))_ | false | | CustomTags defines the custom tags to add to each span.
If provider is kubernetes, pod name and namespace are added by default.
Deprecated: Use Tags instead. |
| `tags` | _object (keys:string, values:string)_ | false | | Tags defines the custom tags to add to each span.
Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the value.
The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information.
If provider is kubernetes, pod name and namespace are added by default.
Same keys take precedence over CustomTags. |
| `spanName` | _[TracingSpanName](#tracingspanname)_ | false | | SpanName defines the name of the span which will be used for tracing.
Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the value.
The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information.
If not set, the span name is provider specific.
e.g. Datadog use `ingress` as the default client span name,
and `router egress` as the server span name. |
diff --git a/test/e2e/testdata/tracing-otel-sampling-fractions.yaml b/test/e2e/testdata/tracing-otel-sampling-fractions.yaml
new file mode 100644
index 0000000000..e79303b902
--- /dev/null
+++ b/test/e2e/testdata/tracing-otel-sampling-fractions.yaml
@@ -0,0 +1,131 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: tracing-otel-overall-sampling-zero
+ namespace: gateway-conformance-infra
+spec:
+ gatewayClassName: "{GATEWAY_CLASS_NAME}"
+ listeners:
+ - name: http
+ port: 80
+ protocol: HTTP
+ allowedRoutes:
+ namespaces:
+ from: All
+ infrastructure:
+ parametersRef:
+ group: gateway.envoyproxy.io
+ kind: EnvoyProxy
+ name: tracing-otel-overall-sampling-zero
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+ name: tracing-otel-overall-sampling-zero
+ namespace: gateway-conformance-infra
+spec:
+ ipFamily: IPv4
+ telemetry:
+ tracing:
+ samplingFraction:
+ numerator: 100
+ clientSamplingFraction:
+ numerator: 100
+ overallSamplingFraction:
+ numerator: 0
+ provider:
+ backendRefs:
+ - name: otel-collector
+ namespace: monitoring
+ port: 4317
+ tags:
+ "provider": "otel-overall-sampling-zero"
+ "k8s.cluster.name": "envoy-gateway"
+ "k8s.pod.name": "%ENVIRONMENT(ENVOY_POD_NAME)%"
+ "k8s.namespace.name": "%ENVIRONMENT(ENVOY_POD_NAMESPACE)%"
+ shutdown:
+ drainTimeout: 5s
+ minDrainDuration: 1s
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: tracing-otel-overall-sampling-zero
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: tracing-otel-overall-sampling-zero
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /otel-overall-sampling-zero
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: tracing-otel-client-sampling-zero
+ namespace: gateway-conformance-infra
+spec:
+ gatewayClassName: "{GATEWAY_CLASS_NAME}"
+ listeners:
+ - name: http
+ port: 80
+ protocol: HTTP
+ allowedRoutes:
+ namespaces:
+ from: All
+ infrastructure:
+ parametersRef:
+ group: gateway.envoyproxy.io
+ kind: EnvoyProxy
+ name: tracing-otel-client-sampling-zero
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+ name: tracing-otel-client-sampling-zero
+ namespace: gateway-conformance-infra
+spec:
+ ipFamily: IPv4
+ telemetry:
+ tracing:
+ samplingFraction:
+ numerator: 0
+ clientSamplingFraction:
+ numerator: 0
+ overallSamplingFraction:
+ numerator: 100
+ provider:
+ backendRefs:
+ - name: otel-collector
+ namespace: monitoring
+ port: 4317
+ tags:
+ "provider": "otel-client-sampling-zero"
+ "k8s.cluster.name": "envoy-gateway"
+ "k8s.pod.name": "%ENVIRONMENT(ENVOY_POD_NAME)%"
+ "k8s.namespace.name": "%ENVIRONMENT(ENVOY_POD_NAMESPACE)%"
+ shutdown:
+ drainTimeout: 5s
+ minDrainDuration: 1s
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: tracing-otel-client-sampling-zero
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: tracing-otel-client-sampling-zero
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /otel-client-sampling-zero
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
diff --git a/test/e2e/tests/tracing.go b/test/e2e/tests/tracing.go
index 706ef16dd7..3b555e7e84 100644
--- a/test/e2e/tests/tracing.go
+++ b/test/e2e/tests/tracing.go
@@ -33,7 +33,11 @@ func init() {
var OpenTelemetryTracingTest = suite.ConformanceTest{
ShortName: "OpenTelemetryTracing",
Description: "Make sure OpenTelemetry tracing is working (default and custom service name)",
- Manifests: []string{"testdata/tracing-otel.yaml", "testdata/tracing-otel-custom-service-name.yaml"},
+ Manifests: []string{
+ "testdata/tracing-otel.yaml",
+ "testdata/tracing-otel-custom-service-name.yaml",
+ "testdata/tracing-otel-sampling-fractions.yaml",
+ },
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
cases := []struct {
name string
@@ -82,6 +86,55 @@ var OpenTelemetryTracingTest = suite.ConformanceTest{
})
}
+ samplingCases := []struct {
+ name string
+ routeName string
+ path string
+ headers map[string]string
+ provider string
+ }{
+ {
+ name: "OverallSamplingFractionZero",
+ routeName: "tracing-otel-overall-sampling-zero",
+ path: "/otel-overall-sampling-zero",
+ provider: "otel-overall-sampling-zero",
+ },
+ {
+ name: "ClientSamplingFractionZero",
+ routeName: "tracing-otel-client-sampling-zero",
+ path: "/otel-client-sampling-zero",
+ headers: map[string]string{
+ "X-Client-Trace-Id": "force-client-trace",
+ },
+ provider: "otel-client-sampling-zero",
+ },
+ }
+ for _, tc := range samplingCases {
+ t.Run(tc.name, func(t *testing.T) {
+ ns := "gateway-conformance-infra"
+ routeNN := types.NamespacedName{Name: tc.routeName, Namespace: ns}
+ gwNN := types.NamespacedName{Name: tc.routeName, Namespace: ns}
+ gwAddr := kubernetes.GatewayAndRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), &gwapiv1.HTTPRoute{}, false, routeNN)
+ expectedResponse := httputils.ExpectedResponse{
+ Request: httputils.Request{
+ Path: tc.path,
+ Headers: tc.headers,
+ },
+ Response: httputils.Response{
+ StatusCodes: []int{200},
+ },
+ Namespace: ns,
+ }
+ httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
+ tags := map[string]string{
+ "component": "proxy",
+ "provider": tc.provider,
+ "service.name": naming.ServiceName(gwNN),
+ }
+ expectNoTraceCountIncrease(t, suite, gwAddr, &expectedResponse, tags)
+ })
+ }
+
t.Run("ControlPlane", func(t *testing.T) {
tags := map[string]string{
"service.name": "envoy-gateway",
@@ -103,6 +156,40 @@ var OpenTelemetryTracingTest = suite.ConformanceTest{
},
}
+func expectNoTraceCountIncrease(t *testing.T, suite *suite.ConformanceTestSuite, gwAddr string, expectedResponse *httputils.ExpectedResponse, tags map[string]string) {
+ t.Helper()
+
+ var preCount int
+ err := wait.PollUntilContextTimeout(t.Context(), time.Second, 15*time.Second, true, func(_ context.Context) (done bool, err error) {
+ count, err := tracing.QueryTraceFromTempo(t, suite.Client, tags)
+ if err != nil {
+ tlog.Logf(t, "failed to get trace count from tempo: %v", err)
+ return false, nil
+ }
+ preCount = count
+ return true, nil
+ })
+ require.NoError(t, err, "failed to get initial trace count from tempo")
+
+ httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, *expectedResponse)
+
+ observedCurrentCount := false
+ err = wait.PollUntilContextTimeout(t.Context(), time.Second, 15*time.Second, true, func(_ context.Context) (bool, error) {
+ curCount, err := tracing.QueryTraceFromTempo(t, suite.Client, tags)
+ if err != nil {
+ tlog.Logf(t, "failed to get current trace count from tempo: %v", err)
+ return false, nil
+ }
+ observedCurrentCount = true
+ if curCount > preCount {
+ return false, fmt.Errorf("trace count increased from %d to %d", preCount, curCount)
+ }
+ return false, nil
+ })
+ require.ErrorIs(t, err, context.DeadlineExceeded)
+ require.True(t, observedCurrentCount, "failed to get current trace count from tempo")
+}
+
var ZipkinTracingTest = suite.ConformanceTest{
ShortName: "ZipkinTracing",
Description: "Make sure Zipkin tracing is working",
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index 37a82f491c..8f8da667ef 100644
--- a/test/helm/gateway-crds-helm/all.out.yaml
+++ b/test/helm/gateway-crds-helm/all.out.yaml
@@ -27769,6 +27769,28 @@ spec:
This takes precedence over EnvoyProxy tracing when set.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -27833,6 +27855,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
samplingFraction:
description: |-
SamplingFraction represents the fraction of requests that should be
@@ -50014,6 +50056,28 @@ spec:
Tracing defines tracing configuration for managed proxies.
If unspecified, will not send tracing data.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -50078,6 +50142,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
provider:
description: Provider defines the tracing provider.
properties:
diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml
index 7adb011c27..389a62580b 100644
--- a/test/helm/gateway-crds-helm/e2e.out.yaml
+++ b/test/helm/gateway-crds-helm/e2e.out.yaml
@@ -3707,6 +3707,28 @@ spec:
This takes precedence over EnvoyProxy tracing when set.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -3771,6 +3793,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
samplingFraction:
description: |-
SamplingFraction represents the fraction of requests that should be
@@ -25952,6 +25994,28 @@ spec:
Tracing defines tracing configuration for managed proxies.
If unspecified, will not send tracing data.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -26016,6 +26080,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
provider:
description: Provider defines the tracing provider.
properties:
diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
index f926f93ec0..dd8f56bd32 100644
--- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
+++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
@@ -3707,6 +3707,28 @@ spec:
This takes precedence over EnvoyProxy tracing when set.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -3771,6 +3793,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
samplingFraction:
description: |-
SamplingFraction represents the fraction of requests that should be
@@ -25952,6 +25994,28 @@ spec:
Tracing defines tracing configuration for managed proxies.
If unspecified, will not send tracing data.
properties:
+ clientSamplingFraction:
+ description: |-
+ ClientSamplingFraction represents the fraction of requests that should be
+ selected for tracing when requested by the client.
+ If unspecified, client-forced tracing is disabled by default and users must
+ set this field to opt in.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
customTags:
additionalProperties:
properties:
@@ -26016,6 +26080,26 @@ spec:
Deprecated: Use Tags instead.
type: object
+ overallSamplingFraction:
+ description: |-
+ OverallSamplingFraction represents the fraction of requests that should be
+ selected for tracing after all other sampling checks have been applied.
+ properties:
+ denominator:
+ default: 100
+ format: int32
+ minimum: 1
+ type: integer
+ numerator:
+ format: int32
+ minimum: 0
+ type: integer
+ required:
+ - numerator
+ type: object
+ x-kubernetes-validations:
+ - message: numerator must be less than or equal to denominator
+ rule: self.numerator <= self.denominator
provider:
description: Provider defines the tracing provider.
properties: