vendor: update buildkit

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2023-02-10 18:19:57 +01:00
parent b1949b7388
commit 8311b0963a
433 changed files with 34791 additions and 13411 deletions

View File

@@ -11,11 +11,11 @@ coverage.*
gen/
/example/fib/fib
/example/fib/traces.txt
/example/jaeger/jaeger
/example/namedtracer/namedtracer
/example/opencensus/opencensus
/example/passthrough/passthrough
/example/prometheus/prometheus
/example/prom-collector/prom-collector
/example/zipkin/zipkin
/example/otel-collector/otel-collector

View File

@@ -9,8 +9,9 @@ linters:
disable-all: true
# Specifically enable linters we want to use.
enable:
- deadcode
- depguard
- errcheck
- godot
- gofmt
- goimports
- gosimple
@@ -19,29 +20,225 @@ linters:
- misspell
- revive
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
issues:
# Maximum issues count per one linter.
# Set to 0 to disable.
# Default: 50
# Setting to unlimited so the linter only is run once to debug all issues.
max-issues-per-linter: 0
# Maximum count of issues with the same text.
# Set to 0 to disable.
# Default: 3
# Setting to unlimited so the linter only is run once to debug all issues.
max-same-issues: 0
# Excluding configuration per-path, per-linter, per-text and per-source.
exclude-rules:
# helpers in tests often (rightfully) pass a *testing.T as their first argument
- path: _test\.go
text: "context.Context should be the first parameter of a function"
# TODO: Having appropriate comments for exported objects helps development,
# even for objects in internal packages. Appropriate comments for all
# exported objects should be added and this exclusion removed.
- path: '.*internal/.*'
text: "exported (method|function|type|const) (.+) should have comment or be unexported"
linters:
- revive
# Yes, they are, but it's okay in a test
# Yes, they are, but it's okay in a test.
- path: _test\.go
text: "exported func.*returns unexported type.*which can be annoying to use"
linters:
- revive
# Example test functions should be treated like main.
- path: example.*_test\.go
text: "calls to (.+) only in main[(][)] or init[(][)] functions"
linters:
- revive
include:
# revive exported should have comment or be unexported.
- EXC0012
# revive package comment should be of the form ...
- EXC0013
linters-settings:
depguard:
# Check the list against standard lib.
# Default: false
include-go-root: true
# A list of packages for the list type specified.
# Default: []
packages:
- "crypto/md5"
- "crypto/sha1"
- "crypto/**/pkix"
ignore-file-rules:
- "**/*_test.go"
additional-guards:
# Do not allow testing packages in non-test files.
- list-type: denylist
include-go-root: true
packages:
- testing
- github.com/stretchr/testify
ignore-file-rules:
- "**/*_test.go"
- "**/*test/*.go"
- "**/internal/matchers/*.go"
godot:
exclude:
# Exclude sentence fragments for lists.
- '^[ ]*[-•]'
# Exclude sentences prefixing a list.
- ':$'
goimports:
local-prefixes: go.opentelemetry.io
misspell:
locale: US
ignore-words:
- cancelled
goimports:
local-prefixes: go.opentelemetry.io
revive:
# Sets the default failure confidence.
# This means that linting errors with less than 0.8 confidence will be ignored.
# Default: 0.8
confidence: 0.01
rules:
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports
- name: blank-imports
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr
- name: bool-literal-in-expr
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr
- name: constant-logical-expr
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument
# TODO (#3372) reenable linter when it is compatible. https://github.com/golangci/golangci-lint/issues/3280
- name: context-as-argument
disabled: true
arguments:
allowTypesBefore: "*testing.T"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type
- name: context-keys-type
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit
- name: deep-exit
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer
- name: defer
disabled: false
arguments:
- ["call-chain", "loop"]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports
- name: dot-imports
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports
- name: duplicated-imports
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return
- name: early-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block
- name: empty-block
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines
- name: empty-lines
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming
- name: error-naming
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return
- name: error-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings
- name: error-strings
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf
- name: errorf
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported
- name: exported
disabled: false
arguments:
- "sayRepetitiveInsteadOfStutters"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter
- name: flag-parameter
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches
- name: identical-branches
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return
- name: if-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement
- name: increment-decrement
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow
- name: indent-error-flow
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing
- name: import-shadowing
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments
- name: package-comments
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range
- name: range
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure
- name: range-val-in-closure
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address
- name: range-val-address
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id
- name: redefines-builtin-id
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format
- name: string-format
disabled: false
arguments:
- - panic
- '/^[^\n]*$/'
- must not contain line breaks
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag
- name: struct-tag
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else
- name: superfluous-else
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal
- name: time-equal
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming
- name: var-naming
disabled: false
arguments:
- ["ID"] # AllowList
- ["Otel", "Aws", "Gcp"] # DenyList
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration
- name: var-declaration
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion
- name: unconditional-recursion
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return
- name: unexported-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error
- name: unhandled-error
disabled: false
arguments:
- "fmt.Fprint"
- "fmt.Fprintf"
- "fmt.Fprintln"
- "fmt.Print"
- "fmt.Printf"
- "fmt.Println"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt
- name: unnecessary-stmt
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break
- name: useless-break
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value
- name: waitgroup-by-value
disabled: false

3
vendor/go.opentelemetry.io/otel/.lycheeignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
http://localhost
http://jaeger-collector
https://github.com/open-telemetry/opentelemetry-go/milestone/

View File

@@ -1,16 +0,0 @@
{
"ignorePatterns": [
{
"pattern": "^http(s)?://localhost"
}
],
"replacementPatterns": [
{
"pattern": "^/registry",
"replacement": "https://opentelemetry.io/registry"
}
],
"retryOn429": true,
"retryCount": 5,
"fallbackRetryDelay": "30s"
}

View File

