build(deps): bump go.opentelemetry.io/otel/sdk from 1.38.0 to 1.39.0
Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.38.0 to 1.39.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.38.0...v1.39.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/sdk dependency-version: 1.39.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
aad42fd3de
commit
c21637cb0f
@@ -97,11 +97,11 @@ require (
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||
go.opentelemetry.io/contrib/zpages v0.63.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel v1.39.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.39.0
|
||||
go.opentelemetry.io/otel/trace v1.39.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
|
||||
golang.org/x/image v0.33.0
|
||||
@@ -384,7 +384,7 @@ require (
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
|
||||
@@ -1315,8 +1315,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/contrib/zpages v0.63.0 h1:TppOKuZGbqXMgsfjqq3i09N5Vbo1JLtLImUqiTPGnX4=
|
||||
go.opentelemetry.io/contrib/zpages v0.63.0/go.mod h1:5F8uugz75ay/MMhRRhxAXY33FuaI8dl7jTxefrIy5qk=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
@@ -1325,14 +1325,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
|
||||
+1
@@ -8,3 +8,4 @@ nam
|
||||
valu
|
||||
thirdparty
|
||||
addOpt
|
||||
observ
|
||||
|
||||
+3
@@ -197,6 +197,9 @@ linters:
|
||||
- float-compare
|
||||
- go-require
|
||||
- require-error
|
||||
usetesting:
|
||||
context-background: true
|
||||
context-todo: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
|
||||
+5
-1
@@ -1,4 +1,5 @@
|
||||
http://localhost
|
||||
https://localhost
|
||||
http://jaeger-collector
|
||||
https://github.com/open-telemetry/opentelemetry-go/milestone/
|
||||
https://github.com/open-telemetry/opentelemetry-go/projects
|
||||
@@ -6,4 +7,7 @@ https://github.com/open-telemetry/opentelemetry-go/projects
|
||||
https?:\/\/github\.com\/open-telemetry\/semantic-conventions\/archive\/refs\/tags\/[^.]+\.zip\[[^]]+]
|
||||
file:///home/runner/work/opentelemetry-go/opentelemetry-go/libraries
|
||||
file:///home/runner/work/opentelemetry-go/opentelemetry-go/manual
|
||||
http://4.3.2.1:78/user/123
|
||||
http://4.3.2.1:78/user/123
|
||||
file:///home/runner/work/opentelemetry-go/opentelemetry-go/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/dns:/:4317
|
||||
# URL works, but it has blocked link checkers.
|
||||
https://dl.acm.org/doi/10.1145/198429.198435
|
||||
|
||||
+71
-1
@@ -11,6 +11,74 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [1.39.0/0.61.0/0.15.0/0.0.14] 2025-12-05
|
||||
|
||||
### Added
|
||||
|
||||
- Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175)
|
||||
- Add `WithInstrumentationAttributeSet` option to `go.opentelemetry.io/otel/log`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/trace` packages.
|
||||
This provides a concurrent-safe and performant alternative to `WithInstrumentationAttributes` by accepting a pre-constructed `attribute.Set`. (#7287)
|
||||
- Add experimental observability for the Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus`.
|
||||
Check the `go.opentelemetry.io/otel/exporters/prometheus/internal/x` package documentation for more information. (#7345)
|
||||
- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#7353)
|
||||
- Add temporality selector functions `DeltaTemporalitySelector`, `CumulativeTemporalitySelector`, `LowMemoryTemporalitySelector` to `go.opentelemetry.io/otel/sdk/metric`. (#7434)
|
||||
- Add experimental observability metrics for simple log processor in `go.opentelemetry.io/otel/sdk/log`. (#7548)
|
||||
- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#7459)
|
||||
- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#7486)
|
||||
- Add experimental observability metrics for simple span processor in `go.opentelemetry.io/otel/sdk/trace`. (#7374)
|
||||
- Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7512)
|
||||
- Add experimental observability metrics for manual reader in `go.opentelemetry.io/otel/sdk/metric`. (#7524)
|
||||
- Add experimental observability metrics for periodic reader in `go.opentelemetry.io/otel/sdk/metric`. (#7571)
|
||||
- Support `OTEL_EXPORTER_OTLP_LOGS_INSECURE` and `OTEL_EXPORTER_OTLP_INSECURE` environmental variables in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7608)
|
||||
- Add `Enabled` method to the `Processor` interface in `go.opentelemetry.io/otel/sdk/log`.
|
||||
All `Processor` implementations now include an `Enabled` method. (#7639)
|
||||
- The `go.opentelemetry.io/otel/semconv/v1.38.0` package.
|
||||
The package contains semantic conventions from the `v1.38.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.38.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.37.0.`(#7648)
|
||||
|
||||
### Changed
|
||||
|
||||
- `Distinct` in `go.opentelemetry.io/otel/attribute` is no longer guaranteed to uniquely identify an attribute set.
|
||||
Collisions between `Distinct` values for different Sets are possible with extremely high cardinality (billions of series per instrument), but are highly unlikely. (#7175)
|
||||
- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/trace` synchronously de-duplicates the passed attributes instead of delegating it to the returned `TracerOption`. (#7266)
|
||||
- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/meter` synchronously de-duplicates the passed attributes instead of delegating it to the returned `MeterOption`. (#7266)
|
||||
- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/log` synchronously de-duplicates the passed attributes instead of delegating it to the returned `LoggerOption`. (#7266)
|
||||
- Rename the `OTEL_GO_X_SELF_OBSERVABILITY` environment variable to `OTEL_GO_X_OBSERVABILITY` in `go.opentelemetry.io/otel/sdk/trace`, `go.opentelemetry.io/otel/sdk/log`, and `go.opentelemetry.io/otel/exporters/stdout/stdouttrace`. (#7302)
|
||||
- Improve performance of histogram `Record` in `go.opentelemetry.io/otel/sdk/metric` when min and max are disabled using `NoMinMax`. (#7306)
|
||||
- Improve error handling for dropped data during translation by using `prometheus.NewInvalidMetric` in `go.opentelemetry.io/otel/exporters/prometheus`.
|
||||
⚠️ **Breaking Change:** Previously, these cases were only logged and scrapes succeeded.
|
||||
Now, when translation would drop data (e.g., invalid label/value), the exporter emits a `NewInvalidMetric`, and Prometheus scrapes **fail with HTTP 500** by default.
|
||||
To preserve the prior behavior (scrapes succeed while errors are logged), configure your Prometheus HTTP handler with: `promhttp.HandlerOpts{ ErrorHandling: promhttp.ContinueOnError }`. (#7363)
|
||||
- Replace fnv hash with xxhash in `go.opentelemetry.io/otel/attribute` for better performance. (#7371)
|
||||
- The default `TranslationStrategy` in `go.opentelemetry.io/exporters/prometheus` is changed from `otlptranslator.NoUTF8EscapingWithSuffixes` to `otlptranslator.UnderscoreEscapingWithSuffixes`. (#7421)
|
||||
- Improve performance of concurrent measurements in `go.opentelemetry.io/otel/sdk/metric`. (#7427)
|
||||
- Include W3C TraceFlags (bits 0–7) in the OTLP `Span.Flags` field in `go.opentelemetry.io/exporters/otlp/otlptrace/otlptracehttp` and `go.opentelemetry.io/exporters/otlp/otlptrace/otlptracegrpc`. (#7438)
|
||||
- The `ErrorType` function in `go.opentelemetry.io/otel/semconv/v1.37.0` now handles custom error types.
|
||||
If an error implements an `ErrorType() string` method, the return value of that method will be used as the error type. (#7442)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `WithInstrumentationAttributes` options in `go.opentelemetry.io/otel/trace`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/log` to properly merge attributes when passed multiple times instead of replacing them.
|
||||
Attributes with duplicate keys will use the last value passed. (#7300)
|
||||
- The equality of `attribute.Set` when using the `Equal` method is not affected by the user overriding the empty set pointed to by `attribute.EmptySet` in `go.opentelemetry.io/otel/attribute`. (#7357)
|
||||
- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#7372)
|
||||
- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7372)
|
||||
- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#7372)
|
||||
- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#7372)
|
||||
- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#7372)
|
||||
- Return partial OTLP export errors to the caller in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#7372)
|
||||
- Fix `AddAttributes`, `SetAttributes`, `SetBody` on `Record` in `go.opentelemetry.io/otel/sdk/log` to not mutate input. (#7403)
|
||||
- Do not double record measurements of `RecordSet` methods in `go.opentelemetry.io/otel/semconv/v1.37.0`. (#7655)
|
||||
- Do not double record measurements of `RecordSet` methods in `go.opentelemetry.io/otel/semconv/v1.36.0`. (#7656)
|
||||
|
||||
### Removed
|
||||
|
||||
- Drop support for [Go 1.23]. (#7274)
|
||||
- Remove the `FilterProcessor` interface in `go.opentelemetry.io/otel/sdk/log`.
|
||||
The `Enabled` method has been added to the `Processor` interface instead.
|
||||
All `Processor` implementations must now implement the `Enabled` method.
|
||||
Custom processors that do not filter records can implement `Enabled` to return `true`. (#7639)
|
||||
|
||||
## [1.38.0/0.60.0/0.14.0/0.0.13] 2025-08-29
|
||||
|
||||
This release is the last to support [Go 1.23].
|
||||
@@ -3430,8 +3498,10 @@ It contains api and sdk for trace and meter.
|
||||
- CircleCI build CI manifest files.
|
||||
- CODEOWNERS file to track owners of this project.
|
||||
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.38.0...HEAD
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.39.0...HEAD
|
||||
[1.39.0/0.61.0/0.15.0/0.0.14]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.39.0
|
||||
[1.38.0/0.60.0/0.14.0/0.0.13]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.38.0
|
||||
[0.59.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/exporters/prometheus/v0.59.1
|
||||
[1.37.0/0.59.0/0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.37.0
|
||||
[0.12.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/log/v0.12.2
|
||||
[0.12.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/log/v0.12.1
|
||||
|
||||
+446
-12
@@ -54,8 +54,8 @@ go get -d go.opentelemetry.io/otel
|
||||
(This may print some warning about "build constraints exclude all Go
|
||||
files", just ignore it.)
|
||||
|
||||
This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You
|
||||
can alternatively use `git` directly with:
|
||||
This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`.
|
||||
Alternatively, you can use `git` directly with:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/open-telemetry/opentelemetry-go
|
||||
@@ -65,8 +65,7 @@ git clone https://github.com/open-telemetry/opentelemetry-go
|
||||
that name is a kind of a redirector to GitHub that `go get` can
|
||||
understand, but `git` does not.)
|
||||
|
||||
This would put the project in the `opentelemetry-go` directory in
|
||||
current working directory.
|
||||
This will add the project as `opentelemetry-go` within the current directory.
|
||||
|
||||
Enter the newly created directory and add your fork as a new remote:
|
||||
|
||||
@@ -109,7 +108,7 @@ A PR is considered **ready to merge** when:
|
||||
|
||||
This is not enforced through automation, but needs to be validated by the
|
||||
maintainer merging.
|
||||
* At least one of the qualified approvals need to be from an
|
||||
* At least one of the qualified approvals needs to be from an
|
||||
[Approver]/[Maintainer] affiliated with a different company than the author
|
||||
of the PR.
|
||||
* PRs introducing changes that have already been discussed and consensus
|
||||
@@ -166,11 +165,11 @@ guidelines](https://opentelemetry.io/docs/specs/otel/library-guidelines).
|
||||
### Focus on Capabilities, Not Structure Compliance
|
||||
|
||||
OpenTelemetry is an evolving specification, one where the desires and
|
||||
use cases are clear, but the method to satisfy those uses cases are
|
||||
use cases are clear, but the methods to satisfy those use cases are
|
||||
not.
|
||||
|
||||
As such, Contributions should provide functionality and behavior that
|
||||
conforms to the specification, but the interface and structure is
|
||||
conforms to the specification, but the interface and structure are
|
||||
flexible.
|
||||
|
||||
It is preferable to have contributions follow the idioms of the
|
||||
@@ -217,7 +216,7 @@ about dependency compatibility.
|
||||
This project does not partition dependencies based on the environment (i.e.
|
||||
`development`, `staging`, `production`).
|
||||
|
||||
Only the dependencies explicitly included in the released modules have be
|
||||
Only the dependencies explicitly included in the released modules have been
|
||||
tested and verified to work with the released code. No other guarantee is made
|
||||
about the compatibility of other dependencies.
|
||||
|
||||
@@ -635,8 +634,8 @@ is not in their root name.
|
||||
|
||||
The use of internal packages should be scoped to a single module. A sub-module
|
||||
should never import from a parent internal package. This creates a coupling
|
||||
between the two modules where a user can upgrade the parent without the child
|
||||
and if the internal package API has changed it will fail to upgrade[^3].
|
||||
between the two modules where a user can upgrade the parent without the child,
|
||||
and if the internal package API has changed, it will fail to upgrade[^3].
|
||||
|
||||
There are two known exceptions to this rule:
|
||||
|
||||
@@ -657,7 +656,7 @@ this.
|
||||
|
||||
### Ignoring context cancellation
|
||||
|
||||
OpenTelemetry API implementations need to ignore the cancellation of the context that are
|
||||
OpenTelemetry API implementations need to ignore the cancellation of the context that is
|
||||
passed when recording a value (e.g. starting a span, recording a measurement, emitting a log).
|
||||
Recording methods should not return an error describing the cancellation state of the context
|
||||
when they complete, nor should they abort any work.
|
||||
@@ -675,6 +674,441 @@ force flushing telemetry, shutting down a signal provider) the context cancellat
|
||||
should be honored. This means all work done on behalf of the user provided context
|
||||
should be canceled.
|
||||
|
||||
### Observability
|
||||
|
||||
OpenTelemetry Go SDK components should be instrumented to enable users observability for the health and performance of the telemetry pipeline itself.
|
||||
This allows operators to understand how well their observability infrastructure is functioning and to identify potential issues before they impact their applications.
|
||||
|
||||
This section outlines the best practices for building instrumentation in OpenTelemetry Go SDK components.
|
||||
|
||||
#### Environment Variable Activation
|
||||
|
||||
Observability features are currently experimental.
|
||||
They should be disabled by default and activated through the `OTEL_GO_X_OBSERVABILITY` environment variable.
|
||||
This follows the established experimental feature pattern used throughout the SDK.
|
||||
|
||||
Components should check for this environment variable using a consistent pattern:
|
||||
|
||||
```go
|
||||
import "go.opentelemetry.io/otel/*/internal/x"
|
||||
|
||||
if x.Observability.Enabled() {
|
||||
// Initialize observability metrics
|
||||
}
|
||||
```
|
||||
|
||||
**References**:
|
||||
|
||||
- [stdouttrace exporter](./exporters/stdout/stdouttrace/internal/x/x.go)
|
||||
- [sdk](./sdk/internal/x/x.go)
|
||||
|
||||
#### Encapsulation
|
||||
|
||||
Instrumentation should be encapsulated within a dedicated `struct` (e.g. `instrumentation`).
|
||||
It should not be mixed into the instrumented component.
|
||||
|
||||
Prefer this:
|
||||
|
||||
```go
|
||||
type SDKComponent struct {
|
||||
inst *instrumentation
|
||||
}
|
||||
|
||||
type instrumentation struct {
|
||||
inflight otelconv.SDKComponentInflight
|
||||
exported otelconv.SDKComponentExported
|
||||
}
|
||||
```
|
||||
|
||||
To this:
|
||||
|
||||
```go
|
||||
// ❌ Avoid this pattern.
|
||||
type SDKComponent struct {
|
||||
/* other SDKComponent fields... */
|
||||
|
||||
inflight otelconv.SDKComponentInflight
|
||||
exported otelconv.SDKComponentExported
|
||||
}
|
||||
```
|
||||
|
||||
The instrumentation code should not bloat the code being instrumented.
|
||||
Likely, this means its own file, or its own package if it is complex or reused.
|
||||
|
||||
#### Initialization
|
||||
|
||||
Instrumentation setup should be explicit, side-effect free, and local to the relevant component.
|
||||
Avoid relying on global or implicit [side effects][side-effect] for initialization.
|
||||
|
||||
Encapsulate setup in constructor functions, ensuring clear ownership and scope:
|
||||
|
||||
```go
|
||||
import (
|
||||
"errors"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
)
|
||||
|
||||
type SDKComponent struct {
|
||||
inst *instrumentation
|
||||
}
|
||||
|
||||
func NewSDKComponent(config Config) (*SDKComponent, error) {
|
||||
inst, err := newInstrumentation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SDKComponent{inst: inst}, nil
|
||||
}
|
||||
|
||||
type instrumentation struct {
|
||||
inflight otelconv.SDKComponentInflight
|
||||
exported otelconv.SDKComponentExported
|
||||
}
|
||||
|
||||
func newInstrumentation() (*instrumentation, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
meter := otel.GetMeterProvider().Meter(
|
||||
"<component-package-name>",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
inst := &instrumentation{}
|
||||
|
||||
var err, e error
|
||||
inst.inflight, e = otelconv.NewSDKComponentInflight(meter)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
inst.exported, e = otelconv.NewSDKComponentExported(meter)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
return inst, err
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// ❌ Avoid this pattern.
|
||||
func (c *Component) initObservability() {
|
||||
// Initialize observability metrics
|
||||
if !x.Observability.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize observability metrics
|
||||
c.inst = &instrumentation{/* ... */}
|
||||
}
|
||||
```
|
||||
|
||||
[side-effect]: https://en.wikipedia.org/wiki/Side_effect_(computer_science)
|
||||
|
||||
#### Performance
|
||||
|
||||
When observability is disabled there should be little to no overhead.
|
||||
|
||||
```go
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
if e.inst != nil {
|
||||
attrs := expensiveOperation()
|
||||
e.inst.recordSpanInflight(ctx, int64(len(spans)), attrs...)
|
||||
}
|
||||
// Export spans...
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// ❌ Avoid this pattern.
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
attrs := expensiveOperation()
|
||||
e.inst.recordSpanInflight(ctx, int64(len(spans)), attrs...)
|
||||
// Export spans...
|
||||
}
|
||||
|
||||
func (i *instrumentation) recordSpanInflight(ctx context.Context, count int64, attrs ...attribute.KeyValue) {
|
||||
if i == nil || i.inflight == nil {
|
||||
return
|
||||
}
|
||||
i.inflight.Add(ctx, count, metric.WithAttributes(attrs...))
|
||||
}
|
||||
```
|
||||
|
||||
When observability is enabled, the instrumentation code paths should be optimized to reduce allocation and computation overhead.
|
||||
|
||||
##### Attribute and Option Allocation Management
|
||||
|
||||
Pool attribute slices and options with [`sync.Pool`] to minimize allocations in measurement calls with dynamic attributes.
|
||||
|
||||
```go
|
||||
var (
|
||||
attrPool = sync.Pool{
|
||||
New: func() any {
|
||||
// Pre-allocate common capacity
|
||||
knownCap := 8 // Adjust based on expected usage
|
||||
s := make([]attribute.KeyValue, 0, knownCap)
|
||||
// Return a pointer to avoid extra allocation on Put().
|
||||
return &s
|
||||
},
|
||||
}
|
||||
|
||||
addOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1 // WithAttributeSet
|
||||
o := make([]metric.AddOption, 0, n)
|
||||
// Return a pointer to avoid extra allocation on Put().
|
||||
return &o
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (i *instrumentation) record(ctx context.Context, value int64, baseAttrs ...attribute.KeyValue) {
|
||||
attrs := attrPool.Get().(*[]attribute.KeyValue)
|
||||
defer func() {
|
||||
*attrs = (*attrs)[:0] // Reset.
|
||||
attrPool.Put(attrs)
|
||||
}()
|
||||
|
||||
*attrs = append(*attrs, baseAttrs...)
|
||||
// Add any dynamic attributes.
|
||||
*attrs = append(*attrs, semconv.OTelComponentName("exporter-1"))
|
||||
|
||||
addOpt := addOptPool.Get().(*[]metric.AddOption)
|
||||
defer func() {
|
||||
*addOpt = (*addOpt)[:0]
|
||||
addOptPool.Put(addOpt)
|
||||
}()
|
||||
|
||||
set := attribute.NewSet(*attrs...)
|
||||
*addOpt = append(*addOpt, metric.WithAttributeSet(set))
|
||||
|
||||
i.counter.Add(ctx, value, *addOpt...)
|
||||
}
|
||||
```
|
||||
|
||||
Pools are most effective when there are many pooled objects of the same sufficiently large size, and the objects are repeatedly used.
|
||||
This amortizes the cost of allocation and synchronization.
|
||||
Ideally, the pools should be scoped to be used as widely as possible within the component to maximize this efficiency while still ensuring correctness.
|
||||
|
||||
[`sync.Pool`]: https://pkg.go.dev/sync#Pool
|
||||
|
||||
##### Cache common attribute sets for repeated measurements
|
||||
|
||||
If a static set of attributes are used for measurements and they are known at compile time, pre-compute and cache these attributes.
|
||||
|
||||
```go
|
||||
type spanLiveSetKey struct {
|
||||
sampled bool
|
||||
}
|
||||
|
||||
var spanLiveSetCache = map[spanLiveSetKey]attribute.Set{
|
||||
{true}: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
{false}: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
func spanLiveSet(sampled bool) attribute.Set {
|
||||
key := spanLiveSetKey{sampled: sampled}
|
||||
return spanLiveSetCache[key]
|
||||
}
|
||||
```
|
||||
|
||||
##### Benchmarking
|
||||
|
||||
Always provide benchmarks when introducing or refactoring instrumentation.
|
||||
Demonstrate the impact (allocs/op, B/op, ns/op) in enabled/disabled scenarios:
|
||||
|
||||
```go
|
||||
func BenchmarkExportSpans(b *testing.B) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
obsEnabled bool
|
||||
}{
|
||||
{"ObsDisabled", false},
|
||||
{"ObsEnabled", true},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
b.Run(scenario.name, func(b *testing.B) {
|
||||
b.Setenv(
|
||||
"OTEL_GO_X_OBSERVABILITY",
|
||||
strconv.FormatBool(scenario.obsEnabled),
|
||||
)
|
||||
|
||||
exporter := NewExporter()
|
||||
spans := generateTestSpans(100)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = exporter.ExportSpans(context.Background(), spans)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Error Handling and Robustness
|
||||
|
||||
Errors should be reported back to the caller if possible, and partial failures should be handled as gracefully as possible.
|
||||
|
||||
```go
|
||||
func newInstrumentation() (*instrumentation, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m := otel.GetMeterProvider().Meter(/* initialize meter */)
|
||||
counter, err := otelconv.NewSDKComponentCounter(m)
|
||||
// Use the partially initialized counter if available.
|
||||
i := &instrumentation{counter: counter}
|
||||
// Return any error to the caller.
|
||||
return i, err
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// ❌ Avoid this pattern.
|
||||
func newInstrumentation() *instrumentation {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m := otel.GetMeterProvider().Meter(/* initialize meter */)
|
||||
counter, err := otelconv.NewSDKComponentCounter(m)
|
||||
if err != nil {
|
||||
// ❌ Do not dump the error to the OTel Handler. Return it to the
|
||||
// caller.
|
||||
otel.Handle(err)
|
||||
// ❌ Do not return nil if we can still use the partially initialized
|
||||
// counter.
|
||||
return nil
|
||||
}
|
||||
return &instrumentation{counter: counter}
|
||||
}
|
||||
```
|
||||
|
||||
If the instrumented component cannot report the error to the user, let it report the error to `otel.Handle`.
|
||||
|
||||
#### Context Propagation
|
||||
|
||||
Ensure observability measurements receive the correct context, especially for trace exemplars and distributed context:
|
||||
|
||||
```go
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
// Use the provided context for observability measurements
|
||||
e.inst.recordSpanExportStarted(ctx, len(spans))
|
||||
|
||||
err := e.doExport(ctx, spans)
|
||||
|
||||
if err != nil {
|
||||
e.inst.recordSpanExportFailed(ctx, len(spans), err)
|
||||
} else {
|
||||
e.inst.recordSpanExportSucceeded(ctx, len(spans))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// ❌ Avoid this pattern.
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
// ❌ Do not break the context propagation.
|
||||
e.inst.recordSpanExportStarted(context.Background(), len(spans))
|
||||
|
||||
err := e.doExport(ctx, spans)
|
||||
|
||||
/* ... */
|
||||
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
#### Semantic Conventions Compliance
|
||||
|
||||
All observability metrics should follow the [OpenTelemetry Semantic Conventions for SDK metrics](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/otel/sdk-metrics.md).
|
||||
|
||||
Use the metric semantic conventions convenience package [otelconv](./semconv/v1.37.0/otelconv/metric.go).
|
||||
|
||||
##### Component Identification
|
||||
|
||||
Component names and types should follow [semantic convention](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/registry/attributes/otel.md#otel-component-attributes).
|
||||
|
||||
If a component is not a well-known type specified in the semantic conventions, use the package path scope type as a stable identifier.
|
||||
|
||||
```go
|
||||
componentType := "go.opentelemetry.io/otel/sdk/trace.Span"
|
||||
```
|
||||
|
||||
```go
|
||||
// ❌ Do not do this.
|
||||
componentType := "trace-span"
|
||||
```
|
||||
|
||||
The component name should be a stable unique identifier for the specific instance of the component.
|
||||
|
||||
Use a global counter to ensure uniqueness if necessary.
|
||||
|
||||
```go
|
||||
// Unique 0-based ID counter for component instances.
|
||||
var componentIDCounter atomic.Int64
|
||||
|
||||
// nextID returns the next unique ID for a component.
|
||||
func nextID() int64 {
|
||||
return componentIDCounter.Add(1) - 1
|
||||
}
|
||||
|
||||
// componentName returns a unique name for the component instance.
|
||||
func componentName() attribute.KeyValue {
|
||||
id := nextID()
|
||||
name := fmt.Sprintf("%s/%d", componentType, id)
|
||||
return semconv.OTelComponentName(name)
|
||||
}
|
||||
```
|
||||
|
||||
The component ID will need to be resettable for deterministic testing.
|
||||
If tests are in a different package than the component being tested (i.e. a `<component package>_test` package name), use a generated `counter` internal package to manage the counter.
|
||||
See [stdouttrace exporter example](./exporters/stdout/stdouttrace/internal/gen.go) for reference.
|
||||
|
||||
#### Testing
|
||||
|
||||
Use deterministic testing with isolated state:
|
||||
|
||||
```go
|
||||
func TestObservability(t *testing.T) {
|
||||
// Restore state after test to ensure this does not affect other tests.
|
||||
prev := otel.GetMeterProvider()
|
||||
t.Cleanup(func() { otel.SetMeterProvider(prev) })
|
||||
|
||||
// Isolate the meter provider for deterministic testing
|
||||
reader := metric.NewManualReader()
|
||||
meterProvider := metric.NewMeterProvider(metric.WithReader(reader))
|
||||
otel.SetMeterProvider(meterProvider)
|
||||
|
||||
// Use t.Setenv to ensure environment variable is restored after test.
|
||||
t.Setenv("OTEL_GO_X_OBSERVABILITY", "true")
|
||||
|
||||
// Reset component ID counter to ensure deterministic component names.
|
||||
componentIDCounter.Store(0)
|
||||
|
||||
/* ... test code ... */
|
||||
}
|
||||
```
|
||||
|
||||
Test order should not affect results.
|
||||
Ensure that any global state (e.g. component ID counters) is reset between tests.
|
||||
|
||||
## Approvers and Maintainers
|
||||
|
||||
### Maintainers
|
||||
@@ -696,7 +1130,6 @@ For more information about the approver role, see the [community repository](htt
|
||||
### Triagers
|
||||
|
||||
- [Alex Kats](https://github.com/akats7), Capital One
|
||||
- [Cheng-Zhen Yang](https://github.com/scorpionknifes), Independent
|
||||
|
||||
For more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager).
|
||||
|
||||
@@ -704,6 +1137,7 @@ For more information about the triager role, see the [community repository](http
|
||||
|
||||
- [Aaron Clawson](https://github.com/MadVikingGod)
|
||||
- [Anthony Mirabella](https://github.com/Aneurysm9)
|
||||
- [Cheng-Zhen Yang](https://github.com/scorpionknifes)
|
||||
- [Chester Cheung](https://github.com/hanyuancheung)
|
||||
- [Evan Torrie](https://github.com/evantorrie)
|
||||
- [Gustavo Silva Paiva](https://github.com/paivagustavo)
|
||||
|
||||
+2
-1
@@ -146,11 +146,12 @@ build-tests/%:
|
||||
|
||||
# Tests
|
||||
|
||||
TEST_TARGETS := test-default test-bench test-short test-verbose test-race test-concurrent-safe
|
||||
TEST_TARGETS := test-default test-bench test-short test-verbose test-race test-concurrent-safe test-fuzz
|
||||
.PHONY: $(TEST_TARGETS) test
|
||||
test-default test-race: ARGS=-race
|
||||
test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=.
|
||||
test-short: ARGS=-short
|
||||
test-fuzz: ARGS=-fuzztime=10s -fuzz
|
||||
test-verbose: ARGS=-v -race
|
||||
test-concurrent-safe: ARGS=-run=ConcurrentSafe -count=100 -race
|
||||
test-concurrent-safe: TIMEOUT=120
|
||||
|
||||
+2
-9
@@ -55,25 +55,18 @@ Currently, this project supports the following environments.
|
||||
|----------|------------|--------------|
|
||||
| Ubuntu | 1.25 | amd64 |
|
||||
| Ubuntu | 1.24 | amd64 |
|
||||
| Ubuntu | 1.23 | amd64 |
|
||||
| Ubuntu | 1.25 | 386 |
|
||||
| Ubuntu | 1.24 | 386 |
|
||||
| Ubuntu | 1.23 | 386 |
|
||||
| Ubuntu | 1.25 | arm64 |
|
||||
| Ubuntu | 1.24 | arm64 |
|
||||
| Ubuntu | 1.23 | arm64 |
|
||||
| macOS 13 | 1.25 | amd64 |
|
||||
| macOS 13 | 1.24 | amd64 |
|
||||
| macOS 13 | 1.23 | amd64 |
|
||||
| macOS | 1.25 | amd64 |
|
||||
| macOS | 1.24 | amd64 |
|
||||
| macOS | 1.25 | arm64 |
|
||||
| macOS | 1.24 | arm64 |
|
||||
| macOS | 1.23 | arm64 |
|
||||
| Windows | 1.25 | amd64 |
|
||||
| Windows | 1.24 | amd64 |
|
||||
| Windows | 1.23 | amd64 |
|
||||
| Windows | 1.25 | 386 |
|
||||
| Windows | 1.24 | 386 |
|
||||
| Windows | 1.23 | 386 |
|
||||
|
||||
While this project should work for other systems, no compatibility guarantees
|
||||
are made for those systems currently.
|
||||
|
||||
+40
-32
@@ -24,7 +24,7 @@ Ensure things look correct before submitting a pull request to include the addit
|
||||
|
||||
## Breaking changes validation
|
||||
|
||||
You can run `make gorelease` that runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes done in the public API.
|
||||
You can run `make gorelease` which runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes made in the public API.
|
||||
|
||||
You can check/report problems with `gorelease` [here](https://golang.org/issues/26420).
|
||||
|
||||
@@ -62,7 +62,7 @@ Update go.mod for submodules to depend on the new release which will happen in t
|
||||
```
|
||||
|
||||
3. Update the [Changelog](./CHANGELOG.md).
|
||||
- Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand.
|
||||
- Make sure all relevant changes for this release are included and are written in language that non-contributors to the project can understand.
|
||||
To verify this, you can look directly at the commits since the `<last tag>`.
|
||||
|
||||
```
|
||||
@@ -107,33 +107,49 @@ It is critical you make sure the version you push upstream is correct.
|
||||
...
|
||||
```
|
||||
|
||||
## Sign artifacts
|
||||
|
||||
To ensure we comply with CNCF best practices, we need to sign the release artifacts.
|
||||
|
||||
Download the `.tar.gz` and `.zip` archives from the [tags page](https://github.com/open-telemetry/opentelemetry-go/tags) for the new release tag.
|
||||
Both archives need to be signed with your GPG key.
|
||||
|
||||
You can use [this script] to verify the contents of the archives before signing them.
|
||||
|
||||
To find your GPG key ID, run:
|
||||
|
||||
```terminal
|
||||
gpg --list-secret-keys --keyid-format=long
|
||||
```
|
||||
|
||||
The key ID is the 16-character string after `sec rsa4096/` (or similar).
|
||||
|
||||
Set environment variables and sign both artifacts:
|
||||
|
||||
```terminal
|
||||
export VERSION="<version>" # e.g., v1.32.0
|
||||
export KEY_ID="<your-gpg-key-id>"
|
||||
|
||||
gpg --local-user $KEY_ID --armor --detach-sign opentelemetry-go-$VERSION.tar.gz
|
||||
gpg --local-user $KEY_ID --armor --detach-sign opentelemetry-go-$VERSION.zip
|
||||
```
|
||||
|
||||
You can verify the signatures with:
|
||||
|
||||
```terminal
|
||||
gpg --verify opentelemetry-go-$VERSION.tar.gz.asc opentelemetry-go-$VERSION.tar.gz
|
||||
gpg --verify opentelemetry-go-$VERSION.zip.asc opentelemetry-go-$VERSION.zip
|
||||
```
|
||||
|
||||
[this script]: https://github.com/MrAlias/attest-sh
|
||||
|
||||
## Release
|
||||
|
||||
Finally create a Release for the new `<new tag>` on GitHub.
|
||||
The release body should include all the release notes from the Changelog for this release.
|
||||
|
||||
### Sign the Release Artifact
|
||||
|
||||
To ensure we comply with CNCF best practices, we need to sign the release artifact.
|
||||
The tarball attached to the GitHub release needs to be signed with your GPG key.
|
||||
|
||||
Follow [these steps] to sign the release artifact and upload it to GitHub.
|
||||
You can use [this script] to verify the contents of the tarball before signing it.
|
||||
|
||||
Be sure to use the correct GPG key when signing the release artifact.
|
||||
|
||||
```terminal
|
||||
gpg --local-user <key-id> --armor --detach-sign opentelemetry-go-<version>.tar.gz
|
||||
```
|
||||
|
||||
You can verify the signature with:
|
||||
|
||||
```terminal
|
||||
gpg --verify opentelemetry-go-<version>.tar.gz.asc opentelemetry-go-<version>.tar.gz
|
||||
```
|
||||
|
||||
[these steps]: https://wiki.debian.org/Creating%20signed%20GitHub%20releases
|
||||
[this script]: https://github.com/MrAlias/attest-sh
|
||||
***IMPORTANT***: GitHub Releases are immutable once created.
|
||||
You must upload the signed artifacts (`.tar.gz`, `.tar.gz.asc`, `.zip`, and `.zip.asc`) when creating the release, as they cannot be added or modified later.
|
||||
|
||||
## Post-Release
|
||||
|
||||
@@ -160,14 +176,6 @@ This helps track what changes were included in each release.
|
||||
|
||||
Once all related issues and PRs have been added to the milestone, close the milestone.
|
||||
|
||||
### Demo Repository
|
||||
|
||||
Bump the dependencies in the following Go services:
|
||||
|
||||
- [`accounting`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accounting)
|
||||
- [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkout)
|
||||
- [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/product-catalog)
|
||||
|
||||
### Close the `Version Release` issue
|
||||
|
||||
Once the todo list in the `Version Release` issue is complete, close the issue.
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ is designed so the following goals can be achieved.
|
||||
in either the module path or the import path.
|
||||
* In addition to public APIs, telemetry produced by stable instrumentation
|
||||
will remain stable and backwards compatible. This is to avoid breaking
|
||||
alerts and dashboard.
|
||||
alerts and dashboards.
|
||||
* Modules will be used to encapsulate instrumentation, detectors, exporters,
|
||||
propagators, and any other independent sets of related components.
|
||||
* Experimental modules still under active development will be versioned at
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ type (
|
||||
// set into a wire representation.
|
||||
Encoder interface {
|
||||
// Encode returns the serialized encoding of the attribute set using
|
||||
// its Iterator. This result may be cached by a attribute.Set.
|
||||
// its Iterator. This result may be cached by an attribute.Set.
|
||||
Encode(iterator Iterator) string
|
||||
|
||||
// ID returns a value that is unique for each class of attribute
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute/internal/xxhash"
|
||||
)
|
||||
|
||||
// Type identifiers. These identifiers are hashed before the value of the
|
||||
// corresponding type. This is done to distinguish values that are hashed with
|
||||
// the same value representation (e.g. `int64(1)` and `true`, []int64{0} and
|
||||
// int64(0)).
|
||||
//
|
||||
// These are all 8 byte length strings converted to a uint64 representation. A
|
||||
// uint64 is used instead of the string directly as an optimization, it avoids
|
||||
// the for loop in [xxhash] which adds minor overhead.
|
||||
const (
|
||||
boolID uint64 = 7953749933313450591 // "_boolean" (little endian)
|
||||
int64ID uint64 = 7592915492740740150 // "64_bit_i" (little endian)
|
||||
float64ID uint64 = 7376742710626956342 // "64_bit_f" (little endian)
|
||||
stringID uint64 = 6874584755375207263 // "_string_" (little endian)
|
||||
boolSliceID uint64 = 6875993255270243167 // "_[]bool_" (little endian)
|
||||
int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian)
|
||||
float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian)
|
||||
stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian)
|
||||
)
|
||||
|
||||
// hashKVs returns a new xxHash64 hash of kvs.
|
||||
func hashKVs(kvs []KeyValue) uint64 {
|
||||
h := xxhash.New()
|
||||
for _, kv := range kvs {
|
||||
h = hashKV(h, kv)
|
||||
}
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
// hashKV returns the xxHash64 hash of kv with h as the base.
|
||||
func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash {
|
||||
h = h.String(string(kv.Key))
|
||||
|
||||
switch kv.Value.Type() {
|
||||
case BOOL:
|
||||
h = h.Uint64(boolID)
|
||||
h = h.Uint64(kv.Value.numeric)
|
||||
case INT64:
|
||||
h = h.Uint64(int64ID)
|
||||
h = h.Uint64(kv.Value.numeric)
|
||||
case FLOAT64:
|
||||
h = h.Uint64(float64ID)
|
||||
// Assumes numeric stored with math.Float64bits.
|
||||
h = h.Uint64(kv.Value.numeric)
|
||||
case STRING:
|
||||
h = h.Uint64(stringID)
|
||||
h = h.String(kv.Value.stringly)
|
||||
case BOOLSLICE:
|
||||
h = h.Uint64(boolSliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Bool(rv.Index(i).Bool())
|
||||
}
|
||||
case INT64SLICE:
|
||||
h = h.Uint64(int64SliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Int64(rv.Index(i).Int())
|
||||
}
|
||||
case FLOAT64SLICE:
|
||||
h = h.Uint64(float64SliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Float64(rv.Index(i).Float())
|
||||
}
|
||||
case STRINGSLICE:
|
||||
h = h.Uint64(stringSliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.String(rv.Index(i).String())
|
||||
}
|
||||
case INVALID:
|
||||
default:
|
||||
// Logging is an alternative, but using the internal logger here
|
||||
// causes an import cycle so it is not done.
|
||||
v := kv.Value.AsInterface()
|
||||
msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", v)
|
||||
panic(msg)
|
||||
}
|
||||
return h
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package xxhash provides a wrapper around the xxhash library for attribute hashing.
|
||||
package xxhash // import "go.opentelemetry.io/otel/attribute/internal/xxhash"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
)
|
||||
|
||||
// Hash wraps xxhash.Digest to provide an API friendly for hashing attribute values.
|
||||
type Hash struct {
|
||||
d *xxhash.Digest
|
||||
}
|
||||
|
||||
// New returns a new initialized xxHash64 hasher.
|
||||
func New() Hash {
|
||||
return Hash{d: xxhash.New()}
|
||||
}
|
||||
|
||||
func (h Hash) Uint64(val uint64) Hash {
|
||||
var buf [8]byte
|
||||
binary.LittleEndian.PutUint64(buf[:], val)
|
||||
// errors from Write are always nil for xxhash
|
||||
// if it returns an err then panic
|
||||
_, err := h.d.Write(buf[:])
|
||||
if err != nil {
|
||||
panic("xxhash write of uint64 failed: " + err.Error())
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h Hash) Bool(val bool) Hash { // nolint:revive // This is a hashing function.
|
||||
if val {
|
||||
return h.Uint64(1)
|
||||
}
|
||||
return h.Uint64(0)
|
||||
}
|
||||
|
||||
func (h Hash) Float64(val float64) Hash {
|
||||
return h.Uint64(math.Float64bits(val))
|
||||
}
|
||||
|
||||
func (h Hash) Int64(val int64) Hash {
|
||||
return h.Uint64(uint64(val)) // nolint:gosec // Overflow doesn't matter since we are hashing.
|
||||
}
|
||||
|
||||
func (h Hash) String(val string) Hash {
|
||||
// errors from WriteString are always nil for xxhash
|
||||
// if it returns an err then panic
|
||||
_, err := h.d.WriteString(val)
|
||||
if err != nil {
|
||||
panic("xxhash write of string failed: " + err.Error())
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// Sum64 returns the current hash value.
|
||||
func (h Hash) Sum64() uint64 {
|
||||
return h.d.Sum64()
|
||||
}
|
||||
+85
-60
@@ -9,6 +9,8 @@ import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute/internal/xxhash"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -23,19 +25,19 @@ type (
|
||||
// the Equals method to ensure stable equivalence checking.
|
||||
//
|
||||
// Users should also use the Distinct returned from Equivalent as a map key
|
||||
// instead of a Set directly. In addition to that type providing guarantees
|
||||
// on stable equivalence, it may also provide performance improvements.
|
||||
// instead of a Set directly. Set has relatively poor performance when used
|
||||
// as a map key compared to Distinct.
|
||||
Set struct {
|
||||
equivalent Distinct
|
||||
hash uint64
|
||||
data any
|
||||
}
|
||||
|
||||
// Distinct is a unique identifier of a Set.
|
||||
// Distinct is an identifier of a Set which is very likely to be unique.
|
||||
//
|
||||
// Distinct is designed to ensure equivalence stability: comparisons will
|
||||
// return the same value across versions. For this reason, Distinct should
|
||||
// always be used as a map key instead of a Set.
|
||||
// Distinct should be used as a map key instead of a Set for to provide better
|
||||
// performance for map operations.
|
||||
Distinct struct {
|
||||
iface any
|
||||
hash uint64
|
||||
}
|
||||
|
||||
// Sortable implements sort.Interface, used for sorting KeyValue.
|
||||
@@ -46,15 +48,34 @@ type (
|
||||
Sortable []KeyValue
|
||||
)
|
||||
|
||||
// Compile time check these types remain comparable.
|
||||
var (
|
||||
_ = isComparable(Set{})
|
||||
_ = isComparable(Distinct{})
|
||||
)
|
||||
|
||||
func isComparable[T comparable](t T) T { return t }
|
||||
|
||||
var (
|
||||
// keyValueType is used in computeDistinctReflect.
|
||||
keyValueType = reflect.TypeOf(KeyValue{})
|
||||
|
||||
// emptySet is returned for empty attribute sets.
|
||||
emptySet = &Set{
|
||||
equivalent: Distinct{
|
||||
iface: [0]KeyValue{},
|
||||
},
|
||||
// emptyHash is the hash of an empty set.
|
||||
emptyHash = xxhash.New().Sum64()
|
||||
|
||||
// userDefinedEmptySet is an empty set. It was mistakenly exposed to users
|
||||
// as something they can assign to, so it must remain addressable and
|
||||
// mutable.
|
||||
//
|
||||
// This is kept for backwards compatibility, but should not be used in new code.
|
||||
userDefinedEmptySet = &Set{
|
||||
hash: emptyHash,
|
||||
data: [0]KeyValue{},
|
||||
}
|
||||
|
||||
emptySet = Set{
|
||||
hash: emptyHash,
|
||||
data: [0]KeyValue{},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -62,33 +83,35 @@ var (
|
||||
//
|
||||
// This is a convenience provided for optimized calling utility.
|
||||
func EmptySet() *Set {
|
||||
return emptySet
|
||||
}
|
||||
|
||||
// reflectValue abbreviates reflect.ValueOf(d).
|
||||
func (d Distinct) reflectValue() reflect.Value {
|
||||
return reflect.ValueOf(d.iface)
|
||||
// Continue to return the pointer to the user-defined empty set for
|
||||
// backwards-compatibility.
|
||||
//
|
||||
// New code should not use this, instead use emptySet.
|
||||
return userDefinedEmptySet
|
||||
}
|
||||
|
||||
// Valid reports whether this value refers to a valid Set.
|
||||
func (d Distinct) Valid() bool {
|
||||
return d.iface != nil
|
||||
func (d Distinct) Valid() bool { return d.hash != 0 }
|
||||
|
||||
// reflectValue abbreviates reflect.ValueOf(d).
|
||||
func (l Set) reflectValue() reflect.Value {
|
||||
return reflect.ValueOf(l.data)
|
||||
}
|
||||
|
||||
// Len returns the number of attributes in this set.
|
||||
func (l *Set) Len() int {
|
||||
if l == nil || !l.equivalent.Valid() {
|
||||
if l == nil || l.hash == 0 {
|
||||
return 0
|
||||
}
|
||||
return l.equivalent.reflectValue().Len()
|
||||
return l.reflectValue().Len()
|
||||
}
|
||||
|
||||
// Get returns the KeyValue at ordered position idx in this set.
|
||||
func (l *Set) Get(idx int) (KeyValue, bool) {
|
||||
if l == nil || !l.equivalent.Valid() {
|
||||
if l == nil || l.hash == 0 {
|
||||
return KeyValue{}, false
|
||||
}
|
||||
value := l.equivalent.reflectValue()
|
||||
value := l.reflectValue()
|
||||
|
||||
if idx >= 0 && idx < value.Len() {
|
||||
// Note: The Go compiler successfully avoids an allocation for
|
||||
@@ -101,10 +124,10 @@ func (l *Set) Get(idx int) (KeyValue, bool) {
|
||||
|
||||
// Value returns the value of a specified key in this set.
|
||||
func (l *Set) Value(k Key) (Value, bool) {
|
||||
if l == nil || !l.equivalent.Valid() {
|
||||
if l == nil || l.hash == 0 {
|
||||
return Value{}, false
|
||||
}
|
||||
rValue := l.equivalent.reflectValue()
|
||||
rValue := l.reflectValue()
|
||||
vlen := rValue.Len()
|
||||
|
||||
idx := sort.Search(vlen, func(idx int) bool {
|
||||
@@ -144,20 +167,29 @@ func (l *Set) ToSlice() []KeyValue {
|
||||
return iter.ToSlice()
|
||||
}
|
||||
|
||||
// Equivalent returns a value that may be used as a map key. The Distinct type
|
||||
// guarantees that the result will equal the equivalent. Distinct value of any
|
||||
// Equivalent returns a value that may be used as a map key. Equal Distinct
|
||||
// values are very likely to be equivalent attribute Sets. Distinct value of any
|
||||
// attribute set with the same elements as this, where sets are made unique by
|
||||
// choosing the last value in the input for any given key.
|
||||
func (l *Set) Equivalent() Distinct {
|
||||
if l == nil || !l.equivalent.Valid() {
|
||||
return emptySet.equivalent
|
||||
if l == nil || l.hash == 0 {
|
||||
return Distinct{hash: emptySet.hash}
|
||||
}
|
||||
return l.equivalent
|
||||
return Distinct{hash: l.hash}
|
||||
}
|
||||
|
||||
// Equals reports whether the argument set is equivalent to this set.
|
||||
func (l *Set) Equals(o *Set) bool {
|
||||
return l.Equivalent() == o.Equivalent()
|
||||
if l.Equivalent() != o.Equivalent() {
|
||||
return false
|
||||
}
|
||||
if l == nil || l.hash == 0 {
|
||||
l = &emptySet
|
||||
}
|
||||
if o == nil || o.hash == 0 {
|
||||
o = &emptySet
|
||||
}
|
||||
return l.data == o.data
|
||||
}
|
||||
|
||||
// Encoded returns the encoded form of this set, according to encoder.
|
||||
@@ -169,12 +201,6 @@ func (l *Set) Encoded(encoder Encoder) string {
|
||||
return encoder.Encode(l.Iter())
|
||||
}
|
||||
|
||||
func empty() Set {
|
||||
return Set{
|
||||
equivalent: emptySet.equivalent,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSet returns a new Set. See the documentation for
|
||||
// NewSetWithSortableFiltered for more details.
|
||||
//
|
||||
@@ -204,7 +230,7 @@ func NewSetWithSortable(kvs []KeyValue, _ *Sortable) Set {
|
||||
func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
|
||||
// Check for empty set.
|
||||
if len(kvs) == 0 {
|
||||
return empty(), nil
|
||||
return emptySet, nil
|
||||
}
|
||||
|
||||
// Stable sort so the following de-duplication can implement
|
||||
@@ -233,10 +259,10 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
|
||||
|
||||
if filter != nil {
|
||||
if div := filteredToFront(kvs, filter); div != 0 {
|
||||
return Set{equivalent: computeDistinct(kvs[div:])}, kvs[:div]
|
||||
return newSet(kvs[div:]), kvs[:div]
|
||||
}
|
||||
}
|
||||
return Set{equivalent: computeDistinct(kvs)}, nil
|
||||
return newSet(kvs), nil
|
||||
}
|
||||
|
||||
// NewSetWithSortableFiltered returns a new Set.
|
||||
@@ -316,7 +342,7 @@ func (l *Set) Filter(re Filter) (Set, []KeyValue) {
|
||||
if first == 0 {
|
||||
// It is safe to assume len(slice) >= 1 given we found at least one
|
||||
// attribute above that needs to be filtered out.
|
||||
return Set{equivalent: computeDistinct(slice[1:])}, slice[:1]
|
||||
return newSet(slice[1:]), slice[:1]
|
||||
}
|
||||
|
||||
// Move the filtered slice[first] to the front (preserving order).
|
||||
@@ -326,25 +352,24 @@ func (l *Set) Filter(re Filter) (Set, []KeyValue) {
|
||||
|
||||
// Do not re-evaluate re(slice[first+1:]).
|
||||
div := filteredToFront(slice[1:first+1], re) + 1
|
||||
return Set{equivalent: computeDistinct(slice[div:])}, slice[:div]
|
||||
return newSet(slice[div:]), slice[:div]
|
||||
}
|
||||
|
||||
// computeDistinct returns a Distinct using either the fixed- or
|
||||
// reflect-oriented code path, depending on the size of the input. The input
|
||||
// slice is assumed to already be sorted and de-duplicated.
|
||||
func computeDistinct(kvs []KeyValue) Distinct {
|
||||
iface := computeDistinctFixed(kvs)
|
||||
if iface == nil {
|
||||
iface = computeDistinctReflect(kvs)
|
||||
// newSet returns a new set based on the sorted and uniqued kvs.
|
||||
func newSet(kvs []KeyValue) Set {
|
||||
s := Set{
|
||||
hash: hashKVs(kvs),
|
||||
data: computeDataFixed(kvs),
|
||||
}
|
||||
return Distinct{
|
||||
iface: iface,
|
||||
if s.data == nil {
|
||||
s.data = computeDataReflect(kvs)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// computeDistinctFixed computes a Distinct for small slices. It returns nil
|
||||
// if the input is too large for this code path.
|
||||
func computeDistinctFixed(kvs []KeyValue) any {
|
||||
// computeDataFixed computes a Set data for small slices. It returns nil if the
|
||||
// input is too large for this code path.
|
||||
func computeDataFixed(kvs []KeyValue) any {
|
||||
switch len(kvs) {
|
||||
case 1:
|
||||
return [1]KeyValue(kvs)
|
||||
@@ -371,9 +396,9 @@ func computeDistinctFixed(kvs []KeyValue) any {
|
||||
}
|
||||
}
|
||||
|
||||
// computeDistinctReflect computes a Distinct using reflection, works for any
|
||||
// size input.
|
||||
func computeDistinctReflect(kvs []KeyValue) any {
|
||||
// computeDataReflect computes a Set data using reflection, works for any size
|
||||
// input.
|
||||
func computeDataReflect(kvs []KeyValue) any {
|
||||
at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem()
|
||||
for i, keyValue := range kvs {
|
||||
*(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue
|
||||
@@ -383,7 +408,7 @@ func computeDistinctReflect(kvs []KeyValue) any {
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Set.
|
||||
func (l *Set) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.equivalent.iface)
|
||||
return json.Marshal(l.data)
|
||||
}
|
||||
|
||||
// MarshalLog is the marshaling function used by the logging system to represent this Set.
|
||||
|
||||
+3
-2
@@ -24,8 +24,9 @@ const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICE
|
||||
var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71}
|
||||
|
||||
func (i Type) String() string {
|
||||
if i < 0 || i >= Type(len(_Type_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_Type_index)-1 {
|
||||
return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Type_name[_Type_index[i]:_Type_index[i+1]]
|
||||
return _Type_name[_Type_index[idx]:_Type_index[idx+1]]
|
||||
}
|
||||
|
||||
+6
-6
@@ -648,7 +648,7 @@ func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||
// If we couldn't find any valid key character,
|
||||
// it means the key is either empty or invalid.
|
||||
if keyStart == keyEnd {
|
||||
return
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// Skip spaces after the key: " key< >= value ".
|
||||
@@ -658,13 +658,13 @@ func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||
// A key can have no value, like: " key ".
|
||||
ok = true
|
||||
p.key = s[keyStart:keyEnd]
|
||||
return
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// If we have not reached the end and we can't find the '=' delimiter,
|
||||
// it means the property is invalid.
|
||||
if s[index] != keyValueDelimiter[0] {
|
||||
return
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// Attempting to parse the value.
|
||||
@@ -690,14 +690,14 @@ func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||
// we have not reached the end, it means the property is
|
||||
// invalid, something like: " key = value value1".
|
||||
if index != len(s) {
|
||||
return
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// Decode a percent-encoded value.
|
||||
rawVal := s[valueStart:valueEnd]
|
||||
unescapeVal, err := url.PathUnescape(rawVal)
|
||||
if err != nil {
|
||||
return
|
||||
return p, ok
|
||||
}
|
||||
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
|
||||
|
||||
@@ -706,7 +706,7 @@ func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||
p.hasValue = true
|
||||
|
||||
p.value = value
|
||||
return
|
||||
return p, ok
|
||||
}
|
||||
|
||||
func skipSpace(s string, offset int) int {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# This is a renovate-friendly source of Docker images.
|
||||
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
|
||||
FROM otel/weaver:v0.17.1@sha256:32523b5e44fb44418786347e9f7dde187d8797adb6d57a2ee99c245346c3cdfe AS weaver
|
||||
FROM otel/weaver:v0.19.0@sha256:3d20814cef548f1d31f27f054fb4cd6a05125641a9f7cc29fc7eb234e8052cd9 AS weaver
|
||||
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ type delegatedInstrument interface {
|
||||
setDelegate(metric.Meter)
|
||||
}
|
||||
|
||||
// instID are the identifying properties of a instrument.
|
||||
// instID are the identifying properties of an instrument.
|
||||
type instID struct {
|
||||
// name is the name of the stream.
|
||||
name string
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
// Meter returns a Meter from the global MeterProvider. The name must be the
|
||||
// name of the library providing instrumentation. This name may be the same as
|
||||
// the instrumented code only if that code provides built-in instrumentation.
|
||||
// If the name is empty, then a implementation defined default name will be
|
||||
// If the name is empty, then an implementation defined default name will be
|
||||
// used instead.
|
||||
//
|
||||
// If this is called before a global MeterProvider is registered the returned
|
||||
|
||||
+34
-4
@@ -3,7 +3,11 @@
|
||||
|
||||
package metric // import "go.opentelemetry.io/otel/metric"
|
||||
|
||||
import "go.opentelemetry.io/otel/attribute"
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// MeterConfig contains options for Meters.
|
||||
type MeterConfig struct {
|
||||
@@ -62,12 +66,38 @@ func WithInstrumentationVersion(version string) MeterOption {
|
||||
})
|
||||
}
|
||||
|
||||
// WithInstrumentationAttributes sets the instrumentation attributes.
|
||||
// WithInstrumentationAttributes adds the instrumentation attributes.
|
||||
//
|
||||
// The passed attributes will be de-duplicated.
|
||||
// This is equivalent to calling [WithInstrumentationAttributeSet] with an
|
||||
// [attribute.Set] created from a clone of the passed attributes.
|
||||
// [WithInstrumentationAttributeSet] is recommended for more control.
|
||||
//
|
||||
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
|
||||
// options are passed, the attributes will be merged together in the order
|
||||
// they are passed. Attributes with duplicate keys will use the last value passed.
|
||||
func WithInstrumentationAttributes(attr ...attribute.KeyValue) MeterOption {
|
||||
set := attribute.NewSet(slices.Clone(attr)...)
|
||||
return WithInstrumentationAttributeSet(set)
|
||||
}
|
||||
|
||||
// WithInstrumentationAttributeSet adds the instrumentation attributes.
|
||||
//
|
||||
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
|
||||
// options are passed, the attributes will be merged together in the order
|
||||
// they are passed. Attributes with duplicate keys will use the last value passed.
|
||||
func WithInstrumentationAttributeSet(set attribute.Set) MeterOption {
|
||||
if set.Len() == 0 {
|
||||
return meterOptionFunc(func(config MeterConfig) MeterConfig {
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
return meterOptionFunc(func(config MeterConfig) MeterConfig {
|
||||
config.attrs = attribute.NewSet(attr...)
|
||||
if config.attrs.Len() == 0 {
|
||||
config.attrs = set
|
||||
} else {
|
||||
config.attrs = mergeSets(config.attrs, set)
|
||||
}
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ func (TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
|
||||
}
|
||||
|
||||
// Clear all flags other than the trace-context supported sampling bit.
|
||||
scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled
|
||||
scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled // nolint:gosec // slice size already checked.
|
||||
|
||||
// Ignore the error returned here. Failure to parse tracestate MUST NOT
|
||||
// affect the parsing of traceparent according to the W3C tracecontext
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk].
|
||||
package x // import "go.opentelemetry.io/otel/sdk/internal/x"
|
||||
|
||||
import "strings"
|
||||
|
||||
// Resource is an experimental feature flag that defines if resource detectors
|
||||
// should be included experimental semantic conventions.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_RESOURCE environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var Resource = newFeature(
|
||||
[]string{"RESOURCE"},
|
||||
func(v string) (string, bool) {
|
||||
if strings.EqualFold(v, "true") {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
},
|
||||
)
|
||||
|
||||
// Observability is an experimental feature flag that determines if SDK
|
||||
// observability metrics are enabled.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var Observability = newFeature(
|
||||
[]string{"OBSERVABILITY", "SELF_OBSERVABILITY"},
|
||||
func(v string) (string, bool) {
|
||||
if strings.EqualFold(v, "true") {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
},
|
||||
)
|
||||
+19
-27
@@ -1,48 +1,38 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x contains support for OTel SDK experimental features.
|
||||
//
|
||||
// This package should only be used for features defined in the specification.
|
||||
// It should not be used for experiments or new project ideas.
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk].
|
||||
package x // import "go.opentelemetry.io/otel/sdk/internal/x"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Resource is an experimental feature flag that defines if resource detectors
|
||||
// should be included experimental semantic conventions.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_RESOURCE environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var Resource = newFeature("RESOURCE", func(v string) (string, bool) {
|
||||
if strings.EqualFold(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
|
||||
keys []string
|
||||
parse func(v string) (T, bool)
|
||||
}
|
||||
|
||||
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
|
||||
func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] {
|
||||
const envKeyRoot = "OTEL_GO_X_"
|
||||
keys := make([]string, 0, len(suffix))
|
||||
for _, s := range suffix {
|
||||
keys = append(keys, envKeyRoot+s)
|
||||
}
|
||||
return Feature[T]{
|
||||
key: envKeyRoot + suffix,
|
||||
keys: keys,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the environment variable key that needs to be set to enable the
|
||||
// Keys returns the environment variable keys that can be set to enable the
|
||||
// feature.
|
||||
func (f Feature[T]) Key() string { return f.key }
|
||||
func (f Feature[T]) Keys() []string { return f.keys }
|
||||
|
||||
// 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
|
||||
@@ -52,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||
//
|
||||
// > 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
|
||||
for _, key := range f.keys {
|
||||
vRaw := os.Getenv(key)
|
||||
if vRaw != "" {
|
||||
return f.parse(vRaw)
|
||||
}
|
||||
}
|
||||
return f.parse(vRaw)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Enabled reports whether the feature is enabled.
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build dragonfly || freebsd || netbsd || openbsd || solaris
|
||||
// +build dragonfly freebsd netbsd openbsd solaris
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows
|
||||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
// +build aix dragonfly freebsd linux netbsd openbsd solaris zos
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
|
||||
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
|
||||
+19
-88
@@ -6,20 +6,14 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/env"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/env"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@@ -33,8 +27,6 @@ const (
|
||||
DefaultMaxExportBatchSize = 512
|
||||
)
|
||||
|
||||
var queueFull = otelconv.ErrorTypeAttr("queue_full")
|
||||
|
||||
// BatchSpanProcessorOption configures a BatchSpanProcessor.
|
||||
type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions)
|
||||
|
||||
@@ -78,10 +70,7 @@ type batchSpanProcessor struct {
|
||||
queue chan ReadOnlySpan
|
||||
dropped uint32
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
callbackRegistration metric.Registration
|
||||
spansProcessedCounter otelconv.SDKProcessorSpanProcessed
|
||||
componentNameAttr attribute.KeyValue
|
||||
inst *observ.BSP
|
||||
|
||||
batch []ReadOnlySpan
|
||||
batchMutex sync.Mutex
|
||||
@@ -124,19 +113,14 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
if x.SelfObservability.Enabled() {
|
||||
bsp.selfObservabilityEnabled = true
|
||||
bsp.componentNameAttr = componentName()
|
||||
|
||||
var err error
|
||||
bsp.spansProcessedCounter, bsp.callbackRegistration, err = newBSPObs(
|
||||
bsp.componentNameAttr,
|
||||
func() int64 { return int64(len(bsp.queue)) },
|
||||
int64(bsp.o.MaxQueueSize),
|
||||
)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
var err error
|
||||
bsp.inst, err = observ.NewBSP(
|
||||
nextProcessorID(),
|
||||
func() int64 { return int64(len(bsp.queue)) },
|
||||
int64(bsp.o.MaxQueueSize),
|
||||
)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
bsp.stopWait.Add(1)
|
||||
@@ -157,51 +141,6 @@ func nextProcessorID() int64 {
|
||||
return processorIDCounter.Add(1) - 1
|
||||
}
|
||||
|
||||
func componentName() attribute.KeyValue {
|
||||
id := nextProcessorID()
|
||||
name := fmt.Sprintf("%s/%d", otelconv.ComponentTypeBatchingSpanProcessor, id)
|
||||
return semconv.OTelComponentName(name)
|
||||
}
|
||||
|
||||
// newBSPObs creates and returns a new set of metrics instruments and a
|
||||
// registration for a BatchSpanProcessor. It is the caller's responsibility
|
||||
// to unregister the registration when it is no longer needed.
|
||||
func newBSPObs(
|
||||
cmpnt attribute.KeyValue,
|
||||
qLen func() int64,
|
||||
qMax int64,
|
||||
) (otelconv.SDKProcessorSpanProcessed, metric.Registration, error) {
|
||||
meter := otel.GetMeterProvider().Meter(
|
||||
selfObsScopeName,
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
qCap, err := otelconv.NewSDKProcessorSpanQueueCapacity(meter)
|
||||
|
||||
qSize, e := otelconv.NewSDKProcessorSpanQueueSize(meter)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
spansProcessed, e := otelconv.NewSDKProcessorSpanProcessed(meter)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
cmpntT := semconv.OTelComponentTypeBatchingSpanProcessor
|
||||
attrs := metric.WithAttributes(cmpnt, cmpntT)
|
||||
|
||||
reg, e := meter.RegisterCallback(
|
||||
func(_ context.Context, o metric.Observer) error {
|
||||
o.ObserveInt64(qSize.Inst(), qLen(), attrs)
|
||||
o.ObserveInt64(qCap.Inst(), qMax, attrs)
|
||||
return nil
|
||||
},
|
||||
qSize.Inst(),
|
||||
qCap.Inst(),
|
||||
)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
return spansProcessed, reg, err
|
||||
}
|
||||
|
||||
// OnStart method does nothing.
|
||||
func (*batchSpanProcessor) OnStart(context.Context, ReadWriteSpan) {}
|
||||
|
||||
@@ -242,8 +181,8 @@ func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
}
|
||||
if bsp.selfObservabilityEnabled {
|
||||
err = errors.Join(err, bsp.callbackRegistration.Unregister())
|
||||
if bsp.inst != nil {
|
||||
err = errors.Join(err, bsp.inst.Shutdown())
|
||||
}
|
||||
})
|
||||
return err
|
||||
@@ -357,10 +296,8 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
|
||||
|
||||
if l := len(bsp.batch); l > 0 {
|
||||
global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped))
|
||||
if bsp.selfObservabilityEnabled {
|
||||
bsp.spansProcessedCounter.Add(ctx, int64(l),
|
||||
bsp.componentNameAttr,
|
||||
bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor))
|
||||
if bsp.inst != nil {
|
||||
bsp.inst.Processed(ctx, int64(l))
|
||||
}
|
||||
err := bsp.e.ExportSpans(ctx, bsp.batch)
|
||||
|
||||
@@ -470,11 +407,8 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R
|
||||
case bsp.queue <- sd:
|
||||
return true
|
||||
case <-ctx.Done():
|
||||
if bsp.selfObservabilityEnabled {
|
||||
bsp.spansProcessedCounter.Add(ctx, 1,
|
||||
bsp.componentNameAttr,
|
||||
bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor),
|
||||
bsp.spansProcessedCounter.AttrErrorType(queueFull))
|
||||
if bsp.inst != nil {
|
||||
bsp.inst.ProcessedQueueFull(ctx, 1)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -490,11 +424,8 @@ func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan)
|
||||
return true
|
||||
default:
|
||||
atomic.AddUint32(&bsp.dropped, 1)
|
||||
if bsp.selfObservabilityEnabled {
|
||||
bsp.spansProcessedCounter.Add(ctx, 1,
|
||||
bsp.componentNameAttr,
|
||||
bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor),
|
||||
bsp.spansProcessedCounter.AttrErrorType(queueFull))
|
||||
if bsp.inst != nil {
|
||||
bsp.inst.ProcessedQueueFull(ctx, 1)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ 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
|
||||
See [go.opentelemetry.io/otel/sdk/internal/x] for information about
|
||||
the experimental features.
|
||||
*/
|
||||
package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
// Package env provides types and functionality for environment variable support
|
||||
// in the OpenTelemetry SDK.
|
||||
package env // import "go.opentelemetry.io/otel/sdk/internal/env"
|
||||
package env // import "go.opentelemetry.io/otel/sdk/trace/internal/env"
|
||||
|
||||
import (
|
||||
"os"
|
||||
Generated
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// ScopeName is the name of the instrumentation scope.
|
||||
ScopeName = "go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
|
||||
// SchemaURL is the schema URL of the instrumentation.
|
||||
SchemaURL = semconv.SchemaURL
|
||||
)
|
||||
|
||||
// ErrQueueFull is the attribute value for the "queue_full" error type.
|
||||
var ErrQueueFull = otelconv.SDKProcessorSpanProcessed{}.AttrErrorType(
|
||||
otelconv.ErrorTypeAttr("queue_full"),
|
||||
)
|
||||
|
||||
// BSPComponentName returns the component name attribute for a
|
||||
// BatchSpanProcessor with the given ID.
|
||||
func BSPComponentName(id int64) attribute.KeyValue {
|
||||
t := otelconv.ComponentTypeBatchingSpanProcessor
|
||||
name := fmt.Sprintf("%s/%d", t, id)
|
||||
return semconv.OTelComponentName(name)
|
||||
}
|
||||
|
||||
// BSP is the instrumentation for an OTel SDK BatchSpanProcessor.
|
||||
type BSP struct {
|
||||
reg metric.Registration
|
||||
|
||||
processed metric.Int64Counter
|
||||
processedOpts []metric.AddOption
|
||||
processedQueueFullOpts []metric.AddOption
|
||||
}
|
||||
|
||||
func NewBSP(id int64, qLen func() int64, qMax int64) (*BSP, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
meter := otel.GetMeterProvider().Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(SchemaURL),
|
||||
)
|
||||
|
||||
qCap, err := otelconv.NewSDKProcessorSpanQueueCapacity(meter)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to create BSP queue capacity metric: %w", err)
|
||||
}
|
||||
qCapInst := qCap.Inst()
|
||||
|
||||
qSize, e := otelconv.NewSDKProcessorSpanQueueSize(meter)
|
||||
if e != nil {
|
||||
e := fmt.Errorf("failed to create BSP queue size metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
qSizeInst := qSize.Inst()
|
||||
|
||||
cmpntT := semconv.OTelComponentTypeBatchingSpanProcessor
|
||||
cmpnt := BSPComponentName(id)
|
||||
set := attribute.NewSet(cmpnt, cmpntT)
|
||||
|
||||
obsOpts := []metric.ObserveOption{metric.WithAttributeSet(set)}
|
||||
reg, e := meter.RegisterCallback(
|
||||
func(_ context.Context, o metric.Observer) error {
|
||||
o.ObserveInt64(qSizeInst, qLen(), obsOpts...)
|
||||
o.ObserveInt64(qCapInst, qMax, obsOpts...)
|
||||
return nil
|
||||
},
|
||||
qSizeInst,
|
||||
qCapInst,
|
||||
)
|
||||
if e != nil {
|
||||
e := fmt.Errorf("failed to register BSP queue size/capacity callback: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
|
||||
processed, e := otelconv.NewSDKProcessorSpanProcessed(meter)
|
||||
if e != nil {
|
||||
e := fmt.Errorf("failed to create BSP processed spans metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
processedOpts := []metric.AddOption{metric.WithAttributeSet(set)}
|
||||
|
||||
set = attribute.NewSet(cmpnt, cmpntT, ErrQueueFull)
|
||||
processedQueueFullOpts := []metric.AddOption{metric.WithAttributeSet(set)}
|
||||
|
||||
return &BSP{
|
||||
reg: reg,
|
||||
processed: processed.Inst(),
|
||||
processedOpts: processedOpts,
|
||||
processedQueueFullOpts: processedQueueFullOpts,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (b *BSP) Shutdown() error { return b.reg.Unregister() }
|
||||
|
||||
func (b *BSP) Processed(ctx context.Context, n int64) {
|
||||
b.processed.Add(ctx, n, b.processedOpts...)
|
||||
}
|
||||
|
||||
func (b *BSP) ProcessedQueueFull(ctx context.Context, n int64) {
|
||||
b.processed.Add(ctx, n, b.processedQueueFullOpts...)
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package observ provides observability instrumentation for the OTel trace SDK
|
||||
// package.
|
||||
package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
)
|
||||
|
||||
var measureAttrsPool = sync.Pool{
|
||||
New: func() any {
|
||||
// "component.name" + "component.type" + "error.type"
|
||||
const n = 1 + 1 + 1
|
||||
s := make([]attribute.KeyValue, 0, n)
|
||||
// Return a pointer to a slice instead of a slice itself
|
||||
// to avoid allocations on every call.
|
||||
return &s
|
||||
},
|
||||
}
|
||||
|
||||
// SSP is the instrumentation for an OTel SDK SimpleSpanProcessor.
|
||||
type SSP struct {
|
||||
spansProcessedCounter metric.Int64Counter
|
||||
addOpts []metric.AddOption
|
||||
attrs []attribute.KeyValue
|
||||
}
|
||||
|
||||
// SSPComponentName returns the component name attribute for a
|
||||
// SimpleSpanProcessor with the given ID.
|
||||
func SSPComponentName(id int64) attribute.KeyValue {
|
||||
t := otelconv.ComponentTypeSimpleSpanProcessor
|
||||
name := fmt.Sprintf("%s/%d", t, id)
|
||||
return semconv.OTelComponentName(name)
|
||||
}
|
||||
|
||||
// NewSSP returns instrumentation for an OTel SDK SimpleSpanProcessor with the
|
||||
// provided ID.
|
||||
//
|
||||
// If the experimental observability is disabled, nil is returned.
|
||||
func NewSSP(id int64) (*SSP, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
meter := otel.GetMeterProvider().Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(SchemaURL),
|
||||
)
|
||||
spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to create SSP processed spans metric: %w", err)
|
||||
}
|
||||
|
||||
componentName := SSPComponentName(id)
|
||||
componentType := spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor)
|
||||
attrs := []attribute.KeyValue{componentName, componentType}
|
||||
addOpts := []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(attrs...))}
|
||||
|
||||
return &SSP{
|
||||
spansProcessedCounter: spansProcessedCounter.Inst(),
|
||||
addOpts: addOpts,
|
||||
attrs: attrs,
|
||||
}, err
|
||||
}
|
||||
|
||||
// SpanProcessed records that a span has been processed by the SimpleSpanProcessor.
|
||||
// If err is non-nil, it records the processing error as an attribute.
|
||||
func (ssp *SSP) SpanProcessed(ctx context.Context, err error) {
|
||||
ssp.spansProcessedCounter.Add(ctx, 1, ssp.addOption(err)...)
|
||||
}
|
||||
|
||||
func (ssp *SSP) addOption(err error) []metric.AddOption {
|
||||
if err == nil {
|
||||
return ssp.addOpts
|
||||
}
|
||||
attrs := measureAttrsPool.Get().(*[]attribute.KeyValue)
|
||||
defer func() {
|
||||
*attrs = (*attrs)[:0] // reset the slice for reuse
|
||||
measureAttrsPool.Put(attrs)
|
||||
}()
|
||||
*attrs = append(*attrs, ssp.attrs...)
|
||||
*attrs = append(*attrs, semconv.ErrorType(err))
|
||||
// Do not inefficiently make a copy of attrs by using
|
||||
// WithAttributes instead of WithAttributeSet.
|
||||
return []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(*attrs...))}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var meterOpts = []metric.MeterOption{
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(SchemaURL),
|
||||
}
|
||||
|
||||
// Tracer is instrumentation for an OTel SDK Tracer.
|
||||
type Tracer struct {
|
||||
enabled bool
|
||||
|
||||
live metric.Int64UpDownCounter
|
||||
started metric.Int64Counter
|
||||
}
|
||||
|
||||
func NewTracer() (Tracer, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return Tracer{}, nil
|
||||
}
|
||||
meter := otel.GetMeterProvider().Meter(ScopeName, meterOpts...)
|
||||
|
||||
var err error
|
||||
l, e := otelconv.NewSDKSpanLive(meter)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create span live metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
|
||||
s, e := otelconv.NewSDKSpanStarted(meter)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create span started metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
|
||||
return Tracer{enabled: true, live: l.Inst(), started: s.Inst()}, err
|
||||
}
|
||||
|
||||
func (t Tracer) Enabled() bool { return t.enabled }
|
||||
|
||||
func (t Tracer) SpanStarted(ctx context.Context, psc trace.SpanContext, span trace.Span) {
|
||||
key := spanStartedKey{
|
||||
parent: parentStateNoParent,
|
||||
sampling: samplingStateDrop,
|
||||
}
|
||||
|
||||
if psc.IsValid() {
|
||||
if psc.IsRemote() {
|
||||
key.parent = parentStateRemoteParent
|
||||
} else {
|
||||
key.parent = parentStateLocalParent
|
||||
}
|
||||
}
|
||||
|
||||
if span.IsRecording() {
|
||||
if span.SpanContext().IsSampled() {
|
||||
key.sampling = samplingStateRecordAndSample
|
||||
} else {
|
||||
key.sampling = samplingStateRecordOnly
|
||||
}
|
||||
}
|
||||
|
||||
opts := spanStartedOpts[key]
|
||||
t.started.Add(ctx, 1, opts...)
|
||||
}
|
||||
|
||||
func (t Tracer) SpanLive(ctx context.Context, span trace.Span) {
|
||||
t.spanLive(ctx, 1, span)
|
||||
}
|
||||
|
||||
func (t Tracer) SpanEnded(ctx context.Context, span trace.Span) {
|
||||
t.spanLive(ctx, -1, span)
|
||||
}
|
||||
|
||||
func (t Tracer) spanLive(ctx context.Context, value int64, span trace.Span) {
|
||||
key := spanLiveKey{sampled: span.SpanContext().IsSampled()}
|
||||
opts := spanLiveOpts[key]
|
||||
t.live.Add(ctx, value, opts...)
|
||||
}
|
||||
|
||||
type parentState int
|
||||
|
||||
const (
|
||||
parentStateNoParent parentState = iota
|
||||
parentStateLocalParent
|
||||
parentStateRemoteParent
|
||||
)
|
||||
|
||||
type samplingState int
|
||||
|
||||
const (
|
||||
samplingStateDrop samplingState = iota
|
||||
samplingStateRecordOnly
|
||||
samplingStateRecordAndSample
|
||||
)
|
||||
|
||||
type spanStartedKey struct {
|
||||
parent parentState
|
||||
sampling samplingState
|
||||
}
|
||||
|
||||
var spanStartedOpts = map[spanStartedKey][]metric.AddOption{
|
||||
{
|
||||
parentStateNoParent,
|
||||
samplingStateDrop,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
|
||||
)),
|
||||
},
|
||||
{
|
||||
parentStateLocalParent,
|
||||
samplingStateDrop,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
|
||||
)),
|
||||
},
|
||||
{
|
||||
parentStateRemoteParent,
|
||||
samplingStateDrop,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
|
||||
)),
|
||||
},
|
||||
|
||||
{
|
||||
parentStateNoParent,
|
||||
samplingStateRecordOnly,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
|
||||
)),
|
||||
},
|
||||
{
|
||||
parentStateLocalParent,
|
||||
samplingStateRecordOnly,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
|
||||
)),
|
||||
},
|
||||
{
|
||||
parentStateRemoteParent,
|
||||
samplingStateRecordOnly,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
|
||||
)),
|
||||
},
|
||||
|
||||
{
|
||||
parentStateNoParent,
|
||||
samplingStateRecordAndSample,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
|
||||
)),
|
||||
},
|
||||
{
|
||||
parentStateLocalParent,
|
||||
samplingStateRecordAndSample,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
|
||||
)),
|
||||
},
|
||||
{
|
||||
parentStateRemoteParent,
|
||||
samplingStateRecordAndSample,
|
||||
}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
|
||||
)),
|
||||
},
|
||||
}
|
||||
|
||||
type spanLiveKey struct {
|
||||
sampled bool
|
||||
}
|
||||
|
||||
var spanLiveOpts = map[spanLiveKey][]metric.AddOption{
|
||||
{true}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
)),
|
||||
},
|
||||
{false}: {
|
||||
metric.WithAttributeSet(attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
)),
|
||||
},
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
# Experimental Features
|
||||
|
||||
The Trace SDK contains features that have not yet stabilized in the OpenTelemetry specification.
|
||||
These features are added to the OpenTelemetry Go Trace SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
|
||||
|
||||
These features 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
@@ -1,63 +0,0 @@
|
||||
// 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.EqualFold(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 reports whether the feature is enabled.
|
||||
func (f Feature[T]) Enabled() bool {
|
||||
_, ok := f.Lookup()
|
||||
return ok
|
||||
}
|
||||
+10
-38
@@ -5,29 +5,21 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
|
||||
selfObsScopeName = "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
const defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
|
||||
|
||||
// tracerProviderConfig.
|
||||
type tracerProviderConfig struct {
|
||||
@@ -163,19 +155,16 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||
t, ok := p.namedTracer[is]
|
||||
if !ok {
|
||||
t = &tracer{
|
||||
provider: p,
|
||||
instrumentationScope: is,
|
||||
selfObservabilityEnabled: x.SelfObservability.Enabled(),
|
||||
provider: p,
|
||||
instrumentationScope: is,
|
||||
}
|
||||
if t.selfObservabilityEnabled {
|
||||
var err error
|
||||
t.spanLiveMetric, t.spanStartedMetric, err = newInst()
|
||||
if err != nil {
|
||||
msg := "failed to create self-observability metrics for tracer: %w"
|
||||
err := fmt.Errorf(msg, err)
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
var err error
|
||||
t.inst, err = observ.NewTracer()
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
p.namedTracer[is] = t
|
||||
}
|
||||
return t, ok
|
||||
@@ -201,23 +190,6 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||
return t
|
||||
}
|
||||
|
||||
func newInst() (otelconv.SDKSpanLive, otelconv.SDKSpanStarted, error) {
|
||||
m := otel.GetMeterProvider().Meter(
|
||||
selfObsScopeName,
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
spanLiveMetric, e := otelconv.NewSDKSpanLive(m)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
spanStartedMetric, e := otelconv.NewSDKSpanStarted(m)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
return spanLiveMetric, spanStartedMetric, err
|
||||
}
|
||||
|
||||
// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors.
|
||||
func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) {
|
||||
// This check prevents calls during a shutdown.
|
||||
|
||||
+30
-1
@@ -6,9 +6,12 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// simpleSpanProcessor is a SpanProcessor that synchronously sends all
|
||||
@@ -17,6 +20,8 @@ type simpleSpanProcessor struct {
|
||||
exporterMu sync.Mutex
|
||||
exporter SpanExporter
|
||||
stopOnce sync.Once
|
||||
|
||||
inst *observ.SSP
|
||||
}
|
||||
|
||||
var _ SpanProcessor = (*simpleSpanProcessor)(nil)
|
||||
@@ -33,11 +38,26 @@ func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor {
|
||||
ssp := &simpleSpanProcessor{
|
||||
exporter: exporter,
|
||||
}
|
||||
|
||||
var err error
|
||||
ssp.inst, err = observ.NewSSP(nextSimpleProcessorID())
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.")
|
||||
|
||||
return ssp
|
||||
}
|
||||
|
||||
var simpleProcessorIDCounter atomic.Int64
|
||||
|
||||
// nextSimpleProcessorID returns an identifier for this simple span processor,
|
||||
// starting with 0 and incrementing by 1 each time it is called.
|
||||
func nextSimpleProcessorID() int64 {
|
||||
return simpleProcessorIDCounter.Add(1) - 1
|
||||
}
|
||||
|
||||
// OnStart does nothing.
|
||||
func (*simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {}
|
||||
|
||||
@@ -46,11 +66,20 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) {
|
||||
ssp.exporterMu.Lock()
|
||||
defer ssp.exporterMu.Unlock()
|
||||
|
||||
var err error
|
||||
if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() {
|
||||
if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil {
|
||||
err = ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s})
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
if ssp.inst != nil {
|
||||
// Add the span to the context to ensure the metric is recorded
|
||||
// with the correct span context.
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext())
|
||||
ssp.inst.SpanProcessed(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown shuts down the exporter this SimpleSpanProcessor exports to.
|
||||
|
||||
+19
-8
@@ -151,6 +151,12 @@ type recordingSpan struct {
|
||||
|
||||
// tracer is the SDK tracer that created this span.
|
||||
tracer *tracer
|
||||
|
||||
// origCtx is the context used when starting this span that has the
|
||||
// recordingSpan instance set as the active span. If not nil, it is used
|
||||
// when ending the span to ensure any metrics are recorded with a context
|
||||
// containing this span without requiring an additional allocation.
|
||||
origCtx context.Context
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -158,6 +164,10 @@ var (
|
||||
_ runtimeTracer = (*recordingSpan)(nil)
|
||||
)
|
||||
|
||||
func (s *recordingSpan) setOrigCtx(ctx context.Context) {
|
||||
s.origCtx = ctx
|
||||
}
|
||||
|
||||
// SpanContext returns the SpanContext of this span.
|
||||
func (s *recordingSpan) SpanContext() trace.SpanContext {
|
||||
if s == nil {
|
||||
@@ -496,14 +506,15 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
if s.tracer.selfObservabilityEnabled {
|
||||
defer func() {
|
||||
// Add the span to the context to ensure the metric is recorded
|
||||
// with the correct span context.
|
||||
ctx := trace.ContextWithSpan(context.Background(), s)
|
||||
set := spanLiveSet(s.spanContext.IsSampled())
|
||||
s.tracer.spanLiveMetric.AddSet(ctx, -1, set)
|
||||
}()
|
||||
if s.tracer.inst.Enabled() {
|
||||
ctx := s.origCtx
|
||||
if ctx == nil {
|
||||
// This should not happen as the origCtx should be set, but
|
||||
// ensure trace information is propagated in the case of an
|
||||
// error.
|
||||
ctx = trace.ContextWithSpan(context.Background(), s)
|
||||
}
|
||||
defer s.tracer.inst.SpanEnded(ctx, s)
|
||||
}
|
||||
|
||||
sps := s.tracer.provider.getSpanProcessors()
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
import "go.opentelemetry.io/otel/sdk/internal/env"
|
||||
import "go.opentelemetry.io/otel/sdk/trace/internal/env"
|
||||
|
||||
const (
|
||||
// DefaultAttributeValueLengthLimit is the default maximum allowed
|
||||
|
||||
+14
-120
@@ -7,9 +7,8 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/observ"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
@@ -20,9 +19,7 @@ type tracer struct {
|
||||
provider *TracerProvider
|
||||
instrumentationScope instrumentation.Scope
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
spanLiveMetric otelconv.SDKSpanLive
|
||||
spanStartedMetric otelconv.SDKSpanStarted
|
||||
inst observ.Tracer
|
||||
}
|
||||
|
||||
var _ trace.Tracer = &tracer{}
|
||||
@@ -53,10 +50,17 @@ func (tr *tracer) Start(
|
||||
|
||||
s := tr.newSpan(ctx, name, &config)
|
||||
newCtx := trace.ContextWithSpan(ctx, s)
|
||||
if tr.selfObservabilityEnabled {
|
||||
if tr.inst.Enabled() {
|
||||
if o, ok := s.(interface{ setOrigCtx(context.Context) }); ok {
|
||||
// If this is a recording span, store the original context.
|
||||
// This allows later retrieval of baggage and other information
|
||||
// that may have been stored in the context at span start time and
|
||||
// to avoid the allocation of repeatedly calling
|
||||
// trace.ContextWithSpan.
|
||||
o.setOrigCtx(newCtx)
|
||||
}
|
||||
psc := trace.SpanContextFromContext(ctx)
|
||||
set := spanStartedSet(psc, s)
|
||||
tr.spanStartedMetric.AddSet(newCtx, 1, set)
|
||||
tr.inst.SpanStarted(newCtx, psc, s)
|
||||
}
|
||||
|
||||
if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() {
|
||||
@@ -168,12 +172,11 @@ func (tr *tracer) newRecordingSpan(
|
||||
s.SetAttributes(sr.Attributes...)
|
||||
s.SetAttributes(config.Attributes()...)
|
||||
|
||||
if tr.selfObservabilityEnabled {
|
||||
if tr.inst.Enabled() {
|
||||
// Propagate any existing values from the context with the new span to
|
||||
// the measurement context.
|
||||
ctx = trace.ContextWithSpan(ctx, s)
|
||||
set := spanLiveSet(s.spanContext.IsSampled())
|
||||
tr.spanLiveMetric.AddSet(ctx, 1, set)
|
||||
tr.inst.SpanLive(ctx, s)
|
||||
}
|
||||
|
||||
return s
|
||||
@@ -183,112 +186,3 @@ func (tr *tracer) newRecordingSpan(
|
||||
func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan {
|
||||
return nonRecordingSpan{tracer: tr, sc: sc}
|
||||
}
|
||||
|
||||
type parentState int
|
||||
|
||||
const (
|
||||
parentStateNoParent parentState = iota
|
||||
parentStateLocalParent
|
||||
parentStateRemoteParent
|
||||
)
|
||||
|
||||
type samplingState int
|
||||
|
||||
const (
|
||||
samplingStateDrop samplingState = iota
|
||||
samplingStateRecordOnly
|
||||
samplingStateRecordAndSample
|
||||
)
|
||||
|
||||
type spanStartedSetKey struct {
|
||||
parent parentState
|
||||
sampling samplingState
|
||||
}
|
||||
|
||||
var spanStartedSetCache = map[spanStartedSetKey]attribute.Set{
|
||||
{parentStateNoParent, samplingStateDrop}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
|
||||
),
|
||||
{parentStateLocalParent, samplingStateDrop}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
|
||||
),
|
||||
{parentStateRemoteParent, samplingStateDrop}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
|
||||
),
|
||||
|
||||
{parentStateNoParent, samplingStateRecordOnly}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
|
||||
),
|
||||
{parentStateLocalParent, samplingStateRecordOnly}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
|
||||
),
|
||||
{parentStateRemoteParent, samplingStateRecordOnly}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
|
||||
),
|
||||
|
||||
{parentStateNoParent, samplingStateRecordAndSample}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
|
||||
),
|
||||
{parentStateLocalParent, samplingStateRecordAndSample}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
|
||||
),
|
||||
{parentStateRemoteParent, samplingStateRecordAndSample}: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
|
||||
),
|
||||
}
|
||||
|
||||
func spanStartedSet(psc trace.SpanContext, span trace.Span) attribute.Set {
|
||||
key := spanStartedSetKey{
|
||||
parent: parentStateNoParent,
|
||||
sampling: samplingStateDrop,
|
||||
}
|
||||
|
||||
if psc.IsValid() {
|
||||
if psc.IsRemote() {
|
||||
key.parent = parentStateRemoteParent
|
||||
} else {
|
||||
key.parent = parentStateLocalParent
|
||||
}
|
||||
}
|
||||
|
||||
if span.IsRecording() {
|
||||
if span.SpanContext().IsSampled() {
|
||||
key.sampling = samplingStateRecordAndSample
|
||||
} else {
|
||||
key.sampling = samplingStateRecordOnly
|
||||
}
|
||||
}
|
||||
|
||||
return spanStartedSetCache[key]
|
||||
}
|
||||
|
||||
type spanLiveSetKey struct {
|
||||
sampled bool
|
||||
}
|
||||
|
||||
var spanLiveSetCache = map[spanLiveSetKey]attribute.Set{
|
||||
{true}: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
{false}: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
func spanLiveSet(sampled bool) attribute.Set {
|
||||
key := spanLiveSetKey{sampled: sampled}
|
||||
return spanLiveSetCache[key]
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry SDK in use.
|
||||
func Version() string {
|
||||
return "1.38.0"
|
||||
return "1.39.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ func firstHostPort(source ...string) (host string, port int) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
return host, port
|
||||
}
|
||||
|
||||
// RequestHeader returns the contents of h as OpenTelemetry attributes.
|
||||
|
||||
+5
-5
@@ -287,27 +287,27 @@ func splitHostPort(hostport string) (host string, port int) {
|
||||
addrEnd := strings.LastIndex(hostport, "]")
|
||||
if addrEnd < 0 {
|
||||
// Invalid hostport.
|
||||
return
|
||||
return host, port
|
||||
}
|
||||
if i := strings.LastIndex(hostport[addrEnd:], ":"); i < 0 {
|
||||
host = hostport[1:addrEnd]
|
||||
return
|
||||
return host, port
|
||||
}
|
||||
} else {
|
||||
if i := strings.LastIndex(hostport, ":"); i < 0 {
|
||||
host = hostport
|
||||
return
|
||||
return host, port
|
||||
}
|
||||
}
|
||||
|
||||
host, pStr, err := net.SplitHostPort(hostport)
|
||||
if err != nil {
|
||||
return
|
||||
return host, port
|
||||
}
|
||||
|
||||
p, err := strconv.ParseUint(pStr, 10, 16)
|
||||
if err != nil {
|
||||
return
|
||||
return host, port
|
||||
}
|
||||
return host, int(p) // nolint: gosec // Bit size of 16 checked above.
|
||||
}
|
||||
|
||||
+37
-12
@@ -4,28 +4,53 @@
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// ErrorType returns an [attribute.KeyValue] identifying the error type of err.
|
||||
//
|
||||
// If err is nil, the returned attribute has the default value
|
||||
// [ErrorTypeOther].
|
||||
//
|
||||
// If err's type has the method
|
||||
//
|
||||
// ErrorType() string
|
||||
//
|
||||
// then the returned attribute has the value of err.ErrorType(). Otherwise, the
|
||||
// returned attribute has a value derived from the concrete type of err.
|
||||
//
|
||||
// The key of the returned attribute is [ErrorTypeKey].
|
||||
func ErrorType(err error) attribute.KeyValue {
|
||||
if err == nil {
|
||||
return ErrorTypeOther
|
||||
}
|
||||
t := reflect.TypeOf(err)
|
||||
var value string
|
||||
if t.PkgPath() == "" && t.Name() == "" {
|
||||
// Likely a builtin type.
|
||||
value = t.String()
|
||||
} else {
|
||||
value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
return ErrorTypeOther
|
||||
return ErrorTypeKey.String(errorType(err))
|
||||
}
|
||||
|
||||
func errorType(err error) string {
|
||||
var s string
|
||||
if et, ok := err.(interface{ ErrorType() string }); ok {
|
||||
// Prioritize the ErrorType method if available.
|
||||
s = et.ErrorType()
|
||||
}
|
||||
return ErrorTypeKey.String(value)
|
||||
if s == "" {
|
||||
// Fallback to reflection if the ErrorType method is not supported or
|
||||
// returns an empty value.
|
||||
|
||||
t := reflect.TypeOf(err)
|
||||
pkg, name := t.PkgPath(), t.Name()
|
||||
if pkg != "" && name != "" {
|
||||
s = pkg + "." + name
|
||||
} else {
|
||||
// The type has no package path or name (predeclared, not-defined,
|
||||
// or alias for a not-defined type).
|
||||
//
|
||||
// This is not guaranteed to be unique, but is a best effort.
|
||||
s = t.String()
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
+138
-51
@@ -91,6 +91,11 @@ type ClientActiveRequests struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newClientActiveRequestsOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("Number of active HTTP requests."),
|
||||
metric.WithUnit("{request}"),
|
||||
}
|
||||
|
||||
// NewClientActiveRequests returns a new ClientActiveRequests instrument.
|
||||
func NewClientActiveRequests(
|
||||
m metric.Meter,
|
||||
@@ -101,15 +106,18 @@ func NewClientActiveRequests(
|
||||
return ClientActiveRequests{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientActiveRequestsOpts
|
||||
} else {
|
||||
opt = append(opt, newClientActiveRequestsOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"http.client.active_requests",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("Number of active HTTP requests."),
|
||||
metric.WithUnit("{request}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientActiveRequests{noop.Int64UpDownCounter{}}, err
|
||||
return ClientActiveRequests{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return ClientActiveRequests{i}, nil
|
||||
}
|
||||
@@ -223,6 +231,11 @@ type ClientConnectionDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newClientConnectionDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("The duration of the successfully established outbound HTTP connections."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewClientConnectionDuration returns a new ClientConnectionDuration instrument.
|
||||
func NewClientConnectionDuration(
|
||||
m metric.Meter,
|
||||
@@ -233,15 +246,18 @@ func NewClientConnectionDuration(
|
||||
return ClientConnectionDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientConnectionDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newClientConnectionDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"http.client.connection.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("The duration of the successfully established outbound HTTP connections."),
|
||||
metric.WithUnit("s"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientConnectionDuration{noop.Float64Histogram{}}, err
|
||||
return ClientConnectionDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ClientConnectionDuration{i}, nil
|
||||
}
|
||||
@@ -310,6 +326,7 @@ func (m ClientConnectionDuration) Record(
|
||||
func (m ClientConnectionDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -353,6 +370,11 @@ type ClientOpenConnections struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newClientOpenConnectionsOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("Number of outbound HTTP connections that are currently active or idle on the client."),
|
||||
metric.WithUnit("{connection}"),
|
||||
}
|
||||
|
||||
// NewClientOpenConnections returns a new ClientOpenConnections instrument.
|
||||
func NewClientOpenConnections(
|
||||
m metric.Meter,
|
||||
@@ -363,15 +385,18 @@ func NewClientOpenConnections(
|
||||
return ClientOpenConnections{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientOpenConnectionsOpts
|
||||
} else {
|
||||
opt = append(opt, newClientOpenConnectionsOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"http.client.open_connections",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("Number of outbound HTTP connections that are currently active or idle on the client."),
|
||||
metric.WithUnit("{connection}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientOpenConnections{noop.Int64UpDownCounter{}}, err
|
||||
return ClientOpenConnections{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return ClientOpenConnections{i}, nil
|
||||
}
|
||||
@@ -488,6 +513,11 @@ type ClientRequestBodySize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newClientRequestBodySizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP client request bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewClientRequestBodySize returns a new ClientRequestBodySize instrument.
|
||||
func NewClientRequestBodySize(
|
||||
m metric.Meter,
|
||||
@@ -498,15 +528,18 @@ func NewClientRequestBodySize(
|
||||
return ClientRequestBodySize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientRequestBodySizeOpts
|
||||
} else {
|
||||
opt = append(opt, newClientRequestBodySizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"http.client.request.body.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP client request bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientRequestBodySize{noop.Int64Histogram{}}, err
|
||||
return ClientRequestBodySize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ClientRequestBodySize{i}, nil
|
||||
}
|
||||
@@ -593,6 +626,7 @@ func (m ClientRequestBodySize) Record(
|
||||
func (m ClientRequestBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -662,6 +696,11 @@ type ClientRequestDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newClientRequestDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("Duration of HTTP client requests."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewClientRequestDuration returns a new ClientRequestDuration instrument.
|
||||
func NewClientRequestDuration(
|
||||
m metric.Meter,
|
||||
@@ -672,15 +711,18 @@ func NewClientRequestDuration(
|
||||
return ClientRequestDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientRequestDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newClientRequestDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"http.client.request.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("Duration of HTTP client requests."),
|
||||
metric.WithUnit("s"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientRequestDuration{noop.Float64Histogram{}}, err
|
||||
return ClientRequestDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ClientRequestDuration{i}, nil
|
||||
}
|
||||
@@ -753,6 +795,7 @@ func (m ClientRequestDuration) Record(
|
||||
func (m ClientRequestDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -822,6 +865,11 @@ type ClientResponseBodySize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newClientResponseBodySizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP client response bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewClientResponseBodySize returns a new ClientResponseBodySize instrument.
|
||||
func NewClientResponseBodySize(
|
||||
m metric.Meter,
|
||||
@@ -832,15 +880,18 @@ func NewClientResponseBodySize(
|
||||
return ClientResponseBodySize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientResponseBodySizeOpts
|
||||
} else {
|
||||
opt = append(opt, newClientResponseBodySizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"http.client.response.body.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP client response bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientResponseBodySize{noop.Int64Histogram{}}, err
|
||||
return ClientResponseBodySize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ClientResponseBodySize{i}, nil
|
||||
}
|
||||
@@ -927,6 +978,7 @@ func (m ClientResponseBodySize) Record(
|
||||
func (m ClientResponseBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -996,6 +1048,11 @@ type ServerActiveRequests struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newServerActiveRequestsOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("Number of active HTTP server requests."),
|
||||
metric.WithUnit("{request}"),
|
||||
}
|
||||
|
||||
// NewServerActiveRequests returns a new ServerActiveRequests instrument.
|
||||
func NewServerActiveRequests(
|
||||
m metric.Meter,
|
||||
@@ -1006,15 +1063,18 @@ func NewServerActiveRequests(
|
||||
return ServerActiveRequests{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerActiveRequestsOpts
|
||||
} else {
|
||||
opt = append(opt, newServerActiveRequestsOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"http.server.active_requests",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("Number of active HTTP server requests."),
|
||||
metric.WithUnit("{request}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerActiveRequests{noop.Int64UpDownCounter{}}, err
|
||||
return ServerActiveRequests{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return ServerActiveRequests{i}, nil
|
||||
}
|
||||
@@ -1118,6 +1178,11 @@ type ServerRequestBodySize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newServerRequestBodySizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP server request bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewServerRequestBodySize returns a new ServerRequestBodySize instrument.
|
||||
func NewServerRequestBodySize(
|
||||
m metric.Meter,
|
||||
@@ -1128,15 +1193,18 @@ func NewServerRequestBodySize(
|
||||
return ServerRequestBodySize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerRequestBodySizeOpts
|
||||
} else {
|
||||
opt = append(opt, newServerRequestBodySizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"http.server.request.body.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP server request bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerRequestBodySize{noop.Int64Histogram{}}, err
|
||||
return ServerRequestBodySize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ServerRequestBodySize{i}, nil
|
||||
}
|
||||
@@ -1220,6 +1288,7 @@ func (m ServerRequestBodySize) Record(
|
||||
func (m ServerRequestBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -1299,6 +1368,11 @@ type ServerRequestDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newServerRequestDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("Duration of HTTP server requests."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewServerRequestDuration returns a new ServerRequestDuration instrument.
|
||||
func NewServerRequestDuration(
|
||||
m metric.Meter,
|
||||
@@ -1309,15 +1383,18 @@ func NewServerRequestDuration(
|
||||
return ServerRequestDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerRequestDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newServerRequestDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"http.server.request.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("Duration of HTTP server requests."),
|
||||
metric.WithUnit("s"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerRequestDuration{noop.Float64Histogram{}}, err
|
||||
return ServerRequestDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ServerRequestDuration{i}, nil
|
||||
}
|
||||
@@ -1387,6 +1464,7 @@ func (m ServerRequestDuration) Record(
|
||||
func (m ServerRequestDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -1466,6 +1544,11 @@ type ServerResponseBodySize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newServerResponseBodySizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP server response bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewServerResponseBodySize returns a new ServerResponseBodySize instrument.
|
||||
func NewServerResponseBodySize(
|
||||
m metric.Meter,
|
||||
@@ -1476,15 +1559,18 @@ func NewServerResponseBodySize(
|
||||
return ServerResponseBodySize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerResponseBodySizeOpts
|
||||
} else {
|
||||
opt = append(opt, newServerResponseBodySizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"http.server.response.body.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Size of HTTP server response bodies."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerResponseBodySize{noop.Int64Histogram{}}, err
|
||||
return ServerResponseBodySize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ServerResponseBodySize{i}, nil
|
||||
}
|
||||
@@ -1568,6 +1654,7 @@ func (m ServerResponseBodySize) Record(
|
||||
func (m ServerResponseBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -1638,4 +1725,4 @@ func (ServerResponseBodySize) AttrServerPort(val int) attribute.KeyValue {
|
||||
// the category of synthetic traffic, such as tests or bots.
|
||||
func (ServerResponseBodySize) AttrUserAgentSyntheticType(val UserAgentSyntheticTypeAttr) attribute.KeyValue {
|
||||
return attribute.String("user_agent.synthetic.type", string(val))
|
||||
}
|
||||
}
|
||||
|
||||
+225
-87
@@ -3,7 +3,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package httpconv provides types and functionality for OpenTelemetry semantic
|
||||
// Package otelconv provides types and functionality for OpenTelemetry semantic
|
||||
// conventions in the "otel" namespace.
|
||||
package otelconv
|
||||
|
||||
@@ -172,6 +172,11 @@ type SDKExporterLogExported struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKExporterLogExportedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of log records for which the export has finished, either successful or failed."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}
|
||||
|
||||
// NewSDKExporterLogExported returns a new SDKExporterLogExported instrument.
|
||||
func NewSDKExporterLogExported(
|
||||
m metric.Meter,
|
||||
@@ -182,15 +187,18 @@ func NewSDKExporterLogExported(
|
||||
return SDKExporterLogExported{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterLogExportedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterLogExportedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.exporter.log.exported",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of log records for which the export has finished, either successful or failed."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterLogExported{noop.Int64Counter{}}, err
|
||||
return SDKExporterLogExported{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKExporterLogExported{i}, nil
|
||||
}
|
||||
@@ -319,6 +327,11 @@ type SDKExporterLogInflight struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newSDKExporterLogInflightOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}
|
||||
|
||||
// NewSDKExporterLogInflight returns a new SDKExporterLogInflight instrument.
|
||||
func NewSDKExporterLogInflight(
|
||||
m metric.Meter,
|
||||
@@ -329,15 +342,18 @@ func NewSDKExporterLogInflight(
|
||||
return SDKExporterLogInflight{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterLogInflightOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterLogInflightOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"otel.sdk.exporter.log.inflight",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterLogInflight{noop.Int64UpDownCounter{}}, err
|
||||
return SDKExporterLogInflight{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return SDKExporterLogInflight{i}, nil
|
||||
}
|
||||
@@ -449,6 +465,11 @@ type SDKExporterMetricDataPointExported struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKExporterMetricDataPointExportedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of metric data points for which the export has finished, either successful or failed."),
|
||||
metric.WithUnit("{data_point}"),
|
||||
}
|
||||
|
||||
// NewSDKExporterMetricDataPointExported returns a new
|
||||
// SDKExporterMetricDataPointExported instrument.
|
||||
func NewSDKExporterMetricDataPointExported(
|
||||
@@ -460,15 +481,18 @@ func NewSDKExporterMetricDataPointExported(
|
||||
return SDKExporterMetricDataPointExported{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterMetricDataPointExportedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterMetricDataPointExportedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.exporter.metric_data_point.exported",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of metric data points for which the export has finished, either successful or failed."),
|
||||
metric.WithUnit("{data_point}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterMetricDataPointExported{noop.Int64Counter{}}, err
|
||||
return SDKExporterMetricDataPointExported{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKExporterMetricDataPointExported{i}, nil
|
||||
}
|
||||
@@ -598,6 +622,11 @@ type SDKExporterMetricDataPointInflight struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newSDKExporterMetricDataPointInflightOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."),
|
||||
metric.WithUnit("{data_point}"),
|
||||
}
|
||||
|
||||
// NewSDKExporterMetricDataPointInflight returns a new
|
||||
// SDKExporterMetricDataPointInflight instrument.
|
||||
func NewSDKExporterMetricDataPointInflight(
|
||||
@@ -609,15 +638,18 @@ func NewSDKExporterMetricDataPointInflight(
|
||||
return SDKExporterMetricDataPointInflight{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterMetricDataPointInflightOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterMetricDataPointInflightOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"otel.sdk.exporter.metric_data_point.inflight",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."),
|
||||
metric.WithUnit("{data_point}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterMetricDataPointInflight{noop.Int64UpDownCounter{}}, err
|
||||
return SDKExporterMetricDataPointInflight{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return SDKExporterMetricDataPointInflight{i}, nil
|
||||
}
|
||||
@@ -728,6 +760,11 @@ type SDKExporterOperationDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newSDKExporterOperationDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("The duration of exporting a batch of telemetry records."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewSDKExporterOperationDuration returns a new SDKExporterOperationDuration
|
||||
// instrument.
|
||||
func NewSDKExporterOperationDuration(
|
||||
@@ -739,15 +776,18 @@ func NewSDKExporterOperationDuration(
|
||||
return SDKExporterOperationDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterOperationDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterOperationDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"otel.sdk.exporter.operation.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("The duration of exporting a batch of telemetry records."),
|
||||
metric.WithUnit("s"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterOperationDuration{noop.Float64Histogram{}}, err
|
||||
return SDKExporterOperationDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return SDKExporterOperationDuration{i}, nil
|
||||
}
|
||||
@@ -825,6 +865,7 @@ func (m SDKExporterOperationDuration) Record(
|
||||
func (m SDKExporterOperationDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -893,6 +934,11 @@ type SDKExporterSpanExported struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKExporterSpanExportedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of spans for which the export has finished, either successful or failed."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKExporterSpanExported returns a new SDKExporterSpanExported instrument.
|
||||
func NewSDKExporterSpanExported(
|
||||
m metric.Meter,
|
||||
@@ -903,15 +949,18 @@ func NewSDKExporterSpanExported(
|
||||
return SDKExporterSpanExported{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterSpanExportedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterSpanExportedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.exporter.span.exported",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of spans for which the export has finished, either successful or failed."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterSpanExported{noop.Int64Counter{}}, err
|
||||
return SDKExporterSpanExported{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKExporterSpanExported{i}, nil
|
||||
}
|
||||
@@ -1040,6 +1089,11 @@ type SDKExporterSpanInflight struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newSDKExporterSpanInflightOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKExporterSpanInflight returns a new SDKExporterSpanInflight instrument.
|
||||
func NewSDKExporterSpanInflight(
|
||||
m metric.Meter,
|
||||
@@ -1050,15 +1104,18 @@ func NewSDKExporterSpanInflight(
|
||||
return SDKExporterSpanInflight{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKExporterSpanInflightOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKExporterSpanInflightOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"otel.sdk.exporter.span.inflight",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKExporterSpanInflight{noop.Int64UpDownCounter{}}, err
|
||||
return SDKExporterSpanInflight{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return SDKExporterSpanInflight{i}, nil
|
||||
}
|
||||
@@ -1169,6 +1226,11 @@ type SDKLogCreated struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKLogCreatedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of logs submitted to enabled SDK Loggers."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}
|
||||
|
||||
// NewSDKLogCreated returns a new SDKLogCreated instrument.
|
||||
func NewSDKLogCreated(
|
||||
m metric.Meter,
|
||||
@@ -1179,15 +1241,18 @@ func NewSDKLogCreated(
|
||||
return SDKLogCreated{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKLogCreatedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKLogCreatedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.log.created",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of logs submitted to enabled SDK Loggers."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKLogCreated{noop.Int64Counter{}}, err
|
||||
return SDKLogCreated{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKLogCreated{i}, nil
|
||||
}
|
||||
@@ -1254,6 +1319,11 @@ type SDKMetricReaderCollectionDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newSDKMetricReaderCollectionDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("The duration of the collect operation of the metric reader."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewSDKMetricReaderCollectionDuration returns a new
|
||||
// SDKMetricReaderCollectionDuration instrument.
|
||||
func NewSDKMetricReaderCollectionDuration(
|
||||
@@ -1265,15 +1335,18 @@ func NewSDKMetricReaderCollectionDuration(
|
||||
return SDKMetricReaderCollectionDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKMetricReaderCollectionDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKMetricReaderCollectionDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"otel.sdk.metric_reader.collection.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("The duration of the collect operation of the metric reader."),
|
||||
metric.WithUnit("s"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKMetricReaderCollectionDuration{noop.Float64Histogram{}}, err
|
||||
return SDKMetricReaderCollectionDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return SDKMetricReaderCollectionDuration{i}, nil
|
||||
}
|
||||
@@ -1343,6 +1416,7 @@ func (m SDKMetricReaderCollectionDuration) Record(
|
||||
func (m SDKMetricReaderCollectionDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -1384,6 +1458,11 @@ type SDKProcessorLogProcessed struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKProcessorLogProcessedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of log records for which the processing has finished, either successful or failed."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}
|
||||
|
||||
// NewSDKProcessorLogProcessed returns a new SDKProcessorLogProcessed instrument.
|
||||
func NewSDKProcessorLogProcessed(
|
||||
m metric.Meter,
|
||||
@@ -1394,15 +1473,18 @@ func NewSDKProcessorLogProcessed(
|
||||
return SDKProcessorLogProcessed{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKProcessorLogProcessedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKProcessorLogProcessedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.processor.log.processed",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of log records for which the processing has finished, either successful or failed."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKProcessorLogProcessed{noop.Int64Counter{}}, err
|
||||
return SDKProcessorLogProcessed{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKProcessorLogProcessed{i}, nil
|
||||
}
|
||||
@@ -1515,6 +1597,11 @@ type SDKProcessorLogQueueCapacity struct {
|
||||
metric.Int64ObservableUpDownCounter
|
||||
}
|
||||
|
||||
var newSDKProcessorLogQueueCapacityOpts = []metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}
|
||||
|
||||
// NewSDKProcessorLogQueueCapacity returns a new SDKProcessorLogQueueCapacity
|
||||
// instrument.
|
||||
func NewSDKProcessorLogQueueCapacity(
|
||||
@@ -1526,15 +1613,18 @@ func NewSDKProcessorLogQueueCapacity(
|
||||
return SDKProcessorLogQueueCapacity{noop.Int64ObservableUpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKProcessorLogQueueCapacityOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKProcessorLogQueueCapacityOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64ObservableUpDownCounter(
|
||||
"otel.sdk.processor.log.queue.capacity",
|
||||
append([]metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The maximum number of log records the queue of a given instance of an SDK Log Record processor can hold."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKProcessorLogQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err
|
||||
return SDKProcessorLogQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err
|
||||
}
|
||||
return SDKProcessorLogQueueCapacity{i}, nil
|
||||
}
|
||||
@@ -1581,6 +1671,11 @@ type SDKProcessorLogQueueSize struct {
|
||||
metric.Int64ObservableUpDownCounter
|
||||
}
|
||||
|
||||
var newSDKProcessorLogQueueSizeOpts = []metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The number of log records in the queue of a given instance of an SDK log processor."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}
|
||||
|
||||
// NewSDKProcessorLogQueueSize returns a new SDKProcessorLogQueueSize instrument.
|
||||
func NewSDKProcessorLogQueueSize(
|
||||
m metric.Meter,
|
||||
@@ -1591,15 +1686,18 @@ func NewSDKProcessorLogQueueSize(
|
||||
return SDKProcessorLogQueueSize{noop.Int64ObservableUpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKProcessorLogQueueSizeOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKProcessorLogQueueSizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64ObservableUpDownCounter(
|
||||
"otel.sdk.processor.log.queue.size",
|
||||
append([]metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The number of log records in the queue of a given instance of an SDK log processor."),
|
||||
metric.WithUnit("{log_record}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKProcessorLogQueueSize{noop.Int64ObservableUpDownCounter{}}, err
|
||||
return SDKProcessorLogQueueSize{noop.Int64ObservableUpDownCounter{}}, err
|
||||
}
|
||||
return SDKProcessorLogQueueSize{i}, nil
|
||||
}
|
||||
@@ -1646,6 +1744,11 @@ type SDKProcessorSpanProcessed struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKProcessorSpanProcessedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of spans for which the processing has finished, either successful or failed."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKProcessorSpanProcessed returns a new SDKProcessorSpanProcessed
|
||||
// instrument.
|
||||
func NewSDKProcessorSpanProcessed(
|
||||
@@ -1657,15 +1760,18 @@ func NewSDKProcessorSpanProcessed(
|
||||
return SDKProcessorSpanProcessed{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKProcessorSpanProcessedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKProcessorSpanProcessedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.processor.span.processed",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of spans for which the processing has finished, either successful or failed."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKProcessorSpanProcessed{noop.Int64Counter{}}, err
|
||||
return SDKProcessorSpanProcessed{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKProcessorSpanProcessed{i}, nil
|
||||
}
|
||||
@@ -1778,6 +1884,11 @@ type SDKProcessorSpanQueueCapacity struct {
|
||||
metric.Int64ObservableUpDownCounter
|
||||
}
|
||||
|
||||
var newSDKProcessorSpanQueueCapacityOpts = []metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The maximum number of spans the queue of a given instance of an SDK span processor can hold."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKProcessorSpanQueueCapacity returns a new SDKProcessorSpanQueueCapacity
|
||||
// instrument.
|
||||
func NewSDKProcessorSpanQueueCapacity(
|
||||
@@ -1789,15 +1900,18 @@ func NewSDKProcessorSpanQueueCapacity(
|
||||
return SDKProcessorSpanQueueCapacity{noop.Int64ObservableUpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKProcessorSpanQueueCapacityOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKProcessorSpanQueueCapacityOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64ObservableUpDownCounter(
|
||||
"otel.sdk.processor.span.queue.capacity",
|
||||
append([]metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The maximum number of spans the queue of a given instance of an SDK span processor can hold."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKProcessorSpanQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err
|
||||
return SDKProcessorSpanQueueCapacity{noop.Int64ObservableUpDownCounter{}}, err
|
||||
}
|
||||
return SDKProcessorSpanQueueCapacity{i}, nil
|
||||
}
|
||||
@@ -1844,6 +1958,11 @@ type SDKProcessorSpanQueueSize struct {
|
||||
metric.Int64ObservableUpDownCounter
|
||||
}
|
||||
|
||||
var newSDKProcessorSpanQueueSizeOpts = []metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The number of spans in the queue of a given instance of an SDK span processor."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKProcessorSpanQueueSize returns a new SDKProcessorSpanQueueSize
|
||||
// instrument.
|
||||
func NewSDKProcessorSpanQueueSize(
|
||||
@@ -1855,15 +1974,18 @@ func NewSDKProcessorSpanQueueSize(
|
||||
return SDKProcessorSpanQueueSize{noop.Int64ObservableUpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKProcessorSpanQueueSizeOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKProcessorSpanQueueSizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64ObservableUpDownCounter(
|
||||
"otel.sdk.processor.span.queue.size",
|
||||
append([]metric.Int64ObservableUpDownCounterOption{
|
||||
metric.WithDescription("The number of spans in the queue of a given instance of an SDK span processor."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKProcessorSpanQueueSize{noop.Int64ObservableUpDownCounter{}}, err
|
||||
return SDKProcessorSpanQueueSize{noop.Int64ObservableUpDownCounter{}}, err
|
||||
}
|
||||
return SDKProcessorSpanQueueSize{i}, nil
|
||||
}
|
||||
@@ -1910,6 +2032,11 @@ type SDKSpanLive struct {
|
||||
metric.Int64UpDownCounter
|
||||
}
|
||||
|
||||
var newSDKSpanLiveOpts = []metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of created spans with `recording=true` for which the end operation has not been called yet."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKSpanLive returns a new SDKSpanLive instrument.
|
||||
func NewSDKSpanLive(
|
||||
m metric.Meter,
|
||||
@@ -1920,15 +2047,18 @@ func NewSDKSpanLive(
|
||||
return SDKSpanLive{noop.Int64UpDownCounter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKSpanLiveOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKSpanLiveOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64UpDownCounter(
|
||||
"otel.sdk.span.live",
|
||||
append([]metric.Int64UpDownCounterOption{
|
||||
metric.WithDescription("The number of created spans with `recording=true` for which the end operation has not been called yet."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKSpanLive{noop.Int64UpDownCounter{}}, err
|
||||
return SDKSpanLive{noop.Int64UpDownCounter{}}, err
|
||||
}
|
||||
return SDKSpanLive{i}, nil
|
||||
}
|
||||
@@ -2013,6 +2143,11 @@ type SDKSpanStarted struct {
|
||||
metric.Int64Counter
|
||||
}
|
||||
|
||||
var newSDKSpanStartedOpts = []metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of created spans."),
|
||||
metric.WithUnit("{span}"),
|
||||
}
|
||||
|
||||
// NewSDKSpanStarted returns a new SDKSpanStarted instrument.
|
||||
func NewSDKSpanStarted(
|
||||
m metric.Meter,
|
||||
@@ -2023,15 +2158,18 @@ func NewSDKSpanStarted(
|
||||
return SDKSpanStarted{noop.Int64Counter{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newSDKSpanStartedOpts
|
||||
} else {
|
||||
opt = append(opt, newSDKSpanStartedOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Counter(
|
||||
"otel.sdk.span.started",
|
||||
append([]metric.Int64CounterOption{
|
||||
metric.WithDescription("The number of created spans."),
|
||||
metric.WithUnit("{span}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return SDKSpanStarted{noop.Int64Counter{}}, err
|
||||
return SDKSpanStarted{noop.Int64Counter{}}, err
|
||||
}
|
||||
return SDKSpanStarted{i}, nil
|
||||
}
|
||||
@@ -2123,4 +2261,4 @@ func (SDKSpanStarted) AttrSpanParentOrigin(val SpanParentOriginAttr) attribute.K
|
||||
// value of the sampler for this span.
|
||||
func (SDKSpanStarted) AttrSpanSamplingResult(val SpanSamplingResultAttr) attribute.KeyValue {
|
||||
return attribute.String("otel.span.sampling_result", string(val))
|
||||
}
|
||||
}
|
||||
|
||||
+142
-52
@@ -3,7 +3,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package httpconv provides types and functionality for OpenTelemetry semantic
|
||||
// Package rpcconv provides types and functionality for OpenTelemetry semantic
|
||||
// conventions in the "rpc" namespace.
|
||||
package rpcconv
|
||||
|
||||
@@ -28,6 +28,11 @@ type ClientDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newClientDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("Measures the duration of outbound RPC."),
|
||||
metric.WithUnit("ms"),
|
||||
}
|
||||
|
||||
// NewClientDuration returns a new ClientDuration instrument.
|
||||
func NewClientDuration(
|
||||
m metric.Meter,
|
||||
@@ -38,15 +43,18 @@ func NewClientDuration(
|
||||
return ClientDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newClientDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"rpc.client.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("Measures the duration of outbound RPC."),
|
||||
metric.WithUnit("ms"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientDuration{noop.Float64Histogram{}}, err
|
||||
return ClientDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ClientDuration{i}, nil
|
||||
}
|
||||
@@ -102,6 +110,7 @@ func (m ClientDuration) Record(ctx context.Context, val float64, attrs ...attrib
|
||||
func (m ClientDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -121,6 +130,11 @@ type ClientRequestSize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newClientRequestSizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC request messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewClientRequestSize returns a new ClientRequestSize instrument.
|
||||
func NewClientRequestSize(
|
||||
m metric.Meter,
|
||||
@@ -131,15 +145,18 @@ func NewClientRequestSize(
|
||||
return ClientRequestSize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientRequestSizeOpts
|
||||
} else {
|
||||
opt = append(opt, newClientRequestSizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.client.request.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC request messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientRequestSize{noop.Int64Histogram{}}, err
|
||||
return ClientRequestSize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ClientRequestSize{i}, nil
|
||||
}
|
||||
@@ -189,6 +206,7 @@ func (m ClientRequestSize) Record(ctx context.Context, val int64, attrs ...attri
|
||||
func (m ClientRequestSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -208,6 +226,11 @@ type ClientRequestsPerRPC struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newClientRequestsPerRPCOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages received per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}
|
||||
|
||||
// NewClientRequestsPerRPC returns a new ClientRequestsPerRPC instrument.
|
||||
func NewClientRequestsPerRPC(
|
||||
m metric.Meter,
|
||||
@@ -218,15 +241,18 @@ func NewClientRequestsPerRPC(
|
||||
return ClientRequestsPerRPC{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientRequestsPerRPCOpts
|
||||
} else {
|
||||
opt = append(opt, newClientRequestsPerRPCOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.client.requests_per_rpc",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages received per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientRequestsPerRPC{noop.Int64Histogram{}}, err
|
||||
return ClientRequestsPerRPC{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ClientRequestsPerRPC{i}, nil
|
||||
}
|
||||
@@ -280,6 +306,7 @@ func (m ClientRequestsPerRPC) Record(ctx context.Context, val int64, attrs ...at
|
||||
func (m ClientRequestsPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -299,6 +326,11 @@ type ClientResponseSize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newClientResponseSizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC response messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewClientResponseSize returns a new ClientResponseSize instrument.
|
||||
func NewClientResponseSize(
|
||||
m metric.Meter,
|
||||
@@ -309,15 +341,18 @@ func NewClientResponseSize(
|
||||
return ClientResponseSize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientResponseSizeOpts
|
||||
} else {
|
||||
opt = append(opt, newClientResponseSizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.client.response.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC response messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientResponseSize{noop.Int64Histogram{}}, err
|
||||
return ClientResponseSize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ClientResponseSize{i}, nil
|
||||
}
|
||||
@@ -367,6 +402,7 @@ func (m ClientResponseSize) Record(ctx context.Context, val int64, attrs ...attr
|
||||
func (m ClientResponseSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -386,6 +422,11 @@ type ClientResponsesPerRPC struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newClientResponsesPerRPCOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages sent per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}
|
||||
|
||||
// NewClientResponsesPerRPC returns a new ClientResponsesPerRPC instrument.
|
||||
func NewClientResponsesPerRPC(
|
||||
m metric.Meter,
|
||||
@@ -396,15 +437,18 @@ func NewClientResponsesPerRPC(
|
||||
return ClientResponsesPerRPC{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientResponsesPerRPCOpts
|
||||
} else {
|
||||
opt = append(opt, newClientResponsesPerRPCOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.client.responses_per_rpc",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages sent per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientResponsesPerRPC{noop.Int64Histogram{}}, err
|
||||
return ClientResponsesPerRPC{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ClientResponsesPerRPC{i}, nil
|
||||
}
|
||||
@@ -458,6 +502,7 @@ func (m ClientResponsesPerRPC) Record(ctx context.Context, val int64, attrs ...a
|
||||
func (m ClientResponsesPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -477,6 +522,11 @@ type ServerDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newServerDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("Measures the duration of inbound RPC."),
|
||||
metric.WithUnit("ms"),
|
||||
}
|
||||
|
||||
// NewServerDuration returns a new ServerDuration instrument.
|
||||
func NewServerDuration(
|
||||
m metric.Meter,
|
||||
@@ -487,15 +537,18 @@ func NewServerDuration(
|
||||
return ServerDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newServerDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"rpc.server.duration",
|
||||
append([]metric.Float64HistogramOption{
|
||||
metric.WithDescription("Measures the duration of inbound RPC."),
|
||||
metric.WithUnit("ms"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerDuration{noop.Float64Histogram{}}, err
|
||||
return ServerDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ServerDuration{i}, nil
|
||||
}
|
||||
@@ -551,6 +604,7 @@ func (m ServerDuration) Record(ctx context.Context, val float64, attrs ...attrib
|
||||
func (m ServerDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -570,6 +624,11 @@ type ServerRequestSize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newServerRequestSizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC request messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewServerRequestSize returns a new ServerRequestSize instrument.
|
||||
func NewServerRequestSize(
|
||||
m metric.Meter,
|
||||
@@ -580,15 +639,18 @@ func NewServerRequestSize(
|
||||
return ServerRequestSize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerRequestSizeOpts
|
||||
} else {
|
||||
opt = append(opt, newServerRequestSizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.server.request.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC request messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerRequestSize{noop.Int64Histogram{}}, err
|
||||
return ServerRequestSize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ServerRequestSize{i}, nil
|
||||
}
|
||||
@@ -638,6 +700,7 @@ func (m ServerRequestSize) Record(ctx context.Context, val int64, attrs ...attri
|
||||
func (m ServerRequestSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -657,6 +720,11 @@ type ServerRequestsPerRPC struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newServerRequestsPerRPCOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages received per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}
|
||||
|
||||
// NewServerRequestsPerRPC returns a new ServerRequestsPerRPC instrument.
|
||||
func NewServerRequestsPerRPC(
|
||||
m metric.Meter,
|
||||
@@ -667,15 +735,18 @@ func NewServerRequestsPerRPC(
|
||||
return ServerRequestsPerRPC{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerRequestsPerRPCOpts
|
||||
} else {
|
||||
opt = append(opt, newServerRequestsPerRPCOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.server.requests_per_rpc",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages received per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerRequestsPerRPC{noop.Int64Histogram{}}, err
|
||||
return ServerRequestsPerRPC{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ServerRequestsPerRPC{i}, nil
|
||||
}
|
||||
@@ -729,6 +800,7 @@ func (m ServerRequestsPerRPC) Record(ctx context.Context, val int64, attrs ...at
|
||||
func (m ServerRequestsPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -748,6 +820,11 @@ type ServerResponseSize struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newServerResponseSizeOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC response messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}
|
||||
|
||||
// NewServerResponseSize returns a new ServerResponseSize instrument.
|
||||
func NewServerResponseSize(
|
||||
m metric.Meter,
|
||||
@@ -758,15 +835,18 @@ func NewServerResponseSize(
|
||||
return ServerResponseSize{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerResponseSizeOpts
|
||||
} else {
|
||||
opt = append(opt, newServerResponseSizeOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.server.response.size",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the size of RPC response messages (uncompressed)."),
|
||||
metric.WithUnit("By"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerResponseSize{noop.Int64Histogram{}}, err
|
||||
return ServerResponseSize{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ServerResponseSize{i}, nil
|
||||
}
|
||||
@@ -816,6 +896,7 @@ func (m ServerResponseSize) Record(ctx context.Context, val int64, attrs ...attr
|
||||
func (m ServerResponseSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -835,6 +916,11 @@ type ServerResponsesPerRPC struct {
|
||||
metric.Int64Histogram
|
||||
}
|
||||
|
||||
var newServerResponsesPerRPCOpts = []metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages sent per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}
|
||||
|
||||
// NewServerResponsesPerRPC returns a new ServerResponsesPerRPC instrument.
|
||||
func NewServerResponsesPerRPC(
|
||||
m metric.Meter,
|
||||
@@ -845,15 +931,18 @@ func NewServerResponsesPerRPC(
|
||||
return ServerResponsesPerRPC{noop.Int64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerResponsesPerRPCOpts
|
||||
} else {
|
||||
opt = append(opt, newServerResponsesPerRPCOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Int64Histogram(
|
||||
"rpc.server.responses_per_rpc",
|
||||
append([]metric.Int64HistogramOption{
|
||||
metric.WithDescription("Measures the number of messages sent per RPC."),
|
||||
metric.WithUnit("{count}"),
|
||||
}, opt...)...,
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerResponsesPerRPC{noop.Int64Histogram{}}, err
|
||||
return ServerResponsesPerRPC{noop.Int64Histogram{}}, err
|
||||
}
|
||||
return ServerResponsesPerRPC{i}, nil
|
||||
}
|
||||
@@ -907,6 +996,7 @@ func (m ServerResponsesPerRPC) Record(ctx context.Context, val int64, attrs ...a
|
||||
func (m ServerResponsesPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
@@ -917,4 +1007,4 @@ func (m ServerResponsesPerRPC) RecordSet(ctx context.Context, val int64, set att
|
||||
|
||||
*o = append(*o, metric.WithAttributeSet(set))
|
||||
m.Int64Histogram.Record(ctx, val, *o...)
|
||||
}
|
||||
}
|
||||
|
||||
+42
-3
@@ -4,6 +4,7 @@
|
||||
package trace // import "go.opentelemetry.io/otel/trace"
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -304,12 +305,50 @@ func WithInstrumentationVersion(version string) TracerOption {
|
||||
})
|
||||
}
|
||||
|
||||
// WithInstrumentationAttributes sets the instrumentation attributes.
|
||||
// mergeSets returns the union of keys between a and b. Any duplicate keys will
|
||||
// use the value associated with b.
|
||||
func mergeSets(a, b attribute.Set) attribute.Set {
|
||||
// NewMergeIterator uses the first value for any duplicates.
|
||||
iter := attribute.NewMergeIterator(&b, &a)
|
||||
merged := make([]attribute.KeyValue, 0, a.Len()+b.Len())
|
||||
for iter.Next() {
|
||||
merged = append(merged, iter.Attribute())
|
||||
}
|
||||
return attribute.NewSet(merged...)
|
||||
}
|
||||
|
||||
// WithInstrumentationAttributes adds the instrumentation attributes.
|
||||
//
|
||||
// The passed attributes will be de-duplicated.
|
||||
// This is equivalent to calling [WithInstrumentationAttributeSet] with an
|
||||
// [attribute.Set] created from a clone of the passed attributes.
|
||||
// [WithInstrumentationAttributeSet] is recommended for more control.
|
||||
//
|
||||
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
|
||||
// options are passed, the attributes will be merged together in the order
|
||||
// they are passed. Attributes with duplicate keys will use the last value passed.
|
||||
func WithInstrumentationAttributes(attr ...attribute.KeyValue) TracerOption {
|
||||
set := attribute.NewSet(slices.Clone(attr)...)
|
||||
return WithInstrumentationAttributeSet(set)
|
||||
}
|
||||
|
||||
// WithInstrumentationAttributeSet adds the instrumentation attributes.
|
||||
//
|
||||
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
|
||||
// options are passed, the attributes will be merged together in the order
|
||||
// they are passed. Attributes with duplicate keys will use the last value passed.
|
||||
func WithInstrumentationAttributeSet(set attribute.Set) TracerOption {
|
||||
if set.Len() == 0 {
|
||||
return tracerOptionFunc(func(config TracerConfig) TracerConfig {
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
return tracerOptionFunc(func(config TracerConfig) TracerConfig {
|
||||
config.attrs = attribute.NewSet(attr...)
|
||||
if config.attrs.Len() == 0 {
|
||||
config.attrs = set
|
||||
} else {
|
||||
config.attrs = mergeSets(config.attrs, set)
|
||||
}
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
+4
@@ -66,6 +66,10 @@ type Span interface {
|
||||
// SetAttributes sets kv as attributes of the Span. If a key from kv
|
||||
// already exists for an attribute of the Span it will be overwritten with
|
||||
// the value contained in kv.
|
||||
//
|
||||
// Note that adding attributes at span creation using [WithAttributes] is preferred
|
||||
// to calling SetAttribute later, as samplers can only consider information
|
||||
// already present during span creation.
|
||||
SetAttributes(kv ...attribute.KeyValue)
|
||||
|
||||
// TracerProvider returns a TracerProvider that can be used to generate
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel"
|
||||
|
||||
// Version is the current release version of OpenTelemetry in use.
|
||||
func Version() string {
|
||||
return "1.38.0"
|
||||
return "1.39.0"
|
||||
}
|
||||
|
||||
+23
-4
@@ -3,7 +3,7 @@
|
||||
|
||||
module-sets:
|
||||
stable-v1:
|
||||
version: v1.38.0
|
||||
version: v1.39.0
|
||||
modules:
|
||||
- go.opentelemetry.io/otel
|
||||
- go.opentelemetry.io/otel/bridge/opencensus
|
||||
@@ -22,11 +22,11 @@ module-sets:
|
||||
- go.opentelemetry.io/otel/sdk/metric
|
||||
- go.opentelemetry.io/otel/trace
|
||||
experimental-metrics:
|
||||
version: v0.60.0
|
||||
version: v0.61.0
|
||||
modules:
|
||||
- go.opentelemetry.io/otel/exporters/prometheus
|
||||
experimental-logs:
|
||||
version: v0.14.0
|
||||
version: v0.15.0
|
||||
modules:
|
||||
- go.opentelemetry.io/otel/log
|
||||
- go.opentelemetry.io/otel/log/logtest
|
||||
@@ -36,9 +36,28 @@ module-sets:
|
||||
- go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
|
||||
- go.opentelemetry.io/otel/exporters/stdout/stdoutlog
|
||||
experimental-schema:
|
||||
version: v0.0.13
|
||||
version: v0.0.14
|
||||
modules:
|
||||
- go.opentelemetry.io/otel/schema
|
||||
excluded-modules:
|
||||
- go.opentelemetry.io/otel/internal/tools
|
||||
- go.opentelemetry.io/otel/trace/internal/telemetry/test
|
||||
modules:
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace:
|
||||
version-refs:
|
||||
- ./internal/version.go
|
||||
go.opentelemetry.io/otel/exporters/prometheus:
|
||||
version-refs:
|
||||
- ./internal/version.go
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc:
|
||||
version-refs:
|
||||
- ./internal/version.go
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc:
|
||||
version-refs:
|
||||
- ./internal/version.go
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp:
|
||||
version-refs:
|
||||
- ./internal/version.go
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp:
|
||||
version-refs:
|
||||
- ./internal/version.go
|
||||
|
||||
Vendored
+11
-10
@@ -2337,11 +2337,12 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv
|
||||
## explicit; go 1.23.0
|
||||
go.opentelemetry.io/contrib/zpages
|
||||
go.opentelemetry.io/contrib/zpages/internal
|
||||
# go.opentelemetry.io/otel v1.38.0
|
||||
## explicit; go 1.23.0
|
||||
# go.opentelemetry.io/otel v1.39.0
|
||||
## explicit; go 1.24.0
|
||||
go.opentelemetry.io/otel
|
||||
go.opentelemetry.io/otel/attribute
|
||||
go.opentelemetry.io/otel/attribute/internal
|
||||
go.opentelemetry.io/otel/attribute/internal/xxhash
|
||||
go.opentelemetry.io/otel/baggage
|
||||
go.opentelemetry.io/otel/codes
|
||||
go.opentelemetry.io/otel/internal/baggage
|
||||
@@ -2370,23 +2371,23 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/counter
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x
|
||||
# go.opentelemetry.io/otel/metric v1.38.0
|
||||
## explicit; go 1.23.0
|
||||
# go.opentelemetry.io/otel/metric v1.39.0
|
||||
## explicit; go 1.24.0
|
||||
go.opentelemetry.io/otel/metric
|
||||
go.opentelemetry.io/otel/metric/embedded
|
||||
go.opentelemetry.io/otel/metric/noop
|
||||
# go.opentelemetry.io/otel/sdk v1.38.0
|
||||
## explicit; go 1.23.0
|
||||
# go.opentelemetry.io/otel/sdk v1.39.0
|
||||
## explicit; go 1.24.0
|
||||
go.opentelemetry.io/otel/sdk
|
||||
go.opentelemetry.io/otel/sdk/instrumentation
|
||||
go.opentelemetry.io/otel/sdk/internal/env
|
||||
go.opentelemetry.io/otel/sdk/internal/x
|
||||
go.opentelemetry.io/otel/sdk/resource
|
||||
go.opentelemetry.io/otel/sdk/trace
|
||||
go.opentelemetry.io/otel/sdk/trace/internal/x
|
||||
go.opentelemetry.io/otel/sdk/trace/internal/env
|
||||
go.opentelemetry.io/otel/sdk/trace/internal/observ
|
||||
go.opentelemetry.io/otel/sdk/trace/tracetest
|
||||
# go.opentelemetry.io/otel/trace v1.38.0
|
||||
## explicit; go 1.23.0
|
||||
# go.opentelemetry.io/otel/trace v1.39.0
|
||||
## explicit; go 1.24.0
|
||||
go.opentelemetry.io/otel/trace
|
||||
go.opentelemetry.io/otel/trace/embedded
|
||||
go.opentelemetry.io/otel/trace/internal/telemetry
|
||||
|
||||
Reference in New Issue
Block a user