You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-08-10 22:31:50 +02:00
sdk/trace: self-observability: span metrics (#7027)
Fixes https://github.com/open-telemetry/opentelemetry-go/issues/7003 Implement following self-observability metrics from https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md for https://pkg.go.dev/go.opentelemetry.io/otel/sdk/trace: - `otel.sdk.span.live` - `otel.sdk.span.started`
This commit is contained in:
@@ -46,6 +46,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- The `go.opentelemetry.io/otel/semconv/v1.36.0` package.
|
||||
The package contains semantic conventions from the `v1.36.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032)
|
||||
- Add experimental self-observability span metrics in `go.opentelemetry.io/otel/sdk/trace`.
|
||||
Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@@ -50,3 +50,5 @@ replace go.opentelemetry.io/otel/trace => ../../../../trace
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/log/logtest => ../../../../sdk/log/logtest
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
@@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
|
@@ -50,3 +50,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/log => ../../../../log
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
@@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
|
@@ -31,3 +31,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../sdk
|
||||
replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
@@ -41,3 +41,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../
|
||||
replace go.opentelemetry.io/otel/trace => ../../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
@@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
|
@@ -40,3 +40,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
||||
replace go.opentelemetry.io/otel/trace => ../../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
@@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
|
@@ -40,3 +40,5 @@ replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
replace go.opentelemetry.io/otel/sdk => ../../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
@@ -29,3 +29,5 @@ require (
|
||||
replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
@@ -30,3 +30,5 @@ replace go.opentelemetry.io/otel => ../..
|
||||
replace go.opentelemetry.io/otel/sdk => ../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
|
||||
|
@@ -10,6 +10,8 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.opentelemetry.io/otel v1.37.0
|
||||
go.opentelemetry.io/otel/metric v1.37.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0
|
||||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/sys v0.34.0
|
||||
@@ -20,10 +22,11 @@ require (
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ./metric
|
||||
|
@@ -32,3 +32,5 @@ replace go.opentelemetry.io/otel/sdk => ../
|
||||
replace go.opentelemetry.io/otel/log => ../../log
|
||||
|
||||
replace go.opentelemetry.io/otel => ../..
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../metric
|
||||
|
@@ -34,3 +34,5 @@ replace go.opentelemetry.io/otel/sdk/log => ../
|
||||
replace go.opentelemetry.io/otel/log => ../../../log
|
||||
|
||||
replace go.opentelemetry.io/otel => ../../..
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../metric
|
||||
|
@@ -6,5 +6,8 @@ Package trace contains support for OpenTelemetry distributed tracing.
|
||||
|
||||
The following assumes a basic familiarity with OpenTelemetry concepts.
|
||||
See https://opentelemetry.io.
|
||||
|
||||
See [go.opentelemetry.io/otel/sdk/trace/internal/x] for information about
|
||||
the experimental features.
|
||||
*/
|
||||
package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
35
sdk/trace/internal/x/README.md
Normal file
35
sdk/trace/internal/x/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Experimental Features
|
||||
|
||||
The metric SDK contains features that have not yet stabilized in the OpenTelemetry specification.
|
||||
These features are added to the OpenTelemetry Go metric SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
|
||||
|
||||
These feature may change in backwards incompatible ways as feedback is applied.
|
||||
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
|
||||
|
||||
## Features
|
||||
|
||||
- [Self-Observability](#self-observability)
|
||||
|
||||
### Self-Observability
|
||||
|
||||
The SDK provides a self-observability feature that allows you to monitor the SDK itself.
|
||||
|
||||
To opt-in, set the environment variable `OTEL_GO_X_SELF_OBSERVABILITY` to `true`.
|
||||
|
||||
When enabled, the SDK will create the following metrics using the global `MeterProvider`:
|
||||
|
||||
- `otel.sdk.span.live`
|
||||
- `otel.sdk.span.started`
|
||||
|
||||
Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics.
|
||||
|
||||
[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md
|
||||
|
||||
## Compatibility and Stability
|
||||
|
||||
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../VERSIONING.md).
|
||||
These features may be removed or modified in successive version releases, including patch versions.
|
||||
|
||||
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
|
||||
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
|
||||
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.
|
63
sdk/trace/internal/x/x.go
Normal file
63
sdk/trace/internal/x/x.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk/trace].
|
||||
package x // import "go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SelfObservability is an experimental feature flag that determines if SDK
|
||||
// self-observability metrics are enabled.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) {
|
||||
if strings.ToLower(v) == "true" {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
})
|
||||
|
||||
// Feature is an experimental feature control flag. It provides a uniform way
|
||||
// to interact with these feature flags and parse their values.
|
||||
type Feature[T any] struct {
|
||||
key string
|
||||
parse func(v string) (T, bool)
|
||||
}
|
||||
|
||||
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
|
||||
const envKeyRoot = "OTEL_GO_X_"
|
||||
return Feature[T]{
|
||||
key: envKeyRoot + suffix,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the environment variable key that needs to be set to enable the
|
||||
// feature.
|
||||
func (f Feature[T]) Key() string { return f.key }
|
||||
|
||||
// Lookup returns the user configured value for the feature and true if the
|
||||
// user has enabled the feature. Otherwise, if the feature is not enabled, a
|
||||
// zero-value and false are returned.
|
||||
func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
|
||||
//
|
||||
// > The SDK MUST interpret an empty value of an environment variable the
|
||||
// > same way as when the variable is unset.
|
||||
vRaw := os.Getenv(f.key)
|
||||
if vRaw == "" {
|
||||
return v, ok
|
||||
}
|
||||
return f.parse(vRaw)
|
||||
}
|
||||
|
||||
// Enabled returns if the feature is enabled.
|
||||
func (f Feature[T]) Enabled() bool {
|
||||
_, ok := f.Lookup()
|
||||
return ok
|
||||
}
|
59
sdk/trace/internal/x/x_test.go
Normal file
59
sdk/trace/internal/x/x_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package x
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSelfObservability(t *testing.T) {
|
||||
const key = "OTEL_GO_X_SELF_OBSERVABILITY"
|
||||
require.Equal(t, key, SelfObservability.Key())
|
||||
|
||||
t.Run("100", run(setenv(key, "100"), assertDisabled(SelfObservability)))
|
||||
t.Run("true", run(setenv(key, "true"), assertEnabled(SelfObservability, "true")))
|
||||
t.Run("True", run(setenv(key, "True"), assertEnabled(SelfObservability, "True")))
|
||||
t.Run("false", run(setenv(key, "false"), assertDisabled(SelfObservability)))
|
||||
t.Run("empty", run(assertDisabled(SelfObservability)))
|
||||
}
|
||||
|
||||
func run(steps ...func(*testing.T)) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
for _, step := range steps {
|
||||
step(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setenv(k, v string) func(t *testing.T) { //nolint:unparam // This is a reusable test utility function.
|
||||
return func(t *testing.T) { t.Setenv(k, v) }
|
||||
}
|
||||
|
||||
func assertEnabled[T any](f Feature[T], want T) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
assert.True(t, f.Enabled(), "not enabled")
|
||||
|
||||
v, ok := f.Lookup()
|
||||
assert.True(t, ok, "Lookup state")
|
||||
assert.Equal(t, want, v, "Lookup value")
|
||||
}
|
||||
}
|
||||
|
||||
func assertDisabled[T any](f Feature[T]) func(*testing.T) {
|
||||
var zero T
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
assert.False(t, f.Enabled(), "enabled")
|
||||
|
||||
v, ok := f.Lookup()
|
||||
assert.False(t, ok, "Lookup state")
|
||||
assert.Equal(t, zero, v, "Lookup value")
|
||||
}
|
||||
}
|
@@ -159,6 +159,7 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||
provider: p,
|
||||
instrumentationScope: is,
|
||||
}
|
||||
t.initSelfObservability()
|
||||
p.namedTracer[is] = t
|
||||
}
|
||||
return t, ok
|
||||
|
@@ -20,7 +20,8 @@ import (
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
@@ -496,6 +497,22 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
if s.tracer.selfObservabilityEnabled {
|
||||
// Determine the sampling result and create the corresponding attribute.
|
||||
var attrSamplingResult attribute.KeyValue
|
||||
if s.spanContext.IsSampled() {
|
||||
attrSamplingResult = s.tracer.spanLiveMetric.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
)
|
||||
} else {
|
||||
attrSamplingResult = s.tracer.spanLiveMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly)
|
||||
}
|
||||
|
||||
s.tracer.spanLiveMetric.Add(context.Background(), -1, attrSamplingResult)
|
||||
}
|
||||
}()
|
||||
|
||||
sps := s.tracer.provider.getSpanProcessors()
|
||||
if len(sps) == 0 {
|
||||
return
|
||||
|
@@ -22,9 +22,14 @@ import (
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@@ -2177,6 +2182,495 @@ func TestAddLinkToNonRecordingSpan(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfObservability(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics)
|
||||
}{
|
||||
{
|
||||
name: "SampledSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
tp := NewTracerProvider()
|
||||
_, span := tp.Tracer("").Start(context.Background(), "StartSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
|
||||
span.End()
|
||||
|
||||
want = metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 0, // No live spans at this point.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got = scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NonRecordingSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
// Create a tracer provider with NeverSample sampler to get non-recording spans.
|
||||
tp := NewTracerProvider(WithSampler(NeverSample()))
|
||||
tp.Tracer("").Start(context.Background(), "NonRecordingSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultDrop,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OnlyRecordingSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
// Create a tracer provider with NeverSample sampler to get non-recording spans.
|
||||
tp := NewTracerProvider(WithSampler(RecordingOnly()))
|
||||
tp.Tracer("").Start(context.Background(), "OnlyRecordingSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoteParentSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
// Create a remote parent context
|
||||
tid, _ := trace.TraceIDFromHex("01020304050607080102040810203040")
|
||||
sid, _ := trace.SpanIDFromHex("0102040810203040")
|
||||
remoteCtx := trace.ContextWithRemoteSpanContext(context.Background(),
|
||||
trace.NewSpanContext(trace.SpanContextConfig{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceFlags: 0x1,
|
||||
Remote: true,
|
||||
}))
|
||||
|
||||
tp := NewTracerProvider()
|
||||
tp.Tracer("").Start(remoteCtx, "ChildSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginRemote,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LocalParentSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
tp := NewTracerProvider()
|
||||
ctx, parentSpan := tp.Tracer("").Start(context.Background(), "ParentSpan")
|
||||
_, childSpan := tp.Tracer("").Start(ctx, "ChildSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 2, // Both parent and child spans are active.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1, // Parent span with no parent of its own.
|
||||
},
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginLocal,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1, // Child span with local parent.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
|
||||
childSpan.End()
|
||||
parentSpan.End()
|
||||
|
||||
want = metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 0, // No live spans after ending both.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginLocal,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got = scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "True")
|
||||
prev := otel.GetMeterProvider()
|
||||
defer otel.SetMeterProvider(prev)
|
||||
r := metric.NewManualReader()
|
||||
mp := metric.NewMeterProvider(metric.WithReader(r))
|
||||
otel.SetMeterProvider(mp)
|
||||
|
||||
scopeMetrics := func() metricdata.ScopeMetrics {
|
||||
var got metricdata.ResourceMetrics
|
||||
err := r.Collect(context.Background(), &got)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, got.ScopeMetrics, 1)
|
||||
return got.ScopeMetrics[0]
|
||||
}
|
||||
tc.test(t, scopeMetrics)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// RecordingOnly creates a Sampler that samples no traces, but enables recording.
|
||||
// The created sampler maintains any tracestate from the parent span context.
|
||||
func RecordingOnly() Sampler {
|
||||
return recordOnlySampler{}
|
||||
}
|
||||
|
||||
type recordOnlySampler struct{}
|
||||
|
||||
// ShouldSample implements Sampler interface. It always returns Record but not Sample.
|
||||
func (s recordOnlySampler) ShouldSample(p SamplingParameters) SamplingResult {
|
||||
psc := trace.SpanContextFromContext(p.ParentContext)
|
||||
return SamplingResult{
|
||||
Decision: RecordOnly,
|
||||
Tracestate: psc.TraceState(),
|
||||
}
|
||||
}
|
||||
|
||||
// Description returns description of the sampler.
|
||||
func (recordOnlySampler) Description() string {
|
||||
return "RecordingOnly"
|
||||
}
|
||||
|
||||
func TestRecordOnlySampler(t *testing.T) {
|
||||
te := NewTestExporter()
|
||||
tp := NewTracerProvider(WithSyncer(te), WithSampler(RecordingOnly()))
|
||||
|
||||
_, span := tp.Tracer("RecordOnly").Start(context.Background(), "test-span")
|
||||
|
||||
assert.True(t, span.IsRecording(), "span should be recording")
|
||||
assert.False(t, span.SpanContext().IsSampled(), "span should not be sampled")
|
||||
|
||||
span.End()
|
||||
|
||||
assert.Zero(t, te.Len(), "no spans should be exported")
|
||||
}
|
||||
|
||||
func BenchmarkTraceStart(b *testing.B) {
|
||||
tracer := NewTracerProvider().Tracer("")
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), trace.SpanContext{})
|
||||
|
@@ -7,7 +7,14 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
@@ -17,10 +24,34 @@ type tracer struct {
|
||||
|
||||
provider *TracerProvider
|
||||
instrumentationScope instrumentation.Scope
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
spanLiveMetric otelconv.SDKSpanLive
|
||||
spanStartedMetric otelconv.SDKSpanStarted
|
||||
}
|
||||
|
||||
var _ trace.Tracer = &tracer{}
|
||||
|
||||
func (tr *tracer) initSelfObservability() {
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
tr.selfObservabilityEnabled = true
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter("go.opentelemetry.io/otel/sdk/trace",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL))
|
||||
|
||||
var err error
|
||||
if tr.spanLiveMetric, err = otelconv.NewSDKSpanLive(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
if tr.spanStartedMetric, err = otelconv.NewSDKSpanStarted(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts a Span and returns it along with a context containing it.
|
||||
//
|
||||
// The Span is created with the provided name and as a child of any existing
|
||||
@@ -46,6 +77,34 @@ func (tr *tracer) Start(
|
||||
}
|
||||
|
||||
s := tr.newSpan(ctx, name, &config)
|
||||
if tr.selfObservabilityEnabled {
|
||||
// Check if the span has a parent span and set the origin attribute accordingly.
|
||||
var attrParentOrigin attribute.KeyValue
|
||||
if psc := trace.SpanContextFromContext(ctx); psc.IsValid() {
|
||||
if psc.IsRemote() {
|
||||
attrParentOrigin = tr.spanStartedMetric.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote)
|
||||
} else {
|
||||
attrParentOrigin = tr.spanStartedMetric.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal)
|
||||
}
|
||||
} else {
|
||||
attrParentOrigin = tr.spanStartedMetric.AttrSpanParentOrigin(otelconv.SpanParentOriginNone)
|
||||
}
|
||||
|
||||
// Determine the sampling result and create the corresponding attribute.
|
||||
var attrSamplingResult attribute.KeyValue
|
||||
if s.SpanContext().IsSampled() && s.IsRecording() {
|
||||
attrSamplingResult = tr.spanStartedMetric.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
)
|
||||
} else if s.IsRecording() {
|
||||
attrSamplingResult = tr.spanStartedMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly)
|
||||
} else {
|
||||
attrSamplingResult = tr.spanStartedMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop)
|
||||
}
|
||||
|
||||
tr.spanStartedMetric.Add(context.Background(), 1, attrParentOrigin, attrSamplingResult)
|
||||
}
|
||||
|
||||
if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() {
|
||||
sps := tr.provider.getSpanProcessors()
|
||||
for _, sp := range sps {
|
||||
@@ -153,6 +212,20 @@ func (tr *tracer) newRecordingSpan(
|
||||
s.SetAttributes(sr.Attributes...)
|
||||
s.SetAttributes(config.Attributes()...)
|
||||
|
||||
if tr.selfObservabilityEnabled {
|
||||
// Determine the sampling result and create the corresponding attribute.
|
||||
var attrSamplingResult attribute.KeyValue
|
||||
if s.spanContext.IsSampled() {
|
||||
attrSamplingResult = tr.spanLiveMetric.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
)
|
||||
} else {
|
||||
attrSamplingResult = tr.spanLiveMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly)
|
||||
}
|
||||
|
||||
tr.spanLiveMetric.Add(context.Background(), 1, attrSamplingResult)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user