@@ -8,6 +8,404 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
## [1.11.2/0.34.0] 2022-12-05
### Added
- The `WithView` `Option` is added to the `go.opentelemetry.io/otel/sdk/metric` package.
This option is used to configure the view(s) a `MeterProvider` will use for all `Reader`s that are registered with it. (#3387)
- Add Instrumentation Scope and Version as info metric and label in Prometheus exporter.
This can be disabled using the `WithoutScopeInfo()` option added to that package.(#3273, #3357)
- OTLP exporters now recognize: (#3363)
- `OTEL_EXPORTER_OTLP_INSECURE`
- `OTEL_EXPORTER_OTLP_TRACES_INSECURE`
- `OTEL_EXPORTER_OTLP_METRICS_INSECURE`
- `OTEL_EXPORTER_OTLP_CLIENT_KEY`
- `OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY`
- `OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY`
- `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE`
- `OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE`
- `OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE`
- The `View` type and related `NewView` function to create a view according to the OpenTelemetry specification are added to `go.opentelemetry.io/otel/sdk/metric`.
These additions are replacements for the `View` type and `New` function from `go.opentelemetry.io/otel/sdk/metric/view`. (#3459)
- The `Instrument` and `InstrumentKind` type are added to `go.opentelemetry.io/otel/sdk/metric`.
These additions are replacements for the `Instrument` and `InstrumentKind` types from `go.opentelemetry.io/otel/sdk/metric/view`. (#3459)
- The `Stream` type is added to `go.opentelemetry.io/otel/sdk/metric` to define a metric data stream a view will produce. (#3459)
- The `AssertHasAttributes` allows instrument authors to test that datapoints returned have appropriate attributes. (#3487)
### Changed
- The `"go.opentelemetry.io/otel/sdk/metric".WithReader` option no longer accepts views to associate with the `Reader`.
Instead, views are now registered directly with the `MeterProvider` via the new `WithView` option.
The views registered with the `MeterProvider` apply to all `Reader`s. (#3387)
- The `Temporality(view.InstrumentKind) metricdata.Temporality` and `Aggregation(view.InstrumentKind) aggregation.Aggregation` methods are added to the `"go.opentelemetry.io/otel/sdk/metric".Exporter` interface. (#3260)
- The `Temporality(view.InstrumentKind) metricdata.Temporality` and `Aggregation(view.InstrumentKind) aggregation.Aggregation` methods are added to the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric".Client` interface. (#3260)
- The `WithTemporalitySelector` and `WithAggregationSelector` `ReaderOption`s have been changed to `ManualReaderOption`s in the `go.opentelemetry.io/otel/sdk/metric` package. (#3260)
- The periodic reader in the `go.opentelemetry.io/otel/sdk/metric` package now uses the temporality and aggregation selectors from its configured exporter instead of accepting them as options. (#3260)
### Fixed
- The `go.opentelemetry.io/otel/exporters/prometheus` exporter fixes duplicated `_total` suffixes. (#3369)
- Remove comparable requirement for `Reader`s. (#3387)
- Cumulative metrics from the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) are defined as monotonic sums, instead of non-monotonic. (#3389)
- Asynchronous counters (`Counter` and `UpDownCounter`) from the metric SDK now produce delta sums when configured with delta temporality. (#3398)
- Exported `Status` codes in the `go.opentelemetry.io/otel/exporters/zipkin` exporter are now exported as all upper case values. (#3340)
- `Aggregation`s from `go.opentelemetry.io/otel/sdk/metric` with no data are not exported. (#3394, #3436)
- Reenabled Attribute Filters in the Metric SDK. (#3396)
- Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggragation. (#3408)
- Do not report empty partial-success responses in the `go.opentelemetry.io/otel/exporters/otlp` exporters. (#3438, #3432)
- Handle partial success responses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` exporters. (#3162, #3440)
- Prevent duplicate Prometheus description, unit, and type. (#3469)
- Prevents panic when using incorrect `attribute.Value.As[Type]Slice()`. (#3489)
## Removed
- The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric.Client` interface is removed. (#3486)
- The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric.New` function is removed. Use the `otlpmetric[http|grpc].New` directly. (#3486)
### Deprecated
- The `go.opentelemetry.io/otel/sdk/metric/view` package is deprecated.
Use `Instrument`, `InstrumentKind`, `View`, and `NewView` in `go.opentelemetry.io/otel/sdk/metric` instead. (#3476)
## [1.11.1/0.33.0] 2022-10-19
### Added
- The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` registers with a Prometheus registerer on creation.
By default, it will register with the default Prometheus registerer.
A non-default registerer can be used by passing the `WithRegisterer` option. (#3239)
- Added the `WithAggregationSelector` option to the `go.opentelemetry.io/otel/exporters/prometheus` package to change the default `AggregationSelector` used. (#3341)
- The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` converts the `Resource` associated with metric exports into a `target_info` metric. (#3285)
### Changed
- The `"go.opentelemetry.io/otel/exporters/prometheus".New` function is updated to return an error.
It will return an error if the exporter fails to register with Prometheus. (#3239)
### Fixed
- The URL-encoded values from the `OTEL_RESOURCE_ATTRIBUTES` environment variable are decoded. (#2963)
- The `baggage.NewMember` function decodes the `value` parameter instead of directly using it.
This fixes the implementation to be compliant with the W3C specification. (#3226)
- Slice attributes of the `attribute` package are now comparable based on their value, not instance. (#3108 #3252)
- The `Shutdown` and `ForceFlush` methods of the `"go.opentelemetry.io/otel/sdk/trace".TraceProvider` no longer return an error when no processor is registered. (#3268)
- The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` cumulatively sums histogram buckets. (#3281)
- The sum of each histogram data point is now uniquely exported by the `go.opentelemetry.io/otel/exporters/otlpmetric` exporters. (#3284, #3293)
- Recorded values for asynchronous counters (`Counter` and `UpDownCounter`) are interpreted as exact, not incremental, sum values by the metric SDK. (#3350, #3278)
- `UpDownCounters` are now correctly output as Prometheus gauges in the `go.opentelemetry.io/otel/exporters/prometheus` exporter. (#3358)
- The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` no longer describes the metrics it will send to Prometheus on startup.
Instead the exporter is defined as an "unchecked" collector for Prometheus.
This fixes the `reader is not registered` warning currently emitted on startup. (#3291 #3342)
- The `go.opentelemetry.io/otel/exporters/prometheus` exporter now correctly adds `_total` suffixes to counter metrics. (#3360)
- The `go.opentelemetry.io/otel/exporters/prometheus` exporter now adds a unit suffix to metric names.
This can be disabled using the `WithoutUnits()` option added to that package. (#3352)
## [1.11.0/0.32.3] 2022-10-12
### Added
- Add default User-Agent header to OTLP exporter requests (`go.opentelemetry.io/otel/exporters/otlptrace/otlptracegrpc` and `go.opentelemetry.io/otel/exporters/otlptrace/otlptracehttp`). (#3261)
### Changed
- `span.SetStatus` has been updated such that calls that lower the status are now no-ops. (#3214)
- Upgrade `golang.org/x/sys/unix` from `v0.0.0-20210423185535-09eb48e85fd7` to `v0.0.0-20220919091848-fb04ddd9f9c8`.
This addresses [GO-2022-0493](https://pkg.go.dev/vuln/GO-2022-0493). (#3235)
## [0.32.2] Metric SDK (Alpha) - 2022-10-11
### Added
- Added an example of using metric views to customize instruments. (#3177)
- Add default User-Agent header to OTLP exporter requests (`go.opentelemetry.io/otel/exporters/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlpmetric/otlpmetrichttp`). (#3261)
### Changed
- Flush pending measurements with the `PeriodicReader` in the `go.opentelemetry.io/otel/sdk/metric` when `ForceFlush` or `Shutdown` are called. (#3220)
- Update histogram default bounds to match the requirements of the latest specification. (#3222)
- Encode the HTTP status code in the OpenTracing bridge (`go.opentelemetry.io/otel/bridge/opentracing`) as an integer. (#3265)
### Fixed
- Use default view if instrument does not match any registered view of a reader. (#3224, #3237)
- Return the same instrument every time a user makes the exact same instrument creation call. (#3229, #3251)
- Return the existing instrument when a view transforms a creation call to match an existing instrument. (#3240, #3251)
- Log a warning when a conflicting instrument (e.g. description, unit, data-type) is created instead of returning an error. (#3251)
- The OpenCensus bridge no longer sends empty batches of metrics. (#3263)
## [0.32.1] Metric SDK (Alpha) - 2022-09-22
### Changed
- The Prometheus exporter sanitizes OpenTelemetry instrument names when exporting.
Invalid characters are replaced with `_`. (#3212)
### Added
- The metric portion of the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) has been reintroduced. (#3192)
- The OpenCensus bridge example (`go.opentelemetry.io/otel/example/opencensus`) has been reintroduced. (#3206)
### Fixed
- Updated go.mods to point to valid versions of the sdk. (#3216)
- Set the `MeterProvider` resource on all exported metric data. (#3218)
## [0.32.0] Revised Metric SDK (Alpha) - 2022-09-18
### Changed
- The metric SDK in `go.opentelemetry.io/otel/sdk/metric` is completely refactored to comply with the OpenTelemetry specification.
Please see the package documentation for how the new SDK is initialized and configured. (#3175)
- Update the minimum supported go version to go1.18. Removes support for go1.17 (#3179)
### Removed
- The metric portion of the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) has been removed.
A new bridge compliant with the revised metric SDK will be added back in a future release. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/aggregator/histogram` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/aggregator/sum` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/aggregator` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/controller/basic` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/controller/controllertest` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/controller/time` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/export/aggregation` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/export` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/metrictest` package is removed.
A replacement package that supports the new metric SDK will be added back in a future release. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/number` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/processor/basic` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/processor/processortest` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/processor/reducer` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/registry` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/sdkapi` package is removed, see the new metric SDK. (#3175)
- The `go.opentelemetry.io/otel/sdk/metric/selector/simple` package is removed, see the new metric SDK. (#3175)
- The `"go.opentelemetry.io/otel/sdk/metric".ErrUninitializedInstrument` variable was removed. (#3175)
- The `"go.opentelemetry.io/otel/sdk/metric".ErrBadInstrument` variable was removed. (#3175)
- The `"go.opentelemetry.io/otel/sdk/metric".Accumulator` type was removed, see the `MeterProvider`in the new metric SDK. (#3175)
- The `"go.opentelemetry.io/otel/sdk/metric".NewAccumulator` function was removed, see `NewMeterProvider`in the new metric SDK. (#3175)
- The deprecated `"go.opentelemetry.io/otel/sdk/metric".AtomicFieldOffsets` function was removed. (#3175)
## [1.10.0] - 2022-09-09
### Added
- Support Go 1.19. (#3077)
Include compatibility testing and document support. (#3077)
- Support the OTLP ExportTracePartialSuccess response; these are passed to the registered error handler. (#3106)
- Upgrade go.opentelemetry.io/proto/otlp from v0.18.0 to v0.19.0 (#3107)
### Changed
- Fix misidentification of OpenTelemetry `SpanKind` in OpenTracing bridge (`go.opentelemetry.io/otel/bridge/opentracing`). (#3096)
- Attempting to start a span with a nil `context` will no longer cause a panic. (#3110)
- All exporters will be shutdown even if one reports an error (#3091)
- Ensure valid UTF-8 when truncating over-length attribute values. (#3156)
## [1.9.0/0.0.3] - 2022-08-01
### Added
- Add support for Schema Files format 1.1.x (metric "split" transform) with the new `go.opentelemetry.io/otel/schema/v1.1` package. (#2999)
- Add the `go.opentelemetry.io/otel/semconv/v1.11.0` package.
The package contains semantic conventions from the `v1.11.0` version of the OpenTelemetry specification. (#3009)
- Add the `go.opentelemetry.io/otel/semconv/v1.12.0` package.
The package contains semantic conventions from the `v1.12.0` version of the OpenTelemetry specification. (#3010)
- Add the `http.method` attribute to HTTP server metric from all `go.opentelemetry.io/otel/semconv/*` packages. (#3018)
### Fixed
- Invalid warning for context setup being deferred in `go.opentelemetry.io/otel/bridge/opentracing` package. (#3029)
## [1.8.0/0.31.0] - 2022-07-08
### Added
- Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods
of the `"go.opentelemetry.io/otel/bridge/opentracing".BridgeTracer` type. (#2911)
### Changed
- The `crosslink` make target has been updated to use the `go.opentelemetry.io/build-tools/crosslink` package. (#2886)
- In the `go.opentelemetry.io/otel/sdk/instrumentation` package rename `Library` to `Scope` and alias `Library` as `Scope` (#2976)
- Move metric no-op implementation form `nonrecording` to `metric` package. (#2866)
### Removed
- Support for go1.16. Support is now only for go1.17 and go1.18 (#2917)
### Deprecated
- The `Library` struct in the `go.opentelemetry.io/otel/sdk/instrumentation` package is deprecated.
Use the equivalent `Scope` struct instead. (#2977)
- The `ReadOnlySpan.InstrumentationLibrary` method from the `go.opentelemetry.io/otel/sdk/trace` package is deprecated.
Use the equivalent `ReadOnlySpan.InstrumentationScope` method instead. (#2977)
## [1.7.0/0.30.0] - 2022-04-28
### Added
- Add the `go.opentelemetry.io/otel/semconv/v1.8.0` package.
The package contains semantic conventions from the `v1.8.0` version of the OpenTelemetry specification. (#2763)
- Add the `go.opentelemetry.io/otel/semconv/v1.9.0` package.
The package contains semantic conventions from the `v1.9.0` version of the OpenTelemetry specification. (#2792)
- Add the `go.opentelemetry.io/otel/semconv/v1.10.0` package.
The package contains semantic conventions from the `v1.10.0` version of the OpenTelemetry specification. (#2842)
- Added an in-memory exporter to metrictest to aid testing with a full SDK. (#2776)
### Fixed
- Globally delegated instruments are unwrapped before delegating asynchronous callbacks. (#2784)
- Remove import of `testing` package in non-tests builds of the `go.opentelemetry.io/otel` package. (#2786)
### Changed
- The `WithLabelEncoder` option from the `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` package is renamed to `WithAttributeEncoder`. (#2790)
- The `LabelFilterSelector` interface from `go.opentelemetry.io/otel/sdk/metric/processor/reducer` is renamed to `AttributeFilterSelector`.
The method included in the renamed interface also changed from `LabelFilterFor` to `AttributeFilterFor`. (#2790)
- The `Metadata.Labels` method from the `go.opentelemetry.io/otel/sdk/metric/export` package is renamed to `Metadata.Attributes`.
Consequentially, the `Record` type from the same package also has had the embedded method renamed. (#2790)
### Deprecated
- The `Iterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated.
Use the equivalent `Iterator.Attribute` method instead. (#2790)
- The `Iterator.IndexedLabel` method in the `go.opentelemetry.io/otel/attribute` package is deprecated.
Use the equivalent `Iterator.IndexedAttribute` method instead. (#2790)
- The `MergeIterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated.
Use the equivalent `MergeIterator.Attribute` method instead. (#2790)
### Removed
- Removed the `Batch` type from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package. (#2864)
- Removed the `Measurement` type from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package. (#2864)
## [0.29.0] - 2022-04-11
### Added
- The metrics global package was added back into several test files. (#2764)
- The `Meter` function is added back to the `go.opentelemetry.io/otel/metric/global` package.
This function is a convenience function equivalent to calling `global.MeterProvider().Meter(...)`. (#2750)
### Removed
- Removed module the `go.opentelemetry.io/otel/sdk/export/metric`.
Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2720)
### Changed
- Don't panic anymore when setting a global MeterProvider to itself. (#2749)
- Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` from `v0.12.1` to `v0.15.0`.
This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibraryMetrics` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeMetrics`. (#2748)
## [1.6.3] - 2022-04-07
### Fixed
- Allow non-comparable global `MeterProvider`, `TracerProvider`, and `TextMapPropagator` types to be set. (#2772, #2773)
## [1.6.2] - 2022-04-06
### Changed
- Don't panic anymore when setting a global TracerProvider or TextMapPropagator to itself. (#2749)
- Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace` from `v0.12.1` to `v0.15.0`.
This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibrarySpans` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeSpans`. (#2748)
## [1.6.1] - 2022-03-28
### Fixed
- The `go.opentelemetry.io/otel/schema/*` packages now use the correct schema URL for their `SchemaURL` constant.
Instead of using `"https://opentelemetry.io/schemas/v<version>"` they now use the correct URL without a `v` prefix, `"https://opentelemetry.io/schemas/<version>"`. (#2743, #2744)
### Security
- Upgrade `go.opentelemetry.io/proto/otlp` from `v0.12.0` to `v0.12.1`.
This includes an indirect upgrade of `github.com/grpc-ecosystem/grpc-gateway` which resolves [a vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2019-11254) from `gopkg.in/yaml.v2` in version `v2.2.3`. (#2724, #2728)
## [1.6.0/0.28.0] - 2022-03-23
### ⚠️ Notice ⚠️
This update is a breaking change of the unstable Metrics API.
Code instrumented with the `go.opentelemetry.io/otel/metric` will need to be modified.
### Added
- Add metrics exponential histogram support.
New mapping functions have been made available in `sdk/metric/aggregator/exponential/mapping` for other OpenTelemetry projects to take dependencies on. (#2502)
- Add Go 1.18 to our compatibility tests. (#2679)
- Allow configuring the Sampler with the `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` environment variables. (#2305, #2517)
- Add the `metric/global` for obtaining and setting the global `MeterProvider`. (#2660)
### Changed
- The metrics API has been significantly changed to match the revised OpenTelemetry specification.
High-level changes include:
- Synchronous and asynchronous instruments are now handled by independent `InstrumentProvider`s.
These `InstrumentProvider`s are managed with a `Meter`.
- Synchronous and asynchronous instruments are grouped into their own packages based on value types.
- Asynchronous callbacks can now be registered with a `Meter`.
Be sure to check out the metric module documentation for more information on how to use the revised API. (#2587, #2660)
### Fixed
- Fallback to general attribute limits when span specific ones are not set in the environment. (#2675, #2677)
## [1.5.0] - 2022-03-16
### Added
- Log the Exporters configuration in the TracerProviders message. (#2578)
- Added support to configure the span limits with environment variables.
The following environment variables are supported. (#2606, #2637)
- `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT`
- `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT`
- `OTEL_SPAN_EVENT_COUNT_LIMIT`
- `OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT`
- `OTEL_SPAN_LINK_COUNT_LIMIT`
- `OTEL_LINK_ATTRIBUTE_COUNT_LIMIT`
If the provided environment variables are invalid (negative), the default values would be used.
- Rename the `gc` runtime name to `go` (#2560)
- Add resource container ID detection. (#2418)
- Add span attribute value length limit.
The new `AttributeValueLengthLimit` field is added to the `"go.opentelemetry.io/otel/sdk/trace".SpanLimits` type to configure this limit for a `TracerProvider`.
The default limit for this resource is "unlimited". (#2637)
- Add the `WithRawSpanLimits` option to `go.opentelemetry.io/otel/sdk/trace`.
This option replaces the `WithSpanLimits` option.
Zero or negative values will not be changed to the default value like `WithSpanLimits` does.
Setting a limit to zero will effectively disable the related resource it limits and setting to a negative value will mean that resource is unlimited.
Consequentially, limits should be constructed using `NewSpanLimits` and updated accordingly. (#2637)
### Changed
- Drop oldest tracestate `Member` when capacity is reached. (#2592)
- Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601)
- Unify path cleaning functionally in the `otlpmetric` and `otlptrace` configuration. (#2639)
- Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640)
- Introduce new internal `envconfig` package for OTLP exporters. (#2608)
- If `http.Request.Host` is empty, fall back to use `URL.Host` when populating `http.host` in the `semconv` packages. (#2661)
### Fixed
- Remove the OTLP trace exporter limit of SpanEvents when exporting. (#2616)
- Default to port `4318` instead of `4317` for the `otlpmetrichttp` and `otlptracehttp` client. (#2614, #2625)
- Unlimited span limits are now supported (negative values). (#2636, #2637)
### Deprecated
- Deprecated `"go.opentelemetry.io/otel/sdk/trace".WithSpanLimits`.
Use `WithRawSpanLimits` instead.
That option allows setting unlimited and zero limits, this option does not.
This option will be kept until the next major version incremented release. (#2637)
## [1.4.1] - 2022-02-16
### Fixed
@@ -1689,7 +2087,23 @@ 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.4.1...HEAD
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...HEAD
[1.11.2/0.34.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.11.2
[1.11.1/0.33.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.11.1
[1.11.0/0.32.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.11.0
[0.32.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/sdk/metric/v0.32.2
[0.32.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/sdk/metric/v0.32.1
[0.32.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/sdk/metric/v0.32.0
[1.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.10.0
[1.9.0/0.0.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.9.0
[1.8.0/0.31.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.8.0
[1.7.0/0.30.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.7.0
[0.29.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.29.0
[1.6.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.3
[1.6.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.2
[1.6.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.1
[1.6.0/0.28.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.0
[1.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.5.0
[1.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.1
[1.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.0
[1.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.3.0

View File

@@ -12,6 +12,6 @@
# https://help.github.com/en/articles/about-code-owners
#
* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared
* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu
CODEOWNERS @MrAlias @Aneurysm9 @MadVikingGod

View File

@@ -506,8 +506,9 @@ Approvers:
- [Josh MacDonald](https://github.com/jmacd), LightStep
- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics
- [David Ashpole](https://github.com/dashpole), Google
- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep
- [Robert Pająk](https://github.com/pellared), Splunk
- [Chester Cheung](https://github.com/hanyuancheung), Tencent
- [Damien Mathieu](https://github.com/dmathieu), Auth0/Okta
Maintainers:
@@ -515,6 +516,10 @@ Maintainers:
- [Anthony Mirabella](https://github.com/Aneurysm9), AWS
- [Tyler Yahn](https://github.com/MrAlias), Splunk
Emeritus:
- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep
### Become an Approver or a Maintainer
See the [community membership document in OpenTelemetry community

View File

@@ -17,7 +17,7 @@ TOOLS_MOD_DIR := ./internal/tools
ALL_DOCS := $(shell find . -name '*.md' -type f | sort)
ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
OTEL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(ALL_GO_MOD_DIRS))
ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort)
ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | grep -E -v '^./example|^$(TOOLS_MOD_DIR)' | sort)
GO = go
TIMEOUT = 60
@@ -25,7 +25,7 @@ TIMEOUT = 60
.DEFAULT_GOAL := precommit
.PHONY: precommit ci
precommit: license-check misspell go-mod-tidy golangci-lint-fix test-default
precommit: dependabot-generate license-check vanity-import-fix misspell go-mod-tidy golangci-lint-fix test-default
ci: dependabot-check license-check lint vanity-import-check build test-default check-clean-work-tree test-coverage
# Tools
@@ -45,7 +45,13 @@ SEMCONVGEN = $(TOOLS)/semconvgen
$(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen
CROSSLINK = $(TOOLS)/crosslink
$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink
$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink
SEMCONVKIT = $(TOOLS)/semconvkit
$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit
DBOTCONF = $(TOOLS)/dbotconf
$(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/build-tools/dbotconf
GOLANGCI_LINT = $(TOOLS)/golangci-lint
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint
@@ -66,7 +72,7 @@ GOJQ = $(TOOLS)/gojq
$(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq
.PHONY: tools
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD)
tools: $(CROSSLINK) $(DBOTCONF) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT)
# Build
@@ -123,6 +129,7 @@ test-coverage: | $(GOCOVMERGE)
(cd "$${dir}" && \
$(GO) list ./... \
| grep -v third_party \
| grep -v 'semconv/v.*' \
| xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \
$(GO) tool cover -html=coverage.out -o coverage.html); \
done; \
@@ -140,8 +147,8 @@ golangci-lint/%: | $(GOLANGCI_LINT)
.PHONY: crosslink
crosslink: | $(CROSSLINK)
@echo "cross-linking all go modules" \
&& $(CROSSLINK)
@echo "Updating intra-repository dependencies in all go modules" \
&& $(CROSSLINK) --root=$(shell pwd) --prune
.PHONY: go-mod-tidy
go-mod-tidy: $(ALL_GO_MOD_DIRS:%=go-mod-tidy/%)
@@ -149,7 +156,7 @@ go-mod-tidy/%: DIR=$*
go-mod-tidy/%: | crosslink
@echo "$(GO) mod tidy in $(DIR)" \
&& cd $(DIR) \
&& $(GO) mod tidy
&& $(GO) mod tidy -compat=1.18
.PHONY: lint-modules
lint-modules: go-mod-tidy
@@ -159,7 +166,11 @@ lint: misspell lint-modules golangci-lint
.PHONY: vanity-import-check
vanity-import-check: | $(PORTO)
@$(PORTO) --include-internal -l .
@$(PORTO) --include-internal -l . || echo "(run: make vanity-import-fix)"
.PHONY: vanity-import-fix
vanity-import-fix: | $(PORTO)
@$(PORTO) --include-internal -w .
.PHONY: misspell
misspell: | $(MISSPELL)
@@ -167,7 +178,7 @@ misspell: | $(MISSPELL)
.PHONY: license-check
license-check:
@licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*') ; do \
@licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*' ! -path './.git/*' ) ; do \
awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \
done); \
if [ -n "$${licRes}" ]; then \
@@ -175,20 +186,14 @@ license-check:
exit 1; \
fi
DEPENDABOT_PATH=./.github/dependabot.yml
DEPENDABOT_CONFIG = .github/dependabot.yml
.PHONY: dependabot-check
dependabot-check:
@result=$$( \
for f in $$( find . -type f -name go.mod -exec dirname {} \; | sed 's/^.//' ); \
do grep -q "directory: \+$$f" $(DEPENDABOT_PATH) \
|| echo "$$f"; \
done; \
); \
if [ -n "$$result" ]; then \
echo "missing dependabot entry:"; echo "$$result"; \
echo "new modules need to be added to the $(DEPENDABOT_PATH) file"; \
exit 1; \
fi
dependabot-check: | $(DBOTCONF)
@$(DBOTCONF) verify $(DEPENDABOT_CONFIG) || echo "(run: make dependabot-generate)"
.PHONY: dependabot-generate
dependabot-generate: | $(DBOTCONF)
@$(DBOTCONF) generate > $(DEPENDABOT_CONFIG)
.PHONY: check-clean-work-tree
check-clean-work-tree:
@@ -200,6 +205,15 @@ check-clean-work-tree:
exit 1; \
fi
SEMCONVPKG ?= "semconv/"
.PHONY: semconv-generate
semconv-generate: | $(SEMCONVGEN) $(SEMCONVKIT)
@[ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry specification tag"; exit 1 )
@[ "$(OTEL_SPEC_REPO)" ] || ( echo "OTEL_SPEC_REPO unset: missing path to opentelemetry specification repo"; exit 1 )
@$(SEMCONVGEN) -i "$(OTEL_SPEC_REPO)/semantic_conventions/trace" -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
@$(SEMCONVGEN) -i "$(OTEL_SPEC_REPO)/semantic_conventions/resource" -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
@$(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)"
.PHONY: prerelease
prerelease: | $(MULTIMOD)
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )

View File

@@ -30,27 +30,36 @@ Project versioning information and stability guarantees can be found in the
### Compatibility
OpenTelemetry-Go attempts to track the current supported versions of the
[Go language](https://golang.org/doc/devel/release#policy). The release
schedule after a new minor version of go is as follows:
OpenTelemetry-Go ensures compatibility with the current supported versions of
the [Go language](https://golang.org/doc/devel/release#policy):
- The first release or one month, which ever is sooner, will add build steps for the new go version.
- The first release after three months will remove support for the oldest go version.
> Each major Go release is supported until there are two newer major releases.
> For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release.
This project is tested on the following systems.
For versions of Go that are no longer supported upstream, opentelemetry-go will
stop ensuring compatibility with these versions in the following manner:
- A minor release of opentelemetry-go will be made to add support for the new
supported release of Go.
- The following minor release of opentelemetry-go will remove compatibility
testing for the oldest (now archived upstream) version of Go. This, and
future, releases of opentelemetry-go may include features only supported by
the currently supported versions of Go.
Currently, this project supports the following environments.
| OS | Go Version | Architecture |
| ------- | ---------- | ------------ |
| Ubuntu | 1.17 | amd64 |
| Ubuntu | 1.16 | amd64 |
| Ubuntu | 1.17 | 386 |
| Ubuntu | 1.16 | 386 |
| MacOS | 1.17 | amd64 |
| MacOS | 1.16 | amd64 |
| Windows | 1.17 | amd64 |
| Windows | 1.16 | amd64 |
| Windows | 1.17 | 386 |
| Windows | 1.16 | 386 |
| Ubuntu | 1.19 | amd64 |
| Ubuntu | 1.18 | amd64 |
| Ubuntu | 1.19 | 386 |
| Ubuntu | 1.18 | 386 |
| MacOS | 1.19 | amd64 |
| MacOS | 1.18 | amd64 |
| Windows | 1.19 | amd64 |
| Windows | 1.18 | amd64 |
| Windows | 1.19 | 386 |
| Windows | 1.18 | 386 |
While this project should work for other systems, no compatibility guarantees
are made for those systems currently.
@@ -76,7 +85,7 @@ libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/
If you need to extend the telemetry an instrumentation library provides or want
to build your own instrumentation for your application directly you will need
to use the
[go.opentelemetry.io/otel/api](https://pkg.go.dev/go.opentelemetry.io/otel/api)
[Go otel](https://pkg.go.dev/go.opentelemetry.io/otel)
package. The included [examples](./example/) are a good way to see some
practical uses of this process.
@@ -95,8 +104,6 @@ All officially supported exporters for the OpenTelemetry project are contained i
| [stdout](./exporters/stdout/) | ✓ | ✓ |
| [Zipkin](./exporters/zipkin/) | | ✓ |
Additionally, OpenTelemetry community supported exporters can be found in the [contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/exporters).
## Contributing
See the [contributing documentation](CONTRIBUTING.md).

View File

@@ -2,35 +2,23 @@
## Semantic Convention Generation
If a new version of the OpenTelemetry Specification has been released it will be necessary to generate a new
semantic convention package from the YAML definitions in the specification repository. There is a `semconvgen` utility
installed by `make tools` that can be used to generate the a package with the name matching the specification
version number under the `semconv` package. This will ideally be done soon after the specification release is
tagged. Make sure that the specification repo contains a checkout of the the latest tagged release so that the
generated files match the released semantic conventions.
New versions of the [OpenTelemetry specification] mean new versions of the `semconv` package need to be generated.
The `semconv-generate` make target is used for this.
There are currently two categories of semantic conventions that must be generated, `resource` and `trace`.
1. Checkout a local copy of the [OpenTelemetry specification] to the desired release tag.
2. Run the `make semconv-generate ...` target from this repository.
```
.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/resource -t semconv/template.j2
.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/trace -t semconv/template.j2
For example,
```sh
export TAG="v1.7.0" # Change to the release version you are generating.
export OTEL_SPEC_REPO="/absolute/path/to/opentelemetry-specification"
git -C "$OTEL_SPEC_REPO" checkout "tags/$TAG"
make semconv-generate # Uses the exported TAG and OTEL_SPEC_REPO.
```
Using default values for all options other than `input` will result in using the `template.j2` template to
generate `resource.go` and `trace.go` in `/path/to/otelgo/repo/semconv/<version>`.
There are several ancillary files that are not generated and should be copied into the new package from the
prior package, with updates made as appropriate to canonical import path statements and constant values.
These files include:
* doc.go
* exception.go
* http(_test)?.go
* schema.go
Uses of the previous schema version in this repository should be updated to use the newly generated version.
No tooling for this exists at present, so use find/replace in your editor of choice or craft a `grep | sed`
pipeline if you like living on the edge.
This should create a new sub-package of [`semconv`](./semconv).
Ensure things look correct before submitting a pull request to include the addition.
## Pre-Release
@@ -130,3 +118,5 @@ Once verified be sure to [make a release for the `contrib` repository](https://g
Update [the documentation](./website_docs) for [the OpenTelemetry website](https://opentelemetry.io/docs/go/).
Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate.
[OpenTelemetry specification]: https://github.com/open-telemetry/opentelemetry-specification

View File

@@ -21,19 +21,17 @@ import (
)
type (
// Encoder is a mechanism for serializing a label set into a
// specific string representation that supports caching, to
// avoid repeated serialization. An example could be an
// exporter encoding the label set into a wire representation.
// Encoder is a mechanism for serializing an attribute set into a specific
// string representation that supports caching, to avoid repeated
// serialization. An example could be an exporter encoding the attribute
// set into a wire representation.
Encoder interface {
// Encode returns the serialized encoding of the label
// set using its Iterator. This result may be cached
// by a attribute.Set.
// Encode returns the serialized encoding of the attribute set using
// its Iterator. This result may be cached by a attribute.Set.
Encode(iterator Iterator) string
// ID returns a value that is unique for each class of
// label encoder. Label encoders allocate these using
// `NewEncoderID`.
// ID returns a value that is unique for each class of attribute
// encoder. Attribute encoders allocate these using `NewEncoderID`.
ID() EncoderID
}
@@ -43,54 +41,53 @@ type (
value uint64
}
// defaultLabelEncoder uses a sync.Pool of buffers to reduce
// the number of allocations used in encoding labels. This
// implementation encodes a comma-separated list of key=value,
// with '/'-escaping of '=', ',', and '\'.
defaultLabelEncoder struct {
// pool is a pool of labelset builders. The buffers in this
// pool grow to a size that most label encodings will not
// allocate new memory.
// defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of
// allocations used in encoding attributes. This implementation encodes a
// comma-separated list of key=value, with '/'-escaping of '=', ',', and
// '\'.
defaultAttrEncoder struct {
// pool is a pool of attribute set builders. The buffers in this pool
// grow to a size that most attribute encodings will not allocate new
// memory.
pool sync.Pool // *bytes.Buffer
}
)
// escapeChar is used to ensure uniqueness of the label encoding where
// keys or values contain either '=' or ','. Since there is no parser
// needed for this encoding and its only requirement is to be unique,
// this choice is arbitrary. Users will see these in some exporters
// (e.g., stdout), so the backslash ('\') is used as a conventional choice.
// escapeChar is used to ensure uniqueness of the attribute encoding where
// keys or values contain either '=' or ','. Since there is no parser needed
// for this encoding and its only requirement is to be unique, this choice is
// arbitrary. Users will see these in some exporters (e.g., stdout), so the
// backslash ('\') is used as a conventional choice.
const escapeChar = '\\'
var (
_ Encoder = &defaultLabelEncoder{}
_ Encoder = &defaultAttrEncoder{}
// encoderIDCounter is for generating IDs for other label
// encoders.
// encoderIDCounter is for generating IDs for other attribute encoders.
encoderIDCounter uint64
defaultEncoderOnce sync.Once
defaultEncoderID = NewEncoderID()
defaultEncoderInstance *defaultLabelEncoder
defaultEncoderInstance *defaultAttrEncoder
)
// NewEncoderID returns a unique label encoder ID. It should be
// called once per each type of label encoder. Preferably in init() or
// in var definition.
// NewEncoderID returns a unique attribute encoder ID. It should be called
// once per each type of attribute encoder. Preferably in init() or in var
// definition.
func NewEncoderID() EncoderID {
return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
}
// DefaultEncoder returns a label encoder that encodes labels
// in such a way that each escaped label's key is followed by an equal
// sign and then by an escaped label's value. All key-value pairs are
// separated by a comma.
// DefaultEncoder returns an attribute encoder that encodes attributes in such
// a way that each escaped attribute's key is followed by an equal sign and
// then by an escaped attribute's value. All key-value pairs are separated by
// a comma.
//
// Escaping is done by prepending a backslash before either a
// backslash, equal sign or a comma.
// Escaping is done by prepending a backslash before either a backslash, equal
// sign or a comma.
func DefaultEncoder() Encoder {
defaultEncoderOnce.Do(func() {
defaultEncoderInstance = &defaultLabelEncoder{
defaultEncoderInstance = &defaultAttrEncoder{
pool: sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
@@ -101,15 +98,14 @@ func DefaultEncoder() Encoder {
return defaultEncoderInstance
}
// Encode is a part of an implementation of the LabelEncoder
// interface.
func (d *defaultLabelEncoder) Encode(iter Iterator) string {
// Encode is a part of an implementation of the AttributeEncoder interface.
func (d *defaultAttrEncoder) Encode(iter Iterator) string {
buf := d.pool.Get().(*bytes.Buffer)
defer d.pool.Put(buf)
buf.Reset()
for iter.Next() {
i, keyValue := iter.IndexedLabel()
i, keyValue := iter.IndexedAttribute()
if i > 0 {
_, _ = buf.WriteRune(',')
}
@@ -126,8 +122,8 @@ func (d *defaultLabelEncoder) Encode(iter Iterator) string {
return buf.String()
}
// ID is a part of an implementation of the LabelEncoder interface.
func (*defaultLabelEncoder) ID() EncoderID {
// ID is a part of an implementation of the AttributeEncoder interface.
func (*defaultAttrEncoder) ID() EncoderID {
return defaultEncoderID
}
@@ -137,9 +133,9 @@ func copyAndEscape(buf *bytes.Buffer, val string) {
for _, ch := range val {
switch ch {
case '=', ',', escapeChar:
buf.WriteRune(escapeChar)
_, _ = buf.WriteRune(escapeChar)
}
buf.WriteRune(ch)
_, _ = buf.WriteRune(ch)
}
}

View File

@@ -14,16 +14,16 @@
package attribute // import "go.opentelemetry.io/otel/attribute"
// Iterator allows iterating over the set of labels in order,
// sorted by key.
// Iterator allows iterating over the set of attributes in order, sorted by
// key.
type Iterator struct {
storage *Set
idx int
}
// MergeIterator supports iterating over two sets of labels while
// eliminating duplicate values from the combined set. The first
// iterator value takes precedence.
// MergeIterator supports iterating over two sets of attributes while
// eliminating duplicate values from the combined set. The first iterator
// value takes precedence.
type MergeIterator struct {
one oneIterator
two oneIterator
@@ -31,13 +31,13 @@ type MergeIterator struct {
}
type oneIterator struct {
iter Iterator
done bool
label KeyValue
iter Iterator
done bool
attr KeyValue
}
// Next moves the iterator to the next position. Returns false if there
// are no more labels.
// Next moves the iterator to the next position. Returns false if there are no
// more attributes.
func (i *Iterator) Next() bool {
i.idx++
return i.idx < i.Len()
@@ -45,30 +45,41 @@ func (i *Iterator) Next() bool {
// Label returns current KeyValue. Must be called only after Next returns
// true.
//
// Deprecated: Use Attribute instead.
func (i *Iterator) Label() KeyValue {
return i.Attribute()
}
// Attribute returns the current KeyValue of the Iterator. It must be called
// only after Next returns true.
func (i *Iterator) Attribute() KeyValue {
kv, _ := i.storage.Get(i.idx)
return kv
}
// Attribute is a synonym for Label().
func (i *Iterator) Attribute() KeyValue {
return i.Label()
}
// IndexedLabel returns current index and attribute. Must be called only
// after Next returns true.
//
// Deprecated: Use IndexedAttribute instead.
func (i *Iterator) IndexedLabel() (int, KeyValue) {
return i.idx, i.Label()
return i.idx, i.Attribute()
}
// Len returns a number of labels in the iterator's `*Set`.
// IndexedAttribute returns current index and attribute. Must be called only
// after Next returns true.
func (i *Iterator) IndexedAttribute() (int, KeyValue) {
return i.idx, i.Attribute()
}
// Len returns a number of attributes in the iterated set.
func (i *Iterator) Len() int {
return i.storage.Len()
}
// ToSlice is a convenience function that creates a slice of labels
// from the passed iterator. The iterator is set up to start from the
// beginning before creating the slice.
// ToSlice is a convenience function that creates a slice of attributes from
// the passed iterator. The iterator is set up to start from the beginning
// before creating the slice.
func (i *Iterator) ToSlice() []KeyValue {
l := i.Len()
if l == 0 {
@@ -77,12 +88,12 @@ func (i *Iterator) ToSlice() []KeyValue {
i.idx = -1
slice := make([]KeyValue, 0, l)
for i.Next() {
slice = append(slice, i.Label())
slice = append(slice, i.Attribute())
}
return slice
}
// NewMergeIterator returns a MergeIterator for merging two label sets
// NewMergeIterator returns a MergeIterator for merging two attribute sets.
// Duplicates are resolved by taking the value from the first set.
func NewMergeIterator(s1, s2 *Set) MergeIterator {
mi := MergeIterator{
@@ -102,42 +113,49 @@ func makeOne(iter Iterator) oneIterator {
func (oi *oneIterator) advance() {
if oi.done = !oi.iter.Next(); !oi.done {
oi.label = oi.iter.Label()
oi.attr = oi.iter.Attribute()
}
}
// Next returns true if there is another label available.
// Next returns true if there is another attribute available.
func (m *MergeIterator) Next() bool {
if m.one.done && m.two.done {
return false
}
if m.one.done {
m.current = m.two.label
m.current = m.two.attr
m.two.advance()
return true
}
if m.two.done {
m.current = m.one.label
m.current = m.one.attr
m.one.advance()
return true
}
if m.one.label.Key == m.two.label.Key {
m.current = m.one.label // first iterator label value wins
if m.one.attr.Key == m.two.attr.Key {
m.current = m.one.attr // first iterator attribute value wins
m.one.advance()
m.two.advance()
return true
}
if m.one.label.Key < m.two.label.Key {
m.current = m.one.label
if m.one.attr.Key < m.two.attr.Key {
m.current = m.one.attr
m.one.advance()
return true
}
m.current = m.two.label
m.current = m.two.attr
m.two.advance()
return true
}
// Label returns the current value after Next() returns true.
//
// Deprecated: Use Attribute instead.
func (m *MergeIterator) Label() KeyValue {
return m.current
}
// Attribute returns the current value after Next() returns true.
func (m *MergeIterator) Attribute() KeyValue {
return m.current
}

View File

@@ -21,49 +21,42 @@ import (
)
type (
// Set is the representation for a distinct label set. It
// manages an immutable set of labels, with an internal cache
// for storing label encodings.
// Set is the representation for a distinct attribute set. It manages an
// immutable set of attributes, with an internal cache for storing
// attribute encodings.
//
// This type supports the `Equivalent` method of comparison
// using values of type `Distinct`.
//
// This type is used to implement:
// 1. Metric labels
// 2. Resource sets
// 3. Correlation map (TODO)
// This type supports the Equivalent method of comparison using values of
// type Distinct.
Set struct {
equivalent Distinct
}
// Distinct wraps a variable-size array of `KeyValue`,
// constructed with keys in sorted order. This can be used as
// a map key or for equality checking between Sets.
// Distinct wraps a variable-size array of KeyValue, constructed with keys
// in sorted order. This can be used as a map key or for equality checking
// between Sets.
Distinct struct {
iface interface{}
}
// Filter supports removing certain labels from label sets.
// When the filter returns true, the label will be kept in
// the filtered label set. When the filter returns false, the
// label is excluded from the filtered label set, and the
// label instead appears in the `removed` list of excluded labels.
// Filter supports removing certain attributes from attribute sets. When
// the filter returns true, the attribute will be kept in the filtered
// attribute set. When the filter returns false, the attribute is excluded
// from the filtered attribute set, and the attribute instead appears in
// the removed list of excluded attributes.
Filter func(KeyValue) bool
// Sortable implements `sort.Interface`, used for sorting
// `KeyValue`. This is an exported type to support a
// memory optimization. A pointer to one of these is needed
// for the call to `sort.Stable()`, which the caller may
// provide in order to avoid an allocation. See
// `NewSetWithSortable()`.
// Sortable implements sort.Interface, used for sorting KeyValue. This is
// an exported type to support a memory optimization. A pointer to one of
// these is needed for the call to sort.Stable(), which the caller may
// provide in order to avoid an allocation. See NewSetWithSortable().
Sortable []KeyValue
)
var (
// keyValueType is used in `computeDistinctReflect`.
// keyValueType is used in computeDistinctReflect.
keyValueType = reflect.TypeOf(KeyValue{})
// emptySet is returned for empty label sets.
// emptySet is returned for empty attribute sets.
emptySet = &Set{
equivalent: Distinct{
iface: [0]KeyValue{},
@@ -78,30 +71,30 @@ func EmptySet() *Set {
return emptySet
}
// reflect abbreviates `reflect.ValueOf`.
func (d Distinct) reflect() reflect.Value {
// reflectValue abbreviates reflect.ValueOf(d).
func (d Distinct) reflectValue() reflect.Value {
return reflect.ValueOf(d.iface)
}
// Valid returns true if this value refers to a valid `*Set`.
// Valid returns true if this value refers to a valid Set.
func (d Distinct) Valid() bool {
return d.iface != nil
}
// Len returns the number of labels in this set.
// Len returns the number of attributes in this set.
func (l *Set) Len() int {
if l == nil || !l.equivalent.Valid() {
return 0
}
return l.equivalent.reflect().Len()
return l.equivalent.reflectValue().Len()
}
// Get returns the KeyValue at ordered position `idx` in this set.
// Get returns the KeyValue at ordered position idx in this set.
func (l *Set) Get(idx int) (KeyValue, bool) {
if l == nil {
return KeyValue{}, false
}
value := l.equivalent.reflect()
value := l.equivalent.reflectValue()
if idx >= 0 && idx < value.Len() {
// Note: The Go compiler successfully avoids an allocation for
@@ -117,7 +110,7 @@ func (l *Set) Value(k Key) (Value, bool) {
if l == nil {
return Value{}, false
}
rValue := l.equivalent.reflect()
rValue := l.equivalent.reflectValue()
vlen := rValue.Len()
idx := sort.Search(vlen, func(idx int) bool {
@@ -142,7 +135,7 @@ func (l *Set) HasValue(k Key) bool {
return ok
}
// Iter returns an iterator for visiting the labels in this set.
// Iter returns an iterator for visiting the attributes in this set.
func (l *Set) Iter() Iterator {
return Iterator{
storage: l,
@@ -150,18 +143,17 @@ func (l *Set) Iter() Iterator {
}
}
// ToSlice returns the set of labels belonging to this set, sorted,
// where keys appear no more than once.
// ToSlice returns the set of attributes belonging to this set, sorted, where
// keys appear no more than once.
func (l *Set) ToSlice() []KeyValue {
iter := l.Iter()
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 label set with the same elements as this,
// where sets are made unique by choosing the last value in the input
// for any given key.
// 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
// 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
@@ -174,8 +166,7 @@ func (l *Set) Equals(o *Set) bool {
return l.Equivalent() == o.Equivalent()
}
// Encoded returns the encoded form of this set, according to
// `encoder`.
// Encoded returns the encoded form of this set, according to encoder.
func (l *Set) Encoded(encoder Encoder) string {
if l == nil || encoder == nil {
return ""
@@ -190,11 +181,11 @@ func empty() Set {
}
}
// NewSet returns a new `Set`. See the documentation for
// `NewSetWithSortableFiltered` for more details.
// NewSet returns a new Set. See the documentation for
// NewSetWithSortableFiltered for more details.
//
// Except for empty sets, this method adds an additional allocation
// compared with calls that include a `*Sortable`.
// Except for empty sets, this method adds an additional allocation compared
// with calls that include a Sortable.
func NewSet(kvs ...KeyValue) Set {
// Check for empty set.
if len(kvs) == 0 {
@@ -204,10 +195,10 @@ func NewSet(kvs ...KeyValue) Set {
return s
}
// NewSetWithSortable returns a new `Set`. See the documentation for
// `NewSetWithSortableFiltered` for more details.
// NewSetWithSortable returns a new Set. See the documentation for
// NewSetWithSortableFiltered for more details.
//
// This call includes a `*Sortable` option as a memory optimization.
// This call includes a Sortable option as a memory optimization.
func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set {
// Check for empty set.
if len(kvs) == 0 {
@@ -217,12 +208,11 @@ func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set {
return s
}
// NewSetWithFiltered returns a new `Set`. See the documentation for
// `NewSetWithSortableFiltered` for more details.
// NewSetWithFiltered returns a new Set. See the documentation for
// NewSetWithSortableFiltered for more details.
//
// This call includes a `Filter` to include/exclude label keys from
// the return value. Excluded keys are returned as a slice of label
// values.
// This call includes a Filter to include/exclude attribute keys from the
// return value. Excluded keys are returned as a slice of attribute values.
func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
// Check for empty set.
if len(kvs) == 0 {
@@ -231,7 +221,7 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
return NewSetWithSortableFiltered(kvs, new(Sortable), filter)
}
// NewSetWithSortableFiltered returns a new `Set`.
// NewSetWithSortableFiltered returns a new Set.
//
// Duplicate keys are eliminated by taking the last value. This
// re-orders the input slice so that unique last-values are contiguous
@@ -243,17 +233,16 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
// - Caller sees the reordering, but doesn't lose values
// - Repeated call preserve last-value wins.
//
// Note that methods are defined on `*Set`, although this returns `Set`.
// Callers can avoid memory allocations by:
// Note that methods are defined on Set, although this returns Set. Callers
// can avoid memory allocations by:
//
// - allocating a `Sortable` for use as a temporary in this method
// - allocating a `Set` for storing the return value of this
// constructor.
// - allocating a Sortable for use as a temporary in this method
// - allocating a Set for storing the return value of this constructor.
//
// The result maintains a cache of encoded labels, by attribute.EncoderID.
// The result maintains a cache of encoded attributes, by attribute.EncoderID.
// This value should not be copied after its first use.
//
// The second `[]KeyValue` return value is a list of labels that were
// The second []KeyValue return value is a list of attributes that were
// excluded by the Filter (if non-nil).
func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) {
// Check for empty set.
@@ -293,13 +282,13 @@ func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (S
}, nil
}
// filterSet reorders `kvs` so that included keys are contiguous at
// the end of the slice, while excluded keys precede the included keys.
// filterSet reorders kvs so that included keys are contiguous at the end of
// the slice, while excluded keys precede the included keys.
func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
var excluded []KeyValue
// Move labels that do not match the filter so
// they're adjacent before calling computeDistinct().
// Move attributes that do not match the filter so they're adjacent before
// calling computeDistinct().
distinctPosition := len(kvs)
// Swap indistinct keys forward and distinct keys toward the
@@ -319,8 +308,8 @@ func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
}, excluded
}
// Filter returns a filtered copy of this `Set`. See the
// documentation for `NewSetWithSortableFiltered` for more details.
// Filter returns a filtered copy of this Set. See the documentation for
// NewSetWithSortableFiltered for more details.
func (l *Set) Filter(re Filter) (Set, []KeyValue) {
if re == nil {
return Set{
@@ -333,9 +322,9 @@ func (l *Set) Filter(re Filter) (Set, []KeyValue) {
return filterSet(l.ToSlice(), re)
}
// 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.
// 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 {
@@ -346,8 +335,8 @@ func computeDistinct(kvs []KeyValue) Distinct {
}
}
// computeDistinctFixed computes a `Distinct` for small slices. It
// returns nil if the input is too large for this code path.
// computeDistinctFixed computes a Distinct for small slices. It returns nil
// if the input is too large for this code path.
func computeDistinctFixed(kvs []KeyValue) interface{} {
switch len(kvs) {
case 1:
@@ -395,8 +384,8 @@ func computeDistinctFixed(kvs []KeyValue) interface{} {
}
}
// computeDistinctReflect computes a `Distinct` using reflection,
// works for any size input.
// computeDistinctReflect computes a Distinct using reflection, works for any
// size input.
func computeDistinctReflect(kvs []KeyValue) interface{} {
at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem()
for i, keyValue := range kvs {
@@ -405,7 +394,7 @@ func computeDistinctReflect(kvs []KeyValue) interface{} {
return at.Interface()
}
// MarshalJSON returns the JSON encoding of the `*Set`.
// MarshalJSON returns the JSON encoding of the Set.
func (l *Set) MarshalJSON() ([]byte, error) {
return json.Marshal(l.equivalent.iface)
}
@@ -419,17 +408,17 @@ func (l Set) MarshalLog() interface{} {
return kvs
}
// Len implements `sort.Interface`.
// Len implements sort.Interface.
func (l *Sortable) Len() int {
return len(*l)
}
// Swap implements `sort.Interface`.
// Swap implements sort.Interface.
func (l *Sortable) Swap(i, j int) {
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
}
// Less implements `sort.Interface`.
// Less implements sort.Interface.
func (l *Sortable) Less(i, j int) bool {
return (*l)[i].Key < (*l)[j].Key
}

View File

@@ -17,15 +17,17 @@ package attribute // import "go.opentelemetry.io/otel/attribute"
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"go.opentelemetry.io/otel/internal"
"go.opentelemetry.io/otel/internal/attribute"
)
//go:generate stringer -type=Type
// Type describes the type of the data Value holds.
type Type int
type Type int // nolint: revive // redefines builtin Type.
// Value represents the value part in key-value pairs.
type Value struct {
@@ -66,12 +68,7 @@ func BoolValue(v bool) Value {
// BoolSliceValue creates a BOOLSLICE Value.
func BoolSliceValue(v []bool) Value {
cp := make([]bool, len(v))
copy(cp, v)
return Value{
vtype: BOOLSLICE,
slice: &cp,
}
return Value{vtype: BOOLSLICE, slice: attribute.SliceValue(v)}
}
// IntValue creates an INT64 Value.
@@ -81,13 +78,14 @@ func IntValue(v int) Value {
// IntSliceValue creates an INTSLICE Value.
func IntSliceValue(v []int) Value {
cp := make([]int64, 0, len(v))
for _, i := range v {
cp = append(cp, int64(i))
var int64Val int64
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(int64Val)))
for i, val := range v {
cp.Elem().Index(i).SetInt(int64(val))
}
return Value{
vtype: INT64SLICE,
slice: &cp,
slice: cp.Elem().Interface(),
}
}
@@ -101,12 +99,7 @@ func Int64Value(v int64) Value {
// Int64SliceValue creates an INT64SLICE Value.
func Int64SliceValue(v []int64) Value {
cp := make([]int64, len(v))
copy(cp, v)
return Value{
vtype: INT64SLICE,
slice: &cp,
}
return Value{vtype: INT64SLICE, slice: attribute.SliceValue(v)}
}
// Float64Value creates a FLOAT64 Value.
@@ -119,12 +112,7 @@ func Float64Value(v float64) Value {
// Float64SliceValue creates a FLOAT64SLICE Value.
func Float64SliceValue(v []float64) Value {
cp := make([]float64, len(v))
copy(cp, v)
return Value{
vtype: FLOAT64SLICE,
slice: &cp,
}
return Value{vtype: FLOAT64SLICE, slice: attribute.SliceValue(v)}
}
// StringValue creates a STRING Value.
@@ -137,12 +125,7 @@ func StringValue(v string) Value {
// StringSliceValue creates a STRINGSLICE Value.
func StringSliceValue(v []string) Value {
cp := make([]string, len(v))
copy(cp, v)
return Value{
vtype: STRINGSLICE,
slice: &cp,
}
return Value{vtype: STRINGSLICE, slice: attribute.SliceValue(v)}
}
// Type returns a type of the Value.
@@ -159,10 +142,14 @@ func (v Value) AsBool() bool {
// AsBoolSlice returns the []bool value. Make sure that the Value's type is
// BOOLSLICE.
func (v Value) AsBoolSlice() []bool {
if s, ok := v.slice.(*[]bool); ok {
return *s
if v.vtype != BOOLSLICE {
return nil
}
return nil
return v.asBoolSlice()
}
func (v Value) asBoolSlice() []bool {
return attribute.AsSlice[bool](v.slice)
}
// AsInt64 returns the int64 value. Make sure that the Value's type is
@@ -174,10 +161,14 @@ func (v Value) AsInt64() int64 {
// AsInt64Slice returns the []int64 value. Make sure that the Value's type is
// INT64SLICE.
func (v Value) AsInt64Slice() []int64 {
if s, ok := v.slice.(*[]int64); ok {
return *s
if v.vtype != INT64SLICE {
return nil
}
return nil
return v.asInt64Slice()
}
func (v Value) asInt64Slice() []int64 {
return attribute.AsSlice[int64](v.slice)
}
// AsFloat64 returns the float64 value. Make sure that the Value's
@@ -189,10 +180,14 @@ func (v Value) AsFloat64() float64 {
// AsFloat64Slice returns the []float64 value. Make sure that the Value's type is
// FLOAT64SLICE.
func (v Value) AsFloat64Slice() []float64 {
if s, ok := v.slice.(*[]float64); ok {
return *s
if v.vtype != FLOAT64SLICE {
return nil
}
return nil
return v.asFloat64Slice()
}
func (v Value) asFloat64Slice() []float64 {
return attribute.AsSlice[float64](v.slice)
}
// AsString returns the string value. Make sure that the Value's type
@@ -204,10 +199,14 @@ func (v Value) AsString() string {
// AsStringSlice returns the []string value. Make sure that the Value's type is
// STRINGSLICE.
func (v Value) AsStringSlice() []string {
if s, ok := v.slice.(*[]string); ok {
return *s
if v.vtype != STRINGSLICE {
return nil
}
return nil
return v.asStringSlice()
}
func (v Value) asStringSlice() []string {
return attribute.AsSlice[string](v.slice)
}
type unknownValueType struct{}
@@ -218,19 +217,19 @@ func (v Value) AsInterface() interface{} {
case BOOL:
return v.AsBool()
case BOOLSLICE:
return v.AsBoolSlice()
return v.asBoolSlice()
case INT64:
return v.AsInt64()
case INT64SLICE:
return v.AsInt64Slice()
return v.asInt64Slice()
case FLOAT64:
return v.AsFloat64()
case FLOAT64SLICE:
return v.AsFloat64Slice()
return v.asFloat64Slice()
case STRING:
return v.stringly
case STRINGSLICE:
return v.AsStringSlice()
return v.asStringSlice()
}
return unknownValueType{}
}
@@ -239,19 +238,19 @@ func (v Value) AsInterface() interface{} {
func (v Value) Emit() string {
switch v.Type() {
case BOOLSLICE:
return fmt.Sprint(*(v.slice.(*[]bool)))
return fmt.Sprint(v.asBoolSlice())
case BOOL:
return strconv.FormatBool(v.AsBool())
case INT64SLICE:
return fmt.Sprint(*(v.slice.(*[]int64)))
return fmt.Sprint(v.asInt64Slice())
case INT64:
return strconv.FormatInt(v.AsInt64(), 10)
case FLOAT64SLICE:
return fmt.Sprint(*(v.slice.(*[]float64)))
return fmt.Sprint(v.asFloat64Slice())
case FLOAT64:
return fmt.Sprint(v.AsFloat64())
case STRINGSLICE:
return fmt.Sprint(*(v.slice.(*[]string)))
return fmt.Sprint(v.asStringSlice())
case STRING:
return v.stringly
default:

View File

@@ -68,6 +68,9 @@ type Property struct {
hasData bool
}
// NewKeyProperty returns a new Property for key.
//
// If key is invalid, an error will be returned.
func NewKeyProperty(key string) (Property, error) {
if !keyRe.MatchString(key) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
@@ -77,6 +80,9 @@ func NewKeyProperty(key string) (Property, error) {
return p, nil
}
// NewKeyValueProperty returns a new Property for key with value.
//
// If key or value are invalid, an error will be returned.
func NewKeyValueProperty(key, value string) (Property, error) {
if !keyRe.MatchString(key) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
@@ -151,7 +157,7 @@ func (p Property) Key() string {
return p.key
}
// Value returns the Property value. Additionally a boolean value is returned
// Value returns the Property value. Additionally, a boolean value is returned
// indicating if the returned value is the empty if the Property has a value
// that is empty or if the value is not set.
func (p Property) Value() (string, bool) {
@@ -244,8 +250,9 @@ type Member struct {
hasData bool
}
// NewMember returns a new Member from the passed arguments. An error is
// returned if the created Member would be invalid according to the W3C
// NewMember returns a new Member from the passed arguments. The key will be
// used directly while the value will be url decoded after validation. An error
// is returned if the created Member would be invalid according to the W3C
// Baggage specification.
func NewMember(key, value string, props ...Property) (Member, error) {
m := Member{
@@ -257,7 +264,11 @@ func NewMember(key, value string, props ...Property) (Member, error) {
if err := m.validate(); err != nil {
return newInvalidMember(), err
}
decodedValue, err := url.QueryUnescape(value)
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
m.value = decodedValue
return m, nil
}
@@ -322,8 +333,9 @@ func parseMember(member string) (Member, error) {
return Member{key: key, value: value, properties: props, hasData: true}, nil
}
// validate ensures m conforms to the W3C Baggage specification, returning an
// error otherwise.
// validate ensures m conforms to the W3C Baggage specification.
// A key is just an ASCII string, but a value must be URL encoded UTF-8,
// returning an error otherwise.
func (m Member) validate() error {
if !m.hasData {
return fmt.Errorf("%w: %q", errInvalidMember, m)
@@ -386,7 +398,7 @@ func New(members ...Member) (Baggage, error) {
}
}
// Check member numbers after deduplicating.
// Check member numbers after deduplication.
if len(b) > maxMembers {
return Baggage{}, errMemberNumber
}
@@ -448,7 +460,7 @@ func Parse(bStr string) (Baggage, error) {
func (b Baggage) Member(key string) Member {
v, ok := b.list[key]
if !ok {
// We do not need to worry about distiguising between the situation
// We do not need to worry about distinguishing between the situation
// where a zero-valued Member is included in the Baggage because a
// zero-valued Member is invalid according to the W3C Baggage
// specification (it has an empty key).
@@ -459,6 +471,7 @@ func (b Baggage) Member(key string) Member {
key: key,
value: v.Value,
properties: fromInternalProperties(v.Properties),
hasData: true,
}
}
@@ -478,6 +491,7 @@ func (b Baggage) Members() []Member {
key: k,
value: v.Value,
properties: fromInternalProperties(v.Properties),
hasData: true,
})
}
return members

View File

@@ -23,10 +23,20 @@ import (
const (
// Unset is the default status code.
Unset Code = 0
// Error indicates the operation contains an error.
//
// NOTE: The error code in OTLP is 2.
// The value of this enum is only relevant to the internals
// of the Go SDK.
Error Code = 1
// Ok indicates operation has been validated by an Application developers
// or Operator to have completed successfully, or contain no error.
//
// NOTE: The Ok code in OTLP is 1.
// The value of this enum is only relevant to the internals
// of the Go SDK.
Ok Code = 2
maxCode = 3

View File

@@ -0,0 +1,34 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package internal contains common functionality for all OTLP exporters.
package internal // import "go.opentelemetry.io/otel/exporters/otlp/internal"
import (
"fmt"
"path"
"strings"
)
// CleanPath returns a path with all spaces trimmed and all redundancies removed. If urlPath is empty or cleaning it results in an empty string, defaultPath is returned instead.
func CleanPath(urlPath string, defaultPath string) string {
tmp := path.Clean(strings.TrimSpace(urlPath))
if tmp == "." {
return defaultPath
}
if !path.IsAbs(tmp) {
tmp = fmt.Sprintf("/%s", tmp)
}
return tmp
}

View File

@@ -0,0 +1,199 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package envconfig // import "go.opentelemetry.io/otel/exporters/otlp/internal/envconfig"
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net/url"
"strconv"
"strings"
"time"
"go.opentelemetry.io/otel/internal/global"
)
// ConfigFn is the generic function used to set a config.
type ConfigFn func(*EnvOptionsReader)
// EnvOptionsReader reads the required environment variables.
type EnvOptionsReader struct {
GetEnv func(string) string
ReadFile func(string) ([]byte, error)
Namespace string
}
// Apply runs every ConfigFn.
func (e *EnvOptionsReader) Apply(opts ...ConfigFn) {
for _, o := range opts {
o(e)
}
}
// GetEnvValue gets an OTLP environment variable value of the specified key
// using the GetEnv function.
// This function prepends the OTLP specified namespace to all key lookups.
func (e *EnvOptionsReader) GetEnvValue(key string) (string, bool) {
v := strings.TrimSpace(e.GetEnv(keyWithNamespace(e.Namespace, key)))
return v, v != ""
}
// WithString retrieves the specified config and passes it to ConfigFn as a string.
func WithString(n string, fn func(string)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
fn(v)
}
}
}
// WithBool returns a ConfigFn that reads the environment variable n and if it exists passes its parsed bool value to fn.
func WithBool(n string, fn func(bool)) ConfigFn {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
b := strings.ToLower(v) == "true"
fn(b)
}
}
}
// WithDuration retrieves the specified config and passes it to ConfigFn as a duration.
func WithDuration(n string, fn func(time.Duration)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
d, err := strconv.Atoi(v)
if err != nil {
global.Error(err, "parse duration", "input", v)
return
}
fn(time.Duration(d) * time.Millisecond)
}
}
}
// WithHeaders retrieves the specified config and passes it to ConfigFn as a map of HTTP headers.
func WithHeaders(n string, fn func(map[string]string)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
fn(stringToHeader(v))
}
}
}
// WithURL retrieves the specified config and passes it to ConfigFn as a net/url.URL.
func WithURL(n string, fn func(*url.URL)) func(e *EnvOptionsReader) {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
u, err := url.Parse(v)
if err != nil {
global.Error(err, "parse url", "input", v)
return
}
fn(u)
}
}
}
// WithCertPool returns a ConfigFn that reads the environment variable n as a filepath to a TLS certificate pool. If it exists, it is parsed as a crypto/x509.CertPool and it is passed to fn.
func WithCertPool(n string, fn func(*x509.CertPool)) ConfigFn {
return func(e *EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
b, err := e.ReadFile(v)
if err != nil {
global.Error(err, "read tls ca cert file", "file", v)
return
}
c, err := createCertPool(b)
if err != nil {
global.Error(err, "create tls cert pool")
return
}
fn(c)
}
}
}
// WithClientCert returns a ConfigFn that reads the environment variable nc and nk as filepaths to a client certificate and key pair. If they exists, they are parsed as a crypto/tls.Certificate and it is passed to fn.
func WithClientCert(nc, nk string, fn func(tls.Certificate)) ConfigFn {
return func(e *EnvOptionsReader) {
vc, okc := e.GetEnvValue(nc)
vk, okk := e.GetEnvValue(nk)
if !okc || !okk {
return
}
cert, err := e.ReadFile(vc)
if err != nil {
global.Error(err, "read tls client cert", "file", vc)
return
}
key, err := e.ReadFile(vk)
if err != nil {
global.Error(err, "read tls client key", "file", vk)
return
}
crt, err := tls.X509KeyPair(cert, key)
if err != nil {
global.Error(err, "create tls client key pair")
return
}
fn(crt)
}
}
func keyWithNamespace(ns, key string) string {
if ns == "" {
return key
}
return fmt.Sprintf("%s_%s", ns, key)
}
func stringToHeader(value string) map[string]string {
headersPairs := strings.Split(value, ",")
headers := make(map[string]string)
for _, header := range headersPairs {
nameValue := strings.SplitN(header, "=", 2)
if len(nameValue) < 2 {
global.Error(errors.New("missing '="), "parse headers", "input", nameValue)
continue
}
name, err := url.QueryUnescape(nameValue[0])
if err != nil {
global.Error(err, "escape header key", "key", nameValue[0])
continue
}
trimmedName := strings.TrimSpace(name)
value, err := url.QueryUnescape(nameValue[1])
if err != nil {
global.Error(err, "escape header value", "value", nameValue[1])
continue
}
trimmedValue := strings.TrimSpace(value)
headers[trimmedName] = trimmedValue
}
return headers
}
func createCertPool(certBytes []byte) (*x509.CertPool, error) {
cp := x509.NewCertPool()
if ok := cp.AppendCertsFromPEM(certBytes); !ok {
return nil, errors.New("failed to append certificate to the cert pool")
}
return cp, nil
}

View File

@@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package registry provides a non-standalone implementation of
MeterProvider that adds uniqueness checking for instrument descriptors
on top of other MeterProvider it wraps.
// Package internal contains common functionality for all OTLP exporters.
package internal // import "go.opentelemetry.io/otel/exporters/otlp/internal"
This package is currently in a pre-GA phase. Backwards incompatible changes
may be introduced in subsequent minor version releases as we work to track the
evolving OpenTelemetry specification and user feedback.
*/
package registry // import "go.opentelemetry.io/otel/internal/metric/registry"
import "go.opentelemetry.io/otel"
// GetUserAgentHeader return an OTLP header value form "OTel OTLP Exporter Go/{{ .Version }}"
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#user-agent
func GetUserAgentHeader() string {
return "OTel OTLP Exporter Go/" + otel.Version()
}

View File

@@ -0,0 +1,64 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package internal // import "go.opentelemetry.io/otel/exporters/otlp/internal"
import "fmt"
// PartialSuccess represents the underlying error for all handling
// OTLP partial success messages. Use `errors.Is(err,
// PartialSuccess{})` to test whether an error passed to the OTel
// error handler belongs to this category.
type PartialSuccess struct {
ErrorMessage string
RejectedItems int64
RejectedKind string
}
var _ error = PartialSuccess{}
// Error implements the error interface.
func (ps PartialSuccess) Error() string {
msg := ps.ErrorMessage
if msg == "" {
msg = "empty message"
}
return fmt.Sprintf("OTLP partial success: %s (%d %s rejected)", msg, ps.RejectedItems, ps.RejectedKind)
}
// Is supports the errors.Is() interface.
func (ps PartialSuccess) Is(err error) bool {
_, ok := err.(PartialSuccess)
return ok
}
// TracePartialSuccessError returns an error describing a partial success
// response for the trace signal.
func TracePartialSuccessError(itemsRejected int64, errorMessage string) error {
return PartialSuccess{
ErrorMessage: errorMessage,
RejectedItems: itemsRejected,
RejectedKind: "spans",
}
}
// MetricPartialSuccessError returns an error describing a partial success
// response for the metric signal.
func MetricPartialSuccessError(itemsRejected int64, errorMessage string) error {
return PartialSuccess{
ErrorMessage: errorMessage,
RejectedItems: itemsRejected,
RejectedKind: "metric data points",
}
}

View File

@@ -12,8 +12,8 @@ go get -u go.opentelemetry.io/otel/exporters/otlp/otlptrace
## Examples
- [Exporter setup and examples](./otlptracehttp/example_test.go)
- [Full example sending telemetry to a local collector](../../../example/otel-collector)
- [HTTP Exporter setup and examples](./otlptracehttp/example_test.go)
- [Full example of gRPC Exporter sending telemetry to a local collector](../../../example/otel-collector)
## [`otlptrace`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace)
@@ -38,12 +38,14 @@ override the default configuration. For more information about how each of
these environment variables is interpreted, see [the OpenTelemetry
specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/protocol/exporter.md).
| Environment variable | Option | Default value |
| ------------------------------------------------------------------------ |------------------------------ | ----------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `WithEndpoint` `WithInsecure` | `https://localhost:4317` |
| `OTEL_EXPORTER_OTLP_CERTIFICATE` `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` | `WithTLSClientConfig` | |
| `OTEL_EXPORTER_OTLP_HEADERS` `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | `WithHeaders` | |
| `OTEL_EXPORTER_OTLP_COMPRESSION` `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` | `WithCompression` | |
| `OTEL_EXPORTER_OTLP_TIMEOUT` `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | `WithTimeout` | `10s` |
| Environment variable | Option | Default value |
| ------------------------------------------------------------------------ |------------------------------ | -------------------------------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `WithEndpoint` `WithInsecure` | `https://localhost:4317` or `https://localhost:4318`[^1] |
| `OTEL_EXPORTER_OTLP_CERTIFICATE` `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` | `WithTLSClientConfig` | |
| `OTEL_EXPORTER_OTLP_HEADERS` `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | `WithHeaders` | |
| `OTEL_EXPORTER_OTLP_COMPRESSION` `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` | `WithCompression` | |
| `OTEL_EXPORTER_OTLP_TIMEOUT` `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | `WithTimeout` | `10s` |
[^1]: The gRPC client defaults to `https://localhost:4317` and the HTTP client `https://localhost:4318`.
Configuration using options have precedence over the environment variables.

View File

@@ -100,3 +100,14 @@ func NewUnstarted(client Client) *Exporter {
client: client,
}
}
// MarshalLog is the marshaling function used by the logging system to represent this exporter.
func (e *Exporter) MarshalLog() interface{} {
return struct {
Type string
Client Client
}{
Type: "otlptrace",
Client: e.client,
}
}

View File

@@ -16,66 +16,59 @@ package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/
import (
"crypto/tls"
"fmt"
"io/ioutil"
"crypto/x509"
"net/url"
"os"
"path"
"strconv"
"strings"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/internal/envconfig"
)
var DefaultEnvOptionsReader = EnvOptionsReader{
GetEnv: os.Getenv,
ReadFile: ioutil.ReadFile,
// DefaultEnvOptionsReader is the default environments reader.
var DefaultEnvOptionsReader = envconfig.EnvOptionsReader{
GetEnv: os.Getenv,
ReadFile: os.ReadFile,
Namespace: "OTEL_EXPORTER_OTLP",
}
// ApplyGRPCEnvConfigs applies the env configurations for gRPC.
func ApplyGRPCEnvConfigs(cfg Config) Config {
return DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
}
func ApplyHTTPEnvConfigs(cfg Config) Config {
return DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
}
type EnvOptionsReader struct {
GetEnv func(string) string
ReadFile func(filename string) ([]byte, error)
}
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv()
for _, opt := range opts {
cfg = opt.ApplyHTTPOption(cfg)
}
return cfg
}
func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv()
opts := getOptionsFromEnv()
for _, opt := range opts {
cfg = opt.ApplyGRPCOption(cfg)
}
return cfg
}
func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
var opts []GenericOption
// ApplyHTTPEnvConfigs applies the env configurations for HTTP.
func ApplyHTTPEnvConfigs(cfg Config) Config {
opts := getOptionsFromEnv()
for _, opt := range opts {
cfg = opt.ApplyHTTPOption(cfg)
}
return cfg
}
// Endpoint
if v, ok := e.getEnvValue("TRACES_ENDPOINT"); ok {
u, err := url.Parse(v)
// Ignore invalid values.
if err == nil {
// This is used to set the scheme for OTLP/HTTP.
if insecureSchema(u.Scheme) {
opts = append(opts, WithInsecure())
} else {
opts = append(opts, WithSecure())
}
func getOptionsFromEnv() []GenericOption {
opts := []GenericOption{}
tlsConf := &tls.Config{}
DefaultEnvOptionsReader.Apply(
envconfig.WithURL("ENDPOINT", func(u *url.URL) {
opts = append(opts, withEndpointScheme(u))
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host
// For OTLP/HTTP endpoint URLs without a per-signal
// configuration, the passed endpoint is used as a base URL
// and the signals are sent to these paths relative to that.
cfg.Traces.URLPath = path.Join(u.Path, DefaultTracesPath)
return cfg
}, withEndpointForGRPC(u)))
}),
envconfig.WithURL("TRACES_ENDPOINT", func(u *url.URL) {
opts = append(opts, withEndpointScheme(u))
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host
// For endpoint URLs for OTLP/HTTP per-signal variables, the
@@ -88,140 +81,70 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
}
cfg.Traces.URLPath = path
return cfg
}, func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
return cfg
}))
}
} else if v, ok = e.getEnvValue("ENDPOINT"); ok {
u, err := url.Parse(v)
// Ignore invalid values.
if err == nil {
// This is used to set the scheme for OTLP/HTTP.
if insecureSchema(u.Scheme) {
opts = append(opts, WithInsecure())
} else {
opts = append(opts, WithSecure())
}
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host
// For OTLP/HTTP endpoint URLs without a per-signal
// configuration, the passed endpoint is used as a base URL
// and the signals are sent to these paths relative to that.
cfg.Traces.URLPath = path.Join(u.Path, DefaultTracesPath)
return cfg
}, func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
return cfg
}))
}
}
// Certificate File
if path, ok := e.getEnvValue("CERTIFICATE"); ok {
if tls, err := e.readTLSConfig(path); err == nil {
opts = append(opts, WithTLSClientConfig(tls))
} else {
otel.Handle(fmt.Errorf("failed to configure otlp exporter certificate '%s': %w", path, err))
}
}
if path, ok := e.getEnvValue("TRACES_CERTIFICATE"); ok {
if tls, err := e.readTLSConfig(path); err == nil {
opts = append(opts, WithTLSClientConfig(tls))
} else {
otel.Handle(fmt.Errorf("failed to configure otlp traces exporter certificate '%s': %w", path, err))
}
}
// Headers
if h, ok := e.getEnvValue("HEADERS"); ok {
opts = append(opts, WithHeaders(stringToHeader(h)))
}
if h, ok := e.getEnvValue("TRACES_HEADERS"); ok {
opts = append(opts, WithHeaders(stringToHeader(h)))
}
// Compression
if c, ok := e.getEnvValue("COMPRESSION"); ok {
opts = append(opts, WithCompression(stringToCompression(c)))
}
if c, ok := e.getEnvValue("TRACES_COMPRESSION"); ok {
opts = append(opts, WithCompression(stringToCompression(c)))
}
// Timeout
if t, ok := e.getEnvValue("TIMEOUT"); ok {
if d, err := strconv.Atoi(t); err == nil {
opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond))
}
}
if t, ok := e.getEnvValue("TRACES_TIMEOUT"); ok {
if d, err := strconv.Atoi(t); err == nil {
opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond))
}
}
}, withEndpointForGRPC(u)))
}),
envconfig.WithCertPool("CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }),
envconfig.WithCertPool("TRACES_CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }),
envconfig.WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }),
envconfig.WithClientCert("TRACES_CLIENT_CERTIFICATE", "TRACES_CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }),
withTLSConfig(tlsConf, func(c *tls.Config) { opts = append(opts, WithTLSClientConfig(c)) }),
envconfig.WithBool("INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }),
envconfig.WithBool("TRACES_INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }),
envconfig.WithHeaders("HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
envconfig.WithHeaders("TRACES_HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
WithEnvCompression("COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
WithEnvCompression("TRACES_COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
envconfig.WithDuration("TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
envconfig.WithDuration("TRACES_TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
)
return opts
}
func insecureSchema(schema string) bool {
switch strings.ToLower(schema) {
func withEndpointScheme(u *url.URL) GenericOption {
switch strings.ToLower(u.Scheme) {
case "http", "unix":
return true
return WithInsecure()
default:
return false
return WithSecure()
}
}
// getEnvValue gets an OTLP environment variable value of the specified key using the GetEnv function.
// This function already prepends the OTLP prefix to all key lookup.
func (e *EnvOptionsReader) getEnvValue(key string) (string, bool) {
v := strings.TrimSpace(e.GetEnv(fmt.Sprintf("OTEL_EXPORTER_OTLP_%s", key)))
return v, v != ""
}
func (e *EnvOptionsReader) readTLSConfig(path string) (*tls.Config, error) {
b, err := e.ReadFile(path)
if err != nil {
return nil, err
func withEndpointForGRPC(u *url.URL) func(cfg Config) Config {
return func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
return cfg
}
return CreateTLSConfig(b)
}
func stringToCompression(value string) Compression {
switch value {
case "gzip":
return GzipCompression
}
// WithEnvCompression retrieves the specified config and passes it to ConfigFn as a Compression.
func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOptionsReader) {
return func(e *envconfig.EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
cp := NoCompression
if v == "gzip" {
cp = GzipCompression
}
return NoCompression
}
func stringToHeader(value string) map[string]string {
headersPairs := strings.Split(value, ",")
headers := make(map[string]string)
for _, header := range headersPairs {
nameValue := strings.SplitN(header, "=", 2)
if len(nameValue) < 2 {
continue
fn(cp)
}
}
}
// revive:disable-next-line:flag-parameter
func withInsecure(b bool) GenericOption {
if b {
return WithInsecure()
}
return WithSecure()
}
func withTLSConfig(c *tls.Config, fn func(*tls.Config)) func(e *envconfig.EnvOptionsReader) {
return func(e *envconfig.EnvOptionsReader) {
if c.RootCAs != nil || len(c.Certificates) > 0 {
fn(c)
}
name, err := url.QueryUnescape(nameValue[0])
if err != nil {
continue
}
trimmedName := strings.TrimSpace(name)
value, err := url.QueryUnescape(nameValue[1])
if err != nil {
continue
}
trimmedValue := strings.TrimSpace(value)
headers[trimmedName] = trimmedValue
}
return headers
}

View File

@@ -25,6 +25,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding/gzip"
"go.opentelemetry.io/otel/exporters/otlp/internal"
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
)
@@ -65,24 +66,39 @@ type (
}
)
func NewDefaultConfig() Config {
c := Config{
// NewHTTPConfig returns a new Config with all settings applied from opts and
// any unset setting using the default HTTP config values.
func NewHTTPConfig(opts ...HTTPOption) Config {
cfg := Config{
Traces: SignalConfig{
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorPort),
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
URLPath: DefaultTracesPath,
Compression: NoCompression,
Timeout: DefaultTimeout,
},
RetryConfig: retry.DefaultConfig,
}
return c
cfg = ApplyHTTPEnvConfigs(cfg)
for _, opt := range opts {
cfg = opt.ApplyHTTPOption(cfg)
}
cfg.Traces.URLPath = internal.CleanPath(cfg.Traces.URLPath, DefaultTracesPath)
return cfg
}
// NewGRPCConfig returns a new Config with all settings applied from opts and
// any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config {
cfg := NewDefaultConfig()
cfg := Config{
Traces: SignalConfig{
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
URLPath: DefaultTracesPath,
Compression: NoCompression,
Timeout: DefaultTimeout,
},
RetryConfig: retry.DefaultConfig,
DialOptions: []grpc.DialOption{grpc.WithUserAgent(internal.GetUserAgentHeader())},
}
cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts {
cfg = opt.ApplyGRPCOption(cfg)

View File

@@ -15,9 +15,10 @@
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
const (
// DefaultCollectorPort is the port the Exporter will attempt connect to
// if no collector port is provided.
DefaultCollectorPort uint16 = 4317
// DefaultCollectorGRPCPort is the default gRPC port of the collector.
DefaultCollectorGRPCPort uint16 = 4317
// DefaultCollectorHTTPPort is the default HTTP port of the collector.
DefaultCollectorHTTPPort uint16 = 4318
// DefaultCollectorHost is the host address the Exporter will attempt
// connect to if no collector address is provided.
DefaultCollectorHost string = "localhost"
@@ -36,7 +37,7 @@ const (
GzipCompression
)
// Marshaler describes the kind of message format sent to the collector
// Marshaler describes the kind of message format sent to the collector.
type Marshaler int
const (

View File

@@ -48,8 +48,8 @@ func Iterator(iter attribute.Iterator) []*commonpb.KeyValue {
}
// ResourceAttributes transforms a Resource OTLP key-values.
func ResourceAttributes(resource *resource.Resource) []*commonpb.KeyValue {
return Iterator(resource.Iter())
func ResourceAttributes(res *resource.Resource) []*commonpb.KeyValue {
return Iterator(res.Iter())
}
// KeyValue transforms an attribute KeyValue into an OTLP key-value.

View File

@@ -19,11 +19,11 @@ import (
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
)
func InstrumentationLibrary(il instrumentation.Library) *commonpb.InstrumentationLibrary {
if il == (instrumentation.Library{}) {
func InstrumentationScope(il instrumentation.Scope) *commonpb.InstrumentationScope {
if il == (instrumentation.Scope{}) {
return nil
}
return &commonpb.InstrumentationLibrary{
return &commonpb.InstrumentationScope{
Name: il.Name,
Version: il.Version,
}

View File

@@ -23,10 +23,6 @@ import (
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
const (
maxEventsPerSpan = 128
)
// Spans transforms a slice of OpenTelemetry spans into a slice of OTLP
// ResourceSpans.
func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans {
@@ -36,11 +32,11 @@ func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans {
rsm := make(map[attribute.Distinct]*tracepb.ResourceSpans)
type ilsKey struct {
type key struct {
r attribute.Distinct
il instrumentation.Library
is instrumentation.Scope
}
ilsm := make(map[ilsKey]*tracepb.InstrumentationLibrarySpans)
ssm := make(map[key]*tracepb.ScopeSpans)
var resources int
for _, sd := range sdl {
@@ -49,30 +45,30 @@ func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans {
}
rKey := sd.Resource().Equivalent()
iKey := ilsKey{
k := key{
r: rKey,
il: sd.InstrumentationLibrary(),
is: sd.InstrumentationScope(),
}
ils, iOk := ilsm[iKey]
scopeSpan, iOk := ssm[k]
if !iOk {
// Either the resource or instrumentation library were unknown.
ils = &tracepb.InstrumentationLibrarySpans{
InstrumentationLibrary: InstrumentationLibrary(sd.InstrumentationLibrary()),
Spans: []*tracepb.Span{},
SchemaUrl: sd.InstrumentationLibrary().SchemaURL,
// Either the resource or instrumentation scope were unknown.
scopeSpan = &tracepb.ScopeSpans{
Scope: InstrumentationScope(sd.InstrumentationScope()),
Spans: []*tracepb.Span{},
SchemaUrl: sd.InstrumentationScope().SchemaURL,
}
}
ils.Spans = append(ils.Spans, span(sd))
ilsm[iKey] = ils
scopeSpan.Spans = append(scopeSpan.Spans, span(sd))
ssm[k] = scopeSpan
rs, rOk := rsm[rKey]
if !rOk {
resources++
// The resource was unknown.
rs = &tracepb.ResourceSpans{
Resource: Resource(sd.Resource()),
InstrumentationLibrarySpans: []*tracepb.InstrumentationLibrarySpans{ils},
SchemaUrl: sd.Resource().SchemaURL(),
Resource: Resource(sd.Resource()),
ScopeSpans: []*tracepb.ScopeSpans{scopeSpan},
SchemaUrl: sd.Resource().SchemaURL(),
}
rsm[rKey] = rs
continue
@@ -82,9 +78,9 @@ func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans {
// library lookup was unknown because if so we need to add it to the
// ResourceSpans. Otherwise, the instrumentation library has already
// been seen and the append we did above will be included it in the
// InstrumentationLibrarySpans reference.
// ScopeSpans reference.
if !iOk {
rs.InstrumentationLibrarySpans = append(rs.InstrumentationLibrarySpans, ils)
rs.ScopeSpans = append(rs.ScopeSpans, scopeSpan)
}
}
@@ -162,9 +158,10 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link {
sid := otLink.SpanContext.SpanID()
sl = append(sl, &tracepb.Span_Link{
TraceId: tid[:],
SpanId: sid[:],
Attributes: KeyValues(otLink.Attributes),
TraceId: tid[:],
SpanId: sid[:],
Attributes: KeyValues(otLink.Attributes),
DroppedAttributesCount: uint32(otLink.DroppedAttributeCount),
})
}
return sl
@@ -176,29 +173,16 @@ func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event {
return nil
}
evCount := len(es)
if evCount > maxEventsPerSpan {
evCount = maxEventsPerSpan
}
events := make([]*tracepb.Span_Event, 0, evCount)
nEvents := 0
events := make([]*tracepb.Span_Event, len(es))
// Transform message events
for _, e := range es {
if nEvents >= maxEventsPerSpan {
break
for i := 0; i < len(es); i++ {
events[i] = &tracepb.Span_Event{
Name: es[i].Name,
TimeUnixNano: uint64(es[i].Time.UnixNano()),
Attributes: KeyValues(es[i].Attributes),
DroppedAttributesCount: uint32(es[i].DroppedAttributeCount),
}
nEvents++
events = append(events,
&tracepb.Span_Event{
Name: e.Name,
TimeUnixNano: uint64(e.Time.UnixNano()),
Attributes: KeyValues(e.Attributes),
// TODO (rghetia) : Add Drop Counts when supported.
},
)
}
return events
}

View File

@@ -26,6 +26,8 @@ import (
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/internal"
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
@@ -196,9 +198,17 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
defer cancel()
return c.requestFunc(ctx, func(iCtx context.Context) error {
_, err := c.tsc.Export(iCtx, &coltracepb.ExportTraceServiceRequest{
resp, err := c.tsc.Export(iCtx, &coltracepb.ExportTraceServiceRequest{
ResourceSpans: protoSpans,
})
if resp != nil && resp.PartialSuccess != nil {
msg := resp.PartialSuccess.GetErrorMessage()
n := resp.PartialSuccess.GetRejectedSpans()
if n != 0 || msg != "" {
err := internal.TracePartialSuccessError(n, msg)
otel.Handle(err)
}
}
// nil is converted to OK.
if status.Code(err) == codes.OK {
// Success.
@@ -265,11 +275,22 @@ func retryable(err error) (bool, time.Duration) {
// throttleDelay returns a duration to wait for if an explicit throttle time
// is included in the response status.
func throttleDelay(status *status.Status) time.Duration {
for _, detail := range status.Details() {
func throttleDelay(s *status.Status) time.Duration {
for _, detail := range s.Details() {
if t, ok := detail.(*errdetails.RetryInfo); ok {
return t.RetryDelay.AsDuration()
}
}
return 0
}
// MarshalLog is the marshaling function used by the logging system to represent this Client.
func (c *client) MarshalLog() interface{} {
return struct {
Type string
Endpoint string
}{
Type: "otlphttpgrpc",
Endpoint: c.endpoint,
}
}

View File

@@ -84,8 +84,7 @@ func WithReconnectionPeriod(rp time.Duration) Option {
}
func compressorToCompression(compressor string) otlpconfig.Compression {
switch compressor {
case "gzip":
if compressor == "gzip" {
return otlpconfig.GzipCompression
}

View File

@@ -20,18 +20,17 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"sync"
"time"
"google.golang.org/protobuf/proto"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/internal"
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
@@ -43,7 +42,7 @@ const contentTypeProto = "application/x-protobuf"
var gzPool = sync.Pool{
New: func() interface{} {
w := gzip.NewWriter(ioutil.Discard)
w := gzip.NewWriter(io.Discard)
return w
},
}
@@ -79,26 +78,7 @@ var _ otlptrace.Client = (*client)(nil)
// NewClient creates a new HTTP trace client.
func NewClient(opts ...Option) otlptrace.Client {
cfg := otlpconfig.NewDefaultConfig()
cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg)
for _, opt := range opts {
cfg = opt.applyHTTPOption(cfg)
}
for pathPtr, defaultPath := range map[*string]string{
&cfg.Traces.URLPath: otlpconfig.DefaultTracesPath,
} {
tmp := strings.TrimSpace(*pathPtr)
if tmp == "" {
tmp = defaultPath
} else {
tmp = path.Clean(tmp)
if !path.IsAbs(tmp) {
tmp = fmt.Sprintf("/%s", tmp)
}
}
*pathPtr = tmp
}
cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(opts)...)
httpClient := &http.Client{
Transport: ourTransport,
@@ -121,7 +101,7 @@ func NewClient(opts ...Option) otlptrace.Client {
}
}
// Start does nothing in a HTTP client
// Start does nothing in a HTTP client.
func (d *client) Start(ctx context.Context) error {
// nothing to do
select {
@@ -176,28 +156,49 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
return err
}
var rErr error
if resp != nil && resp.Body != nil {
defer func() {
if err := resp.Body.Close(); err != nil {
otel.Handle(err)
}
}()
}
switch resp.StatusCode {
case http.StatusOK:
// Success, do not retry.
case http.StatusTooManyRequests,
http.StatusServiceUnavailable:
// Retry-able failure.
rErr = newResponseError(resp.Header)
// Going to retry, drain the body to reuse the connection.
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
_ = resp.Body.Close()
// Read the partial success message, if any.
var respData bytes.Buffer
if _, err := io.Copy(&respData, resp.Body); err != nil {
return err
}
default:
rErr = fmt.Errorf("failed to send %s to %s: %s", d.name, request.URL, resp.Status)
}
if err := resp.Body.Close(); err != nil {
return err
if respData.Len() != 0 {
var respProto coltracepb.ExportTraceServiceResponse
if err := proto.Unmarshal(respData.Bytes(), &respProto); err != nil {
return err
}
if respProto.PartialSuccess != nil {
msg := respProto.PartialSuccess.GetErrorMessage()
n := respProto.PartialSuccess.GetRejectedSpans()
if n != 0 || msg != "" {
err := internal.TracePartialSuccessError(n, msg)
otel.Handle(err)
}
}
}
return nil
case http.StatusTooManyRequests, http.StatusServiceUnavailable:
// Retry-able failures. Drain the body to reuse the connection.
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
otel.Handle(err)
}
return newResponseError(resp.Header)
default:
return fmt.Errorf("failed to send %s to %s: %s", d.name, request.URL, resp.Status)
}
return rErr
})
}
@@ -208,6 +209,8 @@ func (d *client) newRequest(body []byte) (request, error) {
return request{Request: r}, err
}
r.Header.Set("User-Agent", internal.GetUserAgentHeader())
for k, v := range d.cfg.Headers {
r.Header.Set(k, v)
}
@@ -243,10 +246,23 @@ func (d *client) newRequest(body []byte) (request, error) {
return req, nil
}
// MarshalLog is the marshaling function used by the logging system to represent this Client.
func (d *client) MarshalLog() interface{} {
return struct {
Type string
Endpoint string
Insecure bool
}{
Type: "otlphttphttp",
Endpoint: d.cfg.Endpoint,
Insecure: d.cfg.Insecure,
}
}
// bodyReader returns a closure returning a new reader for buf.
func bodyReader(buf []byte) func() io.ReadCloser {
return func() io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader(buf))
return io.NopCloser(bytes.NewReader(buf))
}
}

View File

@@ -40,6 +40,14 @@ type Option interface {
applyHTTPOption(otlpconfig.Config) otlpconfig.Config
}
func asHTTPOptions(opts []Option) []otlpconfig.HTTPOption {
converted := make([]otlpconfig.HTTPOption, len(opts))
for i, o := range opts {
converted[i] = otlpconfig.NewHTTPOption(o.applyHTTPOption)
}
return converted
}
// RetryConfig defines configuration for retrying batches in case of export
// failure using an exponential backoff.
type RetryConfig retry.Config
@@ -55,7 +63,7 @@ func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config
// WithEndpoint allows one to set the address of the collector
// endpoint that the driver will use to send spans. If
// unset, it will instead try to use
// the default endpoint (localhost:4317). Note that the endpoint
// the default endpoint (localhost:4318). Note that the endpoint
// must not contain any URL path.
func WithEndpoint(endpoint string) Option {
return wrappedOption{otlpconfig.WithEndpoint(endpoint)}

View File

@@ -56,7 +56,6 @@ func defaultErrorHandler() *delegator {
lock: &sync.RWMutex{},
eh: &errLogger{l: log.New(os.Stderr, "", log.LstdFlags)},
}
}
// errLogger logs errors if no delegate is set, otherwise they are delegated.
@@ -92,7 +91,7 @@ func SetErrorHandler(h ErrorHandler) {
globalErrorHandler.setDelegate(h)
}
// Handle is a convenience function for ErrorHandler().Handle(err)
// Handle is a convenience function for ErrorHandler().Handle(err).
func Handle(err error) {
GetErrorHandler().Handle(err)
}

View File

@@ -0,0 +1,45 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package attribute provide several helper functions for some commonly used
logic of processing attributes.
*/
package attribute // import "go.opentelemetry.io/otel/internal/attribute"
import (
"reflect"
)
// SliceValue convert a slice into an array with same elements as slice.
func SliceValue[T bool | int64 | float64 | string](v []T) any {
var zero T
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
copy(cp.Elem().Slice(0, len(v)).Interface().([]T), v)
return cp.Elem().Interface()
}
// AsSlice convert an array into a slice into with same elements as array.
func AsSlice[T bool | int64 | float64 | string](v any) []T {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Array {
return nil
}
var zero T
correctLen := rv.Len()
correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero))
cpy := reflect.New(correctType)
_ = reflect.Copy(cpy.Elem(), rv)
return cpy.Elem().Slice(0, correctLen).Interface().([]T)
}

View File

@@ -39,8 +39,7 @@ type baggageState struct {
// Passing nil SetHookFunc creates a context with no set hook to call.
func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Context {
var s baggageState
switch v := parent.Value(baggageKey).(type) {
case baggageState:
if v, ok := parent.Value(baggageKey).(baggageState); ok {
s = v
}
@@ -54,8 +53,7 @@ func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Contex
// Passing nil GetHookFunc creates a context with no get hook to call.
func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Context {
var s baggageState
switch v := parent.Value(baggageKey).(type) {
case baggageState:
if v, ok := parent.Value(baggageKey).(baggageState); ok {
s = v
}
@@ -67,8 +65,7 @@ func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Contex
// returns a context without any baggage.
func ContextWithList(parent context.Context, list List) context.Context {
var s baggageState
switch v := parent.Value(baggageKey).(type) {
case baggageState:
if v, ok := parent.Value(baggageKey).(baggageState); ok {
s = v
}

View File

@@ -33,7 +33,7 @@ var globalLoggerLock = &sync.RWMutex{}
// SetLogger overrides the globalLogger with l.
//
// To see Info messages use a logger with `l.V(1).Enabled() == true`
// To see Debug messages use a logger with `l.V(5).Enabled() == true`
// To see Debug messages use a logger with `l.V(5).Enabled() == true`.
func SetLogger(l logr.Logger) {
globalLoggerLock.Lock()
defer globalLoggerLock.Unlock()
@@ -41,7 +41,7 @@ func SetLogger(l logr.Logger) {
}
// Info prints messages about the general state of the API or SDK.
// This should usually be less then 5 messages a minute
// This should usually be less then 5 messages a minute.
func Info(msg string, keysAndValues ...interface{}) {
globalLoggerLock.RLock()
defer globalLoggerLock.RUnlock()

View File

@@ -15,6 +15,7 @@
package global // import "go.opentelemetry.io/otel/internal/global"
import (
"errors"
"sync"
"sync/atomic"
@@ -47,17 +48,24 @@ func TracerProvider() trace.TracerProvider {
// SetTracerProvider is the internal implementation for global.SetTracerProvider.
func SetTracerProvider(tp trace.TracerProvider) {
current := TracerProvider()
if _, cOk := current.(*tracerProvider); cOk {
if _, tpOk := tp.(*tracerProvider); tpOk && current == tp {
// Do not assign the default delegating TracerProvider to delegate
// to itself.
Error(
errors.New("no delegate configured in tracer provider"),
"Setting tracer provider to it's current value. No delegate will be configured",
)
return
}
}
delegateTraceOnce.Do(func() {
current := TracerProvider()
if current == tp {
// Setting the provider to the prior default is nonsense, panic.
// Panic is acceptable because we are likely still early in the
// process lifetime.
panic("invalid TracerProvider, the global instance cannot be reinstalled")
} else if def, ok := current.(*tracerProvider); ok {
if def, ok := current.(*tracerProvider); ok {
def.setDelegate(tp)
}
})
globalTracer.Store(tracerProviderHolder{tp: tp})
}
@@ -69,15 +77,24 @@ func TextMapPropagator() propagation.TextMapPropagator {
// SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator.
func SetTextMapPropagator(p propagation.TextMapPropagator) {
current := TextMapPropagator()
if _, cOk := current.(*textMapPropagator); cOk {
if _, pOk := p.(*textMapPropagator); pOk && current == p {
// Do not assign the default delegating TextMapPropagator to
// delegate to itself.
Error(
errors.New("no delegate configured in text map propagator"),
"Setting text map propagator to it's current value. No delegate will be configured",
)
return
}
}
// For the textMapPropagator already returned by TextMapPropagator
// delegate to p.
delegateTextMapPropagatorOnce.Do(func() {
if current := TextMapPropagator(); current == p {
// Setting the provider to the prior default is nonsense, panic.
// Panic is acceptable because we are likely still early in the
// process lifetime.
panic("invalid TextMapPropagator, the global instance cannot be reinstalled")
} else if def, ok := current.(*textMapPropagator); ok {
if def, ok := current.(*textMapPropagator); ok {
def.SetDelegate(p)
}
})
@@ -96,11 +113,3 @@ func defaultPropagatorsValue() *atomic.Value {
v.Store(propagatorsHolder{tm: newTextMapPropagator()})
return v
}
// ResetForTest restores the initial global state, for testing purposes.
func ResetForTest() {
globalTracer = defaultTracerValue()
globalPropagators = defaultPropagatorsValue()
delegateTraceOnce = sync.Once{}
delegateTextMapPropagatorOnce = sync.Once{}
}

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,287 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package global // import "go.opentelemetry.io/otel/internal/metric/global"
import (
"context"
"sync"
"sync/atomic"
"unsafe"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/internal/metric/registry"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// This file contains the forwarding implementation of MeterProvider used as
// the default global instance. Metric events using instruments provided by
// this implementation are no-ops until the first Meter implementation is set
// as the global provider.
//
// The implementation here uses Mutexes to maintain a list of active Meters in
// the MeterProvider and Instruments in each Meter, under the assumption that
// these interfaces are not performance-critical.
//
// We have the invariant that setDelegate() will be called before a new
// MeterProvider implementation is registered as the global provider. Mutexes
// in the MeterProvider and Meters ensure that each instrument has a delegate
// before the global provider is set.
//
// Metric uniqueness checking is implemented by calling the exported
// methods of the api/metric/registry package.
type meterKey struct {
InstrumentationName string
InstrumentationVersion string
SchemaURL string
}
type meterProvider struct {
delegate metric.MeterProvider
// lock protects `delegate` and `meters`.
lock sync.Mutex
// meters maintains a unique entry for every named Meter
// that has been registered through the global instance.
meters map[meterKey]*meterEntry
}
type meterImpl struct {
delegate unsafe.Pointer // (*metric.MeterImpl)
lock sync.Mutex
syncInsts []*syncImpl
asyncInsts []*asyncImpl
}
type meterEntry struct {
unique sdkapi.MeterImpl
impl meterImpl
}
type instrument struct {
descriptor sdkapi.Descriptor
}
type syncImpl struct {
delegate unsafe.Pointer // (*sdkapi.SyncImpl)
instrument
}
type asyncImpl struct {
delegate unsafe.Pointer // (*sdkapi.AsyncImpl)
instrument
runner sdkapi.AsyncRunner
}
var _ metric.MeterProvider = &meterProvider{}
var _ sdkapi.MeterImpl = &meterImpl{}
var _ sdkapi.InstrumentImpl = &syncImpl{}
var _ sdkapi.AsyncImpl = &asyncImpl{}
func (inst *instrument) Descriptor() sdkapi.Descriptor {
return inst.descriptor
}
// MeterProvider interface and delegation
func newMeterProvider() *meterProvider {
return &meterProvider{
meters: map[meterKey]*meterEntry{},
}
}
func (p *meterProvider) setDelegate(provider metric.MeterProvider) {
p.lock.Lock()
defer p.lock.Unlock()
p.delegate = provider
for key, entry := range p.meters {
entry.impl.setDelegate(key, provider)
}
p.meters = nil
}
func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
p.lock.Lock()
defer p.lock.Unlock()
if p.delegate != nil {
return p.delegate.Meter(instrumentationName, opts...)
}
cfg := metric.NewMeterConfig(opts...)
key := meterKey{
InstrumentationName: instrumentationName,
InstrumentationVersion: cfg.InstrumentationVersion(),
SchemaURL: cfg.SchemaURL(),
}
entry, ok := p.meters[key]
if !ok {
entry = &meterEntry{}
// Note: This code implements its own MeterProvider
// name-uniqueness logic because there is
// synchronization required at the moment of
// delegation. We use the same instrument-uniqueness
// checking the real SDK uses here:
entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl)
p.meters[key] = entry
}
return metric.WrapMeterImpl(entry.unique)
}
// Meter interface and delegation
func (m *meterImpl) setDelegate(key meterKey, provider metric.MeterProvider) {
m.lock.Lock()
defer m.lock.Unlock()
d := new(sdkapi.MeterImpl)
*d = provider.Meter(
key.InstrumentationName,
metric.WithInstrumentationVersion(key.InstrumentationVersion),
metric.WithSchemaURL(key.SchemaURL),
).MeterImpl()
m.delegate = unsafe.Pointer(d)
for _, inst := range m.syncInsts {
inst.setDelegate(*d)
}
m.syncInsts = nil
for _, obs := range m.asyncInsts {
obs.setDelegate(*d)
}
m.asyncInsts = nil
}
func (m *meterImpl) NewSyncInstrument(desc sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
m.lock.Lock()
defer m.lock.Unlock()
if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
return (*meterPtr).NewSyncInstrument(desc)
}
inst := &syncImpl{
instrument: instrument{
descriptor: desc,
},
}
m.syncInsts = append(m.syncInsts, inst)
return inst, nil
}
// Synchronous delegation
func (inst *syncImpl) setDelegate(d sdkapi.MeterImpl) {
implPtr := new(sdkapi.SyncImpl)
var err error
*implPtr, err = d.NewSyncInstrument(inst.descriptor)
if err != nil {
// TODO: There is no standard way to deliver this error to the user.
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
// Note that the default SDK will not generate any errors yet, this is
// only for added safety.
panic(err)
}
atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr))
}
func (inst *syncImpl) Implementation() interface{} {
if implPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
return (*implPtr).Implementation()
}
return inst
}
// Async delegation
func (m *meterImpl) NewAsyncInstrument(
desc sdkapi.Descriptor,
runner sdkapi.AsyncRunner,
) (sdkapi.AsyncImpl, error) {
m.lock.Lock()
defer m.lock.Unlock()
if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
return (*meterPtr).NewAsyncInstrument(desc, runner)
}
inst := &asyncImpl{
instrument: instrument{
descriptor: desc,
},
runner: runner,
}
m.asyncInsts = append(m.asyncInsts, inst)
return inst, nil
}
func (obs *asyncImpl) Implementation() interface{} {
if implPtr := (*sdkapi.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
return (*implPtr).Implementation()
}
return obs
}
func (obs *asyncImpl) setDelegate(d sdkapi.MeterImpl) {
implPtr := new(sdkapi.AsyncImpl)
var err error
*implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner)
if err != nil {
// TODO: There is no standard way to deliver this error to the user.
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
// Note that the default SDK will not generate any errors yet, this is
// only for added safety.
panic(err)
}
atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr))
}
// Metric updates
func (m *meterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) {
if delegatePtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil {
(*delegatePtr).RecordBatch(ctx, labels, measurements...)
}
}
func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
if instPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
(*instPtr).RecordOne(ctx, number, labels)
}
}
func AtomicFieldOffsets() map[string]uintptr {
return map[string]uintptr{
"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate),
"meterImpl.delegate": unsafe.Offsetof(meterImpl{}.delegate),
"syncImpl.delegate": unsafe.Offsetof(syncImpl{}.delegate),
"asyncImpl.delegate": unsafe.Offsetof(asyncImpl{}.delegate),
}
}

View File

@@ -1,139 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package registry // import "go.opentelemetry.io/otel/internal/metric/registry"
import (
"context"
"fmt"
"sync"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// UniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
// uniqueness checking for instrument descriptors.
type UniqueInstrumentMeterImpl struct {
lock sync.Mutex
impl sdkapi.MeterImpl
state map[string]sdkapi.InstrumentImpl
}
var _ sdkapi.MeterImpl = (*UniqueInstrumentMeterImpl)(nil)
// ErrMetricKindMismatch is the standard error for mismatched metric
// instrument definitions.
var ErrMetricKindMismatch = fmt.Errorf(
"a metric was already registered by this name with another kind or number type")
// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl
// with the addition of instrument name uniqueness checking.
func NewUniqueInstrumentMeterImpl(impl sdkapi.MeterImpl) *UniqueInstrumentMeterImpl {
return &UniqueInstrumentMeterImpl{
impl: impl,
state: map[string]sdkapi.InstrumentImpl{},
}
}
// MeterImpl gives the caller access to the underlying MeterImpl
// used by this UniqueInstrumentMeterImpl.
func (u *UniqueInstrumentMeterImpl) MeterImpl() sdkapi.MeterImpl {
return u.impl
}
// RecordBatch implements sdkapi.MeterImpl.
func (u *UniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, ms ...sdkapi.Measurement) {
u.impl.RecordBatch(ctx, labels, ms...)
}
// NewMetricKindMismatchError formats an error that describes a
// mismatched metric instrument definition.
func NewMetricKindMismatchError(desc sdkapi.Descriptor) error {
return fmt.Errorf("metric %s registered as %s %s: %w",
desc.Name(),
desc.NumberKind(),
desc.InstrumentKind(),
ErrMetricKindMismatch)
}
// Compatible determines whether two sdkapi.Descriptors are considered
// the same for the purpose of uniqueness checking.
func Compatible(candidate, existing sdkapi.Descriptor) bool {
return candidate.InstrumentKind() == existing.InstrumentKind() &&
candidate.NumberKind() == existing.NumberKind()
}
// checkUniqueness returns an ErrMetricKindMismatch error if there is
// a conflict between a descriptor that was already registered and the
// `descriptor` argument. If there is an existing compatible
// registration, this returns the already-registered instrument. If
// there is no conflict and no prior registration, returns (nil, nil).
func (u *UniqueInstrumentMeterImpl) checkUniqueness(descriptor sdkapi.Descriptor) (sdkapi.InstrumentImpl, error) {
impl, ok := u.state[descriptor.Name()]
if !ok {
return nil, nil
}
if !Compatible(descriptor, impl.Descriptor()) {
return nil, NewMetricKindMismatchError(impl.Descriptor())
}
return impl, nil
}
// NewSyncInstrument implements sdkapi.MeterImpl.
func (u *UniqueInstrumentMeterImpl) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
u.lock.Lock()
defer u.lock.Unlock()
impl, err := u.checkUniqueness(descriptor)
if err != nil {
return nil, err
} else if impl != nil {
return impl.(sdkapi.SyncImpl), nil
}
syncInst, err := u.impl.NewSyncInstrument(descriptor)
if err != nil {
return nil, err
}
u.state[descriptor.Name()] = syncInst
return syncInst, nil
}
// NewAsyncInstrument implements sdkapi.MeterImpl.
func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(
descriptor sdkapi.Descriptor,
runner sdkapi.AsyncRunner,
) (sdkapi.AsyncImpl, error) {
u.lock.Lock()
defer u.lock.Unlock()
impl, err := u.checkUniqueness(descriptor)
if err != nil {
return nil, err
} else if impl != nil {
return impl.(sdkapi.AsyncImpl), nil
}
asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner)
if err != nil {
return nil, err
}
u.state[descriptor.Name()] = asyncInst
return asyncInst, nil
}

View File

@@ -19,7 +19,7 @@ import (
"unsafe"
)
func BoolToRaw(b bool) uint64 {
func BoolToRaw(b bool) uint64 { // nolint:revive // b is not a control flag.
if b {
return 1
}

View File

@@ -14,65 +14,6 @@
package metric // import "go.opentelemetry.io/otel/metric"
import (
"go.opentelemetry.io/otel/metric/unit"
)
// InstrumentConfig contains options for metric instrument descriptors.
type InstrumentConfig struct {
description string
unit unit.Unit
}
// Description describes the instrument in human-readable terms.
func (cfg *InstrumentConfig) Description() string {
return cfg.description
}
// Unit describes the measurement unit for a instrument.
func (cfg *InstrumentConfig) Unit() unit.Unit {
return cfg.unit
}
// InstrumentOption is an interface for applying metric instrument options.
type InstrumentOption interface {
// ApplyMeter is used to set a InstrumentOption value of a
// InstrumentConfig.
applyInstrument(InstrumentConfig) InstrumentConfig
}
// NewInstrumentConfig creates a new InstrumentConfig
// and applies all the given options.
func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
var config InstrumentConfig
for _, o := range opts {
config = o.applyInstrument(config)
}
return config
}
type instrumentOptionFunc func(InstrumentConfig) InstrumentConfig
func (fn instrumentOptionFunc) applyInstrument(cfg InstrumentConfig) InstrumentConfig {
return fn(cfg)
}
// WithDescription applies provided description.
func WithDescription(desc string) InstrumentOption {
return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.description = desc
return cfg
})
}
// WithUnit applies provided unit.
func WithUnit(unit unit.Unit) InstrumentOption {
return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.unit = unit
return cfg
})
}
// MeterConfig contains options for Meters.
type MeterConfig struct {
instrumentationVersion string
@@ -80,18 +21,18 @@ type MeterConfig struct {
}
// InstrumentationVersion is the version of the library providing instrumentation.
func (cfg *MeterConfig) InstrumentationVersion() string {
func (cfg MeterConfig) InstrumentationVersion() string {
return cfg.instrumentationVersion
}
// SchemaURL is the schema_url of the library providing instrumentation.
func (cfg *MeterConfig) SchemaURL() string {
func (cfg MeterConfig) SchemaURL() string {
return cfg.schemaURL
}
// MeterOption is an interface for applying Meter options.
type MeterOption interface {
// ApplyMeter is used to set a MeterOption value of a MeterConfig.
// applyMeter is used to set a MeterOption value of a MeterConfig.
applyMeter(MeterConfig) MeterConfig
}

View File

@@ -19,49 +19,5 @@ OpenTelemetry API.
This package is currently in a pre-GA phase. Backwards incompatible changes
may be introduced in subsequent minor version releases as we work to track the
evolving OpenTelemetry specification and user feedback.
Measurements can be made about an operation being performed or the state of a
system in general. These measurements can be crucial to the reliable operation
of code and provide valuable insights about the inner workings of a system.
Measurements are made using instruments provided by this package. The type of
instrument used will depend on the type of measurement being made and of what
part of a system is being measured.
Instruments are categorized as Synchronous or Asynchronous and independently
as Adding or Grouping. Synchronous instruments are called by the user with a
Context. Asynchronous instruments are called by the SDK during collection.
Adding instruments are semantically intended for capturing a sum. Grouping
instruments are intended for capturing a distribution.
Adding instruments may be monotonic, in which case they are non-decreasing
and naturally define a rate.
The synchronous instrument names are:
Counter: adding, monotonic
UpDownCounter: adding
Histogram: grouping
and the asynchronous instruments are:
CounterObserver: adding, monotonic
UpDownCounterObserver: adding
GaugeObserver: grouping
All instruments are provided with support for either float64 or int64 input
values.
An instrument is created using a Meter. Additionally, a Meter is used to
record batches of synchronous measurements or asynchronous observations. A
Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to
the instrumentation it instruments and must be named and versioned when
created with a MeterProvider with the name and version of the instrumentation
library.
Instrumentation should be designed to accept a MeterProvider from which it can
create its own unique Meter. Alternatively, the registered global
MeterProvider from the go.opentelemetry.io/otel package can be used as a
default.
*/
package metric // import "go.opentelemetry.io/otel/metric"

View File

@@ -0,0 +1,42 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package global // import "go.opentelemetry.io/otel/metric/global"
import (
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/internal/global"
)
// Meter returns a Meter from the global MeterProvider. The
// instrumentationName 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 instrumentationName is
// empty, then a implementation defined default name will be used instead.
//
// This is short for MeterProvider().Meter(name).
func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
return MeterProvider().Meter(instrumentationName, opts...)
}
// MeterProvider returns the registered global trace provider.
// If none is registered then a No-op MeterProvider is returned.
func MeterProvider() metric.MeterProvider {
return global.MeterProvider()
}
// SetMeterProvider registers `mp` as the global meter provider.
func SetMeterProvider(mp metric.MeterProvider) {
global.SetMeterProvider(mp)
}

View File

@@ -1,49 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package global // import "go.opentelemetry.io/otel/metric/global"
import (
"go.opentelemetry.io/otel/internal/metric/global"
"go.opentelemetry.io/otel/metric"
)
// Meter creates an implementation of the Meter interface from the global
// MeterProvider. The instrumentationName 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
// instrumentationName is empty, then a implementation defined default name
// will be used instead.
//
// This is short for MeterProvider().Meter(name)
func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
return GetMeterProvider().Meter(instrumentationName, opts...)
}
// GetMeterProvider returns the registered global meter provider. If
// none is registered then a default meter provider is returned that
// forwards the Meter interface to the first registered Meter.
//
// Use the meter provider to create a named meter. E.g.
// meter := global.MeterProvider().Meter("example.com/foo")
// or
// meter := global.Meter("example.com/foo")
func GetMeterProvider() metric.MeterProvider {
return global.MeterProvider()
}
// SetMeterProvider registers `mp` as the global meter provider.
func SetMeterProvider(mp metric.MeterProvider) {
global.SetMeterProvider(mp)
}

View File

@@ -0,0 +1,80 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package asyncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
//
// Warning: methods may be added to this interface in minor releases.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Gauge creates an instrument for recording the current value.
Gauge(name string, opts ...instrument.Option) (Gauge, error)
}
// Counter is an instrument that records increasing values.
//
// Warning: methods may be added to this interface in minor releases.
type Counter interface {
// Observe records the state of the instrument to be x. Implementations
// will assume x to be the cumulative sum of the count.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// UpDownCounter is an instrument that records increasing or decreasing values.
//
// Warning: methods may be added to this interface in minor releases.
type UpDownCounter interface {
// Observe records the state of the instrument to be x. Implementations
// will assume x to be the cumulative sum of the count.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// Gauge is an instrument that records independent readings.
//
// Warning: methods may be added to this interface in minor releases.
type Gauge interface {
// Observe records the state of the instrument to be x.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}

View File

@@ -0,0 +1,80 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package asyncint64 // import "go.opentelemetry.io/otel/metric/instrument/asyncint64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
//
// Warning: methods may be added to this interface in minor releases.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Gauge creates an instrument for recording the current value.
Gauge(name string, opts ...instrument.Option) (Gauge, error)
}
// Counter is an instrument that records increasing values.
//
// Warning: methods may be added to this interface in minor releases.
type Counter interface {
// Observe records the state of the instrument to be x. Implementations
// will assume x to be the cumulative sum of the count.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// UpDownCounter is an instrument that records increasing or decreasing values.
//
// Warning: methods may be added to this interface in minor releases.
type UpDownCounter interface {
// Observe records the state of the instrument to be x. Implementations
// will assume x to be the cumulative sum of the count.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// Gauge is an instrument that records independent readings.
//
// Warning: methods may be added to this interface in minor releases.
type Gauge interface {
// Observe records the state of the instrument to be x.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}

View File

@@ -0,0 +1,69 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package instrument // import "go.opentelemetry.io/otel/metric/instrument"
import "go.opentelemetry.io/otel/metric/unit"
// Config contains options for metric instrument descriptors.
type Config struct {
description string
unit unit.Unit
}
// Description describes the instrument in human-readable terms.
func (cfg Config) Description() string {
return cfg.description
}
// Unit describes the measurement unit for an instrument.
func (cfg Config) Unit() unit.Unit {
return cfg.unit
}
// Option is an interface for applying metric instrument options.
type Option interface {
applyInstrument(Config) Config
}
// NewConfig creates a new Config and applies all the given options.
func NewConfig(opts ...Option) Config {
var config Config
for _, o := range opts {
config = o.applyInstrument(config)
}
return config
}
type optionFunc func(Config) Config
func (fn optionFunc) applyInstrument(cfg Config) Config {
return fn(cfg)
}
// WithDescription applies provided description.
func WithDescription(desc string) Option {
return optionFunc(func(cfg Config) Config {
cfg.description = desc
return cfg
})
}
// WithUnit applies provided unit.
func WithUnit(u unit.Unit) Option {
return optionFunc(func(cfg Config) Config {
cfg.unit = u
return cfg
})
}

View File

@@ -0,0 +1,30 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package instrument // import "go.opentelemetry.io/otel/metric/instrument"
// Asynchronous instruments are instruments that are updated within a Callback.
// If an instrument is observed outside of it's callback it should be an error.
//
// This interface is used as a grouping mechanism.
type Asynchronous interface {
asynchronous()
}
// Synchronous instruments are updated in line with application code.
//
// This interface is used as a grouping mechanism.
type Synchronous interface {
synchronous()
}

View File

@@ -0,0 +1,64 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package syncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/syncfloat64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
//
// Warning: methods may be added to this interface in minor releases.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Histogram creates an instrument for recording a distribution of values.
Histogram(name string, opts ...instrument.Option) (Histogram, error)
}
// Counter is an instrument that records increasing values.
//
// Warning: methods may be added to this interface in minor releases.
type Counter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// UpDownCounter is an instrument that records increasing or decreasing values.
//
// Warning: methods may be added to this interface in minor releases.
type UpDownCounter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// Histogram is an instrument that records a distribution of values.
//
// Warning: methods may be added to this interface in minor releases.
type Histogram interface {
// Record adds an additional value to the distribution.
Record(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
instrument.Synchronous
}

View File

@@ -0,0 +1,64 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package syncint64 // import "go.opentelemetry.io/otel/metric/instrument/syncint64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
//
// Warning: methods may be added to this interface in minor releases.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Histogram creates an instrument for recording a distribution of values.
Histogram(name string, opts ...instrument.Option) (Histogram, error)
}
// Counter is an instrument that records increasing values.
//
// Warning: methods may be added to this interface in minor releases.
type Counter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// UpDownCounter is an instrument that records increasing or decreasing values.
//
// Warning: methods may be added to this interface in minor releases.
type UpDownCounter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// Histogram is an instrument that records a distribution of values.
//
// Warning: methods may be added to this interface in minor releases.
type Histogram interface {
// Record adds an additional value to the distribution.
Record(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
instrument.Synchronous
}

View File

@@ -0,0 +1,360 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package global // import "go.opentelemetry.io/otel/metric/internal/global"
import (
"context"
"sync/atomic"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
)
type afCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //asyncfloat64.Counter
instrument.Asynchronous
}
func (i *afCounter) setDelegate(m metric.Meter) {
ctr, err := m.AsyncFloat64().Counter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *afCounter) Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(asyncfloat64.Counter).Observe(ctx, x, attrs...)
}
}
func (i *afCounter) unwrap() instrument.Asynchronous {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(asyncfloat64.Counter)
}
return nil
}
type afUpDownCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //asyncfloat64.UpDownCounter
instrument.Asynchronous
}
func (i *afUpDownCounter) setDelegate(m metric.Meter) {
ctr, err := m.AsyncFloat64().UpDownCounter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *afUpDownCounter) Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(asyncfloat64.UpDownCounter).Observe(ctx, x, attrs...)
}
}
func (i *afUpDownCounter) unwrap() instrument.Asynchronous {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(asyncfloat64.UpDownCounter)
}
return nil
}
type afGauge struct {
name string
opts []instrument.Option
delegate atomic.Value //asyncfloat64.Gauge
instrument.Asynchronous
}
func (i *afGauge) setDelegate(m metric.Meter) {
ctr, err := m.AsyncFloat64().Gauge(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *afGauge) Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(asyncfloat64.Gauge).Observe(ctx, x, attrs...)
}
}
func (i *afGauge) unwrap() instrument.Asynchronous {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(asyncfloat64.Gauge)
}
return nil
}
type aiCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //asyncint64.Counter
instrument.Asynchronous
}
func (i *aiCounter) setDelegate(m metric.Meter) {
ctr, err := m.AsyncInt64().Counter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *aiCounter) Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(asyncint64.Counter).Observe(ctx, x, attrs...)
}
}
func (i *aiCounter) unwrap() instrument.Asynchronous {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(asyncint64.Counter)
}
return nil
}
type aiUpDownCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //asyncint64.UpDownCounter
instrument.Asynchronous
}
func (i *aiUpDownCounter) setDelegate(m metric.Meter) {
ctr, err := m.AsyncInt64().UpDownCounter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *aiUpDownCounter) Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(asyncint64.UpDownCounter).Observe(ctx, x, attrs...)
}
}
func (i *aiUpDownCounter) unwrap() instrument.Asynchronous {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(asyncint64.UpDownCounter)
}
return nil
}
type aiGauge struct {
name string
opts []instrument.Option
delegate atomic.Value //asyncint64.Gauge
instrument.Asynchronous
}
func (i *aiGauge) setDelegate(m metric.Meter) {
ctr, err := m.AsyncInt64().Gauge(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *aiGauge) Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(asyncint64.Gauge).Observe(ctx, x, attrs...)
}
}
func (i *aiGauge) unwrap() instrument.Asynchronous {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(asyncint64.Gauge)
}
return nil
}
// Sync Instruments.
type sfCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //syncfloat64.Counter
instrument.Synchronous
}
func (i *sfCounter) setDelegate(m metric.Meter) {
ctr, err := m.SyncFloat64().Counter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *sfCounter) Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(syncfloat64.Counter).Add(ctx, incr, attrs...)
}
}
type sfUpDownCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //syncfloat64.UpDownCounter
instrument.Synchronous
}
func (i *sfUpDownCounter) setDelegate(m metric.Meter) {
ctr, err := m.SyncFloat64().UpDownCounter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *sfUpDownCounter) Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(syncfloat64.UpDownCounter).Add(ctx, incr, attrs...)
}
}
type sfHistogram struct {
name string
opts []instrument.Option
delegate atomic.Value //syncfloat64.Histogram
instrument.Synchronous
}
func (i *sfHistogram) setDelegate(m metric.Meter) {
ctr, err := m.SyncFloat64().Histogram(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *sfHistogram) Record(ctx context.Context, x float64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(syncfloat64.Histogram).Record(ctx, x, attrs...)
}
}
type siCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //syncint64.Counter
instrument.Synchronous
}
func (i *siCounter) setDelegate(m metric.Meter) {
ctr, err := m.SyncInt64().Counter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *siCounter) Add(ctx context.Context, x int64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(syncint64.Counter).Add(ctx, x, attrs...)
}
}
type siUpDownCounter struct {
name string
opts []instrument.Option
delegate atomic.Value //syncint64.UpDownCounter
instrument.Synchronous
}
func (i *siUpDownCounter) setDelegate(m metric.Meter) {
ctr, err := m.SyncInt64().UpDownCounter(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *siUpDownCounter) Add(ctx context.Context, x int64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(syncint64.UpDownCounter).Add(ctx, x, attrs...)
}
}
type siHistogram struct {
name string
opts []instrument.Option
delegate atomic.Value //syncint64.Histogram
instrument.Synchronous
}
func (i *siHistogram) setDelegate(m metric.Meter) {
ctr, err := m.SyncInt64().Histogram(i.name, i.opts...)
if err != nil {
otel.Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *siHistogram) Record(ctx context.Context, x int64, attrs ...attribute.KeyValue) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(syncint64.Histogram).Record(ctx, x, attrs...)
}
}

View File

@@ -0,0 +1,347 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package global // import "go.opentelemetry.io/otel/metric/internal/global"
import (
"context"
"sync"
"sync/atomic"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
)
// meterProvider is a placeholder for a configured SDK MeterProvider.
//
// All MeterProvider functionality is forwarded to a delegate once
// configured.
type meterProvider struct {
mtx sync.Mutex
meters map[il]*meter
delegate metric.MeterProvider
}
type il struct {
name string
version string
}
// setDelegate configures p to delegate all MeterProvider functionality to
// provider.
//
// All Meters provided prior to this function call are switched out to be
// Meters provided by provider. All instruments and callbacks are recreated and
// delegated.
//
// It is guaranteed by the caller that this happens only once.
func (p *meterProvider) setDelegate(provider metric.MeterProvider) {
p.mtx.Lock()
defer p.mtx.Unlock()
p.delegate = provider
if len(p.meters) == 0 {
return
}
for _, meter := range p.meters {
meter.setDelegate(provider)
}
p.meters = nil
}
// Meter implements MeterProvider.
func (p *meterProvider) Meter(name string, opts ...metric.MeterOption) metric.Meter {
p.mtx.Lock()
defer p.mtx.Unlock()
if p.delegate != nil {
return p.delegate.Meter(name, opts...)
}
// At this moment it is guaranteed that no sdk is installed, save the meter in the meters map.
c := metric.NewMeterConfig(opts...)
key := il{
name: name,
version: c.InstrumentationVersion(),
}
if p.meters == nil {
p.meters = make(map[il]*meter)
}
if val, ok := p.meters[key]; ok {
return val
}
t := &meter{name: name, opts: opts}
p.meters[key] = t
return t
}
// meter is a placeholder for a metric.Meter.
//
// All Meter functionality is forwarded to a delegate once configured.
// Otherwise, all functionality is forwarded to a NoopMeter.
type meter struct {
name string
opts []metric.MeterOption
mtx sync.Mutex
instruments []delegatedInstrument
callbacks []delegatedCallback
delegate atomic.Value // metric.Meter
}
type delegatedInstrument interface {
setDelegate(metric.Meter)
}
// setDelegate configures m to delegate all Meter functionality to Meters
// created by provider.
//
// All subsequent calls to the Meter methods will be passed to the delegate.
//
// It is guaranteed by the caller that this happens only once.
func (m *meter) setDelegate(provider metric.MeterProvider) {
meter := provider.Meter(m.name, m.opts...)
m.delegate.Store(meter)
m.mtx.Lock()
defer m.mtx.Unlock()
for _, inst := range m.instruments {
inst.setDelegate(meter)
}
for _, callback := range m.callbacks {
callback.setDelegate(meter)
}
m.instruments = nil
m.callbacks = nil
}
// AsyncInt64 is the namespace for the Asynchronous Integer instruments.
//
// To Observe data with instruments it must be registered in a callback.
func (m *meter) AsyncInt64() asyncint64.InstrumentProvider {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.AsyncInt64()
}
return (*aiInstProvider)(m)
}
// AsyncFloat64 is the namespace for the Asynchronous Float instruments.
//
// To Observe data with instruments it must be registered in a callback.
func (m *meter) AsyncFloat64() asyncfloat64.InstrumentProvider {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.AsyncFloat64()
}
return (*afInstProvider)(m)
}
// RegisterCallback captures the function that will be called during Collect.
//
// It is only valid to call Observe within the scope of the passed function,
// and only on the instruments that were registered with this call.
func (m *meter) RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error {
if del, ok := m.delegate.Load().(metric.Meter); ok {
insts = unwrapInstruments(insts)
return del.RegisterCallback(insts, function)
}
m.mtx.Lock()
defer m.mtx.Unlock()
m.callbacks = append(m.callbacks, delegatedCallback{
instruments: insts,
function: function,
})
return nil
}
type wrapped interface {
unwrap() instrument.Asynchronous
}
func unwrapInstruments(instruments []instrument.Asynchronous) []instrument.Asynchronous {
out := make([]instrument.Asynchronous, 0, len(instruments))
for _, inst := range instruments {
if in, ok := inst.(wrapped); ok {
out = append(out, in.unwrap())
} else {
out = append(out, inst)
}
}
return out
}
// SyncInt64 is the namespace for the Synchronous Integer instruments.
func (m *meter) SyncInt64() syncint64.InstrumentProvider {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.SyncInt64()
}
return (*siInstProvider)(m)
}
// SyncFloat64 is the namespace for the Synchronous Float instruments.
func (m *meter) SyncFloat64() syncfloat64.InstrumentProvider {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.SyncFloat64()
}
return (*sfInstProvider)(m)
}
type delegatedCallback struct {
instruments []instrument.Asynchronous
function func(context.Context)
}
func (c *delegatedCallback) setDelegate(m metric.Meter) {
insts := unwrapInstruments(c.instruments)
err := m.RegisterCallback(insts, c.function)
if err != nil {
otel.Handle(err)
}
}
type afInstProvider meter
// Counter creates an instrument for recording increasing values.
func (ip *afInstProvider) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &afCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// UpDownCounter creates an instrument for recording changes of a value.
func (ip *afInstProvider) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &afUpDownCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// Gauge creates an instrument for recording the current value.
func (ip *afInstProvider) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &afGauge{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
type aiInstProvider meter
// Counter creates an instrument for recording increasing values.
func (ip *aiInstProvider) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &aiCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// UpDownCounter creates an instrument for recording changes of a value.
func (ip *aiInstProvider) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &aiUpDownCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// Gauge creates an instrument for recording the current value.
func (ip *aiInstProvider) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &aiGauge{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
type sfInstProvider meter
// Counter creates an instrument for recording increasing values.
func (ip *sfInstProvider) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &sfCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// UpDownCounter creates an instrument for recording changes of a value.
func (ip *sfInstProvider) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &sfUpDownCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// Histogram creates an instrument for recording a distribution of values.
func (ip *sfInstProvider) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &sfHistogram{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
type siInstProvider meter
// Counter creates an instrument for recording increasing values.
func (ip *siInstProvider) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &siCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// UpDownCounter creates an instrument for recording changes of a value.
func (ip *siInstProvider) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &siUpDownCounter{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}
// Histogram creates an instrument for recording a distribution of values.
func (ip *siInstProvider) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) {
ip.mtx.Lock()
defer ip.mtx.Unlock()
ctr := &siHistogram{name: name, opts: opts}
ip.instruments = append(ip.instruments, ctr)
return ctr, nil
}

View File

@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// htmp://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,55 +12,57 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package global // import "go.opentelemetry.io/otel/internal/metric/global"
package global // import "go.opentelemetry.io/otel/metric/internal/global"
import (
"errors"
"sync"
"sync/atomic"
"go.opentelemetry.io/otel/internal/global"
"go.opentelemetry.io/otel/metric"
)
var (
globalMeterProvider = defaultMeterProvider()
delegateMeterOnce sync.Once
)
type meterProviderHolder struct {
mp metric.MeterProvider
}
var (
globalMeter = defaultMeterValue()
delegateMeterOnce sync.Once
)
// MeterProvider is the internal implementation for global.MeterProvider.
func MeterProvider() metric.MeterProvider {
return globalMeter.Load().(meterProviderHolder).mp
return globalMeterProvider.Load().(meterProviderHolder).mp
}
// SetMeterProvider is the internal implementation for global.SetMeterProvider.
func SetMeterProvider(mp metric.MeterProvider) {
delegateMeterOnce.Do(func() {
current := MeterProvider()
current := MeterProvider()
if _, cOk := current.(*meterProvider); cOk {
if _, mpOk := mp.(*meterProvider); mpOk && current == mp {
// Do not assign the default delegating MeterProvider to delegate
// to itself.
global.Error(
errors.New("no delegate configured in meter provider"),
"Setting meter provider to it's current value. No delegate will be configured",
)
return
}
}
if current == mp {
// Setting the provider to the prior default is nonsense, panic.
// Panic is acceptable because we are likely still early in the
// process lifetime.
panic("invalid MeterProvider, the global instance cannot be reinstalled")
} else if def, ok := current.(*meterProvider); ok {
delegateMeterOnce.Do(func() {
if def, ok := current.(*meterProvider); ok {
def.setDelegate(mp)
}
})
globalMeter.Store(meterProviderHolder{mp: mp})
globalMeterProvider.Store(meterProviderHolder{mp: mp})
}
func defaultMeterValue() *atomic.Value {
func defaultMeterProvider() *atomic.Value {
v := &atomic.Value{}
v.Store(meterProviderHolder{mp: newMeterProvider()})
v.Store(meterProviderHolder{mp: &meterProvider{}})
return v
}
// ResetForTest restores the initial global state, for testing purposes.
func ResetForTest() {
globalMeter = defaultMeterValue()
delegateMeterOnce = sync.Once{}
}

64
vendor/go.opentelemetry.io/otel/metric/meter.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric // import "go.opentelemetry.io/otel/metric"
import (
"context"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
)
// MeterProvider provides access to named Meter instances, for instrumenting
// an application or library.
//
// Warning: methods may be added to this interface in minor releases.
type MeterProvider interface {
// Meter creates an instance of a `Meter` interface. The instrumentationName
// 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 instrumentationName is empty, then a
// implementation defined default name will be used instead.
Meter(instrumentationName string, opts ...MeterOption) Meter
}
// Meter provides access to instrument instances for recording metrics.
//
// Warning: methods may be added to this interface in minor releases.
type Meter interface {
// AsyncInt64 is the namespace for the Asynchronous Integer instruments.
//
// To Observe data with instruments it must be registered in a callback.
AsyncInt64() asyncint64.InstrumentProvider
// AsyncFloat64 is the namespace for the Asynchronous Float instruments
//
// To Observe data with instruments it must be registered in a callback.
AsyncFloat64() asyncfloat64.InstrumentProvider
// RegisterCallback captures the function that will be called during Collect.
//
// It is only valid to call Observe within the scope of the passed function,
// and only on the instruments that were registered with this call.
RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error
// SyncInt64 is the namespace for the Synchronous Integer instruments
SyncInt64() syncint64.InstrumentProvider
// SyncFloat64 is the namespace for the Synchronous Float instruments
SyncFloat64() syncfloat64.InstrumentProvider
}

View File

@@ -1,538 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric // import "go.opentelemetry.io/otel/metric"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// MeterProvider supports named Meter instances.
type MeterProvider interface {
// Meter creates an implementation of the Meter interface.
// The instrumentationName 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
// instrumentationName is empty, then a implementation defined default
// name will be used instead.
Meter(instrumentationName string, opts ...MeterOption) Meter
}
// Meter is the creator of metric instruments.
//
// An uninitialized Meter is a no-op implementation.
type Meter struct {
impl sdkapi.MeterImpl
}
// WrapMeterImpl constructs a `Meter` implementation from a
// `MeterImpl` implementation.
func WrapMeterImpl(impl sdkapi.MeterImpl) Meter {
return Meter{
impl: impl,
}
}
// Measurement is used for reporting a synchronous batch of metric
// values. Instances of this type should be created by synchronous
// instruments (e.g., Int64Counter.Measurement()).
//
// Note: This is an alias because it is a first-class member of the
// API but is also part of the lower-level sdkapi interface.
type Measurement = sdkapi.Measurement
// Observation is used for reporting an asynchronous batch of metric
// values. Instances of this type should be created by asynchronous
// instruments (e.g., Int64GaugeObserver.Observation()).
//
// Note: This is an alias because it is a first-class member of the
// API but is also part of the lower-level sdkapi interface.
type Observation = sdkapi.Observation
// RecordBatch atomically records a batch of measurements.
func (m Meter) RecordBatch(ctx context.Context, ls []attribute.KeyValue, ms ...Measurement) {
if m.impl == nil {
return
}
m.impl.RecordBatch(ctx, ls, ms...)
}
// NewBatchObserver creates a new BatchObserver that supports
// making batches of observations for multiple instruments.
func (m Meter) NewBatchObserver(callback BatchObserverFunc) BatchObserver {
return BatchObserver{
meter: m,
runner: newBatchAsyncRunner(callback),
}
}
// NewInt64Counter creates a new integer Counter instrument with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewInt64Counter(name string, options ...InstrumentOption) (Int64Counter, error) {
return wrapInt64CounterInstrument(
m.newSync(name, sdkapi.CounterInstrumentKind, number.Int64Kind, options))
}
// NewFloat64Counter creates a new floating point Counter with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewFloat64Counter(name string, options ...InstrumentOption) (Float64Counter, error) {
return wrapFloat64CounterInstrument(
m.newSync(name, sdkapi.CounterInstrumentKind, number.Float64Kind, options))
}
// NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewInt64UpDownCounter(name string, options ...InstrumentOption) (Int64UpDownCounter, error) {
return wrapInt64UpDownCounterInstrument(
m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Int64Kind, options))
}
// NewFloat64UpDownCounter creates a new floating point UpDownCounter with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewFloat64UpDownCounter(name string, options ...InstrumentOption) (Float64UpDownCounter, error) {
return wrapFloat64UpDownCounterInstrument(
m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Float64Kind, options))
}
// NewInt64Histogram creates a new integer Histogram instrument with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewInt64Histogram(name string, opts ...InstrumentOption) (Int64Histogram, error) {
return wrapInt64HistogramInstrument(
m.newSync(name, sdkapi.HistogramInstrumentKind, number.Int64Kind, opts))
}
// NewFloat64Histogram creates a new floating point Histogram with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewFloat64Histogram(name string, opts ...InstrumentOption) (Float64Histogram, error) {
return wrapFloat64HistogramInstrument(
m.newSync(name, sdkapi.HistogramInstrumentKind, number.Float64Kind, opts))
}
// NewInt64GaugeObserver creates a new integer GaugeObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64GaugeObserver, error) {
if callback == nil {
return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64GaugeObserverInstrument(
m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts,
newInt64AsyncRunner(callback)))
}
// NewFloat64GaugeObserver creates a new floating point GaugeObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64GaugeObserver, error) {
if callback == nil {
return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64GaugeObserverInstrument(
m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts,
newFloat64AsyncRunner(callback)))
}
// NewInt64CounterObserver creates a new integer CounterObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewInt64CounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64CounterObserver, error) {
if callback == nil {
return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64CounterObserverInstrument(
m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts,
newInt64AsyncRunner(callback)))
}
// NewFloat64CounterObserver creates a new floating point CounterObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64CounterObserver, error) {
if callback == nil {
return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64CounterObserverInstrument(
m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts,
newFloat64AsyncRunner(callback)))
}
// NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) {
if callback == nil {
return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64UpDownCounterObserverInstrument(
m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts,
newInt64AsyncRunner(callback)))
}
// NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) {
if callback == nil {
return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64UpDownCounterObserverInstrument(
m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts,
newFloat64AsyncRunner(callback)))
}
// NewInt64GaugeObserver creates a new integer GaugeObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewInt64GaugeObserver(name string, opts ...InstrumentOption) (Int64GaugeObserver, error) {
if b.runner == nil {
return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64GaugeObserverInstrument(
b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts, b.runner))
}
// NewFloat64GaugeObserver creates a new floating point GaugeObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewFloat64GaugeObserver(name string, opts ...InstrumentOption) (Float64GaugeObserver, error) {
if b.runner == nil {
return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64GaugeObserverInstrument(
b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts,
b.runner))
}
// NewInt64CounterObserver creates a new integer CounterObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewInt64CounterObserver(name string, opts ...InstrumentOption) (Int64CounterObserver, error) {
if b.runner == nil {
return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64CounterObserverInstrument(
b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts, b.runner))
}
// NewFloat64CounterObserver creates a new floating point CounterObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewFloat64CounterObserver(name string, opts ...InstrumentOption) (Float64CounterObserver, error) {
if b.runner == nil {
return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64CounterObserverInstrument(
b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts,
b.runner))
}
// NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewInt64UpDownCounterObserver(name string, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) {
if b.runner == nil {
return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64UpDownCounterObserverInstrument(
b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts, b.runner))
}
// NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewFloat64UpDownCounterObserver(name string, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) {
if b.runner == nil {
return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64UpDownCounterObserverInstrument(
b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts,
b.runner))
}
// MeterImpl returns the underlying MeterImpl of this Meter.
func (m Meter) MeterImpl() sdkapi.MeterImpl {
return m.impl
}
// newAsync constructs one new asynchronous instrument.
func (m Meter) newAsync(
name string,
mkind sdkapi.InstrumentKind,
nkind number.Kind,
opts []InstrumentOption,
runner sdkapi.AsyncRunner,
) (
sdkapi.AsyncImpl,
error,
) {
if m.impl == nil {
return sdkapi.NewNoopAsyncInstrument(), nil
}
cfg := NewInstrumentConfig(opts...)
desc := sdkapi.NewDescriptor(name, mkind, nkind, cfg.description, cfg.unit)
return m.impl.NewAsyncInstrument(desc, runner)
}
// newSync constructs one new synchronous instrument.
func (m Meter) newSync(
name string,
metricKind sdkapi.InstrumentKind,
numberKind number.Kind,
opts []InstrumentOption,
) (
sdkapi.SyncImpl,
error,
) {
if m.impl == nil {
return sdkapi.NewNoopSyncInstrument(), nil
}
cfg := NewInstrumentConfig(opts...)
desc := sdkapi.NewDescriptor(name, metricKind, numberKind, cfg.description, cfg.unit)
return m.impl.NewSyncInstrument(desc)
}
// MeterMust is a wrapper for Meter interfaces that panics when any
// instrument constructor encounters an error.
type MeterMust struct {
meter Meter
}
// BatchObserverMust is a wrapper for BatchObserver that panics when
// any instrument constructor encounters an error.
type BatchObserverMust struct {
batch BatchObserver
}
// Must constructs a MeterMust implementation from a Meter, allowing
// the application to panic when any instrument constructor yields an
// error.
func Must(meter Meter) MeterMust {
return MeterMust{meter: meter}
}
// NewInt64Counter calls `Meter.NewInt64Counter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64Counter(name string, cos ...InstrumentOption) Int64Counter {
if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64Counter(name string, cos ...InstrumentOption) Float64Counter {
if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...InstrumentOption) Int64UpDownCounter {
if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...InstrumentOption) Float64UpDownCounter {
if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64Histogram calls `Meter.NewInt64Histogram` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64Histogram(name string, mos ...InstrumentOption) Int64Histogram {
if inst, err := mm.meter.NewInt64Histogram(name, mos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64Histogram calls `Meter.NewFloat64Histogram` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64Histogram(name string, mos ...InstrumentOption) Float64Histogram {
if inst, err := mm.meter.NewFloat64Histogram(name, mos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64GaugeObserver calls `Meter.NewInt64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64GaugeObserver {
if inst, err := mm.meter.NewInt64GaugeObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64GaugeObserver calls `Meter.NewFloat64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64GaugeObserver {
if inst, err := mm.meter.NewFloat64GaugeObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64CounterObserver calls `Meter.NewInt64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64CounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64CounterObserver {
if inst, err := mm.meter.NewInt64CounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64CounterObserver calls `Meter.NewFloat64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64CounterObserver {
if inst, err := mm.meter.NewFloat64CounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64UpDownCounterObserver calls `Meter.NewInt64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64UpDownCounterObserver {
if inst, err := mm.meter.NewInt64UpDownCounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64UpDownCounterObserver calls `Meter.NewFloat64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64UpDownCounterObserver {
if inst, err := mm.meter.NewFloat64UpDownCounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewBatchObserver returns a wrapper around BatchObserver that panics
// when any instrument constructor returns an error.
func (mm MeterMust) NewBatchObserver(callback BatchObserverFunc) BatchObserverMust {
return BatchObserverMust{
batch: mm.meter.NewBatchObserver(callback),
}
}
// NewInt64GaugeObserver calls `BatchObserver.NewInt64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewInt64GaugeObserver(name string, oos ...InstrumentOption) Int64GaugeObserver {
if inst, err := bm.batch.NewInt64GaugeObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64GaugeObserver calls `BatchObserver.NewFloat64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewFloat64GaugeObserver(name string, oos ...InstrumentOption) Float64GaugeObserver {
if inst, err := bm.batch.NewFloat64GaugeObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64CounterObserver calls `BatchObserver.NewInt64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewInt64CounterObserver(name string, oos ...InstrumentOption) Int64CounterObserver {
if inst, err := bm.batch.NewInt64CounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64CounterObserver calls `BatchObserver.NewFloat64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewFloat64CounterObserver(name string, oos ...InstrumentOption) Float64CounterObserver {
if inst, err := bm.batch.NewFloat64CounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64UpDownCounterObserver calls `BatchObserver.NewInt64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewInt64UpDownCounterObserver(name string, oos ...InstrumentOption) Int64UpDownCounterObserver {
if inst, err := bm.batch.NewInt64UpDownCounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64UpDownCounterObserver calls `BatchObserver.NewFloat64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewFloat64UpDownCounterObserver(name string, oos ...InstrumentOption) Float64UpDownCounterObserver {
if inst, err := bm.batch.NewFloat64UpDownCounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}

View File

@@ -1,464 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric // import "go.opentelemetry.io/otel/metric"
import (
"context"
"errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// ErrSDKReturnedNilImpl is returned when a new `MeterImpl` returns nil.
var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation")
// Int64ObserverFunc is a type of callback that integral
// observers run.
type Int64ObserverFunc func(context.Context, Int64ObserverResult)
// Float64ObserverFunc is a type of callback that floating point
// observers run.
type Float64ObserverFunc func(context.Context, Float64ObserverResult)
// BatchObserverFunc is a callback argument for use with any
// Observer instrument that will be reported as a batch of
// observations.
type BatchObserverFunc func(context.Context, BatchObserverResult)
// Int64ObserverResult is passed to an observer callback to capture
// observations for one asynchronous integer metric instrument.
type Int64ObserverResult struct {
instrument sdkapi.AsyncImpl
function func([]attribute.KeyValue, ...Observation)
}
// Float64ObserverResult is passed to an observer callback to capture
// observations for one asynchronous floating point metric instrument.
type Float64ObserverResult struct {
instrument sdkapi.AsyncImpl
function func([]attribute.KeyValue, ...Observation)
}
// BatchObserverResult is passed to a batch observer callback to
// capture observations for multiple asynchronous instruments.
type BatchObserverResult struct {
function func([]attribute.KeyValue, ...Observation)
}
// Observe captures a single integer value from the associated
// instrument callback, with the given labels.
func (ir Int64ObserverResult) Observe(value int64, labels ...attribute.KeyValue) {
ir.function(labels, sdkapi.NewObservation(ir.instrument, number.NewInt64Number(value)))
}
// Observe captures a single floating point value from the associated
// instrument callback, with the given labels.
func (fr Float64ObserverResult) Observe(value float64, labels ...attribute.KeyValue) {
fr.function(labels, sdkapi.NewObservation(fr.instrument, number.NewFloat64Number(value)))
}
// Observe captures a multiple observations from the associated batch
// instrument callback, with the given labels.
func (br BatchObserverResult) Observe(labels []attribute.KeyValue, obs ...Observation) {
br.function(labels, obs...)
}
var _ sdkapi.AsyncSingleRunner = (*Int64ObserverFunc)(nil)
var _ sdkapi.AsyncSingleRunner = (*Float64ObserverFunc)(nil)
var _ sdkapi.AsyncBatchRunner = (*BatchObserverFunc)(nil)
// newInt64AsyncRunner returns a single-observer callback for integer Observer instruments.
func newInt64AsyncRunner(c Int64ObserverFunc) sdkapi.AsyncSingleRunner {
return &c
}
// newFloat64AsyncRunner returns a single-observer callback for floating point Observer instruments.
func newFloat64AsyncRunner(c Float64ObserverFunc) sdkapi.AsyncSingleRunner {
return &c
}
// newBatchAsyncRunner returns a batch-observer callback use with multiple Observer instruments.
func newBatchAsyncRunner(c BatchObserverFunc) sdkapi.AsyncBatchRunner {
return &c
}
// AnyRunner implements AsyncRunner.
func (*Int64ObserverFunc) AnyRunner() {}
// AnyRunner implements AsyncRunner.
func (*Float64ObserverFunc) AnyRunner() {}
// AnyRunner implements AsyncRunner.
func (*BatchObserverFunc) AnyRunner() {}
// Run implements AsyncSingleRunner.
func (i *Int64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
(*i)(ctx, Int64ObserverResult{
instrument: impl,
function: function,
})
}
// Run implements AsyncSingleRunner.
func (f *Float64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
(*f)(ctx, Float64ObserverResult{
instrument: impl,
function: function,
})
}
// Run implements AsyncBatchRunner.
func (b *BatchObserverFunc) Run(ctx context.Context, function func([]attribute.KeyValue, ...Observation)) {
(*b)(ctx, BatchObserverResult{
function: function,
})
}
// wrapInt64GaugeObserverInstrument converts an AsyncImpl into Int64GaugeObserver.
func wrapInt64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64GaugeObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64GaugeObserver{asyncInstrument: common}, err
}
// wrapFloat64GaugeObserverInstrument converts an AsyncImpl into Float64GaugeObserver.
func wrapFloat64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64GaugeObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64GaugeObserver{asyncInstrument: common}, err
}
// wrapInt64CounterObserverInstrument converts an AsyncImpl into Int64CounterObserver.
func wrapInt64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64CounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64CounterObserver{asyncInstrument: common}, err
}
// wrapFloat64CounterObserverInstrument converts an AsyncImpl into Float64CounterObserver.
func wrapFloat64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64CounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64CounterObserver{asyncInstrument: common}, err
}
// wrapInt64UpDownCounterObserverInstrument converts an AsyncImpl into Int64UpDownCounterObserver.
func wrapInt64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64UpDownCounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64UpDownCounterObserver{asyncInstrument: common}, err
}
// wrapFloat64UpDownCounterObserverInstrument converts an AsyncImpl into Float64UpDownCounterObserver.
func wrapFloat64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64UpDownCounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64UpDownCounterObserver{asyncInstrument: common}, err
}
// BatchObserver represents an Observer callback that can report
// observations for multiple instruments.
type BatchObserver struct {
meter Meter
runner sdkapi.AsyncBatchRunner
}
// Int64GaugeObserver is a metric that captures a set of int64 values at a
// point in time.
type Int64GaugeObserver struct {
asyncInstrument
}
// Float64GaugeObserver is a metric that captures a set of float64 values
// at a point in time.
type Float64GaugeObserver struct {
asyncInstrument
}
// Int64CounterObserver is a metric that captures a precomputed sum of
// int64 values at a point in time.
type Int64CounterObserver struct {
asyncInstrument
}
// Float64CounterObserver is a metric that captures a precomputed sum of
// float64 values at a point in time.
type Float64CounterObserver struct {
asyncInstrument
}
// Int64UpDownCounterObserver is a metric that captures a precomputed sum of
// int64 values at a point in time.
type Int64UpDownCounterObserver struct {
asyncInstrument
}
// Float64UpDownCounterObserver is a metric that captures a precomputed sum of
// float64 values at a point in time.
type Float64UpDownCounterObserver struct {
asyncInstrument
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (i Int64GaugeObserver) Observation(v int64) Observation {
return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (f Float64GaugeObserver) Observation(v float64) Observation {
return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (i Int64CounterObserver) Observation(v int64) Observation {
return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (f Float64CounterObserver) Observation(v float64) Observation {
return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (i Int64UpDownCounterObserver) Observation(v int64) Observation {
return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (f Float64UpDownCounterObserver) Observation(v float64) Observation {
return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
}
// syncInstrument contains a SyncImpl.
type syncInstrument struct {
instrument sdkapi.SyncImpl
}
// asyncInstrument contains a AsyncImpl.
type asyncInstrument struct {
instrument sdkapi.AsyncImpl
}
// AsyncImpl implements AsyncImpl.
func (a asyncInstrument) AsyncImpl() sdkapi.AsyncImpl {
return a.instrument
}
// SyncImpl returns the implementation object for synchronous instruments.
func (s syncInstrument) SyncImpl() sdkapi.SyncImpl {
return s.instrument
}
func (s syncInstrument) float64Measurement(value float64) Measurement {
return sdkapi.NewMeasurement(s.instrument, number.NewFloat64Number(value))
}
func (s syncInstrument) int64Measurement(value int64) Measurement {
return sdkapi.NewMeasurement(s.instrument, number.NewInt64Number(value))
}
func (s syncInstrument) directRecord(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
s.instrument.RecordOne(ctx, number, labels)
}
// checkNewAsync receives an AsyncImpl and potential
// error, and returns the same types, checking for and ensuring that
// the returned interface is not nil.
func checkNewAsync(instrument sdkapi.AsyncImpl, err error) (asyncInstrument, error) {
if instrument == nil {
if err == nil {
err = ErrSDKReturnedNilImpl
}
instrument = sdkapi.NewNoopAsyncInstrument()
}
return asyncInstrument{
instrument: instrument,
}, err
}
// checkNewSync receives an SyncImpl and potential
// error, and returns the same types, checking for and ensuring that
// the returned interface is not nil.
func checkNewSync(instrument sdkapi.SyncImpl, err error) (syncInstrument, error) {
if instrument == nil {
if err == nil {
err = ErrSDKReturnedNilImpl
}
// Note: an alternate behavior would be to synthesize a new name
// or group all duplicately-named instruments of a certain type
// together and use a tag for the original name, e.g.,
// name = 'invalid.counter.int64'
// label = 'original-name=duplicate-counter-name'
instrument = sdkapi.NewNoopSyncInstrument()
}
return syncInstrument{
instrument: instrument,
}, err
}
// wrapInt64CounterInstrument converts a SyncImpl into Int64Counter.
func wrapInt64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Counter, error) {
common, err := checkNewSync(syncInst, err)
return Int64Counter{syncInstrument: common}, err
}
// wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter.
func wrapFloat64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Counter, error) {
common, err := checkNewSync(syncInst, err)
return Float64Counter{syncInstrument: common}, err
}
// wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter.
func wrapInt64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64UpDownCounter, error) {
common, err := checkNewSync(syncInst, err)
return Int64UpDownCounter{syncInstrument: common}, err
}
// wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter.
func wrapFloat64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64UpDownCounter, error) {
common, err := checkNewSync(syncInst, err)
return Float64UpDownCounter{syncInstrument: common}, err
}
// wrapInt64HistogramInstrument converts a SyncImpl into Int64Histogram.
func wrapInt64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Histogram, error) {
common, err := checkNewSync(syncInst, err)
return Int64Histogram{syncInstrument: common}, err
}
// wrapFloat64HistogramInstrument converts a SyncImpl into Float64Histogram.
func wrapFloat64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Histogram, error) {
common, err := checkNewSync(syncInst, err)
return Float64Histogram{syncInstrument: common}, err
}
// Float64Counter is a metric that accumulates float64 values.
type Float64Counter struct {
syncInstrument
}
// Int64Counter is a metric that accumulates int64 values.
type Int64Counter struct {
syncInstrument
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Float64Counter) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Int64Counter) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Float64Counter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewFloat64Number(value), labels)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Int64Counter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewInt64Number(value), labels)
}
// Float64UpDownCounter is a metric instrument that sums floating
// point values.
type Float64UpDownCounter struct {
syncInstrument
}
// Int64UpDownCounter is a metric instrument that sums integer values.
type Int64UpDownCounter struct {
syncInstrument
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Float64UpDownCounter) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Int64UpDownCounter) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewFloat64Number(value), labels)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewInt64Number(value), labels)
}
// Float64Histogram is a metric that records float64 values.
type Float64Histogram struct {
syncInstrument
}
// Int64Histogram is a metric that records int64 values.
type Int64Histogram struct {
syncInstrument
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Float64Histogram) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Int64Histogram) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}
// Record adds a new value to the list of Histogram's records. The
// labels should contain the keys and values to be associated with
// this value.
func (c Float64Histogram) Record(ctx context.Context, value float64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewFloat64Number(value), labels)
}
// Record adds a new value to the Histogram's distribution. The
// labels should contain the keys and values to be associated with
// this value.
func (c Int64Histogram) Record(ctx context.Context, value int64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewInt64Number(value), labels)
}

View File

@@ -14,17 +14,168 @@
package metric // import "go.opentelemetry.io/otel/metric"
type noopMeterProvider struct{}
import (
"context"
// NewNoopMeterProvider returns an implementation of MeterProvider that
// performs no operations. The Meter and Instrument created from the returned
// MeterProvider also perform no operations.
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
)
// NewNoopMeterProvider creates a MeterProvider that does not record any metrics.
func NewNoopMeterProvider() MeterProvider {
return noopMeterProvider{}
}
var _ MeterProvider = noopMeterProvider{}
type noopMeterProvider struct{}
func (noopMeterProvider) Meter(instrumentationName string, opts ...MeterOption) Meter {
return Meter{}
func (noopMeterProvider) Meter(string, ...MeterOption) Meter {
return noopMeter{}
}
// NewNoopMeter creates a Meter that does not record any metrics.
func NewNoopMeter() Meter {
return noopMeter{}
}
type noopMeter struct{}
// AsyncInt64 creates an instrument that does not record any metrics.
func (noopMeter) AsyncInt64() asyncint64.InstrumentProvider {
return nonrecordingAsyncInt64Instrument{}
}
// AsyncFloat64 creates an instrument that does not record any metrics.
func (noopMeter) AsyncFloat64() asyncfloat64.InstrumentProvider {
return nonrecordingAsyncFloat64Instrument{}
}
// SyncInt64 creates an instrument that does not record any metrics.
func (noopMeter) SyncInt64() syncint64.InstrumentProvider {
return nonrecordingSyncInt64Instrument{}
}
// SyncFloat64 creates an instrument that does not record any metrics.
func (noopMeter) SyncFloat64() syncfloat64.InstrumentProvider {
return nonrecordingSyncFloat64Instrument{}
}
// RegisterCallback creates a register callback that does not record any metrics.
func (noopMeter) RegisterCallback([]instrument.Asynchronous, func(context.Context)) error {
return nil
}
type nonrecordingAsyncFloat64Instrument struct {
instrument.Asynchronous
}
var (
_ asyncfloat64.InstrumentProvider = nonrecordingAsyncFloat64Instrument{}
_ asyncfloat64.Counter = nonrecordingAsyncFloat64Instrument{}
_ asyncfloat64.UpDownCounter = nonrecordingAsyncFloat64Instrument{}
_ asyncfloat64.Gauge = nonrecordingAsyncFloat64Instrument{}
)
func (n nonrecordingAsyncFloat64Instrument) Counter(string, ...instrument.Option) (asyncfloat64.Counter, error) {
return n, nil
}
func (n nonrecordingAsyncFloat64Instrument) UpDownCounter(string, ...instrument.Option) (asyncfloat64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingAsyncFloat64Instrument) Gauge(string, ...instrument.Option) (asyncfloat64.Gauge, error) {
return n, nil
}
func (nonrecordingAsyncFloat64Instrument) Observe(context.Context, float64, ...attribute.KeyValue) {
}
type nonrecordingAsyncInt64Instrument struct {
instrument.Asynchronous
}
var (
_ asyncint64.InstrumentProvider = nonrecordingAsyncInt64Instrument{}
_ asyncint64.Counter = nonrecordingAsyncInt64Instrument{}
_ asyncint64.UpDownCounter = nonrecordingAsyncInt64Instrument{}
_ asyncint64.Gauge = nonrecordingAsyncInt64Instrument{}
)
func (n nonrecordingAsyncInt64Instrument) Counter(string, ...instrument.Option) (asyncint64.Counter, error) {
return n, nil
}
func (n nonrecordingAsyncInt64Instrument) UpDownCounter(string, ...instrument.Option) (asyncint64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingAsyncInt64Instrument) Gauge(string, ...instrument.Option) (asyncint64.Gauge, error) {
return n, nil
}
func (nonrecordingAsyncInt64Instrument) Observe(context.Context, int64, ...attribute.KeyValue) {
}
type nonrecordingSyncFloat64Instrument struct {
instrument.Synchronous
}
var (
_ syncfloat64.InstrumentProvider = nonrecordingSyncFloat64Instrument{}
_ syncfloat64.Counter = nonrecordingSyncFloat64Instrument{}
_ syncfloat64.UpDownCounter = nonrecordingSyncFloat64Instrument{}
_ syncfloat64.Histogram = nonrecordingSyncFloat64Instrument{}
)
func (n nonrecordingSyncFloat64Instrument) Counter(string, ...instrument.Option) (syncfloat64.Counter, error) {
return n, nil
}
func (n nonrecordingSyncFloat64Instrument) UpDownCounter(string, ...instrument.Option) (syncfloat64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingSyncFloat64Instrument) Histogram(string, ...instrument.Option) (syncfloat64.Histogram, error) {
return n, nil
}
func (nonrecordingSyncFloat64Instrument) Add(context.Context, float64, ...attribute.KeyValue) {
}
func (nonrecordingSyncFloat64Instrument) Record(context.Context, float64, ...attribute.KeyValue) {
}
type nonrecordingSyncInt64Instrument struct {
instrument.Synchronous
}
var (
_ syncint64.InstrumentProvider = nonrecordingSyncInt64Instrument{}
_ syncint64.Counter = nonrecordingSyncInt64Instrument{}
_ syncint64.UpDownCounter = nonrecordingSyncInt64Instrument{}
_ syncint64.Histogram = nonrecordingSyncInt64Instrument{}
)
func (n nonrecordingSyncInt64Instrument) Counter(string, ...instrument.Option) (syncint64.Counter, error) {
return n, nil
}
func (n nonrecordingSyncInt64Instrument) UpDownCounter(string, ...instrument.Option) (syncint64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingSyncInt64Instrument) Histogram(string, ...instrument.Option) (syncint64.Histogram, error) {
return n, nil
}
func (nonrecordingSyncInt64Instrument) Add(context.Context, int64, ...attribute.KeyValue) {
}
func (nonrecordingSyncInt64Instrument) Record(context.Context, int64, ...attribute.KeyValue) {
}

View File

@@ -1,24 +0,0 @@
// Code generated by "stringer -type=Kind"; DO NOT EDIT.
package number
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Int64Kind-0]
_ = x[Float64Kind-1]
}
const _Kind_name = "Int64KindFloat64Kind"
var _Kind_index = [...]uint8{0, 9, 20}
func (i Kind) String() string {
if i < 0 || i >= Kind(len(_Kind_index)-1) {
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
}

View File

@@ -1,538 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package number // import "go.opentelemetry.io/otel/metric/number"
//go:generate stringer -type=Kind
import (
"fmt"
"math"
"sync/atomic"
"go.opentelemetry.io/otel/internal"
)
// Kind describes the data type of the Number.
type Kind int8
const (
// Int64Kind means that the Number stores int64.
Int64Kind Kind = iota
// Float64Kind means that the Number stores float64.
Float64Kind
)
// Zero returns a zero value for a given Kind
func (k Kind) Zero() Number {
switch k {
case Int64Kind:
return NewInt64Number(0)
case Float64Kind:
return NewFloat64Number(0.)
default:
return Number(0)
}
}
// Minimum returns the minimum representable value
// for a given Kind
func (k Kind) Minimum() Number {
switch k {
case Int64Kind:
return NewInt64Number(math.MinInt64)
case Float64Kind:
return NewFloat64Number(-1. * math.MaxFloat64)
default:
return Number(0)
}
}
// Maximum returns the maximum representable value
// for a given Kind
func (k Kind) Maximum() Number {
switch k {
case Int64Kind:
return NewInt64Number(math.MaxInt64)
case Float64Kind:
return NewFloat64Number(math.MaxFloat64)
default:
return Number(0)
}
}
// Number represents either an integral or a floating point value. It
// needs to be accompanied with a source of Kind that describes
// the actual type of the value stored within Number.
type Number uint64
// - constructors
// NewNumberFromRaw creates a new Number from a raw value.
func NewNumberFromRaw(r uint64) Number {
return Number(r)
}
// NewInt64Number creates an integral Number.
func NewInt64Number(i int64) Number {
return NewNumberFromRaw(internal.Int64ToRaw(i))
}
// NewFloat64Number creates a floating point Number.
func NewFloat64Number(f float64) Number {
return NewNumberFromRaw(internal.Float64ToRaw(f))
}
// NewNumberSignChange returns a number with the same magnitude and
// the opposite sign. `kind` must describe the kind of number in `nn`.
func NewNumberSignChange(kind Kind, nn Number) Number {
switch kind {
case Int64Kind:
return NewInt64Number(-nn.AsInt64())
case Float64Kind:
return NewFloat64Number(-nn.AsFloat64())
}
return nn
}
// - as x
// AsNumber gets the Number.
func (n *Number) AsNumber() Number {
return *n
}
// AsRaw gets the uninterpreted raw value. Might be useful for some
// atomic operations.
func (n *Number) AsRaw() uint64 {
return uint64(*n)
}
// AsInt64 assumes that the value contains an int64 and returns it as
// such.
func (n *Number) AsInt64() int64 {
return internal.RawToInt64(n.AsRaw())
}
// AsFloat64 assumes that the measurement value contains a float64 and
// returns it as such.
func (n *Number) AsFloat64() float64 {
return internal.RawToFloat64(n.AsRaw())
}
// - as x atomic
// AsNumberAtomic gets the Number atomically.
func (n *Number) AsNumberAtomic() Number {
return NewNumberFromRaw(n.AsRawAtomic())
}
// AsRawAtomic gets the uninterpreted raw value atomically. Might be
// useful for some atomic operations.
func (n *Number) AsRawAtomic() uint64 {
return atomic.LoadUint64(n.AsRawPtr())
}
// AsInt64Atomic assumes that the number contains an int64 and returns
// it as such atomically.
func (n *Number) AsInt64Atomic() int64 {
return atomic.LoadInt64(n.AsInt64Ptr())
}
// AsFloat64Atomic assumes that the measurement value contains a
// float64 and returns it as such atomically.
func (n *Number) AsFloat64Atomic() float64 {
return internal.RawToFloat64(n.AsRawAtomic())
}
// - as x ptr
// AsRawPtr gets the pointer to the raw, uninterpreted raw
// value. Might be useful for some atomic operations.
func (n *Number) AsRawPtr() *uint64 {
return (*uint64)(n)
}
// AsInt64Ptr assumes that the number contains an int64 and returns a
// pointer to it.
func (n *Number) AsInt64Ptr() *int64 {
return internal.RawPtrToInt64Ptr(n.AsRawPtr())
}
// AsFloat64Ptr assumes that the number contains a float64 and returns a
// pointer to it.
func (n *Number) AsFloat64Ptr() *float64 {
return internal.RawPtrToFloat64Ptr(n.AsRawPtr())
}
// - coerce
// CoerceToInt64 casts the number to int64. May result in
// data/precision loss.
func (n *Number) CoerceToInt64(kind Kind) int64 {
switch kind {
case Int64Kind:
return n.AsInt64()
case Float64Kind:
return int64(n.AsFloat64())
default:
// you get what you deserve
return 0
}
}
// CoerceToFloat64 casts the number to float64. May result in
// data/precision loss.
func (n *Number) CoerceToFloat64(kind Kind) float64 {
switch kind {
case Int64Kind:
return float64(n.AsInt64())
case Float64Kind:
return n.AsFloat64()
default:
// you get what you deserve
return 0
}
}
// - set
// SetNumber sets the number to the passed number. Both should be of
// the same kind.
func (n *Number) SetNumber(nn Number) {
*n.AsRawPtr() = nn.AsRaw()
}
// SetRaw sets the number to the passed raw value. Both number and the
// raw number should represent the same kind.
func (n *Number) SetRaw(r uint64) {
*n.AsRawPtr() = r
}
// SetInt64 assumes that the number contains an int64 and sets it to
// the passed value.
func (n *Number) SetInt64(i int64) {
*n.AsInt64Ptr() = i
}
// SetFloat64 assumes that the number contains a float64 and sets it
// to the passed value.
func (n *Number) SetFloat64(f float64) {
*n.AsFloat64Ptr() = f
}
// - set atomic
// SetNumberAtomic sets the number to the passed number
// atomically. Both should be of the same kind.
func (n *Number) SetNumberAtomic(nn Number) {
atomic.StoreUint64(n.AsRawPtr(), nn.AsRaw())
}
// SetRawAtomic sets the number to the passed raw value
// atomically. Both number and the raw number should represent the
// same kind.
func (n *Number) SetRawAtomic(r uint64) {
atomic.StoreUint64(n.AsRawPtr(), r)
}
// SetInt64Atomic assumes that the number contains an int64 and sets
// it to the passed value atomically.
func (n *Number) SetInt64Atomic(i int64) {
atomic.StoreInt64(n.AsInt64Ptr(), i)
}
// SetFloat64Atomic assumes that the number contains a float64 and
// sets it to the passed value atomically.
func (n *Number) SetFloat64Atomic(f float64) {
atomic.StoreUint64(n.AsRawPtr(), internal.Float64ToRaw(f))
}
// - swap
// SwapNumber sets the number to the passed number and returns the old
// number. Both this number and the passed number should be of the
// same kind.
func (n *Number) SwapNumber(nn Number) Number {
old := *n
n.SetNumber(nn)
return old
}
// SwapRaw sets the number to the passed raw value and returns the old
// raw value. Both number and the raw number should represent the same
// kind.
func (n *Number) SwapRaw(r uint64) uint64 {
old := n.AsRaw()
n.SetRaw(r)
return old
}
// SwapInt64 assumes that the number contains an int64, sets it to the
// passed value and returns the old int64 value.
func (n *Number) SwapInt64(i int64) int64 {
old := n.AsInt64()
n.SetInt64(i)
return old
}
// SwapFloat64 assumes that the number contains an float64, sets it to
// the passed value and returns the old float64 value.
func (n *Number) SwapFloat64(f float64) float64 {
old := n.AsFloat64()
n.SetFloat64(f)
return old
}
// - swap atomic
// SwapNumberAtomic sets the number to the passed number and returns
// the old number atomically. Both this number and the passed number
// should be of the same kind.
func (n *Number) SwapNumberAtomic(nn Number) Number {
return NewNumberFromRaw(atomic.SwapUint64(n.AsRawPtr(), nn.AsRaw()))
}
// SwapRawAtomic sets the number to the passed raw value and returns
// the old raw value atomically. Both number and the raw number should
// represent the same kind.
func (n *Number) SwapRawAtomic(r uint64) uint64 {
return atomic.SwapUint64(n.AsRawPtr(), r)
}
// SwapInt64Atomic assumes that the number contains an int64, sets it
// to the passed value and returns the old int64 value atomically.
func (n *Number) SwapInt64Atomic(i int64) int64 {
return atomic.SwapInt64(n.AsInt64Ptr(), i)
}
// SwapFloat64Atomic assumes that the number contains an float64, sets
// it to the passed value and returns the old float64 value
// atomically.
func (n *Number) SwapFloat64Atomic(f float64) float64 {
return internal.RawToFloat64(atomic.SwapUint64(n.AsRawPtr(), internal.Float64ToRaw(f)))
}
// - add
// AddNumber assumes that this and the passed number are of the passed
// kind and adds the passed number to this number.
func (n *Number) AddNumber(kind Kind, nn Number) {
switch kind {
case Int64Kind:
n.AddInt64(nn.AsInt64())
case Float64Kind:
n.AddFloat64(nn.AsFloat64())
}
}
// AddRaw assumes that this number and the passed raw value are of the
// passed kind and adds the passed raw value to this number.
func (n *Number) AddRaw(kind Kind, r uint64) {
n.AddNumber(kind, NewNumberFromRaw(r))
}
// AddInt64 assumes that the number contains an int64 and adds the
// passed int64 to it.
func (n *Number) AddInt64(i int64) {
*n.AsInt64Ptr() += i
}
// AddFloat64 assumes that the number contains a float64 and adds the
// passed float64 to it.
func (n *Number) AddFloat64(f float64) {
*n.AsFloat64Ptr() += f
}
// - add atomic
// AddNumberAtomic assumes that this and the passed number are of the
// passed kind and adds the passed number to this number atomically.
func (n *Number) AddNumberAtomic(kind Kind, nn Number) {
switch kind {
case Int64Kind:
n.AddInt64Atomic(nn.AsInt64())
case Float64Kind:
n.AddFloat64Atomic(nn.AsFloat64())
}
}
// AddRawAtomic assumes that this number and the passed raw value are
// of the passed kind and adds the passed raw value to this number
// atomically.
func (n *Number) AddRawAtomic(kind Kind, r uint64) {
n.AddNumberAtomic(kind, NewNumberFromRaw(r))
}
// AddInt64Atomic assumes that the number contains an int64 and adds
// the passed int64 to it atomically.
func (n *Number) AddInt64Atomic(i int64) {
atomic.AddInt64(n.AsInt64Ptr(), i)
}
// AddFloat64Atomic assumes that the number contains a float64 and
// adds the passed float64 to it atomically.
func (n *Number) AddFloat64Atomic(f float64) {
for {
o := n.AsFloat64Atomic()
if n.CompareAndSwapFloat64(o, o+f) {
break
}
}
}
// - compare and swap (atomic only)
// CompareAndSwapNumber does the atomic CAS operation on this
// number. This number and passed old and new numbers should be of the
// same kind.
func (n *Number) CompareAndSwapNumber(on, nn Number) bool {
return atomic.CompareAndSwapUint64(n.AsRawPtr(), on.AsRaw(), nn.AsRaw())
}
// CompareAndSwapRaw does the atomic CAS operation on this
// number. This number and passed old and new raw values should be of
// the same kind.
func (n *Number) CompareAndSwapRaw(or, nr uint64) bool {
return atomic.CompareAndSwapUint64(n.AsRawPtr(), or, nr)
}
// CompareAndSwapInt64 assumes that this number contains an int64 and
// does the atomic CAS operation on it.
func (n *Number) CompareAndSwapInt64(oi, ni int64) bool {
return atomic.CompareAndSwapInt64(n.AsInt64Ptr(), oi, ni)
}
// CompareAndSwapFloat64 assumes that this number contains a float64 and
// does the atomic CAS operation on it.
func (n *Number) CompareAndSwapFloat64(of, nf float64) bool {
return atomic.CompareAndSwapUint64(n.AsRawPtr(), internal.Float64ToRaw(of), internal.Float64ToRaw(nf))
}
// - compare
// CompareNumber compares two Numbers given their kind. Both numbers
// should have the same kind. This returns:
// 0 if the numbers are equal
// -1 if the subject `n` is less than the argument `nn`
// +1 if the subject `n` is greater than the argument `nn`
func (n *Number) CompareNumber(kind Kind, nn Number) int {
switch kind {
case Int64Kind:
return n.CompareInt64(nn.AsInt64())
case Float64Kind:
return n.CompareFloat64(nn.AsFloat64())
default:
// you get what you deserve
return 0
}
}
// CompareRaw compares two numbers, where one is input as a raw
// uint64, interpreting both values as a `kind` of number.
func (n *Number) CompareRaw(kind Kind, r uint64) int {
return n.CompareNumber(kind, NewNumberFromRaw(r))
}
// CompareInt64 assumes that the Number contains an int64 and performs
// a comparison between the value and the other value. It returns the
// typical result of the compare function: -1 if the value is less
// than the other, 0 if both are equal, 1 if the value is greater than
// the other.
func (n *Number) CompareInt64(i int64) int {
this := n.AsInt64()
if this < i {
return -1
} else if this > i {
return 1
}
return 0
}
// CompareFloat64 assumes that the Number contains a float64 and
// performs a comparison between the value and the other value. It
// returns the typical result of the compare function: -1 if the value
// is less than the other, 0 if both are equal, 1 if the value is
// greater than the other.
//
// Do not compare NaN values.
func (n *Number) CompareFloat64(f float64) int {
this := n.AsFloat64()
if this < f {
return -1
} else if this > f {
return 1
}
return 0
}
// - relations to zero
// IsPositive returns true if the actual value is greater than zero.
func (n *Number) IsPositive(kind Kind) bool {
return n.compareWithZero(kind) > 0
}
// IsNegative returns true if the actual value is less than zero.
func (n *Number) IsNegative(kind Kind) bool {
return n.compareWithZero(kind) < 0
}
// IsZero returns true if the actual value is equal to zero.
func (n *Number) IsZero(kind Kind) bool {
return n.compareWithZero(kind) == 0
}
// - misc
// Emit returns a string representation of the raw value of the
// Number. A %d is used for integral values, %f for floating point
// values.
func (n *Number) Emit(kind Kind) string {
switch kind {
case Int64Kind:
return fmt.Sprintf("%d", n.AsInt64())
case Float64Kind:
return fmt.Sprintf("%f", n.AsFloat64())
default:
return ""
}
}
// AsInterface returns the number as an interface{}, typically used
// for Kind-correct JSON conversion.
func (n *Number) AsInterface(kind Kind) interface{} {
switch kind {
case Int64Kind:
return n.AsInt64()
case Float64Kind:
return n.AsFloat64()
default:
return math.NaN()
}
}
// - private stuff
func (n *Number) compareWithZero(kind Kind) int {
switch kind {
case Int64Kind:
return n.CompareInt64(0)
case Float64Kind:
return n.CompareFloat64(0.)
default:
// you get what you deserve
return 0
}
}

View File

@@ -1,70 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
import (
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/unit"
)
// Descriptor contains all the settings that describe an instrument,
// including its name, metric kind, number kind, and the configurable
// options.
type Descriptor struct {
name string
instrumentKind InstrumentKind
numberKind number.Kind
description string
unit unit.Unit
}
// NewDescriptor returns a Descriptor with the given contents.
func NewDescriptor(name string, ikind InstrumentKind, nkind number.Kind, description string, unit unit.Unit) Descriptor {
return Descriptor{
name: name,
instrumentKind: ikind,
numberKind: nkind,
description: description,
unit: unit,
}
}
// Name returns the metric instrument's name.
func (d Descriptor) Name() string {
return d.name
}
// InstrumentKind returns the specific kind of instrument.
func (d Descriptor) InstrumentKind() InstrumentKind {
return d.instrumentKind
}
// Description provides a human-readable description of the metric
// instrument.
func (d Descriptor) Description() string {
return d.description
}
// Unit describes the units of the metric instrument. Unitless
// metrics return the empty string.
func (d Descriptor) Unit() unit.Unit {
return d.unit
}
// NumberKind returns whether this instrument is declared over int64,
// float64, or uint64 values.
func (d Descriptor) NumberKind() number.Kind {
return d.numberKind
}

View File

@@ -1,80 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:generate stringer -type=InstrumentKind
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
// InstrumentKind describes the kind of instrument.
type InstrumentKind int8
const (
// HistogramInstrumentKind indicates a Histogram instrument.
HistogramInstrumentKind InstrumentKind = iota
// GaugeObserverInstrumentKind indicates an GaugeObserver instrument.
GaugeObserverInstrumentKind
// CounterInstrumentKind indicates a Counter instrument.
CounterInstrumentKind
// UpDownCounterInstrumentKind indicates a UpDownCounter instrument.
UpDownCounterInstrumentKind
// CounterObserverInstrumentKind indicates a CounterObserver instrument.
CounterObserverInstrumentKind
// UpDownCounterObserverInstrumentKind indicates a UpDownCounterObserver
// instrument.
UpDownCounterObserverInstrumentKind
)
// Synchronous returns whether this is a synchronous kind of instrument.
func (k InstrumentKind) Synchronous() bool {
switch k {
case CounterInstrumentKind, UpDownCounterInstrumentKind, HistogramInstrumentKind:
return true
}
return false
}
// Asynchronous returns whether this is an asynchronous kind of instrument.
func (k InstrumentKind) Asynchronous() bool {
return !k.Synchronous()
}
// Adding returns whether this kind of instrument adds its inputs (as opposed to Grouping).
func (k InstrumentKind) Adding() bool {
switch k {
case CounterInstrumentKind, UpDownCounterInstrumentKind, CounterObserverInstrumentKind, UpDownCounterObserverInstrumentKind:
return true
}
return false
}
// Grouping returns whether this kind of instrument groups its inputs (as opposed to Adding).
func (k InstrumentKind) Grouping() bool {
return !k.Adding()
}
// Monotonic returns whether this kind of instrument exposes a non-decreasing sum.
func (k InstrumentKind) Monotonic() bool {
switch k {
case CounterInstrumentKind, CounterObserverInstrumentKind:
return true
}
return false
}
// PrecomputedSum returns whether this kind of instrument receives precomputed sums.
func (k InstrumentKind) PrecomputedSum() bool {
return k.Adding() && k.Asynchronous()
}

View File

@@ -1,28 +0,0 @@
// Code generated by "stringer -type=InstrumentKind"; DO NOT EDIT.
package sdkapi
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[HistogramInstrumentKind-0]
_ = x[GaugeObserverInstrumentKind-1]
_ = x[CounterInstrumentKind-2]
_ = x[UpDownCounterInstrumentKind-3]
_ = x[CounterObserverInstrumentKind-4]
_ = x[UpDownCounterObserverInstrumentKind-5]
}
const _InstrumentKind_name = "HistogramInstrumentKindGaugeObserverInstrumentKindCounterInstrumentKindUpDownCounterInstrumentKindCounterObserverInstrumentKindUpDownCounterObserverInstrumentKind"
var _InstrumentKind_index = [...]uint8{0, 23, 50, 71, 98, 127, 162}
func (i InstrumentKind) String() string {
if i < 0 || i >= InstrumentKind(len(_InstrumentKind_index)-1) {
return "InstrumentKind(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _InstrumentKind_name[_InstrumentKind_index[i]:_InstrumentKind_index[i+1]]
}

View File

@@ -1,66 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number"
)
type noopInstrument struct {
descriptor Descriptor
}
type noopSyncInstrument struct{ noopInstrument }
type noopAsyncInstrument struct{ noopInstrument }
var _ SyncImpl = noopSyncInstrument{}
var _ AsyncImpl = noopAsyncInstrument{}
// NewNoopSyncInstrument returns a No-op implementation of the
// synchronous instrument interface.
func NewNoopSyncInstrument() SyncImpl {
return noopSyncInstrument{
noopInstrument{
descriptor: Descriptor{
instrumentKind: CounterInstrumentKind,
},
},
}
}
// NewNoopAsyncInstrument returns a No-op implementation of the
// asynchronous instrument interface.
func NewNoopAsyncInstrument() AsyncImpl {
return noopAsyncInstrument{
noopInstrument{
descriptor: Descriptor{
instrumentKind: CounterObserverInstrumentKind,
},
},
}
}
func (noopInstrument) Implementation() interface{} {
return nil
}
func (n noopInstrument) Descriptor() Descriptor {
return n.descriptor
}
func (noopSyncInstrument) RecordOne(context.Context, number.Number, []attribute.KeyValue) {
}

View File

@@ -1,159 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number"
)
// MeterImpl is the interface an SDK must implement to supply a Meter
// implementation.
type MeterImpl interface {
// RecordBatch atomically records a batch of measurements.
RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurement ...Measurement)
// NewSyncInstrument returns a newly constructed
// synchronous instrument implementation or an error, should
// one occur.
NewSyncInstrument(descriptor Descriptor) (SyncImpl, error)
// NewAsyncInstrument returns a newly constructed
// asynchronous instrument implementation or an error, should
// one occur.
NewAsyncInstrument(
descriptor Descriptor,
runner AsyncRunner,
) (AsyncImpl, error)
}
// InstrumentImpl is a common interface for synchronous and
// asynchronous instruments.
type InstrumentImpl interface {
// Implementation returns the underlying implementation of the
// instrument, which allows the implementation to gain access
// to its own representation especially from a `Measurement`.
Implementation() interface{}
// Descriptor returns a copy of the instrument's Descriptor.
Descriptor() Descriptor
}
// SyncImpl is the implementation-level interface to a generic
// synchronous instrument (e.g., Histogram and Counter instruments).
type SyncImpl interface {
InstrumentImpl
// RecordOne captures a single synchronous metric event.
RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue)
}
// AsyncImpl is an implementation-level interface to an
// asynchronous instrument (e.g., Observer instruments).
type AsyncImpl interface {
InstrumentImpl
}
// AsyncRunner is expected to convert into an AsyncSingleRunner or an
// AsyncBatchRunner. SDKs will encounter an error if the AsyncRunner
// does not satisfy one of these interfaces.
type AsyncRunner interface {
// AnyRunner is a non-exported method with no functional use
// other than to make this a non-empty interface.
AnyRunner()
}
// AsyncSingleRunner is an interface implemented by single-observer
// callbacks.
type AsyncSingleRunner interface {
// Run accepts a single instrument and function for capturing
// observations of that instrument. Each call to the function
// receives one captured observation. (The function accepts
// multiple observations so the same implementation can be
// used for batch runners.)
Run(ctx context.Context, single AsyncImpl, capture func([]attribute.KeyValue, ...Observation))
AsyncRunner
}
// AsyncBatchRunner is an interface implemented by batch-observer
// callbacks.
type AsyncBatchRunner interface {
// Run accepts a function for capturing observations of
// multiple instruments.
Run(ctx context.Context, capture func([]attribute.KeyValue, ...Observation))
AsyncRunner
}
// NewMeasurement constructs a single observation, a binding between
// an asynchronous instrument and a number.
func NewMeasurement(instrument SyncImpl, number number.Number) Measurement {
return Measurement{
instrument: instrument,
number: number,
}
}
// Measurement is a low-level type used with synchronous instruments
// as a direct interface to the SDK via `RecordBatch`.
type Measurement struct {
// number needs to be aligned for 64-bit atomic operations.
number number.Number
instrument SyncImpl
}
// SyncImpl returns the instrument that created this measurement.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (m Measurement) SyncImpl() SyncImpl {
return m.instrument
}
// Number returns a number recorded in this measurement.
func (m Measurement) Number() number.Number {
return m.number
}
// NewObservation constructs a single observation, a binding between
// an asynchronous instrument and a number.
func NewObservation(instrument AsyncImpl, number number.Number) Observation {
return Observation{
instrument: instrument,
number: number,
}
}
// Observation is a low-level type used with asynchronous instruments
// as a direct interface to the SDK via `BatchObserver`.
type Observation struct {
// number needs to be aligned for 64-bit atomic operations.
number number.Number
instrument AsyncImpl
}
// AsyncImpl returns the instrument that created this observation.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (m Observation) AsyncImpl() AsyncImpl {
return m.instrument
}
// Number returns a number recorded in this observation.
func (m Observation) Number() number.Number {
return m.number
}

View File

@@ -14,6 +14,7 @@
package unit // import "go.opentelemetry.io/otel/metric/unit"
// Unit is a determinate standard quantity of measurement.
type Unit string
// Units defined by OpenTelemetry.

View File

@@ -0,0 +1,24 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package instrumentation provides types to represent the code libraries that
// provide OpenTelemetry instrumentation. These types are used in the
// OpenTelemetry signal pipelines to identify the source of telemetry.
//
// See
// https://github.com/open-telemetry/oteps/blob/d226b677d73a785523fe9b9701be13225ebc528d/text/0083-component.md
// and
// https://github.com/open-telemetry/oteps/blob/d226b677d73a785523fe9b9701be13225ebc528d/text/0201-scope-attributes.md
// for more information.
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"

View File

@@ -12,22 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package instrumentation provides an instrumentation library structure to be
passed to both the OpenTelemetry Tracer and Meter components.
For more information see
[this](https://github.com/open-telemetry/oteps/blob/main/text/0083-component.md).
*/
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"
// Library represents the instrumentation library.
type Library struct {
// Name is the name of the instrumentation library. This should be the
// Go package name of that library.
Name string
// Version is the version of the instrumentation library.
Version string
// SchemaURL of the telemetry emitted by the library.
SchemaURL string
}
// Deprecated: please use Scope instead.
type Library = Scope

View File

@@ -12,12 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package number provides a number abstraction for instruments that
either support int64 or float64 input values.
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"
This package is currently in a pre-GA phase. Backwards incompatible changes
may be introduced in subsequent minor version releases as we work to track the
evolving OpenTelemetry specification and user feedback.
*/
package number // import "go.opentelemetry.io/otel/metric/number"
// Scope represents the instrumentation scope.
type Scope struct {
// Name is the name of the instrumentation scope. This should be the
// Go package name of that scope.
Name string
// Version is the version of the instrumentation scope.
Version string
// SchemaURL of the telemetry emitted by the scope.
SchemaURL string
}

View File

@@ -21,27 +21,72 @@ import (
"go.opentelemetry.io/otel/internal/global"
)
// Environment variable names
// Environment variable names.
const (
// BatchSpanProcessorScheduleDelayKey
// Delay interval between two consecutive exports.
// i.e. 5000
// BatchSpanProcessorScheduleDelayKey is the delay interval between two
// consecutive exports (i.e. 5000).
BatchSpanProcessorScheduleDelayKey = "OTEL_BSP_SCHEDULE_DELAY"
// BatchSpanProcessorExportTimeoutKey
// Maximum allowed time to export data.
// i.e. 3000
// BatchSpanProcessorExportTimeoutKey is the maximum allowed time to
// export data (i.e. 3000).
BatchSpanProcessorExportTimeoutKey = "OTEL_BSP_EXPORT_TIMEOUT"
// BatchSpanProcessorMaxQueueSizeKey
// Maximum queue size
// i.e. 2048
// BatchSpanProcessorMaxQueueSizeKey is the maximum queue size (i.e. 2048).
BatchSpanProcessorMaxQueueSizeKey = "OTEL_BSP_MAX_QUEUE_SIZE"
// BatchSpanProcessorMaxExportBatchSizeKey
// Maximum batch size
// Note: Must be less than or equal to EnvBatchSpanProcessorMaxQueueSize
// i.e. 512
// BatchSpanProcessorMaxExportBatchSizeKey is the maximum batch size (i.e.
// 512). Note: it must be less than or equal to
// EnvBatchSpanProcessorMaxQueueSize.
BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE"
// AttributeValueLengthKey is the maximum allowed attribute value size.
AttributeValueLengthKey = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT"
// AttributeCountKey is the maximum allowed span attribute count.
AttributeCountKey = "OTEL_ATTRIBUTE_COUNT_LIMIT"
// SpanAttributeValueLengthKey is the maximum allowed attribute value size
// for a span.
SpanAttributeValueLengthKey = "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT"
// SpanAttributeCountKey is the maximum allowed span attribute count for a
// span.
SpanAttributeCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT"
// SpanEventCountKey is the maximum allowed span event count.
SpanEventCountKey = "OTEL_SPAN_EVENT_COUNT_LIMIT"
// SpanEventAttributeCountKey is the maximum allowed attribute per span
// event count.
SpanEventAttributeCountKey = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"
// SpanLinkCountKey is the maximum allowed span link count.
SpanLinkCountKey = "OTEL_SPAN_LINK_COUNT_LIMIT"
// SpanLinkAttributeCountKey is the maximum allowed attribute per span
// link count.
SpanLinkAttributeCountKey = "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT"
)
// firstInt returns the value of the first matching environment variable from
// keys. If the value is not an integer or no match is found, defaultValue is
// returned.
func firstInt(defaultValue int, keys ...string) int {
for _, key := range keys {
value, ok := os.LookupEnv(key)
if !ok {
continue
}
intValue, err := strconv.Atoi(value)
if err != nil {
global.Info("Got invalid value, number value expected.", key, value)
return defaultValue
}
return intValue
}
return defaultValue
}
// IntEnvOr returns the int value of the environment variable with name key if
// it exists and the value is an int. Otherwise, defaultValue is returned.
func IntEnvOr(key string, defaultValue int) int {
@@ -86,3 +131,47 @@ func BatchSpanProcessorMaxQueueSize(defaultValue int) int {
func BatchSpanProcessorMaxExportBatchSize(defaultValue int) int {
return IntEnvOr(BatchSpanProcessorMaxExportBatchSizeKey, defaultValue)
}
// SpanAttributeValueLength returns the environment variable value for the
// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the
// environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT is
// returned or defaultValue if that is not set.
func SpanAttributeValueLength(defaultValue int) int {
return firstInt(defaultValue, SpanAttributeValueLengthKey, AttributeValueLengthKey)
}
// SpanAttributeCount returns the environment variable value for the
// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the
// environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT is returned or
// defaultValue if that is not set.
func SpanAttributeCount(defaultValue int) int {
return firstInt(defaultValue, SpanAttributeCountKey, AttributeCountKey)
}
// SpanEventCount returns the environment variable value for the
// OTEL_SPAN_EVENT_COUNT_LIMIT key if it exists, otherwise defaultValue is
// returned.
func SpanEventCount(defaultValue int) int {
return IntEnvOr(SpanEventCountKey, defaultValue)
}
// SpanEventAttributeCount returns the environment variable value for the
// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue
// is returned.
func SpanEventAttributeCount(defaultValue int) int {
return IntEnvOr(SpanEventAttributeCountKey, defaultValue)
}
// SpanLinkCount returns the environment variable value for the
// OTEL_SPAN_LINK_COUNT_LIMIT key if it exists, otherwise defaultValue is
// returned.
func SpanLinkCount(defaultValue int) int {
return IntEnvOr(SpanLinkCountKey, defaultValue)
}
// SpanLinkAttributeCount returns the environment variable value for the
// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue is
// returned.
func SpanLinkAttributeCount(defaultValue int) int {
return IntEnvOr(SpanLinkAttributeCountKey, defaultValue)
}

View File

@@ -27,7 +27,7 @@ var (
ErrPartialResource = errors.New("partial resource")
)
// Detector detects OpenTelemetry resource information
// Detector detects OpenTelemetry resource information.
type Detector interface {
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.

View File

@@ -22,7 +22,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)
type (
@@ -92,7 +92,7 @@ func (sd stringDetector) Detect(ctx context.Context) (*Resource, error) {
return NewWithAttributes(sd.schemaURL, sd.K.String(value)), nil
}
// Detect implements Detector
// Detect implements Detector.
func (defaultServiceNameDetector) Detect(ctx context.Context) (*Resource, error) {
return StringDetector(
semconv.SchemaURL,

View File

@@ -110,7 +110,16 @@ func WithOSDescription() Option {
}
// WithProcess adds all the Process attributes to the configured Resource.
// See individual WithProcess* functions to configure specific attributes.
//
// Warning! This option will include process command line arguments. If these
// contain sensitive information it will be included in the exported resource.
//
// This option is equivalent to calling WithProcessPID,
// WithProcessExecutableName, WithProcessExecutablePath,
// WithProcessCommandArgs, WithProcessOwner, WithProcessRuntimeName,
// WithProcessRuntimeVersion, and WithProcessRuntimeDescription. See each
// option function for information about what resource attributes each
// includes.
func WithProcess() Option {
return WithDetectors(
processPIDDetector{},
@@ -143,7 +152,11 @@ func WithProcessExecutablePath() Option {
}
// WithProcessCommandArgs adds an attribute with all the command arguments (including
// the command/executable itself) as received by the process the configured Resource.
// the command/executable itself) as received by the process to the configured
// Resource.
//
// Warning! This option will include process command line arguments. If these
// contain sensitive information it will be included in the exported resource.
func WithProcessCommandArgs() Option {
return WithDetectors(processCommandArgsDetector{})
}
@@ -171,3 +184,16 @@ func WithProcessRuntimeVersion() Option {
func WithProcessRuntimeDescription() Option {
return WithDetectors(processRuntimeDescriptionDetector{})
}
// WithContainer adds all the Container attributes to the configured Resource.
// See individual WithContainer* functions to configure specific attributes.
func WithContainer() Option {
return WithDetectors(
cgroupContainerIDDetector{},
)
}
// WithContainerID adds an attribute with the id of the container to the configured Resource.
func WithContainerID() Option {
return WithDetectors(cgroupContainerIDDetector{})
}

View File

@@ -0,0 +1,100 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package resource // import "go.opentelemetry.io/otel/sdk/resource"
import (
"bufio"
"context"
"errors"
"io"
"os"
"regexp"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)
type containerIDProvider func() (string, error)
var (
containerID containerIDProvider = getContainerIDFromCGroup
cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*-)?([0-9a-f]+)(?:\.|\s*$)`)
)
type cgroupContainerIDDetector struct{}
const cgroupPath = "/proc/self/cgroup"
// Detect returns a *Resource that describes the id of the container.
// If no container id found, an empty resource will be returned.
func (cgroupContainerIDDetector) Detect(ctx context.Context) (*Resource, error) {
containerID, err := containerID()
if err != nil {
return nil, err
}
if containerID == "" {
return Empty(), nil
}
return NewWithAttributes(semconv.SchemaURL, semconv.ContainerIDKey.String(containerID)), nil
}
var (
defaultOSStat = os.Stat
osStat = defaultOSStat
defaultOSOpen = func(name string) (io.ReadCloser, error) {
return os.Open(name)
}
osOpen = defaultOSOpen
)
// getContainerIDFromCGroup returns the id of the container from the cgroup file.
// If no container id found, an empty string will be returned.
func getContainerIDFromCGroup() (string, error) {
if _, err := osStat(cgroupPath); errors.Is(err, os.ErrNotExist) {
// File does not exist, skip
return "", nil
}
file, err := osOpen(cgroupPath)
if err != nil {
return "", err
}
defer file.Close()
return getContainerIDFromReader(file), nil
}
// getContainerIDFromReader returns the id of the container from reader.
func getContainerIDFromReader(reader io.Reader) string {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
if id := getContainerIDFromLine(line); id != "" {
return id
}
}
return ""
}
// getContainerIDFromLine returns the id of the container from one string line.
func getContainerIDFromLine(line string) string {
matches := cgroupContainerIDRe.FindStringSubmatch(line)
if len(matches) <= 1 {
return ""
}
return matches[1]
}

View File

@@ -17,11 +17,13 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource"
import (
"context"
"fmt"
"net/url"
"os"
"strings"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)
const (
@@ -42,10 +44,10 @@ var (
// builtin.
type fromEnv struct{}
// compile time assertion that FromEnv implements Detector interface
// compile time assertion that FromEnv implements Detector interface.
var _ Detector = fromEnv{}
// Detect collects resources from environment
// Detect collects resources from environment.
func (fromEnv) Detect(context.Context) (*Resource, error) {
attrs := strings.TrimSpace(os.Getenv(resourceAttrKey))
svcName := strings.TrimSpace(os.Getenv(svcNameKey))
@@ -88,7 +90,14 @@ func constructOTResources(s string) (*Resource, error) {
invalid = append(invalid, p)
continue
}
k, v := strings.TrimSpace(field[0]), strings.TrimSpace(field[1])
k := strings.TrimSpace(field[0])
v, err := url.QueryUnescape(strings.TrimSpace(field[1]))
if err != nil {
// Retain original value if decoding fails, otherwise it will be
// an empty string.
v = field[1]
otel.Handle(err)
}
attrs = append(attrs, attribute.String(k, v))
}
var err error

View File

@@ -19,7 +19,7 @@ import (
"strings"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)
type osDescriptionProvider func() (string, error)

View File

@@ -18,7 +18,6 @@
package resource // import "go.opentelemetry.io/otel/sdk/resource"
import (
"bytes"
"fmt"
"os"
@@ -69,23 +68,14 @@ func uname() (string, error) {
}
return fmt.Sprintf("%s %s %s %s %s",
charsToString(utsName.Sysname[:]),
charsToString(utsName.Nodename[:]),
charsToString(utsName.Release[:]),
charsToString(utsName.Version[:]),
charsToString(utsName.Machine[:]),
unix.ByteSliceToString(utsName.Sysname[:]),
unix.ByteSliceToString(utsName.Nodename[:]),
unix.ByteSliceToString(utsName.Release[:]),
unix.ByteSliceToString(utsName.Version[:]),
unix.ByteSliceToString(utsName.Machine[:]),
), nil
}
// charsToString converts a C-like null-terminated char array to a Go string.
func charsToString(charArray []byte) string {
if i := bytes.IndexByte(charArray, 0); i >= 0 {
charArray = charArray[:i]
}
return string(charArray)
}
// getFirstAvailableFile returns an *os.File of the first available
// file from a list of candidate file paths.
func getFirstAvailableFile(candidates []string) (*os.File, error) {

View File

@@ -22,7 +22,7 @@ import (
"path/filepath"
"runtime"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)
type pidProvider func() int
@@ -39,7 +39,12 @@ var (
defaultExecutablePathProvider executablePathProvider = os.Executable
defaultCommandArgsProvider commandArgsProvider = func() []string { return os.Args }
defaultOwnerProvider ownerProvider = user.Current
defaultRuntimeNameProvider runtimeNameProvider = func() string { return runtime.Compiler }
defaultRuntimeNameProvider runtimeNameProvider = func() string {
if runtime.Compiler == "gc" {
return "go"
}
return runtime.Compiler
}
defaultRuntimeVersionProvider runtimeVersionProvider = runtime.Version
defaultRuntimeOSProvider runtimeOSProvider = func() string { return runtime.GOOS }
defaultRuntimeArchProvider runtimeArchProvider = func() string { return runtime.GOARCH }

View File

@@ -129,6 +129,7 @@ func (r *Resource) Attributes() []attribute.KeyValue {
return r.attrs.ToSlice()
}
// SchemaURL returns the schema URL associated with Resource r.
func (r *Resource) SchemaURL() string {
if r == nil {
return ""
@@ -179,13 +180,14 @@ func Merge(a, b *Resource) (*Resource, error) {
// Merge the schema URL.
var schemaURL string
if a.schemaURL == "" {
switch true {
case a.schemaURL == "":
schemaURL = b.schemaURL
} else if b.schemaURL == "" {
case b.schemaURL == "":
schemaURL = a.schemaURL
} else if a.schemaURL == b.schemaURL {
case a.schemaURL == b.schemaURL:
schemaURL = a.schemaURL
} else {
default:
return Empty(), errMergeConflictSchemaURL
}
@@ -194,7 +196,7 @@ func Merge(a, b *Resource) (*Resource, error) {
mi := attribute.NewMergeIterator(b.Set(), a.Set())
combine := make([]attribute.KeyValue, 0, a.Len()+b.Len())
for mi.Next() {
combine = append(combine, mi.Label())
combine = append(combine, mi.Attribute())
}
merged := NewWithAttributes(schemaURL, combine...)
return merged, nil

View File

@@ -35,8 +35,11 @@ const (
DefaultMaxExportBatchSize = 512
)
// BatchSpanProcessorOption configures a BatchSpanProcessor.
type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions)
// BatchSpanProcessorOptions is configuration settings for a
// BatchSpanProcessor.
type BatchSpanProcessorOptions struct {
// MaxQueueSize is the maximum queue size to buffer spans for delayed processing. If the
// queue gets full it drops the spans. Use BlockOnQueueFull to change this behavior.
@@ -181,7 +184,7 @@ func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error {
var err error
if bsp.e != nil {
flushCh := make(chan struct{})
if bsp.enqueueBlockOnQueueFull(ctx, forceFlushSpan{flushed: flushCh}, true) {
if bsp.enqueueBlockOnQueueFull(ctx, forceFlushSpan{flushed: flushCh}) {
select {
case <-flushCh:
// Processed any items in queue prior to ForceFlush being called
@@ -205,30 +208,43 @@ func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error {
return err
}
// WithMaxQueueSize returns a BatchSpanProcessorOption that configures the
// maximum queue size allowed for a BatchSpanProcessor.
func WithMaxQueueSize(size int) BatchSpanProcessorOption {
return func(o *BatchSpanProcessorOptions) {
o.MaxQueueSize = size
}
}
// WithMaxExportBatchSize returns a BatchSpanProcessorOption that configures
// the maximum export batch size allowed for a BatchSpanProcessor.
func WithMaxExportBatchSize(size int) BatchSpanProcessorOption {
return func(o *BatchSpanProcessorOptions) {
o.MaxExportBatchSize = size
}
}
// WithBatchTimeout returns a BatchSpanProcessorOption that configures the
// maximum delay allowed for a BatchSpanProcessor before it will export any
// held span (whether the queue is full or not).
func WithBatchTimeout(delay time.Duration) BatchSpanProcessorOption {
return func(o *BatchSpanProcessorOptions) {
o.BatchTimeout = delay
}
}
// WithExportTimeout returns a BatchSpanProcessorOption that configures the
// amount of time a BatchSpanProcessor waits for an exporter to export before
// abandoning the export.
func WithExportTimeout(timeout time.Duration) BatchSpanProcessorOption {
return func(o *BatchSpanProcessorOptions) {
o.ExportTimeout = timeout
}
}
// WithBlocking returns a BatchSpanProcessorOption that configures a
// BatchSpanProcessor to wait for enqueue operations to succeed instead of
// dropping data when the queue is full.
func WithBlocking() BatchSpanProcessorOption {
return func(o *BatchSpanProcessorOptions) {
o.BlockOnQueueFull = true
@@ -237,7 +253,6 @@ func WithBlocking() BatchSpanProcessorOption {
// exportSpans is a subroutine of processing and draining the queue.
func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
bsp.timer.Reset(bsp.o.BatchTimeout)
bsp.batchMutex.Lock()
@@ -250,7 +265,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
}
if l := len(bsp.batch); l > 0 {
global.Debug("exporting spans", "count", len(bsp.batch), "dropped", atomic.LoadUint32(&bsp.dropped))
global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped))
err := bsp.e.ExportSpans(ctx, bsp.batch)
// A new batch is always created after exporting, even if the batch failed to be exported.
@@ -335,28 +350,35 @@ func (bsp *batchSpanProcessor) drainQueue() {
}
func (bsp *batchSpanProcessor) enqueue(sd ReadOnlySpan) {
bsp.enqueueBlockOnQueueFull(context.TODO(), sd, bsp.o.BlockOnQueueFull)
ctx := context.TODO()
if bsp.o.BlockOnQueueFull {
bsp.enqueueBlockOnQueueFull(ctx, sd)
} else {
bsp.enqueueDrop(ctx, sd)
}
}
func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd ReadOnlySpan, block bool) bool {
func recoverSendOnClosedChan() {
x := recover()
switch err := x.(type) {
case nil:
return
case runtime.Error:
if err.Error() == "send on closed channel" {
return
}
}
panic(x)
}
func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd ReadOnlySpan) bool {
if !sd.SpanContext().IsSampled() {
return false
}
// This ensures the bsp.queue<- below does not panic as the
// processor shuts down.
defer func() {
x := recover()
switch err := x.(type) {
case nil:
return
case runtime.Error:
if err.Error() == "send on closed channel" {
return
}
}
panic(x)
}()
defer recoverSendOnClosedChan()
select {
case <-bsp.stopCh:
@@ -364,13 +386,27 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R
default:
}
if block {
select {
case bsp.queue <- sd:
return true
case <-ctx.Done():
return false
}
select {
case bsp.queue <- sd:
return true
case <-ctx.Done():
return false
}
}
func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan) bool {
if !sd.SpanContext().IsSampled() {
return false
}
// This ensures the bsp.queue<- below does not panic as the
// processor shuts down.
defer recoverSendOnClosedChan()
select {
case <-bsp.stopCh:
return false
default:
}
select {

View File

@@ -1,68 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trace // import "go.opentelemetry.io/otel/sdk/trace"
// SpanLimits represents the limits of a span.
type SpanLimits struct {
// AttributeCountLimit is the maximum allowed span attribute count.
AttributeCountLimit int
// EventCountLimit is the maximum allowed span event count.
EventCountLimit int
// LinkCountLimit is the maximum allowed span link count.
LinkCountLimit int
// AttributePerEventCountLimit is the maximum allowed attribute per span event count.
AttributePerEventCountLimit int
// AttributePerLinkCountLimit is the maximum allowed attribute per span link count.
AttributePerLinkCountLimit int
}
func (sl *SpanLimits) ensureDefault() {
if sl.EventCountLimit <= 0 {
sl.EventCountLimit = DefaultEventCountLimit
}
if sl.AttributeCountLimit <= 0 {
sl.AttributeCountLimit = DefaultAttributeCountLimit
}
if sl.LinkCountLimit <= 0 {
sl.LinkCountLimit = DefaultLinkCountLimit
}
if sl.AttributePerEventCountLimit <= 0 {
sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit
}
if sl.AttributePerLinkCountLimit <= 0 {
sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit
}
}
const (
// DefaultAttributeCountLimit is the default maximum allowed span attribute count.
DefaultAttributeCountLimit = 128
// DefaultEventCountLimit is the default maximum allowed span event count.
DefaultEventCountLimit = 128
// DefaultLinkCountLimit is the default maximum allowed span link count.
DefaultLinkCountLimit = 128
// DefaultAttributePerEventCountLimit is the default maximum allowed attribute per span event count.
DefaultAttributePerEventCountLimit = 128
// DefaultAttributePerLinkCountLimit is the default maximum allowed attribute per span link count.
DefaultAttributePerLinkCountLimit = 128
)

View File

@@ -29,7 +29,12 @@ func newEvictedQueue(capacity int) evictedQueue {
// add adds value to the evictedQueue eq. If eq is at capacity, the oldest
// queued value will be discarded and the drop count incremented.
func (eq *evictedQueue) add(value interface{}) {
if len(eq.queue) == eq.capacity {
if eq.capacity == 0 {
eq.droppedCount++
return
}
if eq.capacity > 0 && len(eq.queue) == eq.capacity {
// Drop first-in while avoiding allocating more capacity to eq.queue.
copy(eq.queue[:eq.capacity-1], eq.queue[1:])
eq.queue = eq.queue[:eq.capacity-1]

View File

@@ -52,7 +52,7 @@ func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.Trace
gen.Lock()
defer gen.Unlock()
sid := trace.SpanID{}
gen.randSource.Read(sid[:])
_, _ = gen.randSource.Read(sid[:])
return sid
}
@@ -62,9 +62,9 @@ func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.
gen.Lock()
defer gen.Unlock()
tid := trace.TraceID{}
gen.randSource.Read(tid[:])
_, _ = gen.randSource.Read(tid[:])
sid := trace.SpanID{}
gen.randSource.Read(sid[:])
_, _ = gen.randSource.Read(sid[:])
return tid, sid
}

View File

@@ -31,7 +31,7 @@ const (
defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
)
// tracerProviderConfig
// tracerProviderConfig.
type tracerProviderConfig struct {
// processors contains collection of SpanProcessors that are processing pipeline
// for spans in the trace signal.
@@ -70,9 +70,11 @@ func (cfg tracerProviderConfig) MarshalLog() interface{} {
}
}
// TracerProvider is an OpenTelemetry TracerProvider. It provides Tracers to
// instrumentation so it can trace operational flow through a system.
type TracerProvider struct {
mu sync.Mutex
namedTracer map[instrumentation.Library]*tracer
namedTracer map[instrumentation.Scope]*tracer
spanProcessors atomic.Value
// These fields are not protected by the lock mu. They are assumed to be
@@ -88,15 +90,18 @@ var _ trace.TracerProvider = &TracerProvider{}
// NewTracerProvider returns a new and configured TracerProvider.
//
// By default the returned TracerProvider is configured with:
// - a ParentBased(AlwaysSample) Sampler
// - a random number IDGenerator
// - the resource.Default() Resource
// - the default SpanLimits.
// - a ParentBased(AlwaysSample) Sampler
// - a random number IDGenerator
// - the resource.Default() Resource
// - the default SpanLimits.
//
// The passed opts are used to override these default values and configure the
// returned TracerProvider appropriately.
func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
o := tracerProviderConfig{}
o := tracerProviderConfig{
spanLimits: NewSpanLimits(),
}
o = applyTracerProviderEnvConfigs(o)
for _, opt := range opts {
o = opt.apply(o)
@@ -105,18 +110,19 @@ func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
o = ensureValidTracerProviderConfig(o)
tp := &TracerProvider{
namedTracer: make(map[instrumentation.Library]*tracer),
namedTracer: make(map[instrumentation.Scope]*tracer),
sampler: o.sampler,
idGenerator: o.idGenerator,
spanLimits: o.spanLimits,
resource: o.resource,
}
global.Info("TracerProvider created", "config", o)
spss := spanProcessorStates{}
for _, sp := range o.processors {
tp.RegisterSpanProcessor(sp)
spss = append(spss, newSpanProcessorState(sp))
}
tp.spanProcessors.Store(spss)
return tp
}
@@ -136,62 +142,56 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
if name == "" {
name = defaultTracerName
}
il := instrumentation.Library{
is := instrumentation.Scope{
Name: name,
Version: c.InstrumentationVersion(),
SchemaURL: c.SchemaURL(),
}
t, ok := p.namedTracer[il]
t, ok := p.namedTracer[is]
if !ok {
t = &tracer{
provider: p,
instrumentationLibrary: il,
provider: p,
instrumentationScope: is,
}
p.namedTracer[il] = t
p.namedTracer[is] = t
global.Info("Tracer created", "name", name, "version", c.InstrumentationVersion(), "schemaURL", c.SchemaURL())
}
return t
}
// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors
func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) {
// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors.
func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) {
p.mu.Lock()
defer p.mu.Unlock()
new := spanProcessorStates{}
if old, ok := p.spanProcessors.Load().(spanProcessorStates); ok {
new = append(new, old...)
}
newSpanSync := &spanProcessorState{
sp: s,
state: &sync.Once{},
}
new = append(new, newSpanSync)
p.spanProcessors.Store(new)
newSPS := spanProcessorStates{}
newSPS = append(newSPS, p.spanProcessors.Load().(spanProcessorStates)...)
newSPS = append(newSPS, newSpanProcessorState(sp))
p.spanProcessors.Store(newSPS)
}
// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors
func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) {
// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors.
func (p *TracerProvider) UnregisterSpanProcessor(sp SpanProcessor) {
p.mu.Lock()
defer p.mu.Unlock()
spss := spanProcessorStates{}
old, ok := p.spanProcessors.Load().(spanProcessorStates)
if !ok || len(old) == 0 {
old := p.spanProcessors.Load().(spanProcessorStates)
if len(old) == 0 {
return
}
spss := spanProcessorStates{}
spss = append(spss, old...)
// stop the span processor if it is started and remove it from the list
var stopOnce *spanProcessorState
var idx int
for i, sps := range spss {
if sps.sp == s {
if sps.sp == sp {
stopOnce = sps
idx = i
}
}
if stopOnce != nil {
stopOnce.state.Do(func() {
if err := s.Shutdown(context.Background()); err != nil {
if err := sp.Shutdown(context.Background()); err != nil {
otel.Handle(err)
}
})
@@ -208,10 +208,7 @@ func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) {
// ForceFlush immediately exports all spans that have not yet been exported for
// all the registered span processors.
func (p *TracerProvider) ForceFlush(ctx context.Context) error {
spss, ok := p.spanProcessors.Load().(spanProcessorStates)
if !ok {
return fmt.Errorf("failed to load span processors")
}
spss := p.spanProcessors.Load().(spanProcessorStates)
if len(spss) == 0 {
return nil
}
@@ -232,14 +229,12 @@ func (p *TracerProvider) ForceFlush(ctx context.Context) error {
// Shutdown shuts down the span processors in the order they were registered.
func (p *TracerProvider) Shutdown(ctx context.Context) error {
spss, ok := p.spanProcessors.Load().(spanProcessorStates)
if !ok {
return fmt.Errorf("failed to load span processors")
}
spss := p.spanProcessors.Load().(spanProcessorStates)
if len(spss) == 0 {
return nil
}
var retErr error
for _, sps := range spss {
select {
case <-ctx.Done():
@@ -252,12 +247,18 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error {
err = sps.sp.Shutdown(ctx)
})
if err != nil {
return err
if retErr == nil {
retErr = err
} else {
// Poor man's list of errors
retErr = fmt.Errorf("%v; %v", retErr, err)
}
}
}
return nil
return retErr
}
// TracerProviderOption configures a TracerProvider.
type TracerProviderOption interface {
apply(tracerProviderConfig) tracerProviderConfig
}
@@ -333,7 +334,10 @@ func WithIDGenerator(g IDGenerator) TracerProviderOption {
// Tracers the TracerProvider creates to make their sampling decisions for the
// Spans they create.
//
// If this option is not used, the TracerProvider will use a
// This option overrides the Sampler configured through the OTEL_TRACES_SAMPLER
// and OTEL_TRACES_SAMPLER_ARG environment variables. If this option is not used
// and the sampler is not configured through environment variables or the environment
// contains invalid/unsupported configuration, the TracerProvider will use a
// ParentBased(AlwaysSample) Sampler by default.
func WithSampler(s Sampler) TracerProviderOption {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
@@ -344,20 +348,91 @@ func WithSampler(s Sampler) TracerProviderOption {
})
}
// WithSpanLimits returns a TracerProviderOption that will configure the
// SpanLimits sl as a TracerProvider's SpanLimits. The configured SpanLimits
// are used used by the Tracers the TracerProvider and the Spans they create
// to limit tracing resources used.
// WithSpanLimits returns a TracerProviderOption that configures a
// TracerProvider to use the SpanLimits sl. These SpanLimits bound any Span
// created by a Tracer from the TracerProvider.
//
// If this option is not used, the TracerProvider will use the default
// SpanLimits.
// If any field of sl is zero or negative it will be replaced with the default
// value for that field.
//
// If this or WithRawSpanLimits are not provided, the TracerProvider will use
// the limits defined by environment variables, or the defaults if unset.
// Refer to the NewSpanLimits documentation for information about this
// relationship.
//
// Deprecated: Use WithRawSpanLimits instead which allows setting unlimited
// and zero limits. This option will be kept until the next major version
// incremented release.
func WithSpanLimits(sl SpanLimits) TracerProviderOption {
if sl.AttributeValueLengthLimit <= 0 {
sl.AttributeValueLengthLimit = DefaultAttributeValueLengthLimit
}
if sl.AttributeCountLimit <= 0 {
sl.AttributeCountLimit = DefaultAttributeCountLimit
}
if sl.EventCountLimit <= 0 {
sl.EventCountLimit = DefaultEventCountLimit
}
if sl.AttributePerEventCountLimit <= 0 {
sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit
}
if sl.LinkCountLimit <= 0 {
sl.LinkCountLimit = DefaultLinkCountLimit
}
if sl.AttributePerLinkCountLimit <= 0 {
sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit
}
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
cfg.spanLimits = sl
return cfg
})
}
// WithRawSpanLimits returns a TracerProviderOption that configures a
// TracerProvider to use these limits. These limits bound any Span created by
// a Tracer from the TracerProvider.
//
// The limits will be used as-is. Zero or negative values will not be changed
// to the default value like WithSpanLimits does. Setting a limit to zero will
// effectively disable the related resource it limits and setting to a
// negative value will mean that resource is unlimited. Consequentially, this
// means that the zero-value SpanLimits will disable all span resources.
// Because of this, limits should be constructed using NewSpanLimits and
// updated accordingly.
//
// If this or WithSpanLimits are not provided, the TracerProvider will use the
// limits defined by environment variables, or the defaults if unset. Refer to
// the NewSpanLimits documentation for information about this relationship.
func WithRawSpanLimits(limits SpanLimits) TracerProviderOption {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
cfg.spanLimits = limits
return cfg
})
}
func applyTracerProviderEnvConfigs(cfg tracerProviderConfig) tracerProviderConfig {
for _, opt := range tracerProviderOptionsFromEnv() {
cfg = opt.apply(cfg)
}
return cfg
}
func tracerProviderOptionsFromEnv() []TracerProviderOption {
var opts []TracerProviderOption
sampler, err := samplerFromEnv()
if err != nil {
otel.Handle(err)
}
if sampler != nil {
opts = append(opts, WithSampler(sampler))
}
return opts
}
// ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid.
func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig {
if cfg.sampler == nil {
@@ -366,7 +441,6 @@ func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderCon
if cfg.idGenerator == nil {
cfg.idGenerator = defaultIDGenerator()
}
cfg.spanLimits.ensureDefault()
if cfg.resource == nil {
cfg.resource = resource.Default()
}

View File

@@ -0,0 +1,108 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trace // import "go.opentelemetry.io/otel/sdk/trace"
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
)
const (
tracesSamplerKey = "OTEL_TRACES_SAMPLER"
tracesSamplerArgKey = "OTEL_TRACES_SAMPLER_ARG"
samplerAlwaysOn = "always_on"
samplerAlwaysOff = "always_off"
samplerTraceIDRatio = "traceidratio"
samplerParentBasedAlwaysOn = "parentbased_always_on"
samplerParsedBasedAlwaysOff = "parentbased_always_off"
samplerParentBasedTraceIDRatio = "parentbased_traceidratio"
)
type errUnsupportedSampler string
func (e errUnsupportedSampler) Error() string {
return fmt.Sprintf("unsupported sampler: %s", string(e))
}
var (
errNegativeTraceIDRatio = errors.New("invalid trace ID ratio: less than 0.0")
errGreaterThanOneTraceIDRatio = errors.New("invalid trace ID ratio: greater than 1.0")
)
type samplerArgParseError struct {
parseErr error
}
func (e samplerArgParseError) Error() string {
return fmt.Sprintf("parsing sampler argument: %s", e.parseErr.Error())
}
func (e samplerArgParseError) Unwrap() error {
return e.parseErr
}
func samplerFromEnv() (Sampler, error) {
sampler, ok := os.LookupEnv(tracesSamplerKey)
if !ok {
return nil, nil
}
sampler = strings.ToLower(strings.TrimSpace(sampler))
samplerArg, hasSamplerArg := os.LookupEnv(tracesSamplerArgKey)
samplerArg = strings.TrimSpace(samplerArg)
switch sampler {
case samplerAlwaysOn:
return AlwaysSample(), nil
case samplerAlwaysOff:
return NeverSample(), nil
case samplerTraceIDRatio:
if !hasSamplerArg {
return TraceIDRatioBased(1.0), nil
}
return parseTraceIDRatio(samplerArg)
case samplerParentBasedAlwaysOn:
return ParentBased(AlwaysSample()), nil
case samplerParsedBasedAlwaysOff:
return ParentBased(NeverSample()), nil
case samplerParentBasedTraceIDRatio:
if !hasSamplerArg {
return ParentBased(TraceIDRatioBased(1.0)), nil
}
ratio, err := parseTraceIDRatio(samplerArg)
return ParentBased(ratio), err
default:
return nil, errUnsupportedSampler(sampler)
}
}
func parseTraceIDRatio(arg string) (Sampler, error) {
v, err := strconv.ParseFloat(arg, 64)
if err != nil {
return TraceIDRatioBased(1.0), samplerArgParseError{err}
}
if v < 0.0 {
return TraceIDRatioBased(1.0), errNegativeTraceIDRatio
}
if v > 1.0 {
return TraceIDRatioBased(1.0), errGreaterThanOneTraceIDRatio
}
return TraceIDRatioBased(v), nil
}

View File

@@ -53,17 +53,17 @@ type SamplingParameters struct {
// SamplingDecision indicates whether a span is dropped, recorded and/or sampled.
type SamplingDecision uint8
// Valid sampling decisions
// Valid sampling decisions.
const (
// Drop will not record the span and all attributes/events will be dropped
// Drop will not record the span and all attributes/events will be dropped.
Drop SamplingDecision = iota
// Record indicates the span's `IsRecording() == true`, but `Sampled` flag
// *must not* be set
// *must not* be set.
RecordOnly
// RecordAndSample has span's `IsRecording() == true` and `Sampled` flag
// *must* be set
// *must* be set.
RecordAndSample
)
@@ -102,6 +102,7 @@ func (ts traceIDRatioSampler) Description() string {
// always sample. Fractions < 0 are treated as zero. To respect the
// parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used
// as a delegate of a `Parent` sampler.
//
//nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
func TraceIDRatioBased(fraction float64) Sampler {
if fraction >= 1 {

View File

@@ -116,7 +116,7 @@ func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error {
return nil
}
// MarshalLog is the marshaling function used by the logging system to represent this exporter.
// MarshalLog is the marshaling function used by the logging system to represent this Span Processor.
func (ssp *simpleSpanProcessor) MarshalLog() interface{} {
return struct {
Type string

View File

@@ -26,22 +26,22 @@ import (
// snapshot is an record of a spans state at a particular checkpointed time.
// It is used as a read-only representation of that state.
type snapshot struct {
name string
spanContext trace.SpanContext
parent trace.SpanContext
spanKind trace.SpanKind
startTime time.Time
endTime time.Time
attributes []attribute.KeyValue
events []Event
links []Link
status Status
childSpanCount int
droppedAttributeCount int
droppedEventCount int
droppedLinkCount int
resource *resource.Resource
instrumentationLibrary instrumentation.Library
name string
spanContext trace.SpanContext
parent trace.SpanContext
spanKind trace.SpanKind
startTime time.Time
endTime time.Time
attributes []attribute.KeyValue
events []Event
links []Link
status Status
childSpanCount int
droppedAttributeCount int
droppedEventCount int
droppedLinkCount int
resource *resource.Resource
instrumentationScope instrumentation.Scope
}
var _ ReadOnlySpan = snapshot{}
@@ -102,10 +102,16 @@ func (s snapshot) Status() Status {
return s.status
}
// InstrumentationScope returns information about the instrumentation
// scope that created the span.
func (s snapshot) InstrumentationScope() instrumentation.Scope {
return s.instrumentationScope
}
// InstrumentationLibrary returns information about the instrumentation
// library that created the span.
func (s snapshot) InstrumentationLibrary() instrumentation.Library {
return s.instrumentationLibrary
return s.instrumentationScope
}
// Resource returns information about the entity that produced the span.

View File

@@ -20,15 +20,17 @@ import (
"reflect"
"runtime"
rt "runtime/trace"
"strings"
"sync"
"time"
"unicode/utf8"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/internal"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
"go.opentelemetry.io/otel/trace"
)
@@ -63,8 +65,12 @@ type ReadOnlySpan interface {
Events() []Event
// Status returns the spans status.
Status() Status
// InstrumentationScope returns information about the instrumentation
// scope that created the span.
InstrumentationScope() instrumentation.Scope
// InstrumentationLibrary returns information about the instrumentation
// library that created the span.
// Deprecated: please use InstrumentationScope instead.
InstrumentationLibrary() instrumentation.Library
// Resource returns information about the entity that produced the span.
Resource() *resource.Resource
@@ -183,15 +189,18 @@ func (s *recordingSpan) SetStatus(code codes.Code, description string) {
if !s.IsRecording() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if s.status.Code > code {
return
}
status := Status{Code: code}
if code == codes.Error {
status.Description = description
}
s.mu.Lock()
s.status = status
s.mu.Unlock()
}
// SetAttributes sets attributes of this span.
@@ -212,10 +221,17 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
s.mu.Lock()
defer s.mu.Unlock()
limit := s.tracer.provider.spanLimits.AttributeCountLimit
if limit == 0 {
// No attributes allowed.
s.droppedAttributes += len(attributes)
return
}
// If adding these attributes could exceed the capacity of s perform a
// de-duplication and truncation while adding to avoid over allocation.
if len(s.attributes)+len(attributes) > s.tracer.provider.spanLimits.AttributeCountLimit {
s.addOverCapAttrs(attributes)
if limit > 0 && len(s.attributes)+len(attributes) > limit {
s.addOverCapAttrs(limit, attributes)
return
}
@@ -227,21 +243,25 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
s.droppedAttributes++
continue
}
a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a)
s.attributes = append(s.attributes, a)
}
}
// addOverCapAttrs adds the attributes attrs to the span s while
// de-duplicating the attributes of s and attrs and dropping attributes that
// exceed the capacity of s.
// exceed the limit.
//
// This method assumes s.mu.Lock is held by the caller.
//
// This method should only be called when there is a possibility that adding
// attrs to s will exceed the capacity of s. Otherwise, attrs should be added
// to s without checking for duplicates and all retrieval methods of the
// attributes for s will de-duplicate as needed.
func (s *recordingSpan) addOverCapAttrs(attrs []attribute.KeyValue) {
// attrs to s will exceed the limit. Otherwise, attrs should be added to s
// without checking for duplicates and all retrieval methods of the attributes
// for s will de-duplicate as needed.
//
// This method assumes limit is a value > 0. The argument should be validated
// by the caller.
func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) {
// In order to not allocate more capacity to s.attributes than needed,
// prune and truncate this addition of attributes while adding.
@@ -265,17 +285,73 @@ func (s *recordingSpan) addOverCapAttrs(attrs []attribute.KeyValue) {
continue
}
if len(s.attributes) >= s.tracer.provider.spanLimits.AttributeCountLimit {
if len(s.attributes) >= limit {
// Do not just drop all of the remaining attributes, make sure
// updates are checked and performed.
s.droppedAttributes++
} else {
a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a)
s.attributes = append(s.attributes, a)
exists[a.Key] = len(s.attributes) - 1
}
}
}
// truncateAttr returns a truncated version of attr. Only string and string
// slice attribute values are truncated. String values are truncated to at
// most a length of limit. Each string slice value is truncated in this fashion
// (the slice length itself is unaffected).
//
// No truncation is perfromed for a negative limit.
func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue {
if limit < 0 {
return attr
}
switch attr.Value.Type() {
case attribute.STRING:
if v := attr.Value.AsString(); len(v) > limit {
return attr.Key.String(safeTruncate(v, limit))
}
case attribute.STRINGSLICE:
v := attr.Value.AsStringSlice()
for i := range v {
if len(v[i]) > limit {
v[i] = safeTruncate(v[i], limit)
}
}
return attr.Key.StringSlice(v)
}
return attr
}
// safeTruncate truncates the string and guarantees valid UTF-8 is returned.
func safeTruncate(input string, limit int) string {
if trunc, ok := safeTruncateValidUTF8(input, limit); ok {
return trunc
}
trunc, _ := safeTruncateValidUTF8(strings.ToValidUTF8(input, ""), limit)
return trunc
}
// safeTruncateValidUTF8 returns a copy of the input string safely truncated to
// limit. The truncation is ensured to occur at the bounds of complete UTF-8
// characters. If invalid encoding of UTF-8 is encountered, input is returned
// with false, otherwise, the truncated input will be returned with true.
func safeTruncateValidUTF8(input string, limit int) (string, bool) {
for cnt := 0; cnt <= limit; {
r, size := utf8.DecodeRuneInString(input[cnt:])
if r == utf8.RuneError {
return input, false
}
if cnt+size > limit {
return input[:cnt], true
}
cnt += size
}
return input, true
}
// End ends the span. This method does nothing if the span is already ended or
// is not being recorded.
//
@@ -334,14 +410,13 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
}
s.mu.Unlock()
if sps, ok := s.tracer.provider.spanProcessors.Load().(spanProcessorStates); ok {
if len(sps) == 0 {
return
}
snap := s.snapshot()
for _, sp := range sps {
sp.sp.OnEnd(snap)
}
sps := s.tracer.provider.spanProcessors.Load().(spanProcessorStates)
if len(sps) == 0 {
return
}
snap := s.snapshot()
for _, sp := range sps {
sp.sp.OnEnd(snap)
}
}
@@ -396,22 +471,23 @@ func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) {
func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...)
e := Event{Name: name, Attributes: c.Attributes(), Time: c.Timestamp()}
// Discard over limited attributes
attributes := c.Attributes()
var discarded int
if len(attributes) > s.tracer.provider.spanLimits.AttributePerEventCountLimit {
discarded = len(attributes) - s.tracer.provider.spanLimits.AttributePerEventCountLimit
attributes = attributes[:s.tracer.provider.spanLimits.AttributePerEventCountLimit]
// Discard attributes over limit.
limit := s.tracer.provider.spanLimits.AttributePerEventCountLimit
if limit == 0 {
// Drop all attributes.
e.DroppedAttributeCount = len(e.Attributes)
e.Attributes = nil
} else if limit > 0 && len(e.Attributes) > limit {
// Drop over capacity.
e.DroppedAttributeCount = len(e.Attributes) - limit
e.Attributes = e.Attributes[:limit]
}
s.mu.Lock()
defer s.mu.Unlock()
s.events.add(Event{
Name: name,
Attributes: attributes,
DroppedAttributeCount: discarded,
Time: c.Timestamp(),
})
s.events.add(e)
s.mu.Unlock()
}
// SetName sets the name of this span. If this span is not being recorded than
@@ -531,12 +607,20 @@ func (s *recordingSpan) Status() Status {
return s.status
}
// InstrumentationScope returns the instrumentation.Scope associated with
// the Tracer that created this span.
func (s *recordingSpan) InstrumentationScope() instrumentation.Scope {
s.mu.Lock()
defer s.mu.Unlock()
return s.tracer.instrumentationScope
}
// InstrumentationLibrary returns the instrumentation.Library associated with
// the Tracer that created this span.
func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library {
s.mu.Lock()
defer s.mu.Unlock()
return s.tracer.instrumentationLibrary
return s.tracer.instrumentationScope
}
// Resource returns the Resource associated with the Tracer that created this
@@ -551,18 +635,23 @@ func (s *recordingSpan) addLink(link trace.Link) {
if !s.IsRecording() || !link.SpanContext.IsValid() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
var droppedAttributeCount int
l := Link{SpanContext: link.SpanContext, Attributes: link.Attributes}
// Discard over limited attributes
if len(link.Attributes) > s.tracer.provider.spanLimits.AttributePerLinkCountLimit {
droppedAttributeCount = len(link.Attributes) - s.tracer.provider.spanLimits.AttributePerLinkCountLimit
link.Attributes = link.Attributes[:s.tracer.provider.spanLimits.AttributePerLinkCountLimit]
// Discard attributes over limit.
limit := s.tracer.provider.spanLimits.AttributePerLinkCountLimit
if limit == 0 {
// Drop all attributes.
l.DroppedAttributeCount = len(l.Attributes)
l.Attributes = nil
} else if limit > 0 && len(l.Attributes) > limit {
l.DroppedAttributeCount = len(l.Attributes) - limit
l.Attributes = l.Attributes[:limit]
}
s.links.add(Link{link.SpanContext, link.Attributes, droppedAttributeCount})
s.mu.Lock()
s.links.add(l)
s.mu.Unlock()
}
// DroppedAttributes returns the number of attributes dropped by the span
@@ -610,7 +699,7 @@ func (s *recordingSpan) snapshot() ReadOnlySpan {
defer s.mu.Unlock()
sd.endTime = s.endTime
sd.instrumentationLibrary = s.tracer.instrumentationLibrary
sd.instrumentationScope = s.tracer.instrumentationScope
sd.name = s.name
sd.parent = s.parent
sd.resource = s.tracer.provider.resource

View File

@@ -0,0 +1,125 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trace // import "go.opentelemetry.io/otel/sdk/trace"
import "go.opentelemetry.io/otel/sdk/internal/env"
const (
// DefaultAttributeValueLengthLimit is the default maximum allowed
// attribute value length, unlimited.
DefaultAttributeValueLengthLimit = -1
// DefaultAttributeCountLimit is the default maximum number of attributes
// a span can have.
DefaultAttributeCountLimit = 128
// DefaultEventCountLimit is the default maximum number of events a span
// can have.
DefaultEventCountLimit = 128
// DefaultLinkCountLimit is the default maximum number of links a span can
// have.
DefaultLinkCountLimit = 128
// DefaultAttributePerEventCountLimit is the default maximum number of
// attributes a span event can have.
DefaultAttributePerEventCountLimit = 128
// DefaultAttributePerLinkCountLimit is the default maximum number of
// attributes a span link can have.
DefaultAttributePerLinkCountLimit = 128
)
// SpanLimits represents the limits of a span.
type SpanLimits struct {
// AttributeValueLengthLimit is the maximum allowed attribute value length.
//
// This limit only applies to string and string slice attribute values.
// Any string longer than this value will be truncated to this length.
//
// Setting this to a negative value means no limit is applied.
AttributeValueLengthLimit int
// AttributeCountLimit is the maximum allowed span attribute count. Any
// attribute added to a span once this limit is reached will be dropped.
//
// Setting this to zero means no attributes will be recorded.
//
// Setting this to a negative value means no limit is applied.
AttributeCountLimit int
// EventCountLimit is the maximum allowed span event count. Any event
// added to a span once this limit is reached means it will be added but
// the oldest event will be dropped.
//
// Setting this to zero means no events we be recorded.
//
// Setting this to a negative value means no limit is applied.
EventCountLimit int
// LinkCountLimit is the maximum allowed span link count. Any link added
// to a span once this limit is reached means it will be added but the
// oldest link will be dropped.
//
// Setting this to zero means no links we be recorded.
//
// Setting this to a negative value means no limit is applied.
LinkCountLimit int
// AttributePerEventCountLimit is the maximum number of attributes allowed
// per span event. Any attribute added after this limit reached will be
// dropped.
//
// Setting this to zero means no attributes will be recorded for events.
//
// Setting this to a negative value means no limit is applied.
AttributePerEventCountLimit int
// AttributePerLinkCountLimit is the maximum number of attributes allowed
// per span link. Any attribute added after this limit reached will be
// dropped.
//
// Setting this to zero means no attributes will be recorded for links.
//
// Setting this to a negative value means no limit is applied.
AttributePerLinkCountLimit int
}
// NewSpanLimits returns a SpanLimits with all limits set to the value their
// corresponding environment variable holds, or the default if unset.
//
// • AttributeValueLengthLimit: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT
// (default: unlimited)
//
// • AttributeCountLimit: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT (default: 128)
//
// • EventCountLimit: OTEL_SPAN_EVENT_COUNT_LIMIT (default: 128)
//
// • AttributePerEventCountLimit: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT (default:
// 128)
//
// • LinkCountLimit: OTEL_SPAN_LINK_COUNT_LIMIT (default: 128)
//
// • AttributePerLinkCountLimit: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT (default: 128)
func NewSpanLimits() SpanLimits {
return SpanLimits{
AttributeValueLengthLimit: env.SpanAttributeValueLength(DefaultAttributeValueLengthLimit),
AttributeCountLimit: env.SpanAttributeCount(DefaultAttributeCountLimit),
EventCountLimit: env.SpanEventCount(DefaultEventCountLimit),
LinkCountLimit: env.SpanLinkCount(DefaultLinkCountLimit),
AttributePerEventCountLimit: env.SpanEventAttributeCount(DefaultAttributePerEventCountLimit),
AttributePerLinkCountLimit: env.SpanLinkAttributeCount(DefaultAttributePerLinkCountLimit),
}
}

View File

@@ -64,4 +64,9 @@ type spanProcessorState struct {
sp SpanProcessor
state *sync.Once
}
func newSpanProcessorState(sp SpanProcessor) *spanProcessorState {
return &spanProcessorState{sp: sp, state: &sync.Once{}}
}
type spanProcessorStates []*spanProcessorState

View File

@@ -23,8 +23,8 @@ import (
)
type tracer struct {
provider *TracerProvider
instrumentationLibrary instrumentation.Library
provider *TracerProvider
instrumentationScope instrumentation.Scope
}
var _ trace.Tracer = &tracer{}
@@ -37,6 +37,11 @@ var _ trace.Tracer = &tracer{}
func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) {
config := trace.NewSpanStartConfig(options...)
if ctx == nil {
// Prevent trace.ContextWithSpan from panicking.
ctx = context.Background()
}
// For local spans created by this SDK, track child span count.
if p := trace.SpanFromContext(ctx); p != nil {
if sdkSpan, ok := p.(*recordingSpan); ok {
@@ -46,7 +51,7 @@ func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanS
s := tr.newSpan(ctx, name, &config)
if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() {
sps, _ := tr.provider.spanProcessors.Load().(spanProcessorStates)
sps := tr.provider.spanProcessors.Load().(spanProcessorStates)
for _, sp := range sps {
sp.sp.OnStart(ctx, rw)
}

View File

@@ -32,6 +32,7 @@ type SpanRecorder struct {
var _ sdktrace.SpanProcessor = (*SpanRecorder)(nil)
// NewSpanRecorder returns a new initialized SpanRecorder.
func NewSpanRecorder() *SpanRecorder {
return new(SpanRecorder)
}

View File

@@ -24,6 +24,7 @@ import (
"go.opentelemetry.io/otel/trace"
)
// SpanStubs is a slice of SpanStub use for testing an SDK.
type SpanStubs []SpanStub
// SpanStubsFromReadOnlySpans returns SpanStubs populated from ro.
@@ -95,29 +96,29 @@ func SpanStubFromReadOnlySpan(ro tracesdk.ReadOnlySpan) SpanStub {
DroppedLinks: ro.DroppedLinks(),
ChildSpanCount: ro.ChildSpanCount(),
Resource: ro.Resource(),
InstrumentationLibrary: ro.InstrumentationLibrary(),
InstrumentationLibrary: ro.InstrumentationScope(),
}
}
// Snapshot returns a read-only copy of the SpanStub.
func (s SpanStub) Snapshot() tracesdk.ReadOnlySpan {
return spanSnapshot{
name: s.Name,
spanContext: s.SpanContext,
parent: s.Parent,
spanKind: s.SpanKind,
startTime: s.StartTime,
endTime: s.EndTime,
attributes: s.Attributes,
events: s.Events,
links: s.Links,
status: s.Status,
droppedAttributes: s.DroppedAttributes,
droppedEvents: s.DroppedEvents,
droppedLinks: s.DroppedLinks,
childSpanCount: s.ChildSpanCount,
resource: s.Resource,
instrumentationLibrary: s.InstrumentationLibrary,
name: s.Name,
spanContext: s.SpanContext,
parent: s.Parent,
spanKind: s.SpanKind,
startTime: s.StartTime,
endTime: s.EndTime,
attributes: s.Attributes,
events: s.Events,
links: s.Links,
status: s.Status,
droppedAttributes: s.DroppedAttributes,
droppedEvents: s.DroppedEvents,
droppedLinks: s.DroppedLinks,
childSpanCount: s.ChildSpanCount,
resource: s.Resource,
instrumentationScope: s.InstrumentationLibrary,
}
}
@@ -125,22 +126,22 @@ type spanSnapshot struct {
// Embed the interface to implement the private method.
tracesdk.ReadOnlySpan
name string
spanContext trace.SpanContext
parent trace.SpanContext
spanKind trace.SpanKind
startTime time.Time
endTime time.Time
attributes []attribute.KeyValue
events []tracesdk.Event
links []tracesdk.Link
status tracesdk.Status
droppedAttributes int
droppedEvents int
droppedLinks int
childSpanCount int
resource *resource.Resource
instrumentationLibrary instrumentation.Library
name string
spanContext trace.SpanContext
parent trace.SpanContext
spanKind trace.SpanKind
startTime time.Time
endTime time.Time
attributes []attribute.KeyValue
events []tracesdk.Event
links []tracesdk.Link
status tracesdk.Status
droppedAttributes int
droppedEvents int
droppedLinks int
childSpanCount int
resource *resource.Resource
instrumentationScope instrumentation.Scope
}
func (s spanSnapshot) Name() string { return s.name }
@@ -158,6 +159,9 @@ func (s spanSnapshot) DroppedLinks() int { return s.droppedLinks
func (s spanSnapshot) DroppedEvents() int { return s.droppedEvents }
func (s spanSnapshot) ChildSpanCount() int { return s.childSpanCount }
func (s spanSnapshot) Resource() *resource.Resource { return s.resource }
func (s spanSnapshot) InstrumentationLibrary() instrumentation.Library {
return s.instrumentationLibrary
func (s spanSnapshot) InstrumentationScope() instrumentation.Scope {
return s.instrumentationScope
}
func (s spanSnapshot) InstrumentationLibrary() instrumentation.Library {
return s.instrumentationScope
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0"
package internal // import "go.opentelemetry.io/otel/semconv/internal"
import (
"fmt"
@@ -21,47 +21,70 @@ import (
"strconv"
"strings"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
// HTTP scheme attributes.
var (
HTTPSchemeHTTP = HTTPSchemeKey.String("http")
HTTPSchemeHTTPS = HTTPSchemeKey.String("https")
)
// SemanticConventions are the semantic convention values defined for a
// version of the OpenTelemetry specification.
type SemanticConventions struct {
EnduserIDKey attribute.Key
HTTPClientIPKey attribute.Key
HTTPFlavorKey attribute.Key
HTTPHostKey attribute.Key
HTTPMethodKey attribute.Key
HTTPRequestContentLengthKey attribute.Key
HTTPRouteKey attribute.Key
HTTPSchemeHTTP attribute.KeyValue
HTTPSchemeHTTPS attribute.KeyValue
HTTPServerNameKey attribute.Key
HTTPStatusCodeKey attribute.Key
HTTPTargetKey attribute.Key
HTTPURLKey attribute.Key
HTTPUserAgentKey attribute.Key
NetHostIPKey attribute.Key
NetHostNameKey attribute.Key
NetHostPortKey attribute.Key
NetPeerIPKey attribute.Key
NetPeerNameKey attribute.Key
NetPeerPortKey attribute.Key
NetTransportIP attribute.KeyValue
NetTransportOther attribute.KeyValue
NetTransportTCP attribute.KeyValue
NetTransportUDP attribute.KeyValue
NetTransportUnix attribute.KeyValue
}
// NetAttributesFromHTTPRequest generates attributes of the net
// namespace as specified by the OpenTelemetry specification for a
// span. The network parameter is a string that net.Dial function
// from standard library can understand.
func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{}
switch network {
case "tcp", "tcp4", "tcp6":
attrs = append(attrs, NetTransportTCP)
attrs = append(attrs, sc.NetTransportTCP)
case "udp", "udp4", "udp6":
attrs = append(attrs, NetTransportUDP)
attrs = append(attrs, sc.NetTransportUDP)
case "ip", "ip4", "ip6":
attrs = append(attrs, NetTransportIP)
attrs = append(attrs, sc.NetTransportIP)
case "unix", "unixgram", "unixpacket":
attrs = append(attrs, NetTransportUnix)
attrs = append(attrs, sc.NetTransportUnix)
default:
attrs = append(attrs, NetTransportOther)
attrs = append(attrs, sc.NetTransportOther)
}
peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr)
if peerIP != "" {
attrs = append(attrs, NetPeerIPKey.String(peerIP))
attrs = append(attrs, sc.NetPeerIPKey.String(peerIP))
}
if peerName != "" {
attrs = append(attrs, NetPeerNameKey.String(peerName))
attrs = append(attrs, sc.NetPeerNameKey.String(peerName))
}
if peerPort != 0 {
attrs = append(attrs, NetPeerPortKey.Int(peerPort))
attrs = append(attrs, sc.NetPeerPortKey.Int(peerPort))
}
hostIP, hostName, hostPort := "", "", 0
@@ -72,13 +95,13 @@ func NetAttributesFromHTTPRequest(network string, request *http.Request) []attri
}
}
if hostIP != "" {
attrs = append(attrs, NetHostIPKey.String(hostIP))
attrs = append(attrs, sc.NetHostIPKey.String(hostIP))
}
if hostName != "" {
attrs = append(attrs, NetHostNameKey.String(hostName))
attrs = append(attrs, sc.NetHostNameKey.String(hostName))
}
if hostPort != 0 {
attrs = append(attrs, NetHostPortKey.Int(hostPort))
attrs = append(attrs, sc.NetHostPortKey.Int(hostPort))
}
return attrs
@@ -111,9 +134,9 @@ func hostIPNamePort(hostWithPort string) (ip string, name string, port int) {
// EndUserAttributesFromHTTPRequest generates attributes of the
// enduser namespace as specified by the OpenTelemetry specification
// for a span.
func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
if username, _, ok := request.BasicAuth(); ok {
return []attribute.KeyValue{EnduserIDKey.String(username)}
return []attribute.KeyValue{sc.EnduserIDKey.String(username)}
}
return nil
}
@@ -121,52 +144,48 @@ func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValu
// HTTPClientAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the client side.
func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{}
if request.Method != "" {
attrs = append(attrs, HTTPMethodKey.String(request.Method))
} else {
attrs = append(attrs, HTTPMethodKey.String(http.MethodGet))
}
// remove any username/password info that may be in the URL
// before adding it to the attributes
userinfo := request.URL.User
request.URL.User = nil
attrs = append(attrs, HTTPURLKey.String(request.URL.String()))
attrs = append(attrs, sc.HTTPURLKey.String(request.URL.String()))
// restore any username/password info that was removed
request.URL.User = userinfo
return append(attrs, httpCommonAttributesFromHTTPRequest(request)...)
return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...)
}
func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{}
if ua := request.UserAgent(); ua != "" {
attrs = append(attrs, HTTPUserAgentKey.String(ua))
attrs = append(attrs, sc.HTTPUserAgentKey.String(ua))
}
if request.ContentLength > 0 {
attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength))
attrs = append(attrs, sc.HTTPRequestContentLengthKey.Int64(request.ContentLength))
}
return append(attrs, httpBasicAttributesFromHTTPRequest(request)...)
return append(attrs, sc.httpBasicAttributesFromHTTPRequest(request)...)
}
func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
// as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality
attrs := []attribute.KeyValue{}
if request.TLS != nil {
attrs = append(attrs, HTTPSchemeHTTPS)
attrs = append(attrs, sc.HTTPSchemeHTTPS)
} else {
attrs = append(attrs, HTTPSchemeHTTP)
attrs = append(attrs, sc.HTTPSchemeHTTP)
}
if request.Host != "" {
attrs = append(attrs, HTTPHostKey.String(request.Host))
attrs = append(attrs, sc.HTTPHostKey.String(request.Host))
} else if request.URL != nil && request.URL.Host != "" {
attrs = append(attrs, sc.HTTPHostKey.String(request.URL.Host))
}
flavor := ""
@@ -176,7 +195,13 @@ func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyVa
flavor = "2"
}
if flavor != "" {
attrs = append(attrs, HTTPFlavorKey.String(flavor))
attrs = append(attrs, sc.HTTPFlavorKey.String(flavor))
}
if request.Method != "" {
attrs = append(attrs, sc.HTTPMethodKey.String(request.Method))
} else {
attrs = append(attrs, sc.HTTPMethodKey.String(http.MethodGet))
}
return attrs
@@ -184,45 +209,44 @@ func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyVa
// HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes
// to be used with server-side HTTP metrics.
func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{}
if serverName != "" {
attrs = append(attrs, HTTPServerNameKey.String(serverName))
attrs = append(attrs, sc.HTTPServerNameKey.String(serverName))
}
return append(attrs, httpBasicAttributesFromHTTPRequest(request)...)
return append(attrs, sc.httpBasicAttributesFromHTTPRequest(request)...)
}
// HTTPServerAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the server side. Currently, only basic authentication is
// supported.
func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue {
func (sc *SemanticConventions) HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue {
attrs := []attribute.KeyValue{
HTTPMethodKey.String(request.Method),
HTTPTargetKey.String(request.RequestURI),
sc.HTTPTargetKey.String(request.RequestURI),
}
if serverName != "" {
attrs = append(attrs, HTTPServerNameKey.String(serverName))
attrs = append(attrs, sc.HTTPServerNameKey.String(serverName))
}
if route != "" {
attrs = append(attrs, HTTPRouteKey.String(route))
attrs = append(attrs, sc.HTTPRouteKey.String(route))
}
if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 {
if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 {
attrs = append(attrs, HTTPClientIPKey.String(addresses[0]))
attrs = append(attrs, sc.HTTPClientIPKey.String(addresses[0]))
}
}
return append(attrs, httpCommonAttributesFromHTTPRequest(request)...)
return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...)
}
// HTTPAttributesFromHTTPStatusCode generates attributes of the http
// namespace as specified by the OpenTelemetry specification for a
// span.
func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue {
func (sc *SemanticConventions) HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue {
attrs := []attribute.KeyValue{
HTTPStatusCodeKey.Int(code),
sc.HTTPStatusCodeKey.Int(code),
}
return attrs
}
@@ -286,9 +310,9 @@ func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind)
return spanCode, ""
}
// Validates the HTTP status code and returns corresponding span status code.
// If the `code` is not a valid HTTP status code, returns span status Error
// and false.
// validateHTTPStatusCode validates the HTTP status code and returns
// corresponding span status code. If the `code` is not a valid HTTP status
// code, returns span status Error and false.
func validateHTTPStatusCode(code int) (codes.Code, bool) {
category := code / 100
ranges, ok := validRangesPerCategory[category]

View File

@@ -16,5 +16,5 @@
//
// OpenTelemetry semantic conventions are agreed standardized naming
// patterns for OpenTelemetry things. This package represents the conventions
// as of the v1.7.0 version of the OpenTelemetry specification.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0"
// as of the v1.12.0 version of the OpenTelemetry specification.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.12.0"

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.12.0"
const (
// ExceptionEventName is the name of the Span event representing an exception.

114
vendor/go.opentelemetry.io/otel/semconv/v1.12.0/http.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.12.0"
import (
"net/http"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/semconv/internal"
"go.opentelemetry.io/otel/trace"
)
// HTTP scheme attributes.
var (
HTTPSchemeHTTP = HTTPSchemeKey.String("http")
HTTPSchemeHTTPS = HTTPSchemeKey.String("https")
)
var sc = &internal.SemanticConventions{
EnduserIDKey: EnduserIDKey,
HTTPClientIPKey: HTTPClientIPKey,
HTTPFlavorKey: HTTPFlavorKey,
HTTPHostKey: HTTPHostKey,
HTTPMethodKey: HTTPMethodKey,
HTTPRequestContentLengthKey: HTTPRequestContentLengthKey,
HTTPRouteKey: HTTPRouteKey,
HTTPSchemeHTTP: HTTPSchemeHTTP,
HTTPSchemeHTTPS: HTTPSchemeHTTPS,
HTTPServerNameKey: HTTPServerNameKey,
HTTPStatusCodeKey: HTTPStatusCodeKey,
HTTPTargetKey: HTTPTargetKey,
HTTPURLKey: HTTPURLKey,
HTTPUserAgentKey: HTTPUserAgentKey,
NetHostIPKey: NetHostIPKey,
NetHostNameKey: NetHostNameKey,
NetHostPortKey: NetHostPortKey,
NetPeerIPKey: NetPeerIPKey,
NetPeerNameKey: NetPeerNameKey,
NetPeerPortKey: NetPeerPortKey,
NetTransportIP: NetTransportIP,
NetTransportOther: NetTransportOther,
NetTransportTCP: NetTransportTCP,
NetTransportUDP: NetTransportUDP,
NetTransportUnix: NetTransportUnix,
}
// NetAttributesFromHTTPRequest generates attributes of the net
// namespace as specified by the OpenTelemetry specification for a
// span. The network parameter is a string that net.Dial function
// from standard library can understand.
func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue {
return sc.NetAttributesFromHTTPRequest(network, request)
}
// EndUserAttributesFromHTTPRequest generates attributes of the
// enduser namespace as specified by the OpenTelemetry specification
// for a span.
func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
return sc.EndUserAttributesFromHTTPRequest(request)
}
// HTTPClientAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the client side.
func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
return sc.HTTPClientAttributesFromHTTPRequest(request)
}
// HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes
// to be used with server-side HTTP metrics.
func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue {
return sc.HTTPServerMetricAttributesFromHTTPRequest(serverName, request)
}
// HTTPServerAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the server side. Currently, only basic authentication is
// supported.
func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue {
return sc.HTTPServerAttributesFromHTTPRequest(serverName, route, request)
}
// HTTPAttributesFromHTTPStatusCode generates attributes of the http
// namespace as specified by the OpenTelemetry specification for a
// span.
func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue {
return sc.HTTPAttributesFromHTTPStatusCode(code)
}
// SpanStatusFromHTTPStatusCode generates a status code and a message
// as specified by the OpenTelemetry specification for a span.
func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) {
return internal.SpanStatusFromHTTPStatusCode(code)
}
// SpanStatusFromHTTPStatusCodeAndSpanKind generates a status code and a message
// as specified by the OpenTelemetry specification for a span.
// Exclude 4xx for SERVER to set the appropriate status.
func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) (codes.Code, string) {
return internal.SpanStatusFromHTTPStatusCodeAndSpanKind(code, spanKind)
}

View File

@@ -14,10 +14,55 @@
// Code generated from semantic convention specification. DO NOT EDIT.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.12.0"
import "go.opentelemetry.io/otel/attribute"
// The web browser in which the application represented by the resource is running. The `browser.*` attributes MUST be used only for resources that represent applications running in a web browser (regardless of whether running on a mobile or desktop device).
const (
// Array of brand name and version separated by a space
//
// Type: string[]
// Required: No
// Stability: stable
// Examples: ' Not A;Brand 99', 'Chromium 99', 'Chrome 99'
// Note: This value is intended to be taken from the [UA client hints
// API](https://wicg.github.io/ua-client-hints/#interface)
// (navigator.userAgentData.brands).
BrowserBrandsKey = attribute.Key("browser.brands")
// The platform on which the browser is running
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Windows', 'macOS', 'Android'
// Note: This value is intended to be taken from the [UA client hints
// API](https://wicg.github.io/ua-client-hints/#interface)
// (navigator.userAgentData.platform). If unavailable, the legacy
// `navigator.platform` API SHOULD NOT be used instead and this attribute SHOULD
// be left unset in order for the values to be consistent.
// The list of possible values is defined in the [W3C User-Agent Client Hints
// specification](https://wicg.github.io/ua-client-hints/#sec-ch-ua-platform).
// Note that some (but not all) of these values can overlap with values in the
// [os.type and os.name attributes](./os.md). However, for consistency, the values
// in the `browser.platform` attribute should capture the exact value that the
// user agent provides.
BrowserPlatformKey = attribute.Key("browser.platform")
// Full user-agent string provided by the browser
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
// (KHTML, '
// 'like Gecko) Chrome/95.0.4638.54 Safari/537.36'
// Note: The user-agent value SHOULD be provided only from browsers that do not
// have a mechanism to retrieve brands and platform individually from the User-
// Agent Client Hints API. To retrieve the value, the legacy `navigator.userAgent`
// API can be used.
BrowserUserAgentKey = attribute.Key("browser.user_agent")
)
// A cloud environment (e.g. GCP, Azure, AWS)
const (
// Name of the cloud provider.
@@ -33,18 +78,19 @@ const (
// Stability: stable
// Examples: '111111111111', 'opentelemetry'
CloudAccountIDKey = attribute.Key("cloud.account.id")
// The geographical region the resource is running. Refer to your provider's docs
// to see the available regions, for example [Alibaba Cloud
// regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS
// regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/),
// [Azure regions](https://azure.microsoft.com/en-us/global-
// infrastructure/geographies/), or [Google Cloud
// regions](https://cloud.google.com/about/locations).
// The geographical region the resource is running.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'us-central1', 'us-east-1'
// Note: Refer to your provider's docs to see the available regions, for example
// [Alibaba Cloud regions](https://www.alibabacloud.com/help/doc-
// detail/40654.htm), [AWS regions](https://aws.amazon.com/about-aws/global-
// infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-
// us/global-infrastructure/geographies/), [Google Cloud
// regions](https://cloud.google.com/about/locations), or [Tencent Cloud
// regions](https://intl.cloud.tencent.com/document/product/213/6091).
CloudRegionKey = attribute.Key("cloud.region")
// Cloud regions often have multiple, isolated locations known as zones to
// increase availability. Availability zone represents the zone where the resource
@@ -75,6 +121,8 @@ var (
CloudProviderAzure = CloudProviderKey.String("azure")
// Google Cloud Platform
CloudProviderGCP = CloudProviderKey.String("gcp")
// Tencent Cloud
CloudProviderTencentCloud = CloudProviderKey.String("tencent_cloud")
)
var (
@@ -92,6 +140,8 @@ var (
CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda")
// AWS Elastic Beanstalk
CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk")
// AWS App Runner
CloudPlatformAWSAppRunner = CloudPlatformKey.String("aws_app_runner")
// Azure Virtual Machines
CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm")
// Azure Container Instances
@@ -112,6 +162,12 @@ var (
CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions")
// Google Cloud App Engine (GAE)
CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine")
// Tencent Cloud Cloud Virtual Machine (CVM)
CloudPlatformTencentCloudCvm = CloudPlatformKey.String("tencent_cloud_cvm")
// Tencent Cloud Elastic Kubernetes Service (EKS)
CloudPlatformTencentCloudEKS = CloudPlatformKey.String("tencent_cloud_eks")
// Tencent Cloud Serverless Cloud Function (SCF)
CloudPlatformTencentCloudScf = CloudPlatformKey.String("tencent_cloud_scf")
)
// Resources used by AWS Elastic Container Service (ECS).
@@ -229,7 +285,7 @@ const (
// A container instance.
const (
// Container name.
// Container name used by container runtime.
//
// Type: string
// Required: No
@@ -320,6 +376,16 @@ const (
// Note: It's recommended this value represents a human readable version of the
// device model rather than a machine readable alternative.
DeviceModelNameKey = attribute.Key("device.model.name")
// The name of the device manufacturer
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Apple', 'Samsung'
// Note: The Android OS provides this field via
// [Build](https://developer.android.com/reference/android/os/Build#MANUFACTURER).
// iOS apps SHOULD hardcode the value `Apple`.
DeviceManufacturerKey = attribute.Key("device.manufacturer")
)
// A serverless instance.
@@ -329,12 +395,24 @@ const (
// Type: string
// Required: Always
// Stability: stable
// Examples: 'my-function'
// Examples: 'my-function', 'myazurefunctionapp/some-function-name'
// Note: This is the name of the function as configured/deployed on the FaaS
// platform and is usually different from the name of the callback function (which
// may be stored in the
// platform and is usually different from the name of the callback
// function (which may be stored in the
// [`code.namespace`/`code.function`](../../trace/semantic_conventions/span-
// general.md#source-code-attributes) span attributes).
// general.md#source-code-attributes)
// span attributes).
// For some cloud providers, the above definition is ambiguous. The following
// definition of function name MUST be used for this attribute
// (and consequently the span name) for the listed cloud providers/products:
// * **Azure:** The full name `<FUNCAPP>/<FUNC>`, i.e., function app name
// followed by a forward slash followed by the function name (this form
// can also be seen in the resource JSON for the function).
// This means that a span attribute MUST be used, as an Azure function
// app can host multiple functions that would usually share
// a TracerProvider (see also the `faas.id` attribute).
FaaSNameKey = attribute.Key("faas.name")
// The unique ID of the single function that this runtime instance executes.
//
@@ -342,27 +420,31 @@ const (
// Required: No
// Stability: stable
// Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function'
// Note: Depending on the cloud provider, use:
// Note: On some cloud providers, it may not be possible to determine the full ID
// at startup,
// so consider setting `faas.id` as a span attribute instead.
// The exact value to use for `faas.id` depends on the cloud provider:
// * **AWS Lambda:** The function
// [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-
// namespaces.html).
// Take care not to use the "invoked ARN" directly but replace any
// [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-
// aliases.html) with the resolved function version, as the same runtime instance
// may be invokable with multiple
// different aliases.
// Take care not to use the "invoked ARN" directly but replace any
// [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-
// aliases.html)
// with the resolved function version, as the same runtime instance may be
// invokable with
// multiple different aliases.
// * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full-
// resource-names)
// * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en-
// us/rest/api/resources/resources/get-by-id).
// On some providers, it may not be possible to determine the full ID at startup,
// which is why this field cannot be made required. For example, on AWS the
// account ID
// part of the ARN is not available without calling another AWS API
// which may be deemed too slow for a short-running lambda function.
// As an alternative, consider setting `faas.id` as a span attribute instead.
// us/rest/api/resources/resources/get-by-id) of the invoked function,
// *not* the function app, having the form
// `/subscriptions/<SUBSCIPTION_GUID>/resourceGroups/<RG>/providers/Microsoft.We
// b/sites/<FUNCAPP>/functions/<FUNC>`.
// This means that a span attribute MUST be used, as an Azure function app can
// host multiple functions that would usually share
// a TracerProvider.
FaaSIDKey = attribute.Key("faas.id")
// The immutable version of the function being executed.
//
@@ -476,6 +558,8 @@ var (
HostArchPPC32 = HostArchKey.String("ppc32")
// 64-bit PowerPC
HostArchPPC64 = HostArchKey.String("ppc64")
// IBM z/Architecture
HostArchS390x = HostArchKey.String("s390x")
// 32-bit x86
HostArchX86 = HostArchKey.String("x86")
)
@@ -540,13 +624,23 @@ const (
// A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates).
const (
// The name of the Container in a Pod template.
// The name of the Container from Pod specification, must be unique within a Pod.
// Container runtime usually uses different globally unique name
// (`container.name`).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'redis'
K8SContainerNameKey = attribute.Key("k8s.container.name")
// Number of times the container was restarted. This attribute can be used to
// identify a particular container (running or stopped) within a container spec.
//
// Type: int
// Required: No
// Stability: stable
// Examples: 0, 2
K8SContainerRestartCountKey = attribute.Key("k8s.container.restart_count")
)
// A Kubernetes ReplicaSet object.
@@ -709,7 +803,7 @@ var (
OSTypeHPUX = OSTypeKey.String("hpux")
// AIX (Advanced Interactive eXecutive)
OSTypeAIX = OSTypeKey.String("aix")
// Oracle Solaris
// SunOS, Oracle Solaris
OSTypeSolaris = OSTypeKey.String("solaris")
// IBM z/OS
OSTypeZOS = OSTypeKey.String("z_os")
@@ -917,6 +1011,8 @@ var (
TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby")
// webjs
TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs")
// swift
TelemetrySDKLanguageSwift = TelemetrySDKLanguageKey.String("swift")
)
// Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime.

View File

@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.12.0"
// SchemaURL is the schema URL that matches the version of the semantic conventions
// that this package defines. Semconv packages starting from v1.4.0 must declare
// non-empty schema URL in the form https://opentelemetry.io/schemas/<version>
const SchemaURL = "https://opentelemetry.io/schemas/v1.7.0"
const SchemaURL = "https://opentelemetry.io/schemas/1.12.0"

Some files were not shown because too many files have changed in this diff Show More