mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-04-15 11:36:44 +02:00
Merge branch 'main' into refactor-errorhandler
This commit is contained in:
commit
c3513021f4
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -266,6 +266,16 @@ updates:
|
||||
schedule:
|
||||
day: sunday
|
||||
interval: weekly
|
||||
-
|
||||
package-ecosystem: gomod
|
||||
directory: /exporters/otlp/internal/retry
|
||||
labels:
|
||||
- dependencies
|
||||
- go
|
||||
- "Skip Changelog"
|
||||
schedule:
|
||||
day: sunday
|
||||
interval: weekly
|
||||
-
|
||||
package-ecosystem: gomod
|
||||
directory: /exporters/otlp/otlptrace
|
||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -8,15 +8,30 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added an internal Logger.
|
||||
This can be used by the SDK and API to provide users with feedback of the internal state.
|
||||
To enable verbose logs configure the logger which will print V(1) logs. For debugging information configure to print V(5) logs. (#2343)
|
||||
- Add the `WithRetry` `Option` and the `RetryConfig` type to the `go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp` package to specify retry behavior consistently. (#2425)
|
||||
|
||||
### Changed
|
||||
|
||||
- The `"go.opentelemetry.io/otel/exporter/otel/otlptrace/otlptracegrpc".Client` now uses the underlying gRPC `ClientConn` to handle name resolution, TCP connection establishment (with retries and backoff) and TLS handshakes, and handling errors on established connections by re-resolving the name and reconnecting. (#2329)
|
||||
- Changed the project minimum supported Go version from 1.15 to 1.16. (#2412)
|
||||
- The `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetricgrpc".Client` now uses the underlying gRPC `ClientConn` to handle name resolution, TCP connection establishment (with retries and backoff) and TLS handshakes, and handling errors on established connections by re-resolving the name and reconnecting. (#2425)
|
||||
- The `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetricgrpc".RetrySettings` type is renamed to `RetryConfig`. (#2425)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecated the `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp".WithMaxAttempts` `Option`, use the new `WithRetry` `Option` instead. (#2425)
|
||||
- Deprecated the `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp".WithBackoff` `Option`, use the new `WithRetry` `Option` instead. (#2425)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove the metric Processor's ability to convert cumulative to delta aggregation temporality. (#2350)
|
||||
- Remove the metric Bound Instruments interface and implementations. (#2399)
|
||||
- Remove the metric MinMaxSumCount kind aggregation and the corresponding OTLP export path. (#2423)
|
||||
|
||||
## [1.2.0] - 2021-11-12
|
||||
|
||||
|
@ -75,3 +75,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ./test
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -5,6 +5,11 @@ github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../../exporters/otlp/internal/retry
|
||||
|
@ -11,6 +11,11 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../opencensus/test
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
|
@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/trace => ../../trace
|
||||
replace go.opentelemetry.io/otel/example/fib => ./
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -8,6 +8,7 @@ replace (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-logr/stdr v1.2.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
@ -73,3 +74,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -18,6 +18,8 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/go-logr/stdr"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
@ -52,6 +54,9 @@ func initTracer() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Set logging level to info to see SDK status messages
|
||||
stdr.SetVerbosity(1)
|
||||
|
||||
// initialize trace provider.
|
||||
initTracer()
|
||||
|
||||
|
@ -75,3 +75,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -5,6 +5,11 @@ github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -74,3 +74,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -22,6 +22,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -74,3 +74,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -21,6 +21,11 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -29,6 +29,11 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
|
@ -30,8 +30,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/prometheus => ../prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp => ../otlp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/jaeger => ./
|
||||
|
||||
replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools
|
||||
@ -75,3 +73,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
72
exporters/otlp/internal/retry/go.mod
Normal file
72
exporters/otlp/internal/retry/go.mod
Normal file
@ -0,0 +1,72 @@
|
||||
module go.opentelemetry.io/otel/exporters/otlp/internal/retry
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
|
||||
replace go.opentelemetry.io/otel => ../../../..
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus => ../../../../bridge/opencensus
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opentracing => ../../../../bridge/opentracing
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/example/jaeger => ../../../../example/jaeger
|
||||
|
||||
replace go.opentelemetry.io/otel/example/namedtracer => ../../../../example/namedtracer
|
||||
|
||||
replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/opencensus
|
||||
|
||||
replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/otel-collector
|
||||
|
||||
replace go.opentelemetry.io/otel/example/passthrough => ../../../../example/passthrough
|
||||
|
||||
replace go.opentelemetry.io/otel/example/prometheus => ../../../../example/prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/jaeger => ../../../jaeger
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ./
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlpmetric
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlpmetric/otlpmetricgrpc
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlptrace
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlptrace/otlptracegrpc
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../otlptrace/otlptracehttp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/prometheus => ../../../prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/internal/metric => ../../../../internal/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/internal/tools => ../../../../internal/tools
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/export/metric => ../../../../sdk/export/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../../../../trace
|
13
exporters/otlp/internal/retry/go.sum
Normal file
13
exporters/otlp/internal/retry/go.sum
Normal file
@ -0,0 +1,13 @@
|
||||
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -12,7 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -54,8 +57,18 @@ type RequestFunc func(context.Context, func(context.Context) error) error
|
||||
|
||||
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
|
||||
// duration should be honored that was included in the error.
|
||||
//
|
||||
// The function must return true if the error argument is retry-able,
|
||||
// otherwise it must return false for the first return parameter.
|
||||
//
|
||||
// The function must return a non-zero time.Duration if the error contains
|
||||
// explicit throttle duration that should be honored, otherwise it must return
|
||||
// a zero valued time.Duration.
|
||||
type EvaluateFunc func(error) (bool, time.Duration)
|
||||
|
||||
// RequestFunc returns a RequestFunc using the evaluate function to determine
|
||||
// if requests can be retried and based on the exponential backoff
|
||||
// configuration of c.
|
||||
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||
if !c.Enabled {
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
@ -3,16 +3,15 @@ module go.opentelemetry.io/otel/exporters/otlp/otlpmetric
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.2
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v0.0.0-00010101000000-000000000000
|
||||
go.opentelemetry.io/otel/metric v0.25.0
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
go.opentelemetry.io/otel/sdk/export/metric v0.25.0
|
||||
go.opentelemetry.io/otel/sdk/metric v0.25.0
|
||||
go.opentelemetry.io/proto/otlp v0.11.0
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
|
||||
google.golang.org/grpc v1.42.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
)
|
||||
@ -21,8 +20,6 @@ replace go.opentelemetry.io/otel => ../../..
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk => ../../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp => ../
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
@ -45,12 +42,12 @@ replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel
|
||||
|
||||
replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough
|
||||
|
||||
replace go.opentelemetry.io/otel/example/prom-collector => ../../../example/prom-collector
|
||||
|
||||
replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../internal/retry
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ./
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ./otlpmetricgrpc
|
||||
|
@ -24,6 +24,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -1,431 +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 connection // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/connection"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig"
|
||||
)
|
||||
|
||||
type Connection struct {
|
||||
// Ensure pointer is 64-bit aligned for atomic operations on both 32 and 64 bit machines.
|
||||
lastConnectErrPtr unsafe.Pointer
|
||||
|
||||
// mu protects the Connection as it is accessed by the
|
||||
// exporter goroutines and background Connection goroutine
|
||||
mu sync.Mutex
|
||||
cc *grpc.ClientConn
|
||||
|
||||
// these fields are read-only after constructor is finished
|
||||
cfg otlpconfig.Config
|
||||
SCfg otlpconfig.SignalConfig
|
||||
metadata metadata.MD
|
||||
newConnectionHandler func(cc *grpc.ClientConn)
|
||||
|
||||
// these channels are created once
|
||||
disconnectedCh chan bool
|
||||
backgroundConnectionDoneCh chan struct{}
|
||||
stopCh chan struct{}
|
||||
|
||||
// this is for tests, so they can replace the closing
|
||||
// routine without a worry of modifying some global variable
|
||||
// or changing it back to original after the test is done
|
||||
closeBackgroundConnectionDoneCh func(ch chan struct{})
|
||||
}
|
||||
|
||||
func NewConnection(cfg otlpconfig.Config, sCfg otlpconfig.SignalConfig, handler func(cc *grpc.ClientConn)) *Connection {
|
||||
c := new(Connection)
|
||||
c.newConnectionHandler = handler
|
||||
c.cfg = cfg
|
||||
c.SCfg = sCfg
|
||||
if len(c.SCfg.Headers) > 0 {
|
||||
c.metadata = metadata.New(c.SCfg.Headers)
|
||||
}
|
||||
c.closeBackgroundConnectionDoneCh = func(ch chan struct{}) {
|
||||
close(ch)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Connection) StartConnection(ctx context.Context) error {
|
||||
c.stopCh = make(chan struct{})
|
||||
c.disconnectedCh = make(chan bool, 1)
|
||||
c.backgroundConnectionDoneCh = make(chan struct{})
|
||||
|
||||
if err := c.connect(ctx); err == nil {
|
||||
c.setStateConnected()
|
||||
} else {
|
||||
c.SetStateDisconnected(err)
|
||||
}
|
||||
go c.indefiniteBackgroundConnection()
|
||||
|
||||
// TODO: proper error handling when initializing connections.
|
||||
// We can report permanent errors, e.g., invalid settings.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connection) LastConnectError() error {
|
||||
errPtr := (*error)(atomic.LoadPointer(&c.lastConnectErrPtr))
|
||||
if errPtr == nil {
|
||||
return nil
|
||||
}
|
||||
return *errPtr
|
||||
}
|
||||
|
||||
func (c *Connection) saveLastConnectError(err error) {
|
||||
var errPtr *error
|
||||
if err != nil {
|
||||
errPtr = &err
|
||||
}
|
||||
atomic.StorePointer(&c.lastConnectErrPtr, unsafe.Pointer(errPtr))
|
||||
}
|
||||
|
||||
func (c *Connection) SetStateDisconnected(err error) {
|
||||
c.saveLastConnectError(err)
|
||||
select {
|
||||
case c.disconnectedCh <- true:
|
||||
default:
|
||||
}
|
||||
c.newConnectionHandler(nil)
|
||||
}
|
||||
|
||||
func (c *Connection) setStateConnected() {
|
||||
c.saveLastConnectError(nil)
|
||||
}
|
||||
|
||||
func (c *Connection) Connected() bool {
|
||||
return c.LastConnectError() == nil
|
||||
}
|
||||
|
||||
const defaultConnReattemptPeriod = 10 * time.Second
|
||||
|
||||
func (c *Connection) indefiniteBackgroundConnection() {
|
||||
defer func() {
|
||||
c.closeBackgroundConnectionDoneCh(c.backgroundConnectionDoneCh)
|
||||
}()
|
||||
|
||||
connReattemptPeriod := c.cfg.ReconnectionPeriod
|
||||
if connReattemptPeriod <= 0 {
|
||||
connReattemptPeriod = defaultConnReattemptPeriod
|
||||
}
|
||||
|
||||
// No strong seeding required, nano time can
|
||||
// already help with pseudo uniqueness.
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024)))
|
||||
|
||||
// maxJitterNanos: 70% of the connectionReattemptPeriod
|
||||
maxJitterNanos := int64(0.7 * float64(connReattemptPeriod))
|
||||
|
||||
for {
|
||||
// Otherwise these will be the normal scenarios to enable
|
||||
// reconnection if we trip out.
|
||||
// 1. If we've stopped, return entirely
|
||||
// 2. Otherwise block until we are disconnected, and
|
||||
// then retry connecting
|
||||
select {
|
||||
case <-c.stopCh:
|
||||
return
|
||||
|
||||
case <-c.disconnectedCh:
|
||||
// Quickly check if we haven't stopped at the
|
||||
// same time.
|
||||
select {
|
||||
case <-c.stopCh:
|
||||
return
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
// Normal scenario that we'll wait for
|
||||
}
|
||||
|
||||
if err := c.connect(context.Background()); err == nil {
|
||||
c.setStateConnected()
|
||||
} else {
|
||||
// this code is unreachable in most cases
|
||||
// c.connect does not establish Connection
|
||||
c.SetStateDisconnected(err)
|
||||
}
|
||||
|
||||
// Apply some jitter to avoid lockstep retrials of other
|
||||
// collector-exporters. Lockstep retrials could result in an
|
||||
// innocent DDOS, by clogging the machine's resources and network.
|
||||
jitter := time.Duration(rng.Int63n(maxJitterNanos))
|
||||
select {
|
||||
case <-c.stopCh:
|
||||
return
|
||||
case <-time.After(connReattemptPeriod + jitter):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) connect(ctx context.Context) error {
|
||||
cc, err := c.dialToCollector(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.setConnection(cc)
|
||||
c.newConnectionHandler(cc)
|
||||
return nil
|
||||
}
|
||||
|
||||
// setConnection sets cc as the client Connection and returns true if
|
||||
// the Connection state changed.
|
||||
func (c *Connection) setConnection(cc *grpc.ClientConn) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// If previous clientConn is same as the current then just return.
|
||||
// This doesn't happen right now as this func is only called with new ClientConn.
|
||||
// It is more about future-proofing.
|
||||
if c.cc == cc {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the previous clientConn was non-nil, close it
|
||||
if c.cc != nil {
|
||||
_ = c.cc.Close()
|
||||
}
|
||||
c.cc = cc
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Connection) dialToCollector(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
if c.cfg.GRPCConn != nil {
|
||||
return c.cfg.GRPCConn, nil
|
||||
}
|
||||
|
||||
dialOpts := []grpc.DialOption{}
|
||||
if c.cfg.ServiceConfig != "" {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(c.cfg.ServiceConfig))
|
||||
}
|
||||
if c.SCfg.GRPCCredentials != nil {
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(c.SCfg.GRPCCredentials))
|
||||
} else if c.SCfg.Insecure {
|
||||
dialOpts = append(dialOpts, grpc.WithInsecure())
|
||||
}
|
||||
if c.SCfg.Compression == otlpconfig.GzipCompression {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
if len(c.cfg.DialOptions) != 0 {
|
||||
dialOpts = append(dialOpts, c.cfg.DialOptions...)
|
||||
}
|
||||
|
||||
ctx, cancel := c.ContextWithStop(ctx)
|
||||
defer cancel()
|
||||
ctx = c.ContextWithMetadata(ctx)
|
||||
return grpc.DialContext(ctx, c.SCfg.Endpoint, dialOpts...)
|
||||
}
|
||||
|
||||
func (c *Connection) ContextWithMetadata(ctx context.Context) context.Context {
|
||||
if c.metadata.Len() > 0 {
|
||||
return metadata.NewOutgoingContext(ctx, c.metadata)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *Connection) Shutdown(ctx context.Context) error {
|
||||
close(c.stopCh)
|
||||
// Ensure that the backgroundConnector returns
|
||||
select {
|
||||
case <-c.backgroundConnectionDoneCh:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
cc := c.cc
|
||||
c.cc = nil
|
||||
c.mu.Unlock()
|
||||
|
||||
if cc != nil {
|
||||
return cc.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connection) ContextWithStop(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
// Unify the parent context Done signal with the Connection's
|
||||
// stop channel.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go func(ctx context.Context, cancel context.CancelFunc) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Nothing to do, either cancelled or deadline
|
||||
// happened.
|
||||
case <-c.stopCh:
|
||||
cancel()
|
||||
}
|
||||
}(ctx, cancel)
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func (c *Connection) DoRequest(ctx context.Context, fn func(context.Context) error) error {
|
||||
expBackoff := newExponentialBackoff(c.cfg.RetrySettings)
|
||||
|
||||
for {
|
||||
err := fn(ctx)
|
||||
if err == nil {
|
||||
// request succeeded.
|
||||
return nil
|
||||
}
|
||||
|
||||
if !c.cfg.RetrySettings.Enabled {
|
||||
return err
|
||||
}
|
||||
|
||||
// We have an error, check gRPC status code.
|
||||
st := status.Convert(err)
|
||||
if st.Code() == codes.OK {
|
||||
// Not really an error, still success.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now, this is this a real error.
|
||||
|
||||
if !shouldRetry(st.Code()) {
|
||||
// It is not a retryable error, we should not retry.
|
||||
return err
|
||||
}
|
||||
|
||||
// Need to retry.
|
||||
|
||||
throttle := getThrottleDuration(st)
|
||||
|
||||
backoffDelay := expBackoff.NextBackOff()
|
||||
if backoffDelay == backoff.Stop {
|
||||
// throw away the batch
|
||||
err = fmt.Errorf("max elapsed time expired: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var delay time.Duration
|
||||
|
||||
if backoffDelay > throttle {
|
||||
delay = backoffDelay
|
||||
} else {
|
||||
if expBackoff.GetElapsedTime()+throttle > expBackoff.MaxElapsedTime {
|
||||
err = fmt.Errorf("max elapsed time expired when respecting server throttle: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Respect server throttling.
|
||||
delay = throttle
|
||||
}
|
||||
|
||||
// back-off, but get interrupted when shutting down or request is cancelled or timed out.
|
||||
err = func() error {
|
||||
dt := time.NewTimer(delay)
|
||||
defer dt.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-c.stopCh:
|
||||
return fmt.Errorf("interrupted due to shutdown: %w", err)
|
||||
case <-dt.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func shouldRetry(code codes.Code) bool {
|
||||
switch code {
|
||||
case codes.OK:
|
||||
// Success. This function should not be called for this code, the best we
|
||||
// can do is tell the caller not to retry.
|
||||
return false
|
||||
|
||||
case codes.Canceled,
|
||||
codes.DeadlineExceeded,
|
||||
codes.ResourceExhausted,
|
||||
codes.Aborted,
|
||||
codes.OutOfRange,
|
||||
codes.Unavailable,
|
||||
codes.DataLoss:
|
||||
// These are retryable errors.
|
||||
return true
|
||||
|
||||
case codes.Unknown,
|
||||
codes.InvalidArgument,
|
||||
codes.Unauthenticated,
|
||||
codes.PermissionDenied,
|
||||
codes.NotFound,
|
||||
codes.AlreadyExists,
|
||||
codes.FailedPrecondition,
|
||||
codes.Unimplemented,
|
||||
codes.Internal:
|
||||
// These are fatal errors, don't retry.
|
||||
return false
|
||||
|
||||
default:
|
||||
// Don't retry on unknown codes.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getThrottleDuration(status *status.Status) time.Duration {
|
||||
// See if throttling information is available.
|
||||
for _, detail := range status.Details() {
|
||||
if t, ok := detail.(*errdetails.RetryInfo); ok {
|
||||
if t.RetryDelay.Seconds > 0 || t.RetryDelay.Nanos > 0 {
|
||||
// We are throttled. Wait before retrying as requested by the server.
|
||||
return time.Duration(t.RetryDelay.Seconds)*time.Second + time.Duration(t.RetryDelay.Nanos)*time.Nanosecond
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func newExponentialBackoff(rs otlpconfig.RetrySettings) *backoff.ExponentialBackOff {
|
||||
// Do not use NewExponentialBackOff since it calls Reset and the code here must
|
||||
// call Reset after changing the InitialInterval (this saves an unnecessary call to Now).
|
||||
expBackoff := &backoff.ExponentialBackOff{
|
||||
InitialInterval: rs.InitialInterval,
|
||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||
Multiplier: backoff.DefaultMultiplier,
|
||||
MaxInterval: rs.MaxInterval,
|
||||
MaxElapsedTime: rs.MaxElapsedTime,
|
||||
Stop: backoff.Stop,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
expBackoff.Reset()
|
||||
|
||||
return expBackoff
|
||||
}
|
@ -1,89 +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 connection
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
)
|
||||
|
||||
func TestGetThrottleDuration(t *testing.T) {
|
||||
tts := []struct {
|
||||
stsFn func() (*status.Status, error)
|
||||
throttle time.Duration
|
||||
}{
|
||||
{
|
||||
stsFn: func() (*status.Status, error) {
|
||||
return status.New(
|
||||
codes.OK,
|
||||
"status with no retry info",
|
||||
), nil
|
||||
},
|
||||
throttle: 0,
|
||||
},
|
||||
{
|
||||
stsFn: func() (*status.Status, error) {
|
||||
st := status.New(codes.ResourceExhausted, "status with retry info")
|
||||
return st.WithDetails(
|
||||
&errdetails.RetryInfo{RetryDelay: durationpb.New(15 * time.Millisecond)},
|
||||
)
|
||||
},
|
||||
throttle: 15 * time.Millisecond,
|
||||
},
|
||||
{
|
||||
stsFn: func() (*status.Status, error) {
|
||||
st := status.New(codes.ResourceExhausted, "status with error info detail")
|
||||
return st.WithDetails(
|
||||
&errdetails.ErrorInfo{Reason: "no throttle detail"},
|
||||
)
|
||||
},
|
||||
throttle: 0,
|
||||
},
|
||||
{
|
||||
stsFn: func() (*status.Status, error) {
|
||||
st := status.New(codes.ResourceExhausted, "status with error info and retry info")
|
||||
return st.WithDetails(
|
||||
&errdetails.ErrorInfo{Reason: "no throttle detail"},
|
||||
&errdetails.RetryInfo{RetryDelay: durationpb.New(13 * time.Minute)},
|
||||
)
|
||||
},
|
||||
throttle: 13 * time.Minute,
|
||||
},
|
||||
{
|
||||
stsFn: func() (*status.Status, error) {
|
||||
st := status.New(codes.ResourceExhausted, "status with two retry info should take the first")
|
||||
return st.WithDetails(
|
||||
&errdetails.RetryInfo{RetryDelay: durationpb.New(13 * time.Minute)},
|
||||
&errdetails.RetryInfo{RetryDelay: durationpb.New(18 * time.Minute)},
|
||||
)
|
||||
},
|
||||
throttle: 13 * time.Minute,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
sts, _ := tt.stsFn()
|
||||
t.Run(sts.Message(), func(t *testing.T) {
|
||||
th := getThrottleDuration(sts)
|
||||
require.Equal(t, tt.throttle, th)
|
||||
})
|
||||
}
|
||||
}
|
@ -239,13 +239,6 @@ func sink(ctx context.Context, in <-chan result) ([]*metricpb.Metric, error) {
|
||||
func Record(temporalitySelector aggregation.TemporalitySelector, r export.Record) (*metricpb.Metric, error) {
|
||||
agg := r.Aggregation()
|
||||
switch agg.Kind() {
|
||||
case aggregation.MinMaxSumCountKind:
|
||||
mmsc, ok := agg.(aggregation.MinMaxSumCount)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg)
|
||||
}
|
||||
return minMaxSumCount(r, mmsc)
|
||||
|
||||
case aggregation.HistogramKind:
|
||||
h, ok := agg.(aggregation.Histogram)
|
||||
if !ok {
|
||||
@ -390,64 +383,6 @@ func sumPoint(record export.Record, num number.Number, start, end time.Time, tem
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// minMaxSumCountValue returns the values of the MinMaxSumCount Aggregator
|
||||
// as discrete values.
|
||||
func minMaxSumCountValues(a aggregation.MinMaxSumCount) (min, max, sum number.Number, count uint64, err error) {
|
||||
if min, err = a.Min(); err != nil {
|
||||
return
|
||||
}
|
||||
if max, err = a.Max(); err != nil {
|
||||
return
|
||||
}
|
||||
if sum, err = a.Sum(); err != nil {
|
||||
return
|
||||
}
|
||||
if count, err = a.Count(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// minMaxSumCount transforms a MinMaxSumCount Aggregator into an OTLP Metric.
|
||||
func minMaxSumCount(record export.Record, a aggregation.MinMaxSumCount) (*metricpb.Metric, error) {
|
||||
desc := record.Descriptor()
|
||||
labels := record.Labels()
|
||||
min, max, sum, count, err := minMaxSumCountValues(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := &metricpb.Metric{
|
||||
Name: desc.Name(),
|
||||
Description: desc.Description(),
|
||||
Unit: string(desc.Unit()),
|
||||
Data: &metricpb.Metric_Summary{
|
||||
Summary: &metricpb.Summary{
|
||||
DataPoints: []*metricpb.SummaryDataPoint{
|
||||
{
|
||||
Sum: sum.CoerceToFloat64(desc.NumberKind()),
|
||||
Attributes: Iterator(labels.Iter()),
|
||||
StartTimeUnixNano: toNanos(record.StartTime()),
|
||||
TimeUnixNano: toNanos(record.EndTime()),
|
||||
Count: uint64(count),
|
||||
QuantileValues: []*metricpb.SummaryDataPoint_ValueAtQuantile{
|
||||
{
|
||||
Quantile: 0.0,
|
||||
Value: min.CoerceToFloat64(desc.NumberKind()),
|
||||
},
|
||||
{
|
||||
Quantile: 1.0,
|
||||
Value: max.CoerceToFloat64(desc.NumberKind()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func histogramValues(a aggregation.Histogram) (boundaries []float64, counts []uint64, err error) {
|
||||
var buckets aggregation.Buckets
|
||||
if buckets, err = a.Histogram(); err != nil {
|
||||
|
@ -31,7 +31,6 @@ import (
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
@ -96,84 +95,6 @@ func TestStringKeyValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinMaxSumCountValue(t *testing.T) {
|
||||
mmscs := minmaxsumcount.New(2, &sdkapi.Descriptor{})
|
||||
mmsc, ckpt := &mmscs[0], &mmscs[1]
|
||||
|
||||
assert.NoError(t, mmsc.Update(context.Background(), 1, &sdkapi.Descriptor{}))
|
||||
assert.NoError(t, mmsc.Update(context.Background(), 10, &sdkapi.Descriptor{}))
|
||||
|
||||
// Prior to checkpointing ErrNoData should be returned.
|
||||
_, _, _, _, err := minMaxSumCountValues(ckpt)
|
||||
assert.EqualError(t, err, aggregation.ErrNoData.Error())
|
||||
|
||||
// Checkpoint to set non-zero values
|
||||
require.NoError(t, mmsc.SynchronizedMove(ckpt, &sdkapi.Descriptor{}))
|
||||
min, max, sum, count, err := minMaxSumCountValues(ckpt)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, min, number.NewInt64Number(1))
|
||||
assert.Equal(t, max, number.NewInt64Number(10))
|
||||
assert.Equal(t, sum, number.NewInt64Number(11))
|
||||
assert.Equal(t, count, uint64(2))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinMaxSumCountDatapoints(t *testing.T) {
|
||||
desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Int64Kind)
|
||||
labels := attribute.NewSet(attribute.String("one", "1"))
|
||||
mmscs := minmaxsumcount.New(2, &sdkapi.Descriptor{})
|
||||
mmsc, ckpt := &mmscs[0], &mmscs[1]
|
||||
|
||||
assert.NoError(t, mmsc.Update(context.Background(), 1, &desc))
|
||||
assert.NoError(t, mmsc.Update(context.Background(), 10, &desc))
|
||||
require.NoError(t, mmsc.SynchronizedMove(ckpt, &desc))
|
||||
expected := []*metricpb.SummaryDataPoint{
|
||||
{
|
||||
Count: 2,
|
||||
Sum: 11,
|
||||
StartTimeUnixNano: uint64(intervalStart.UnixNano()),
|
||||
TimeUnixNano: uint64(intervalEnd.UnixNano()),
|
||||
Attributes: []*commonpb.KeyValue{
|
||||
{
|
||||
Key: "one",
|
||||
Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}},
|
||||
},
|
||||
},
|
||||
QuantileValues: []*metricpb.SummaryDataPoint_ValueAtQuantile{
|
||||
{
|
||||
Quantile: 0.0,
|
||||
Value: 1.0,
|
||||
},
|
||||
{
|
||||
Quantile: 1.0,
|
||||
Value: 10.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd)
|
||||
m, err := minMaxSumCount(record, ckpt)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Nil(t, m.GetGauge())
|
||||
assert.Nil(t, m.GetSum())
|
||||
assert.Nil(t, m.GetHistogram())
|
||||
assert.Equal(t, expected, m.GetSummary().DataPoints)
|
||||
assert.Nil(t, m.GetIntGauge()) // nolint
|
||||
assert.Nil(t, m.GetIntSum()) // nolint
|
||||
assert.Nil(t, m.GetIntHistogram()) // nolint
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinMaxSumCountPropagatesErrors(t *testing.T) {
|
||||
// ErrNoData should be returned by both the Min and Max values of
|
||||
// a MinMaxSumCount Aggregator. Use this fact to check the error is
|
||||
// correctly returned.
|
||||
mmsc := &minmaxsumcount.New(1, &sdkapi.Descriptor{})[0]
|
||||
_, _, _, _, err := minMaxSumCountValues(mmsc)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, aggregation.ErrNoData, err)
|
||||
}
|
||||
|
||||
func TestSumIntDataPoints(t *testing.T) {
|
||||
desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Int64Kind)
|
||||
labels := attribute.NewSet(attribute.String("one", "1"))
|
||||
@ -335,10 +256,6 @@ type testErrLastValue struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type testErrMinMaxSumCount struct {
|
||||
testErrSum
|
||||
}
|
||||
|
||||
func (te *testErrLastValue) LastValue() (number.Number, time.Time, error) {
|
||||
return 0, time.Time{}, te.err
|
||||
}
|
||||
@ -353,23 +270,10 @@ func (te *testErrSum) Kind() aggregation.Kind {
|
||||
return aggregation.SumKind
|
||||
}
|
||||
|
||||
func (te *testErrMinMaxSumCount) Min() (number.Number, error) {
|
||||
return 0, te.err
|
||||
}
|
||||
|
||||
func (te *testErrMinMaxSumCount) Max() (number.Number, error) {
|
||||
return 0, te.err
|
||||
}
|
||||
|
||||
func (te *testErrMinMaxSumCount) Count() (uint64, error) {
|
||||
return 0, te.err
|
||||
}
|
||||
|
||||
var _ export.Aggregator = &testAgg{}
|
||||
var _ aggregation.Aggregation = &testAgg{}
|
||||
var _ aggregation.Sum = &testErrSum{}
|
||||
var _ aggregation.LastValue = &testErrLastValue{}
|
||||
var _ aggregation.MinMaxSumCount = &testErrMinMaxSumCount{}
|
||||
|
||||
func TestRecordAggregatorIncompatibleErrors(t *testing.T) {
|
||||
makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) {
|
||||
@ -393,12 +297,6 @@ func TestRecordAggregatorIncompatibleErrors(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, mpb)
|
||||
require.True(t, errors.Is(err, ErrIncompatibleAgg))
|
||||
|
||||
mpb, err = makeMpb(aggregation.MinMaxSumCountKind, &lastvalue.New(1)[0])
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, mpb)
|
||||
require.True(t, errors.Is(err, ErrIncompatibleAgg))
|
||||
}
|
||||
|
||||
func TestRecordAggregatorUnexpectedErrors(t *testing.T) {
|
||||
@ -421,10 +319,4 @@ func TestRecordAggregatorUnexpectedErrors(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, mpb)
|
||||
require.True(t, errors.Is(err, errEx))
|
||||
|
||||
mpb, err = makeMpb(aggregation.MinMaxSumCountKind, &testErrMinMaxSumCount{testErrSum{errEx}})
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, mpb)
|
||||
require.True(t, errors.Is(err, errEx))
|
||||
}
|
||||
|
@ -20,7 +20,11 @@ import (
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -39,16 +43,6 @@ const (
|
||||
DefaultTimeout time.Duration = 10 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultRetrySettings is a default settings for the retry policy.
|
||||
defaultRetrySettings = RetrySettings{
|
||||
Enabled: true,
|
||||
InitialInterval: 5 * time.Second,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: time.Minute,
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
SignalConfig struct {
|
||||
Endpoint string
|
||||
@ -67,16 +61,13 @@ type (
|
||||
// Signal specific configurations
|
||||
Metrics SignalConfig
|
||||
|
||||
// HTTP configurations
|
||||
MaxAttempts int
|
||||
Backoff time.Duration
|
||||
RetryConfig retry.Config
|
||||
|
||||
// gRPC configurations
|
||||
ReconnectionPeriod time.Duration
|
||||
ServiceConfig string
|
||||
DialOptions []grpc.DialOption
|
||||
GRPCConn *grpc.ClientConn
|
||||
RetrySettings RetrySettings
|
||||
}
|
||||
)
|
||||
|
||||
@ -88,12 +79,46 @@ func NewDefaultConfig() Config {
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
},
|
||||
RetrySettings: defaultRetrySettings,
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// 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()
|
||||
ApplyGRPCEnvConfigs(&cfg)
|
||||
for _, opt := range opts {
|
||||
opt.ApplyGRPCOption(&cfg)
|
||||
}
|
||||
|
||||
if cfg.ServiceConfig != "" {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultServiceConfig(cfg.ServiceConfig))
|
||||
}
|
||||
if cfg.Metrics.GRPCCredentials != nil {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(cfg.Metrics.GRPCCredentials))
|
||||
} else if cfg.Metrics.Insecure {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithInsecure())
|
||||
}
|
||||
if cfg.Metrics.Compression == GzipCompression {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
if len(cfg.DialOptions) != 0 {
|
||||
cfg.DialOptions = append(cfg.DialOptions, cfg.DialOptions...)
|
||||
}
|
||||
if cfg.ReconnectionPeriod != 0 {
|
||||
p := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
MinConnectTimeout: cfg.ReconnectionPeriod,
|
||||
}
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithConnectParams(p))
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type (
|
||||
// GenericOption applies an option to the HTTP or gRPC driver.
|
||||
GenericOption interface {
|
||||
@ -218,9 +243,9 @@ func WithURLPath(urlPath string) GenericOption {
|
||||
})
|
||||
}
|
||||
|
||||
func WithRetry(settings RetrySettings) GenericOption {
|
||||
func WithRetry(rc retry.Config) GenericOption {
|
||||
return newGenericOption(func(cfg *Config) {
|
||||
cfg.RetrySettings = settings
|
||||
cfg.RetryConfig = rc
|
||||
})
|
||||
}
|
||||
|
||||
@ -255,15 +280,3 @@ func WithTimeout(duration time.Duration) GenericOption {
|
||||
cfg.Metrics.Timeout = duration
|
||||
})
|
||||
}
|
||||
|
||||
func WithMaxAttempts(maxAttempts int) GenericOption {
|
||||
return newGenericOption(func(cfg *Config) {
|
||||
cfg.MaxAttempts = maxAttempts
|
||||
})
|
||||
}
|
||||
|
||||
func WithBackoff(duration time.Duration) GenericOption {
|
||||
return newGenericOption(func(cfg *Config) {
|
||||
cfg.Backoff = duration
|
||||
})
|
||||
}
|
||||
|
@ -55,6 +55,14 @@ func initializeExporter(t *testing.T, client otlpmetric.Client) *otlpmetric.Expo
|
||||
}
|
||||
|
||||
func testClientStopHonorsTimeout(t *testing.T, client otlpmetric.Client) {
|
||||
t.Cleanup(func() {
|
||||
// The test is looking for a failed shut down. Call Stop a second time
|
||||
// with an un-expired context to give the client a second chance at
|
||||
// cleaning up. There is not guarantee from the Client interface this
|
||||
// will succeed, therefore, no need to check the error (just give it a
|
||||
// best try).
|
||||
_ = client.Stop(context.Background())
|
||||
})
|
||||
e := initializeExporter(t, client)
|
||||
|
||||
innerCtx, innerCancel := context.WithTimeout(context.Background(), time.Microsecond)
|
||||
@ -68,6 +76,14 @@ func testClientStopHonorsTimeout(t *testing.T, client otlpmetric.Client) {
|
||||
}
|
||||
|
||||
func testClientStopHonorsCancel(t *testing.T, client otlpmetric.Client) {
|
||||
t.Cleanup(func() {
|
||||
// The test is looking for a failed shut down. Call Stop a second time
|
||||
// with an un-expired context to give the client a second chance at
|
||||
// cleaning up. There is not guarantee from the Client interface this
|
||||
// will succeed, therefore, no need to check the error (just give it a
|
||||
// best try).
|
||||
_ = client.Stop(context.Background())
|
||||
})
|
||||
e := initializeExporter(t, client)
|
||||
|
||||
ctx, innerCancel := context.WithCancel(context.Background())
|
||||
|
@ -54,8 +54,6 @@ func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlpmetric.Exporter
|
||||
instruments := map[string]data{
|
||||
"test-int64-counter": {sdkapi.CounterInstrumentKind, number.Int64Kind, 1},
|
||||
"test-float64-counter": {sdkapi.CounterInstrumentKind, number.Float64Kind, 1},
|
||||
"test-int64-histogram": {sdkapi.HistogramInstrumentKind, number.Int64Kind, 2},
|
||||
"test-float64-histogram": {sdkapi.HistogramInstrumentKind, number.Float64Kind, 2},
|
||||
"test-int64-gaugeobserver": {sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, 3},
|
||||
"test-float64-gaugeobserver": {sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, 3},
|
||||
}
|
||||
|
@ -17,91 +17,259 @@ package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpme
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/connection"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig"
|
||||
colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
|
||||
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
connection *connection.Connection
|
||||
endpoint string
|
||||
dialOpts []grpc.DialOption
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
requestFunc retry.RequestFunc
|
||||
|
||||
lock sync.Mutex
|
||||
metricsClient colmetricpb.MetricsServiceClient
|
||||
// stopCtx is used as a parent context for all exports. Therefore, when it
|
||||
// is canceled with the stopFunc all exports are canceled.
|
||||
stopCtx context.Context
|
||||
// stopFunc cancels stopCtx, stopping any active exports.
|
||||
stopFunc context.CancelFunc
|
||||
|
||||
// ourConn keeps track of where conn was created: true if created here on
|
||||
// Start, or false if passed with an option. This is important on Shutdown
|
||||
// as the conn should only be closed if created here on start. Otherwise,
|
||||
// it is up to the processes that passed the conn to close it.
|
||||
ourConn bool
|
||||
conn *grpc.ClientConn
|
||||
mscMu sync.RWMutex
|
||||
msc colmetricpb.MetricsServiceClient
|
||||
}
|
||||
|
||||
var (
|
||||
errNoClient = errors.New("no client")
|
||||
)
|
||||
// Compile time check *client implements otlpmetric.Client.
|
||||
var _ otlpmetric.Client = (*client)(nil)
|
||||
|
||||
// NewClient creates a new gRPC metric client.
|
||||
func NewClient(opts ...Option) otlpmetric.Client {
|
||||
cfg := otlpconfig.NewDefaultConfig()
|
||||
otlpconfig.ApplyGRPCEnvConfigs(&cfg)
|
||||
for _, opt := range opts {
|
||||
opt.applyGRPCOption(&cfg)
|
||||
return newClient(opts...)
|
||||
}
|
||||
|
||||
func newClient(opts ...Option) *client {
|
||||
cfg := otlpconfig.NewGRPCConfig(asGRPCOptions(opts)...)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
c := &client{
|
||||
endpoint: cfg.Metrics.Endpoint,
|
||||
exportTimeout: cfg.Metrics.Timeout,
|
||||
requestFunc: cfg.RetryConfig.RequestFunc(retryable),
|
||||
dialOpts: cfg.DialOptions,
|
||||
stopCtx: ctx,
|
||||
stopFunc: cancel,
|
||||
conn: cfg.GRPCConn,
|
||||
}
|
||||
|
||||
c := &client{}
|
||||
c.connection = connection.NewConnection(cfg, cfg.Metrics, c.handleNewConnection)
|
||||
if len(cfg.Metrics.Headers) > 0 {
|
||||
c.metadata = metadata.New(cfg.Metrics.Headers)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *client) handleNewConnection(cc *grpc.ClientConn) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if cc != nil {
|
||||
c.metricsClient = colmetricpb.NewMetricsServiceClient(cc)
|
||||
} else {
|
||||
c.metricsClient = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Start establishes a connection to the collector.
|
||||
// Start establishes a gRPC connection to the collector.
|
||||
func (c *client) Start(ctx context.Context) error {
|
||||
return c.connection.StartConnection(ctx)
|
||||
}
|
||||
|
||||
// Stop shuts down the connection to the collector.
|
||||
func (c *client) Stop(ctx context.Context) error {
|
||||
return c.connection.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// UploadMetrics sends a batch of metrics to the collector.
|
||||
func (c *client) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error {
|
||||
if !c.connection.Connected() {
|
||||
return fmt.Errorf("metrics exporter is disconnected from the server %s: %w", c.connection.SCfg.Endpoint, c.connection.LastConnectError())
|
||||
if c.conn == nil {
|
||||
// If the caller did not provide a ClientConn when the client was
|
||||
// created, create one using the configuration they did provide.
|
||||
conn, err := grpc.DialContext(ctx, c.endpoint, c.dialOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Keep track that we own the lifecycle of this conn and need to close
|
||||
// it on Shutdown.
|
||||
c.ourConn = true
|
||||
c.conn = conn
|
||||
}
|
||||
|
||||
ctx, cancel := c.connection.ContextWithStop(ctx)
|
||||
defer cancel()
|
||||
ctx, tCancel := context.WithTimeout(ctx, c.connection.SCfg.Timeout)
|
||||
defer tCancel()
|
||||
// The otlpmetric.Client interface states this method is called just once,
|
||||
// so no need to check if already started.
|
||||
c.mscMu.Lock()
|
||||
c.msc = colmetricpb.NewMetricsServiceClient(c.conn)
|
||||
c.mscMu.Unlock()
|
||||
|
||||
ctx = c.connection.ContextWithMetadata(ctx)
|
||||
err := func() error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.metricsClient == nil {
|
||||
return errNoClient
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.connection.DoRequest(ctx, func(ctx context.Context) error {
|
||||
_, err := c.metricsClient.Export(ctx, &colmetricpb.ExportMetricsServiceRequest{
|
||||
ResourceMetrics: protoMetrics,
|
||||
})
|
||||
return err
|
||||
})
|
||||
var errAlreadyStopped = errors.New("the client is already stopped")
|
||||
|
||||
// Stop shuts down the client.
|
||||
//
|
||||
// Any active connections to a remote endpoint are closed if they were created
|
||||
// by the client. Any gRPC connection passed during creation using
|
||||
// WithGRPCConn will not be closed. It is the caller's responsibility to
|
||||
// handle cleanup of that resource.
|
||||
//
|
||||
// This method synchronizes with the UploadMetrics method of the client. It
|
||||
// will wait for any active calls to that method to complete unimpeded, or it
|
||||
// will cancel any active calls if ctx expires. If ctx expires, the context
|
||||
// error will be forwarded as the returned error. All client held resources
|
||||
// will still be released in this situation.
|
||||
//
|
||||
// If the client has already stopped, an error will be returned describing
|
||||
// this.
|
||||
func (c *client) Stop(ctx context.Context) error {
|
||||
// Acquire the c.mscMu lock within the ctx lifetime.
|
||||
acquired := make(chan struct{})
|
||||
go func() {
|
||||
c.mscMu.Lock()
|
||||
close(acquired)
|
||||
}()
|
||||
if err != nil {
|
||||
c.connection.SetStateDisconnected(err)
|
||||
var err error
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// The Stop timeout is reached. Kill any remaining exports to force
|
||||
// the clear of the lock and save the timeout error to return and
|
||||
// signal the shutdown timed out before cleanly stopping.
|
||||
c.stopFunc()
|
||||
err = ctx.Err()
|
||||
|
||||
// To ensure the client is not left in a dirty state c.msc needs to be
|
||||
// set to nil. To avoid the race condition when doing this, ensure
|
||||
// that all the exports are killed (initiated by c.stopFunc).
|
||||
<-acquired
|
||||
case <-acquired:
|
||||
}
|
||||
// Hold the mscMu lock for the rest of the function to ensure no new
|
||||
// exports are started.
|
||||
defer c.mscMu.Unlock()
|
||||
|
||||
// The otlpmetric.Client interface states this method is called only
|
||||
// once, but there is no guarantee it is called after Start. Ensure the
|
||||
// client is started before doing anything and let the called know if they
|
||||
// made a mistake.
|
||||
if c.msc == nil {
|
||||
return errAlreadyStopped
|
||||
}
|
||||
|
||||
// Clear c.msc to signal the client is stopped.
|
||||
c.msc = nil
|
||||
|
||||
if c.ourConn {
|
||||
closeErr := c.conn.Close()
|
||||
// A context timeout error takes precedence over this error.
|
||||
if err == nil && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var errShutdown = errors.New("the client is shutdown")
|
||||
|
||||
// UploadMetrics sends a batch of spans.
|
||||
//
|
||||
// Retryable errors from the server will be handled according to any
|
||||
// RetryConfig the client was created with.
|
||||
func (c *client) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error {
|
||||
// Hold a read lock to ensure a shut down initiated after this starts does
|
||||
// not abandon the export. This read lock acquire has less priority than a
|
||||
// write lock acquire (i.e. Stop), meaning if the client is shutting down
|
||||
// this will come after the shut down.
|
||||
c.mscMu.RLock()
|
||||
defer c.mscMu.RUnlock()
|
||||
|
||||
if c.msc == nil {
|
||||
return errShutdown
|
||||
}
|
||||
|
||||
ctx, cancel := c.exportContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
return c.requestFunc(ctx, func(iCtx context.Context) error {
|
||||
_, err := c.msc.Export(iCtx, &colmetricpb.ExportMetricsServiceRequest{
|
||||
ResourceMetrics: protoMetrics,
|
||||
})
|
||||
// nil is converted to OK.
|
||||
if status.Code(err) == codes.OK {
|
||||
// Success.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// exportContext returns a copy of parent with an appropriate deadline and
|
||||
// cancellation function.
|
||||
//
|
||||
// It is the callers responsibility to cancel the returned context once its
|
||||
// use is complete, via the parent or directly with the returned CancelFunc, to
|
||||
// ensure all resources are correctly released.
|
||||
func (c *client) exportContext(parent context.Context) (context.Context, context.CancelFunc) {
|
||||
var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
|
||||
if c.exportTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(parent, c.exportTimeout)
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(parent)
|
||||
}
|
||||
|
||||
if c.metadata.Len() > 0 {
|
||||
ctx = metadata.NewOutgoingContext(ctx, c.metadata)
|
||||
}
|
||||
|
||||
// Unify the client stopCtx with the parent.
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-c.stopCtx.Done():
|
||||
// Cancel the export as the shutdown has timed out.
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
// retryable returns if err identifies a request that can be retried and a
|
||||
// duration to wait for if an explicit throttle time is included in err.
|
||||
func retryable(err error) (bool, time.Duration) {
|
||||
//func retryable(err error) (bool, time.Duration) {
|
||||
s := status.Convert(err)
|
||||
switch s.Code() {
|
||||
case codes.Canceled,
|
||||
codes.DeadlineExceeded,
|
||||
codes.ResourceExhausted,
|
||||
codes.Aborted,
|
||||
codes.OutOfRange,
|
||||
codes.Unavailable,
|
||||
codes.DataLoss:
|
||||
return true, throttleDelay(s)
|
||||
}
|
||||
|
||||
// Not a retry-able error.
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if t, ok := detail.(*errdetails.RetryInfo); ok {
|
||||
return t.RetryDelay.AsDuration()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -24,12 +24,10 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest"
|
||||
@ -165,386 +163,6 @@ func TestNewExporter_invokeStartThenStopManyTimes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewExporter_collectorConnectionDiesThenReconnectsWhenInRestMode(t *testing.T) {
|
||||
// TODO: Fix this test #1527
|
||||
t.Skip("This test is flaky and needs to be rewritten")
|
||||
mc := runMockCollector(t)
|
||||
|
||||
reconnectionPeriod := 20 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint,
|
||||
otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetrySettings{Enabled: false}),
|
||||
otlpmetricgrpc.WithReconnectionPeriod(reconnectionPeriod))
|
||||
defer func() { require.NoError(t, exp.Shutdown(ctx)) }()
|
||||
|
||||
// Wait for a connection.
|
||||
mc.ln.WaitForConn()
|
||||
|
||||
// We'll now stop the collector right away to simulate a connection
|
||||
// dying in the midst of communication or even not existing before.
|
||||
require.NoError(t, mc.stop())
|
||||
|
||||
// first export, it will send disconnected message to the channel on export failure,
|
||||
// trigger almost immediate reconnection
|
||||
require.Error(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
// second export, it will detect connection issue, change state of exporter to disconnected and
|
||||
// send message to disconnected channel but this time reconnection gouroutine will be in (rest mode, not listening to the disconnected channel)
|
||||
require.Error(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
// as a result we have exporter in disconnected state waiting for disconnection message to reconnect
|
||||
|
||||
// resurrect collector
|
||||
nmc := runMockCollectorAtEndpoint(t, mc.endpoint)
|
||||
|
||||
// make sure reconnection loop hits beginning and goes back to waiting mode
|
||||
// after hitting beginning of the loop it should reconnect
|
||||
nmc.ln.WaitForConn()
|
||||
|
||||
n := 10
|
||||
for i := 0; i < n; i++ {
|
||||
// when disconnected exp.Export doesnt send disconnected messages again
|
||||
// it just quits and return last connection error
|
||||
require.NoError(t, exp.Export(ctx, testResource, oneRecord))
|
||||
}
|
||||
|
||||
nmaMetrics := nmc.getMetrics()
|
||||
|
||||
if g, w := len(nmaMetrics), n; g != w {
|
||||
t.Fatalf("Connected collector: metrics: got %d want %d", g, w)
|
||||
}
|
||||
|
||||
dMetrics := mc.getMetrics()
|
||||
// Expecting 0 metrics to have been received by the original but now dead collector
|
||||
if g, w := len(dMetrics), 0; g != w {
|
||||
t.Fatalf("Disconnected collector: spans: got %d want %d", g, w)
|
||||
}
|
||||
|
||||
require.NoError(t, nmc.Stop())
|
||||
}
|
||||
|
||||
func TestExporterExportFailureAndRecoveryModes(t *testing.T) {
|
||||
tts := []struct {
|
||||
name string
|
||||
errors []error
|
||||
rs otlpmetricgrpc.RetrySettings
|
||||
fn func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector)
|
||||
opts []otlpmetricgrpc.Option
|
||||
}{
|
||||
{
|
||||
name: "Do not retry if succeeded",
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
require.NoError(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 1)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 success request.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Do not retry if 'error' is ok",
|
||||
errors: []error{
|
||||
status.Error(codes.OK, ""),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
require.NoError(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 0)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 error OK request.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail three times and succeed",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: true,
|
||||
MaxElapsedTime: 300 * time.Millisecond,
|
||||
InitialInterval: 2 * time.Millisecond,
|
||||
MaxInterval: 10 * time.Millisecond,
|
||||
},
|
||||
errors: []error{
|
||||
status.Error(codes.Unavailable, "backend under pressure"),
|
||||
status.Error(codes.Unavailable, "backend under pressure"),
|
||||
status.Error(codes.Unavailable, "backend under pressure"),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
require.NoError(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 1)
|
||||
require.Equal(t, 4, mc.metricSvc.requests, "metric service must receive 3 failure requests and 1 success request.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Permanent error should not be retried",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: true,
|
||||
MaxElapsedTime: 300 * time.Millisecond,
|
||||
InitialInterval: 2 * time.Millisecond,
|
||||
MaxInterval: 10 * time.Millisecond,
|
||||
},
|
||||
errors: []error{
|
||||
status.Error(codes.InvalidArgument, "invalid arguments"),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
require.Error(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
metric := mc.getMetrics()
|
||||
|
||||
require.Len(t, metric, 0)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 error requests.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test all transient errors and succeed",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: true,
|
||||
MaxElapsedTime: 500 * time.Millisecond,
|
||||
InitialInterval: 1 * time.Millisecond,
|
||||
MaxInterval: 2 * time.Millisecond,
|
||||
},
|
||||
errors: []error{
|
||||
status.Error(codes.Canceled, ""),
|
||||
status.Error(codes.DeadlineExceeded, ""),
|
||||
status.Error(codes.ResourceExhausted, ""),
|
||||
status.Error(codes.Aborted, ""),
|
||||
status.Error(codes.OutOfRange, ""),
|
||||
status.Error(codes.Unavailable, ""),
|
||||
status.Error(codes.DataLoss, ""),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
require.NoError(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 1)
|
||||
require.Equal(t, 8, mc.metricSvc.requests, "metric service must receive 9 failure requests and 1 success request.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Retry should honor server throttling",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: true,
|
||||
MaxElapsedTime: time.Minute,
|
||||
InitialInterval: time.Nanosecond,
|
||||
MaxInterval: time.Nanosecond,
|
||||
},
|
||||
opts: []otlpmetricgrpc.Option{
|
||||
otlpmetricgrpc.WithTimeout(time.Millisecond * 100),
|
||||
},
|
||||
errors: []error{
|
||||
newThrottlingError(codes.ResourceExhausted, time.Second*30),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
err := exp.Export(ctx, testResource, oneRecord)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, "context deadline exceeded", err.Error())
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 0)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 failure requests and 1 success request.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Retry should fail if server throttling is higher than the MaxElapsedTime",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: true,
|
||||
MaxElapsedTime: time.Millisecond * 100,
|
||||
InitialInterval: time.Nanosecond,
|
||||
MaxInterval: time.Nanosecond,
|
||||
},
|
||||
errors: []error{
|
||||
newThrottlingError(codes.ResourceExhausted, time.Minute),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
err := exp.Export(ctx, testResource, oneRecord)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, "max elapsed time expired when respecting server throttle: rpc error: code = ResourceExhausted desc = ", err.Error())
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 0)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 failure requests and 1 success request.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Retry stops if takes too long",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: true,
|
||||
MaxElapsedTime: time.Millisecond * 100,
|
||||
InitialInterval: time.Millisecond * 50,
|
||||
MaxInterval: time.Millisecond * 50,
|
||||
},
|
||||
errors: []error{
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
err := exp.Export(ctx, testResource, oneRecord)
|
||||
require.Error(t, err)
|
||||
|
||||
require.Equal(t, "max elapsed time expired: rpc error: code = Unavailable desc = unavailable", err.Error())
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 0)
|
||||
require.LessOrEqual(t, 1, mc.metricSvc.requests, "metric service must receive at least 1 failure requests.")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Disabled retry",
|
||||
rs: otlpmetricgrpc.RetrySettings{
|
||||
Enabled: false,
|
||||
},
|
||||
errors: []error{
|
||||
status.Error(codes.Unavailable, "unavailable"),
|
||||
},
|
||||
fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) {
|
||||
err := exp.Export(ctx, testResource, oneRecord)
|
||||
require.Error(t, err)
|
||||
|
||||
require.Equal(t, "rpc error: code = Unavailable desc = unavailable", err.Error())
|
||||
|
||||
metrics := mc.getMetrics()
|
||||
|
||||
require.Len(t, metrics, 0)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 failure requests.")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mc := runMockCollectorWithConfig(t, &mockConfig{
|
||||
errors: tt.errors,
|
||||
})
|
||||
|
||||
opts := []otlpmetricgrpc.Option{
|
||||
otlpmetricgrpc.WithRetry(tt.rs),
|
||||
}
|
||||
|
||||
if len(tt.opts) != 0 {
|
||||
opts = append(opts, tt.opts...)
|
||||
}
|
||||
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint, opts...)
|
||||
|
||||
tt.fn(t, ctx, exp, mc)
|
||||
|
||||
require.NoError(t, mc.Stop())
|
||||
require.NoError(t, exp.Shutdown(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPermanentErrorsShouldNotBeRetried(t *testing.T) {
|
||||
permanentErrors := []*status.Status{
|
||||
status.New(codes.Unknown, "Unknown"),
|
||||
status.New(codes.InvalidArgument, "InvalidArgument"),
|
||||
status.New(codes.NotFound, "NotFound"),
|
||||
status.New(codes.AlreadyExists, "AlreadyExists"),
|
||||
status.New(codes.FailedPrecondition, "FailedPrecondition"),
|
||||
status.New(codes.Unimplemented, "Unimplemented"),
|
||||
status.New(codes.Internal, "Internal"),
|
||||
status.New(codes.PermissionDenied, ""),
|
||||
status.New(codes.Unauthenticated, ""),
|
||||
}
|
||||
|
||||
for _, sts := range permanentErrors {
|
||||
t.Run(sts.Code().String(), func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mc := runMockCollectorWithConfig(t, &mockConfig{
|
||||
errors: []error{sts.Err()},
|
||||
})
|
||||
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint)
|
||||
|
||||
err := exp.Export(ctx, testResource, oneRecord)
|
||||
require.Error(t, err)
|
||||
require.Len(t, mc.getMetrics(), 0)
|
||||
require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 permanent error requests.")
|
||||
|
||||
require.NoError(t, mc.Stop())
|
||||
require.NoError(t, exp.Shutdown(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newThrottlingError(code codes.Code, duration time.Duration) error {
|
||||
s := status.New(code, "")
|
||||
|
||||
s, _ = s.WithDetails(&errdetails.RetryInfo{RetryDelay: durationpb.New(duration)})
|
||||
|
||||
return s.Err()
|
||||
}
|
||||
|
||||
func TestNewExporter_collectorConnectionDiesThenReconnects(t *testing.T) {
|
||||
// TODO: Fix this test #1527
|
||||
t.Skip("This test is flaky and needs to be rewritten")
|
||||
mc := runMockCollector(t)
|
||||
|
||||
reconnectionPeriod := 50 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint,
|
||||
otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetrySettings{Enabled: false}),
|
||||
otlpmetricgrpc.WithReconnectionPeriod(reconnectionPeriod))
|
||||
defer func() { require.NoError(t, exp.Shutdown(ctx)) }()
|
||||
|
||||
mc.ln.WaitForConn()
|
||||
|
||||
// We'll now stop the collector right away to simulate a connection
|
||||
// dying in the midst of communication or even not existing before.
|
||||
require.NoError(t, mc.stop())
|
||||
|
||||
// In the test below, we'll stop the collector many times,
|
||||
// while exporting metrics and test to ensure that we can
|
||||
// reconnect.
|
||||
for j := 0; j < 3; j++ {
|
||||
|
||||
// No endpoint up.
|
||||
require.Error(t, exp.Export(ctx, testResource, oneRecord))
|
||||
|
||||
// Now resurrect the collector by making a new one but reusing the
|
||||
// old endpoint, and the collector should reconnect automatically.
|
||||
nmc := runMockCollectorAtEndpoint(t, mc.endpoint)
|
||||
|
||||
// Give the exporter sometime to reconnect
|
||||
nmc.ln.WaitForConn()
|
||||
|
||||
n := 10
|
||||
for i := 0; i < n; i++ {
|
||||
require.NoError(t, exp.Export(ctx, testResource, oneRecord))
|
||||
}
|
||||
|
||||
nmaMetrics := nmc.getMetrics()
|
||||
// Expecting 10 metrics that were sampled, given that
|
||||
if g, w := len(nmaMetrics), n; g != w {
|
||||
t.Fatalf("Round #%d: Connected collector: spans: got %d want %d", j, g, w)
|
||||
}
|
||||
|
||||
dMetrics := mc.getMetrics()
|
||||
// Expecting 0 metrics to have been received by the original but now dead collector
|
||||
if g, w := len(dMetrics), 0; g != w {
|
||||
t.Fatalf("Round #%d: Disconnected collector: spans: got %d want %d", j, g, w)
|
||||
}
|
||||
|
||||
// Disconnect for the next try.
|
||||
require.NoError(t, nmc.stop())
|
||||
}
|
||||
}
|
||||
|
||||
// This test takes a long time to run: to skip it, run tests using: -short
|
||||
func TestNewExporter_collectorOnBadConnection(t *testing.T) {
|
||||
if testing.Short() {
|
||||
@ -641,7 +259,7 @@ func TestNewExporter_WithTimeout(t *testing.T) {
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint, otlpmetricgrpc.WithTimeout(tt.timeout), otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetrySettings{Enabled: false}))
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint, otlpmetricgrpc.WithTimeout(tt.timeout), otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetryConfig{Enabled: false}))
|
||||
defer func() {
|
||||
_ = exp.Shutdown(ctx)
|
||||
}()
|
||||
@ -662,48 +280,31 @@ func TestNewExporter_WithTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewExporter_withInvalidSecurityConfiguration(t *testing.T) {
|
||||
func TestStartErrorInvalidSecurityConfiguration(t *testing.T) {
|
||||
mc := runMockCollector(t)
|
||||
defer func() {
|
||||
_ = mc.stop()
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
client := otlpmetricgrpc.NewClient(otlpmetricgrpc.WithEndpoint(mc.endpoint))
|
||||
exp, err := otlpmetric.New(ctx, client)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create a new collector exporter: %v", err)
|
||||
}
|
||||
|
||||
err = exp.Export(ctx, testResource, oneRecord)
|
||||
|
||||
expectedErr := fmt.Sprintf("metrics exporter is disconnected from the server %s: grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)", mc.endpoint)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Equal(t, expectedErr, err.Error())
|
||||
|
||||
defer func() {
|
||||
_ = exp.Shutdown(ctx)
|
||||
}()
|
||||
err := client.Start(context.Background())
|
||||
// https://github.com/grpc/grpc-go/blob/a671967dfbaab779d37fd7e597d9248f13806087/clientconn.go#L82
|
||||
assert.EqualError(t, err, "grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)")
|
||||
}
|
||||
|
||||
func TestDisconnected(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// The endpoint is whatever, we want to be disconnected. But we
|
||||
// setting a blocking connection, so dialing to the invalid
|
||||
// endpoint actually fails.
|
||||
exp := newGRPCExporter(t, ctx, "invalid",
|
||||
otlpmetricgrpc.WithReconnectionPeriod(time.Hour),
|
||||
func TestStartErrorInvalidAddress(t *testing.T) {
|
||||
client := otlpmetricgrpc.NewClient(
|
||||
otlpmetricgrpc.WithInsecure(),
|
||||
// Validate the connection in Start (which should return the error).
|
||||
otlpmetricgrpc.WithDialOption(
|
||||
grpc.WithBlock(),
|
||||
grpc.FailOnNonTempDialError(true),
|
||||
),
|
||||
otlpmetricgrpc.WithEndpoint("invalid"),
|
||||
otlpmetricgrpc.WithReconnectionPeriod(time.Hour),
|
||||
)
|
||||
defer func() {
|
||||
assert.NoError(t, exp.Shutdown(ctx))
|
||||
}()
|
||||
|
||||
assert.Error(t, exp.Export(ctx, testResource, oneRecord))
|
||||
err := client.Start(context.Background())
|
||||
assert.EqualError(t, err, `connection error: desc = "transport: error while dialing: dial tcp: address invalid: missing port in address"`)
|
||||
}
|
||||
|
||||
func TestEmptyData(t *testing.T) {
|
||||
|
193
exporters/otlp/otlpmetric/otlpmetricgrpc/client_unit_test.go
Normal file
193
exporters/otlp/otlpmetric/otlpmetricgrpc/client_unit_test.go
Normal file
@ -0,0 +1,193 @@
|
||||
// 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 otlpmetricgrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
)
|
||||
|
||||
func TestThrottleDuration(t *testing.T) {
|
||||
c := codes.ResourceExhausted
|
||||
testcases := []struct {
|
||||
status *status.Status
|
||||
expected time.Duration
|
||||
}{
|
||||
{
|
||||
status: status.New(c, "no retry info"),
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
status: func() *status.Status {
|
||||
s, err := status.New(c, "single retry info").WithDetails(
|
||||
&errdetails.RetryInfo{
|
||||
RetryDelay: durationpb.New(15 * time.Millisecond),
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
expected: 15 * time.Millisecond,
|
||||
},
|
||||
{
|
||||
status: func() *status.Status {
|
||||
s, err := status.New(c, "error info").WithDetails(
|
||||
&errdetails.ErrorInfo{Reason: "no throttle detail"},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
status: func() *status.Status {
|
||||
s, err := status.New(c, "error and retry info").WithDetails(
|
||||
&errdetails.ErrorInfo{Reason: "with throttle detail"},
|
||||
&errdetails.RetryInfo{
|
||||
RetryDelay: durationpb.New(13 * time.Minute),
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
expected: 13 * time.Minute,
|
||||
},
|
||||
{
|
||||
status: func() *status.Status {
|
||||
s, err := status.New(c, "double retry info").WithDetails(
|
||||
&errdetails.RetryInfo{
|
||||
RetryDelay: durationpb.New(13 * time.Minute),
|
||||
},
|
||||
&errdetails.RetryInfo{
|
||||
RetryDelay: durationpb.New(15 * time.Minute),
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
expected: 13 * time.Minute,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.status.Message(), func(t *testing.T) {
|
||||
require.Equal(t, tc.expected, throttleDelay(tc.status))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryable(t *testing.T) {
|
||||
retryableCodes := map[codes.Code]bool{
|
||||
codes.OK: false,
|
||||
codes.Canceled: true,
|
||||
codes.Unknown: false,
|
||||
codes.InvalidArgument: false,
|
||||
codes.DeadlineExceeded: true,
|
||||
codes.NotFound: false,
|
||||
codes.AlreadyExists: false,
|
||||
codes.PermissionDenied: false,
|
||||
codes.ResourceExhausted: true,
|
||||
codes.FailedPrecondition: false,
|
||||
codes.Aborted: true,
|
||||
codes.OutOfRange: true,
|
||||
codes.Unimplemented: false,
|
||||
codes.Internal: false,
|
||||
codes.Unavailable: true,
|
||||
codes.DataLoss: true,
|
||||
codes.Unauthenticated: false,
|
||||
}
|
||||
|
||||
for c, want := range retryableCodes {
|
||||
got, _ := retryable(status.Error(c, ""))
|
||||
assert.Equalf(t, want, got, "evaluate(%s)", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnstartedStop(t *testing.T) {
|
||||
client := NewClient()
|
||||
assert.ErrorIs(t, client.Stop(context.Background()), errAlreadyStopped)
|
||||
}
|
||||
|
||||
func TestUnstartedUploadMetric(t *testing.T) {
|
||||
client := NewClient()
|
||||
assert.ErrorIs(t, client.UploadMetrics(context.Background(), nil), errShutdown)
|
||||
}
|
||||
|
||||
func TestExportContextHonorsParentDeadline(t *testing.T) {
|
||||
now := time.Now()
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
// Without a client timeout, the parent deadline should be used.
|
||||
client := newClient(WithTimeout(0))
|
||||
eCtx, eCancel := client.exportContext(ctx)
|
||||
t.Cleanup(eCancel)
|
||||
|
||||
deadline, ok := eCtx.Deadline()
|
||||
assert.True(t, ok, "deadline not propagated to child context")
|
||||
assert.Equal(t, now, deadline)
|
||||
}
|
||||
|
||||
func TestExportContextHonorsClientTimeout(t *testing.T) {
|
||||
// Setting a timeout should ensure a deadline is set on the context.
|
||||
client := newClient(WithTimeout(1 * time.Second))
|
||||
ctx, cancel := client.exportContext(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
_, ok := ctx.Deadline()
|
||||
assert.True(t, ok, "timeout not set as deadline for child context")
|
||||
}
|
||||
|
||||
func TestExportContextLinksStopSignal(t *testing.T) {
|
||||
rootCtx := context.Background()
|
||||
|
||||
client := newClient(WithInsecure())
|
||||
t.Cleanup(func() { require.NoError(t, client.Stop(rootCtx)) })
|
||||
require.NoError(t, client.Start(rootCtx))
|
||||
|
||||
ctx, cancel := client.exportContext(rootCtx)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
require.False(t, func() bool {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return true
|
||||
default:
|
||||
}
|
||||
return false
|
||||
}(), "context should not be done prior to canceling it")
|
||||
|
||||
// The client.stopFunc cancels the client.stopCtx. This should have been
|
||||
// setup as a parent of ctx. Therefore, it should cancel ctx as well.
|
||||
client.stopFunc()
|
||||
|
||||
// Assert this with Eventually to account for goroutine scheduler timing.
|
||||
assert.Eventually(t, func() bool {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return true
|
||||
default:
|
||||
}
|
||||
return false
|
||||
}, 10*time.Second, time.Microsecond)
|
||||
}
|
@ -5,6 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v0.0.0-00010101000000-000000000000
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.25.0
|
||||
go.opentelemetry.io/otel/metric v0.25.0
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
@ -21,8 +22,6 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp => ../..
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
@ -80,3 +79,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/op
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../internal/retry
|
||||
|
@ -24,6 +24,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -18,8 +18,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@ -94,7 +92,6 @@ type mockCollector struct {
|
||||
metricSvc *mockMetricService
|
||||
|
||||
endpoint string
|
||||
ln *listener
|
||||
stopFunc func()
|
||||
stopOnce sync.Once
|
||||
}
|
||||
@ -160,9 +157,8 @@ func runMockCollectorWithConfig(t *testing.T, mockConfig *mockConfig) *mockColle
|
||||
srv := grpc.NewServer()
|
||||
mc := makeMockCollector(t, mockConfig)
|
||||
collectormetricpb.RegisterMetricsServiceServer(srv, mc.metricSvc)
|
||||
mc.ln = newListener(ln)
|
||||
go func() {
|
||||
_ = srv.Serve((net.Listener)(mc.ln))
|
||||
_ = srv.Serve(ln)
|
||||
}()
|
||||
|
||||
mc.endpoint = ln.Addr().String()
|
||||
@ -171,59 +167,3 @@ func runMockCollectorWithConfig(t *testing.T, mockConfig *mockConfig) *mockColle
|
||||
|
||||
return mc
|
||||
}
|
||||
|
||||
type listener struct {
|
||||
closeOnce sync.Once
|
||||
wrapped net.Listener
|
||||
C chan struct{}
|
||||
}
|
||||
|
||||
func newListener(wrapped net.Listener) *listener {
|
||||
return &listener{
|
||||
wrapped: wrapped,
|
||||
C: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) Close() error { return l.wrapped.Close() }
|
||||
|
||||
func (l *listener) Addr() net.Addr { return l.wrapped.Addr() }
|
||||
|
||||
// Accept waits for and returns the next connection to the listener. It will
|
||||
// send a signal on l.C that a connection has been made before returning.
|
||||
func (l *listener) Accept() (net.Conn, error) {
|
||||
conn, err := l.wrapped.Accept()
|
||||
if err != nil {
|
||||
// Go 1.16 exported net.ErrClosed that could clean up this check, but to
|
||||
// remain backwards compatible with previous versions of Go that we
|
||||
// support the following string evaluation is used instead to keep in line
|
||||
// with the previously recommended way to check this:
|
||||
// https://github.com/golang/go/issues/4373#issuecomment-353076799
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
// If the listener has been closed, do not allow callers of
|
||||
// WaitForConn to wait for a connection that will never come.
|
||||
l.closeOnce.Do(func() { close(l.C) })
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
|
||||
select {
|
||||
case l.C <- struct{}{}:
|
||||
default:
|
||||
// If C is full, assume nobody is listening and move on.
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// WaitForConn will wait indefintely for a connection to be estabilished with
|
||||
// the listener before returning.
|
||||
func (l *listener) WaitForConn() {
|
||||
for {
|
||||
select {
|
||||
case <-l.C:
|
||||
return
|
||||
default:
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,17 +22,29 @@ import (
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig"
|
||||
)
|
||||
|
||||
// Option applies an option to the gRPC client.
|
||||
// Option applies an option to the gRPC driver.
|
||||
type Option interface {
|
||||
applyGRPCOption(*otlpconfig.Config)
|
||||
}
|
||||
|
||||
// RetrySettings defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type RetrySettings otlpconfig.RetrySettings
|
||||
func asGRPCOptions(opts []Option) []otlpconfig.GRPCOption {
|
||||
converted := make([]otlpconfig.GRPCOption, len(opts))
|
||||
for i, o := range opts {
|
||||
converted[i] = otlpconfig.NewGRPCOption(o.applyGRPCOption)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying export of span batches that
|
||||
// failed to be received by the target endpoint.
|
||||
//
|
||||
// This configuration does not define any network retry strategy. That is
|
||||
// entirely handled by the gRPC ClientConn.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
type wrappedOption struct {
|
||||
otlpconfig.GRPCOption
|
||||
@ -42,22 +54,28 @@ func (w wrappedOption) applyGRPCOption(cfg *otlpconfig.Config) {
|
||||
w.ApplyGRPCOption(cfg)
|
||||
}
|
||||
|
||||
// WithInsecure disables client transport security for the exporter's gRPC connection
|
||||
// just like grpc.WithInsecure() https://pkg.go.dev/google.golang.org/grpc#WithInsecure
|
||||
// does. Note, by default, client security is required unless WithInsecure is used.
|
||||
// WithInsecure disables client transport security for the exporter's gRPC
|
||||
// connection just like grpc.WithInsecure()
|
||||
// (https://pkg.go.dev/google.golang.org/grpc#WithInsecure) does. Note, by
|
||||
// default, client security is required unless WithInsecure is used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithInsecure() Option {
|
||||
return wrappedOption{otlpconfig.WithInsecure()}
|
||||
}
|
||||
|
||||
// WithEndpoint allows one to set the endpoint that the exporter will
|
||||
// connect to the collector on. If unset, it will instead try to use
|
||||
// connect to DefaultCollectorHost:DefaultCollectorPort.
|
||||
// WithEndpoint sets the target endpoint the exporter will connect to. If
|
||||
// unset, localhost:4317 will be used as a default.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithEndpoint(endpoint string) Option {
|
||||
return wrappedOption{otlpconfig.WithEndpoint(endpoint)}
|
||||
}
|
||||
|
||||
// WithReconnectionPeriod allows one to set the delay between next connection attempt
|
||||
// after failing to connect with the collector.
|
||||
// WithReconnectionPeriod set the minimum amount of time between connection
|
||||
// attempts to the target endpoint.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithReconnectionPeriod(rp time.Duration) Option {
|
||||
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.ReconnectionPeriod = rp
|
||||
@ -74,25 +92,30 @@ func compressorToCompression(compressor string) otlpconfig.Compression {
|
||||
return otlpconfig.NoCompression
|
||||
}
|
||||
|
||||
// WithCompressor will set the compressor for the gRPC client to use when sending requests.
|
||||
// It is the responsibility of the caller to ensure that the compressor set has been registered
|
||||
// with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some
|
||||
// compressors auto-register on import, such as gzip, which can be registered by calling
|
||||
// WithCompressor sets the compressor for the gRPC client to use when sending
|
||||
// requests. It is the responsibility of the caller to ensure that the
|
||||
// compressor set has been registered with google.golang.org/grpc/encoding.
|
||||
// This can be done by encoding.RegisterCompressor. Some compressors
|
||||
// auto-register on import, such as gzip, which can be registered by calling
|
||||
// `import _ "google.golang.org/grpc/encoding/gzip"`.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithCompressor(compressor string) Option {
|
||||
return wrappedOption{otlpconfig.WithCompression(compressorToCompression(compressor))}
|
||||
}
|
||||
|
||||
// WithHeaders will send the provided headers with gRPC requests.
|
||||
// WithHeaders will send the provided headers with each gRPC requests.
|
||||
func WithHeaders(headers map[string]string) Option {
|
||||
return wrappedOption{otlpconfig.WithHeaders(headers)}
|
||||
}
|
||||
|
||||
// WithTLSCredentials allows the connection to use TLS credentials
|
||||
// when talking to the server. It takes in grpc.TransportCredentials instead
|
||||
// of say a Certificate file or a tls.Certificate, because the retrieving of
|
||||
// these credentials can be done in many ways e.g. plain file, in code tls.Config
|
||||
// or by certificate rotation, so it is up to the caller to decide what to use.
|
||||
// WithTLSCredentials allows the connection to use TLS credentials when
|
||||
// talking to the server. It takes in grpc.TransportCredentials instead of say
|
||||
// a Certificate file or a tls.Certificate, because the retrieving of these
|
||||
// credentials can be done in many ways e.g. plain file, in code tls.Config or
|
||||
// by certificate rotation, so it is up to the caller to decide what to use.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithTLSCredentials(creds credentials.TransportCredentials) Option {
|
||||
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.Metrics.GRPCCredentials = creds
|
||||
@ -100,40 +123,63 @@ func WithTLSCredentials(creds credentials.TransportCredentials) Option {
|
||||
}
|
||||
|
||||
// WithServiceConfig defines the default gRPC service config used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithServiceConfig(serviceConfig string) Option {
|
||||
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.ServiceConfig = serviceConfig
|
||||
})}
|
||||
}
|
||||
|
||||
// WithDialOption opens support to any grpc.DialOption to be used. If it conflicts
|
||||
// with some other configuration the GRPC specified via the collector the ones here will
|
||||
// take preference since they are set last.
|
||||
// WithDialOption sets explicit grpc.DialOptions to use when making a
|
||||
// connection. The options here are appended to the internal grpc.DialOptions
|
||||
// used so they will take precedence over any other internal grpc.DialOptions
|
||||
// they might conflict with.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithDialOption(opts ...grpc.DialOption) Option {
|
||||
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.DialOptions = opts
|
||||
})}
|
||||
}
|
||||
|
||||
// WithGRPCConn allows reusing existing gRPC connection when it has already been
|
||||
// established for other services. When set, other dial options will be ignored.
|
||||
// WithGRPCConn sets conn as the gRPC ClientConn used for all communication.
|
||||
//
|
||||
// This option takes precedence over any other option that relates to
|
||||
// establishing or persisting a gRPC connection to a target endpoint. Any
|
||||
// other option of those types passed will be ignored.
|
||||
//
|
||||
// It is the callers responsibility to close the passed conn. The client
|
||||
// Shutdown method will not close this connection.
|
||||
func WithGRPCConn(conn *grpc.ClientConn) Option {
|
||||
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.GRPCConn = conn
|
||||
})}
|
||||
}
|
||||
|
||||
// WithTimeout tells the client the max waiting time for the backend to process
|
||||
// each metrics batch. If unset, the default will be 10 seconds.
|
||||
// WithTimeout sets the max amount of time a client will attempt to export a
|
||||
// batch of spans. This takes precedence over any retry settings defined with
|
||||
// WithRetry, once this time limit has been reached the export is abandoned
|
||||
// and the batch of spans is dropped.
|
||||
//
|
||||
// If unset, the default timeout will be set to 10 seconds.
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return wrappedOption{otlpconfig.WithTimeout(duration)}
|
||||
}
|
||||
|
||||
// WithRetry configures the retry policy for transient errors that may occurs when
|
||||
// exporting metrics. An exponential back-off algorithm is used to
|
||||
// ensure endpoints are not overwhelmed with retries. If unset, the default
|
||||
// retry policy will retry after 5 seconds and increase exponentially after each
|
||||
// error for a total of 1 minute.
|
||||
func WithRetry(settings RetrySettings) Option {
|
||||
return wrappedOption{otlpconfig.WithRetry(otlpconfig.RetrySettings(settings))}
|
||||
// WithRetry sets the retry policy for transient retryable errors that may be
|
||||
// returned by the target endpoint when exporting a batch of spans.
|
||||
//
|
||||
// If the target endpoint responds with not only a retryable error, but
|
||||
// explicitly returns a backoff time in the response. That time will take
|
||||
// precedence over these settings.
|
||||
//
|
||||
// These settings do not define any network retry strategy. That is entirely
|
||||
// handled by the gRPC ClientConn.
|
||||
//
|
||||
// If unset, the default retry policy will be used. It will retry the export
|
||||
// 5 seconds after receiving a retryable error and increase exponentially
|
||||
// after each error for no more than a total time of 1 minute.
|
||||
func WithRetry(settings RetryConfig) Option {
|
||||
return wrappedOption{otlpconfig.WithRetry(retry.Config(settings))}
|
||||
}
|
||||
|
@ -21,16 +21,17 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig"
|
||||
colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
|
||||
@ -39,6 +40,13 @@ import (
|
||||
|
||||
const contentTypeProto = "application/x-protobuf"
|
||||
|
||||
var gzPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
w := gzip.NewWriter(ioutil.Discard)
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
// Keep it in sync with golang's DefaultTransport from net/http! We
|
||||
// have our own copy to avoid handling a situation where the
|
||||
// DefaultTransport is overwritten with some different implementation
|
||||
@ -57,11 +65,13 @@ var ourTransport = &http.Transport{
|
||||
}
|
||||
|
||||
type client struct {
|
||||
name string
|
||||
cfg otlpconfig.SignalConfig
|
||||
generalCfg otlpconfig.Config
|
||||
client *http.Client
|
||||
stopCh chan struct{}
|
||||
name string
|
||||
cfg otlpconfig.SignalConfig
|
||||
generalCfg otlpconfig.Config
|
||||
requestFunc retry.RequestFunc
|
||||
client *http.Client
|
||||
stopCh chan struct{}
|
||||
stopOnce sync.Once
|
||||
}
|
||||
|
||||
// NewClient creates a new HTTP metric client.
|
||||
@ -73,7 +83,7 @@ func NewClient(opts ...Option) otlpmetric.Client {
|
||||
}
|
||||
|
||||
for pathPtr, defaultPath := range map[*string]string{
|
||||
&cfg.Metrics.URLPath: defaultMetricsPath,
|
||||
&cfg.Metrics.URLPath: otlpconfig.DefaultMetricsPath,
|
||||
} {
|
||||
tmp := strings.TrimSpace(*pathPtr)
|
||||
if tmp == "" {
|
||||
@ -86,15 +96,6 @@ func NewClient(opts ...Option) otlpmetric.Client {
|
||||
}
|
||||
*pathPtr = tmp
|
||||
}
|
||||
if cfg.MaxAttempts <= 0 {
|
||||
cfg.MaxAttempts = defaultMaxAttempts
|
||||
}
|
||||
if cfg.MaxAttempts > defaultMaxAttempts {
|
||||
cfg.MaxAttempts = defaultMaxAttempts
|
||||
}
|
||||
if cfg.Backoff <= 0 {
|
||||
cfg.Backoff = defaultBackoff
|
||||
}
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: ourTransport,
|
||||
@ -108,11 +109,12 @@ func NewClient(opts ...Option) otlpmetric.Client {
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
return &client{
|
||||
name: "metrics",
|
||||
cfg: cfg.Metrics,
|
||||
generalCfg: cfg,
|
||||
stopCh: stopCh,
|
||||
client: httpClient,
|
||||
name: "metrics",
|
||||
cfg: cfg.Metrics,
|
||||
generalCfg: cfg,
|
||||
requestFunc: cfg.RetryConfig.RequestFunc(evaluate),
|
||||
stopCh: stopCh,
|
||||
client: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +131,9 @@ func (d *client) Start(ctx context.Context) error {
|
||||
|
||||
// Stop shuts down the client and interrupt any in-flight request.
|
||||
func (d *client) Stop(ctx context.Context) error {
|
||||
close(d.stopCh)
|
||||
d.stopOnce.Do(func() {
|
||||
close(d.stopCh)
|
||||
})
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
@ -147,41 +151,150 @@ func (d *client) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.Res
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.send(ctx, rawRequest)
|
||||
}
|
||||
|
||||
func (d *client) send(ctx context.Context, rawRequest []byte) error {
|
||||
address := fmt.Sprintf("%s://%s%s", d.getScheme(), d.cfg.Endpoint, d.cfg.URLPath)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = d.contextWithStop(ctx)
|
||||
ctx, cancel := d.contextWithStop(ctx)
|
||||
defer cancel()
|
||||
for i := 0; i < d.generalCfg.MaxAttempts; i++ {
|
||||
response, err := d.singleSend(ctx, rawRequest, address)
|
||||
|
||||
request, err := d.newRequest(rawRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.requestFunc(ctx, func(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
request.reset(ctx)
|
||||
resp, err := d.client.Do(request.Request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// We don't care about the body, so try to read it
|
||||
// into /dev/null and close it immediately. The
|
||||
// reading part is to facilitate connection reuse.
|
||||
_, _ = io.Copy(ioutil.Discard, response.Body)
|
||||
_ = response.Body.Close()
|
||||
switch response.StatusCode {
|
||||
|
||||
var rErr error
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return nil
|
||||
case http.StatusTooManyRequests:
|
||||
fallthrough
|
||||
case http.StatusServiceUnavailable:
|
||||
select {
|
||||
case <-time.After(getWaitDuration(d.generalCfg.Backoff, i)):
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
// 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()
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("failed to send %s to %s with HTTP status %s", d.name, address, response.Status)
|
||||
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
|
||||
}
|
||||
return rErr
|
||||
})
|
||||
}
|
||||
|
||||
func (d *client) newRequest(body []byte) (request, error) {
|
||||
address := fmt.Sprintf("%s://%s%s", d.getScheme(), d.cfg.Endpoint, d.cfg.URLPath)
|
||||
r, err := http.NewRequest(http.MethodPost, address, nil)
|
||||
if err != nil {
|
||||
return request{Request: r}, err
|
||||
}
|
||||
|
||||
for k, v := range d.cfg.Headers {
|
||||
r.Header.Set(k, v)
|
||||
}
|
||||
r.Header.Set("Content-Type", contentTypeProto)
|
||||
|
||||
req := request{Request: r}
|
||||
switch Compression(d.cfg.Compression) {
|
||||
case NoCompression:
|
||||
r.ContentLength = (int64)(len(body))
|
||||
req.bodyReader = bodyReader(body)
|
||||
case GzipCompression:
|
||||
// Ensure the content length is not used.
|
||||
r.ContentLength = -1
|
||||
r.Header.Set("Content-Encoding", "gzip")
|
||||
|
||||
gz := gzPool.Get().(*gzip.Writer)
|
||||
defer gzPool.Put(gz)
|
||||
|
||||
var b bytes.Buffer
|
||||
gz.Reset(&b)
|
||||
|
||||
if _, err := gz.Write(body); err != nil {
|
||||
return req, err
|
||||
}
|
||||
// Close needs to be called to ensure body if fully written.
|
||||
if err := gz.Close(); err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.bodyReader = bodyReader(b.Bytes())
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
// request wraps an http.Request with a resettable body reader.
|
||||
type request struct {
|
||||
*http.Request
|
||||
|
||||
// bodyReader allows the same body to be used for multiple requests.
|
||||
bodyReader func() io.ReadCloser
|
||||
}
|
||||
|
||||
// reset reinitializes the request Body and uses ctx for the request.
|
||||
func (r *request) reset(ctx context.Context) {
|
||||
r.Body = r.bodyReader()
|
||||
r.Request = r.Request.WithContext(ctx)
|
||||
}
|
||||
|
||||
// retryableError represents a request failure that can be retried.
|
||||
type retryableError struct {
|
||||
throttle int64
|
||||
}
|
||||
|
||||
// newResponseError returns a retryableError and will extract any explicit
|
||||
// throttle delay contained in headers.
|
||||
func newResponseError(header http.Header) error {
|
||||
var rErr retryableError
|
||||
if s, ok := header["Retry-After"]; ok {
|
||||
if t, err := strconv.ParseInt(s[0], 10, 64); err == nil {
|
||||
rErr.throttle = t
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("failed to send data to %s after %d tries", address, d.generalCfg.MaxAttempts)
|
||||
return rErr
|
||||
}
|
||||
|
||||
func (e retryableError) Error() string {
|
||||
return "retry-able request failure"
|
||||
}
|
||||
|
||||
// evaluate returns if err is retry-able. If it is and it includes an explicit
|
||||
// throttling delay, that delay is also returned.
|
||||
func evaluate(err error) (bool, time.Duration) {
|
||||
if err == nil {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
rErr, ok := err.(retryableError)
|
||||
if !ok {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
return true, time.Duration(rErr.throttle)
|
||||
}
|
||||
|
||||
func (d *client) getScheme() string {
|
||||
@ -191,26 +304,6 @@ func (d *client) getScheme() string {
|
||||
return "https"
|
||||
}
|
||||
|
||||
func getWaitDuration(backoff time.Duration, i int) time.Duration {
|
||||
// Strategy: after nth failed attempt, attempt resending after
|
||||
// k * initialBackoff + jitter, where k is a random number in
|
||||
// range [0, 2^n-1), and jitter is a random percentage of
|
||||
// initialBackoff from [-5%, 5%).
|
||||
//
|
||||
// Based on
|
||||
// https://en.wikipedia.org/wiki/Exponential_backoff#Example_exponential_backoff_algorithm
|
||||
//
|
||||
// Jitter is our addition.
|
||||
|
||||
// There won't be an overflow, since i is capped to
|
||||
// defaultMaxAttempts (5).
|
||||
upperK := (int64)(1) << (i + 1)
|
||||
jitterPercent := (rand.Float64() - 0.5) / 10.
|
||||
jitter := jitterPercent * (float64)(backoff)
|
||||
k := rand.Int63n(upperK)
|
||||
return (time.Duration)(k)*backoff + (time.Duration)(jitter)
|
||||
}
|
||||
|
||||
func (d *client) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
// Unify the parent context Done signal with the client's stop
|
||||
// channel.
|
||||
@ -226,51 +319,3 @@ func (d *client) contextWithStop(ctx context.Context) (context.Context, context.
|
||||
}(ctx, cancel)
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func (d *client) singleSend(ctx context.Context, rawRequest []byte, address string) (*http.Response, error) {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, address, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader, contentLength, headers := d.prepareBody(rawRequest)
|
||||
// Not closing bodyReader through defer, the HTTP Client's
|
||||
// Transport will do it for us
|
||||
request.Body = bodyReader
|
||||
request.ContentLength = contentLength
|
||||
for key, values := range headers {
|
||||
for _, value := range values {
|
||||
request.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
return d.client.Do(request)
|
||||
}
|
||||
|
||||
func (d *client) prepareBody(rawRequest []byte) (io.ReadCloser, int64, http.Header) {
|
||||
var bodyReader io.ReadCloser
|
||||
headers := http.Header{}
|
||||
for k, v := range d.cfg.Headers {
|
||||
headers.Set(k, v)
|
||||
}
|
||||
contentLength := (int64)(len(rawRequest))
|
||||
headers.Set("Content-Type", contentTypeProto)
|
||||
requestReader := bytes.NewBuffer(rawRequest)
|
||||
switch Compression(d.cfg.Compression) {
|
||||
case NoCompression:
|
||||
bodyReader = ioutil.NopCloser(requestReader)
|
||||
case GzipCompression:
|
||||
preader, pwriter := io.Pipe()
|
||||
go func() {
|
||||
defer pwriter.Close()
|
||||
gzipper := gzip.NewWriter(pwriter)
|
||||
defer gzipper.Close()
|
||||
_, err := io.Copy(gzipper, requestReader)
|
||||
if err != nil {
|
||||
otel.Handle(fmt.Errorf("otlphttp: failed to gzip request: %v", err))
|
||||
}
|
||||
}()
|
||||
headers.Set("Content-Encoding", "gzip")
|
||||
bodyReader = preader
|
||||
contentLength = -1
|
||||
}
|
||||
return bodyReader, contentLength, headers
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ package otlpmetrichttp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
@ -144,32 +143,6 @@ func TestExporterShutdown(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
statuses := []int{
|
||||
http.StatusTooManyRequests,
|
||||
http.StatusServiceUnavailable,
|
||||
}
|
||||
mcCfg := mockCollectorConfig{
|
||||
InjectHTTPStatus: statuses,
|
||||
}
|
||||
mc := runMockCollector(t, mcCfg)
|
||||
defer mc.MustStop(t)
|
||||
client := otlpmetrichttp.NewClient(
|
||||
otlpmetrichttp.WithEndpoint(mc.Endpoint()),
|
||||
otlpmetrichttp.WithInsecure(),
|
||||
otlpmetrichttp.WithMaxAttempts(len(statuses)+1),
|
||||
)
|
||||
ctx := context.Background()
|
||||
exporter, err := otlpmetric.New(ctx, client)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, exporter.Shutdown(ctx))
|
||||
}()
|
||||
err = exporter.Export(ctx, testResource, oneRecord)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, mc.GetMetrics(), 1)
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
mcCfg := mockCollectorConfig{
|
||||
InjectDelay: 100 * time.Millisecond,
|
||||
@ -191,58 +164,6 @@ func TestTimeout(t *testing.T) {
|
||||
assert.Equal(t, true, os.IsTimeout(err))
|
||||
}
|
||||
|
||||
func TestRetryFailed(t *testing.T) {
|
||||
statuses := []int{
|
||||
http.StatusTooManyRequests,
|
||||
http.StatusServiceUnavailable,
|
||||
}
|
||||
mcCfg := mockCollectorConfig{
|
||||
InjectHTTPStatus: statuses,
|
||||
}
|
||||
mc := runMockCollector(t, mcCfg)
|
||||
defer mc.MustStop(t)
|
||||
driver := otlpmetrichttp.NewClient(
|
||||
otlpmetrichttp.WithEndpoint(mc.Endpoint()),
|
||||
otlpmetrichttp.WithInsecure(),
|
||||
otlpmetrichttp.WithMaxAttempts(1),
|
||||
)
|
||||
ctx := context.Background()
|
||||
exporter, err := otlpmetric.New(ctx, driver)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, exporter.Shutdown(ctx))
|
||||
}()
|
||||
err = exporter.Export(ctx, testResource, oneRecord)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, mc.GetMetrics())
|
||||
}
|
||||
|
||||
func TestNoRetry(t *testing.T) {
|
||||
statuses := []int{
|
||||
http.StatusBadRequest,
|
||||
}
|
||||
mcCfg := mockCollectorConfig{
|
||||
InjectHTTPStatus: statuses,
|
||||
}
|
||||
mc := runMockCollector(t, mcCfg)
|
||||
defer mc.MustStop(t)
|
||||
driver := otlpmetrichttp.NewClient(
|
||||
otlpmetrichttp.WithEndpoint(mc.Endpoint()),
|
||||
otlpmetrichttp.WithInsecure(),
|
||||
otlpmetrichttp.WithMaxAttempts(len(statuses)+1),
|
||||
)
|
||||
ctx := context.Background()
|
||||
exporter, err := otlpmetric.New(ctx, driver)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, exporter.Shutdown(ctx))
|
||||
}()
|
||||
err = exporter.Export(ctx, testResource, oneRecord)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, fmt.Sprintf("failed to send metrics to http://%s/v1/metrics with HTTP status 400 Bad Request", mc.endpoint), err.Error())
|
||||
assert.Empty(t, mc.GetMetrics())
|
||||
}
|
||||
|
||||
func TestEmptyData(t *testing.T) {
|
||||
mcCfg := mockCollectorConfig{}
|
||||
mc := runMockCollector(t, mcCfg)
|
||||
@ -263,88 +184,6 @@ func TestEmptyData(t *testing.T) {
|
||||
assert.NotEmpty(t, mc.GetMetrics())
|
||||
}
|
||||
|
||||
func TestUnreasonableMaxAttempts(t *testing.T) {
|
||||
// Max attempts is 5, we set collector to fail 7 times and try
|
||||
// to configure max attempts to be either negative or too
|
||||
// large. Since we set max attempts to 5 in such cases,
|
||||
// exporting to the collector should fail.
|
||||
type testcase struct {
|
||||
name string
|
||||
maxAttempts int
|
||||
}
|
||||
for _, tc := range []testcase{
|
||||
{
|
||||
name: "negative max attempts",
|
||||
maxAttempts: -3,
|
||||
},
|
||||
{
|
||||
name: "too large max attempts",
|
||||
maxAttempts: 10,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
statuses := make([]int, 0, 7)
|
||||
for i := 0; i < cap(statuses); i++ {
|
||||
statuses = append(statuses, http.StatusTooManyRequests)
|
||||
}
|
||||
mcCfg := mockCollectorConfig{
|
||||
InjectHTTPStatus: statuses,
|
||||
}
|
||||
mc := runMockCollector(t, mcCfg)
|
||||
defer mc.MustStop(t)
|
||||
driver := otlpmetrichttp.NewClient(
|
||||
otlpmetrichttp.WithEndpoint(mc.Endpoint()),
|
||||
otlpmetrichttp.WithInsecure(),
|
||||
otlpmetrichttp.WithMaxAttempts(tc.maxAttempts),
|
||||
otlpmetrichttp.WithBackoff(time.Millisecond),
|
||||
)
|
||||
ctx := context.Background()
|
||||
exporter, err := otlpmetric.New(ctx, driver)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, exporter.Shutdown(ctx))
|
||||
}()
|
||||
err = exporter.Export(ctx, testResource, oneRecord)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, mc.GetMetrics())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnreasonableBackoff(t *testing.T) {
|
||||
// This sets backoff to negative value, which gets corrected
|
||||
// to default backoff instead of being used. Default max
|
||||
// attempts is 5, so we set the collector to fail 4 times, but
|
||||
// we set the deadline to 3 times of the default backoff, so
|
||||
// this should show that deadline is not met, meaning that the
|
||||
// retries weren't immediate (as negative backoff could
|
||||
// imply).
|
||||
statuses := make([]int, 0, 4)
|
||||
for i := 0; i < cap(statuses); i++ {
|
||||
statuses = append(statuses, http.StatusTooManyRequests)
|
||||
}
|
||||
mcCfg := mockCollectorConfig{
|
||||
InjectHTTPStatus: statuses,
|
||||
}
|
||||
mc := runMockCollector(t, mcCfg)
|
||||
defer mc.MustStop(t)
|
||||
driver := otlpmetrichttp.NewClient(
|
||||
otlpmetrichttp.WithEndpoint(mc.Endpoint()),
|
||||
otlpmetrichttp.WithInsecure(),
|
||||
otlpmetrichttp.WithBackoff(-time.Millisecond),
|
||||
)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*(300*time.Millisecond))
|
||||
defer cancel()
|
||||
exporter, err := otlpmetric.New(ctx, driver)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, exporter.Shutdown(context.Background()))
|
||||
}()
|
||||
err = exporter.Export(ctx, testResource, oneRecord)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, mc.GetMetrics())
|
||||
}
|
||||
|
||||
func TestCancelledContext(t *testing.T) {
|
||||
statuses := []int{
|
||||
http.StatusBadRequest,
|
||||
|
68
exporters/otlp/otlpmetric/otlpmetrichttp/client_unit_test.go
Normal file
68
exporters/otlp/otlpmetric/otlpmetrichttp/client_unit_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 otlpmetrichttp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnreasonableBackoff(t *testing.T) {
|
||||
cIface := NewClient(
|
||||
WithEndpoint("http://localhost"),
|
||||
WithInsecure(),
|
||||
WithBackoff(-time.Microsecond),
|
||||
)
|
||||
require.IsType(t, &client{}, cIface)
|
||||
c := cIface.(*client)
|
||||
assert.True(t, c.generalCfg.RetryConfig.Enabled)
|
||||
assert.Equal(t, 5*time.Second, c.generalCfg.RetryConfig.InitialInterval)
|
||||
assert.Equal(t, 300*time.Millisecond, c.generalCfg.RetryConfig.MaxInterval)
|
||||
assert.Equal(t, time.Minute, c.generalCfg.RetryConfig.MaxElapsedTime)
|
||||
}
|
||||
|
||||
func TestUnreasonableMaxAttempts(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
maxAttempts int
|
||||
}
|
||||
for _, tc := range []testcase{
|
||||
{
|
||||
name: "negative max attempts",
|
||||
maxAttempts: -3,
|
||||
},
|
||||
{
|
||||
name: "too large max attempts",
|
||||
maxAttempts: 10,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cIface := NewClient(
|
||||
WithEndpoint("http://localhost"),
|
||||
WithInsecure(),
|
||||
WithMaxAttempts(tc.maxAttempts),
|
||||
)
|
||||
require.IsType(t, &client{}, cIface)
|
||||
c := cIface.(*client)
|
||||
assert.True(t, c.generalCfg.RetryConfig.Enabled)
|
||||
assert.Equal(t, 5*time.Second, c.generalCfg.RetryConfig.InitialInterval)
|
||||
assert.Equal(t, 30*time.Second, c.generalCfg.RetryConfig.MaxInterval)
|
||||
assert.Equal(t, 145*time.Second, c.generalCfg.RetryConfig.MaxElapsedTime)
|
||||
})
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v0.0.0-00010101000000-000000000000
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.25.0
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
go.opentelemetry.io/proto/otlp v0.11.0
|
||||
@ -17,8 +17,6 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp => ../..
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
@ -82,3 +80,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/op
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../internal/retry
|
||||
|
@ -4,6 +4,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -23,6 +24,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -18,22 +18,10 @@ import (
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultMaxAttempts describes how many times the driver
|
||||
// should retry the sending of the payload in case of a
|
||||
// retryable error.
|
||||
defaultMaxAttempts int = 5
|
||||
// defaultMetricsPath is a default URL path for endpoint that
|
||||
// receives metrics.
|
||||
defaultMetricsPath string = "/v1/metrics"
|
||||
// defaultBackoff is a default base backoff time used in the
|
||||
// exponential backoff strategy.
|
||||
defaultBackoff time.Duration = 300 * time.Millisecond
|
||||
)
|
||||
|
||||
// Compression describes the compression used for payloads sent to the
|
||||
// collector.
|
||||
type Compression otlpconfig.Compression
|
||||
@ -52,6 +40,10 @@ type Option interface {
|
||||
applyHTTPOption(*otlpconfig.Config)
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying batches in case of export
|
||||
// failure using an exponential backoff.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
type wrappedOption struct {
|
||||
otlpconfig.HTTPOption
|
||||
}
|
||||
@ -84,15 +76,67 @@ func WithURLPath(urlPath string) Option {
|
||||
// will try to send the payload in case of retryable errors.
|
||||
// The max attempts is limited to at most 5 retries. If unset,
|
||||
// default (5) will be used.
|
||||
//
|
||||
// Deprecated: Use WithRetry instead.
|
||||
func WithMaxAttempts(maxAttempts int) Option {
|
||||
return wrappedOption{otlpconfig.WithMaxAttempts(maxAttempts)}
|
||||
if maxAttempts > 5 || maxAttempts < 0 {
|
||||
maxAttempts = 5
|
||||
}
|
||||
return wrappedOption{
|
||||
otlpconfig.NewHTTPOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.RetryConfig.Enabled = true
|
||||
|
||||
var (
|
||||
init = cfg.RetryConfig.InitialInterval
|
||||
maxI = cfg.RetryConfig.MaxInterval
|
||||
maxE = cfg.RetryConfig.MaxElapsedTime
|
||||
)
|
||||
|
||||
if init == 0 {
|
||||
init = retry.DefaultConfig.InitialInterval
|
||||
}
|
||||
if maxI == 0 {
|
||||
maxI = retry.DefaultConfig.MaxInterval
|
||||
}
|
||||
if maxE == 0 {
|
||||
maxE = retry.DefaultConfig.MaxElapsedTime
|
||||
}
|
||||
attempts := int64(maxE+init) / int64(maxI)
|
||||
|
||||
if int64(maxAttempts) == attempts {
|
||||
return
|
||||
}
|
||||
|
||||
maxE = time.Duration(int64(maxAttempts)*int64(maxI)) - init
|
||||
|
||||
cfg.RetryConfig.InitialInterval = init
|
||||
cfg.RetryConfig.MaxInterval = maxI
|
||||
cfg.RetryConfig.MaxElapsedTime = maxE
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// WithBackoff tells the driver to use the duration as a base of the
|
||||
// exponential backoff strategy. If unset, default (300ms) will be
|
||||
// used.
|
||||
//
|
||||
// Deprecated: Use WithRetry instead.
|
||||
func WithBackoff(duration time.Duration) Option {
|
||||
return wrappedOption{otlpconfig.WithBackoff(duration)}
|
||||
if duration < 0 {
|
||||
duration = 300 * time.Millisecond
|
||||
}
|
||||
return wrappedOption{
|
||||
otlpconfig.NewHTTPOption(func(cfg *otlpconfig.Config) {
|
||||
cfg.RetryConfig.Enabled = true
|
||||
cfg.RetryConfig.MaxInterval = duration
|
||||
if cfg.RetryConfig.InitialInterval == 0 {
|
||||
cfg.RetryConfig.InitialInterval = retry.DefaultConfig.InitialInterval
|
||||
}
|
||||
if cfg.RetryConfig.MaxElapsedTime == 0 {
|
||||
cfg.RetryConfig.MaxElapsedTime = retry.DefaultConfig.MaxElapsedTime
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// WithTLSClientConfig can be used to set up a custom TLS
|
||||
@ -120,3 +164,12 @@ func WithHeaders(headers map[string]string) Option {
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return wrappedOption{otlpconfig.WithTimeout(duration)}
|
||||
}
|
||||
|
||||
// WithRetry configures the retry policy for transient errors that may occurs
|
||||
// when exporting traces. An exponential back-off algorithm is used to ensure
|
||||
// endpoints are not overwhelmed with retries. If unset, the default retry
|
||||
// policy will retry after 5 seconds and increase exponentially after each
|
||||
// error for a total of 1 minute.
|
||||
func WithRetry(rc RetryConfig) Option {
|
||||
return wrappedOption{otlpconfig.WithRetry(retry.Config(rc))}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ module go.opentelemetry.io/otel/exporters/otlp/otlptrace
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.2
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v0.0.0-00010101000000-000000000000
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
go.opentelemetry.io/otel/trace v1.2.0
|
||||
go.opentelemetry.io/proto/otlp v0.11.0
|
||||
@ -77,3 +77,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/openc
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../internal/retry
|
||||
|
@ -22,6 +22,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,9 +26,9 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v0.0.0-00010101000000-000000000000
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
go.opentelemetry.io/proto/otlp v0.11.0
|
||||
@ -77,3 +78,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/op
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../internal/retry
|
||||
|
@ -22,6 +22,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -22,8 +22,8 @@ import (
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
)
|
||||
|
||||
// Option applies an option to the gRPC driver.
|
||||
|
@ -31,9 +31,9 @@ import (
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel v1.2.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v0.0.0-00010101000000-000000000000
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0
|
||||
go.opentelemetry.io/otel/sdk v1.2.0
|
||||
go.opentelemetry.io/otel/trace v1.2.0
|
||||
@ -75,3 +76,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/op
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../internal/retry
|
||||
|
@ -22,6 +22,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -18,8 +18,8 @@ import (
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
)
|
||||
|
||||
// Compression describes the compression used for payloads sent to the
|
||||
|
@ -34,8 +34,6 @@ replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp => ../otlp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../otlp/otlptrace
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlp/otlptrace/otlptracegrpc
|
||||
@ -77,3 +75,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../otlp/internal/retry
|
||||
|
@ -21,6 +21,11 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -75,3 +75,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/openc
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../otlp/internal/retry
|
||||
|
@ -2,6 +2,11 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -37,8 +37,6 @@ var _ exportmetric.Exporter = &metricExporter{}
|
||||
|
||||
type line struct {
|
||||
Name string `json:"Name"`
|
||||
Min interface{} `json:"Min,omitempty"`
|
||||
Max interface{} `json:"Max,omitempty"`
|
||||
Sum interface{} `json:"Sum,omitempty"`
|
||||
Count interface{} `json:"Count,omitempty"`
|
||||
LastValue interface{} `json:"Last,omitempty"`
|
||||
@ -83,26 +81,6 @@ func (e *metricExporter) Export(_ context.Context, res *resource.Resource, reade
|
||||
return err
|
||||
}
|
||||
expose.Sum = value.AsInterface(kind)
|
||||
}
|
||||
|
||||
if mmsc, ok := agg.(aggregation.MinMaxSumCount); ok {
|
||||
count, err := mmsc.Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expose.Count = count
|
||||
|
||||
max, err := mmsc.Max()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expose.Max = max.AsInterface(kind)
|
||||
|
||||
min, err := mmsc.Min()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expose.Min = min.AsInterface(kind)
|
||||
} else if lv, ok := agg.(aggregation.LastValue); ok {
|
||||
value, timestamp, err := lv.LastValue()
|
||||
if err != nil {
|
||||
|
@ -156,18 +156,6 @@ func TestStdoutLastValueFormat(t *testing.T) {
|
||||
require.Equal(t, `[{"Name":"name.lastvalue{R=V,instrumentation.name=test,A=B,C=D}","Last":123.456}]`, fix.Output())
|
||||
}
|
||||
|
||||
func TestStdoutMinMaxSumCount(t *testing.T) {
|
||||
fix := newFixture(t)
|
||||
|
||||
counter := metric.Must(fix.meter).NewFloat64Counter("name.minmaxsumcount")
|
||||
counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D"))
|
||||
counter.Add(fix.ctx, 876.543, attribute.String("A", "B"), attribute.String("C", "D"))
|
||||
|
||||
require.NoError(t, fix.cont.Stop(fix.ctx))
|
||||
|
||||
require.Equal(t, `[{"Name":"name.minmaxsumcount{R=V,instrumentation.name=test,A=B,C=D}","Min":123.456,"Max":876.543,"Sum":999.999,"Count":2}]`, fix.Output())
|
||||
}
|
||||
|
||||
func TestStdoutHistogramFormat(t *testing.T) {
|
||||
fix := newFixture(t, stdoutmetric.WithPrettyPrint())
|
||||
|
||||
@ -201,7 +189,6 @@ func TestStdoutNoData(t *testing.T) {
|
||||
}
|
||||
|
||||
runTwoAggs("lastvalue")
|
||||
runTwoAggs("minmaxsumcount")
|
||||
}
|
||||
|
||||
func TestStdoutResource(t *testing.T) {
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/openc
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -31,8 +31,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/prometheus => ../prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp => ../otlp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/jaeger => ../jaeger
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/zipkin => ./
|
||||
@ -76,3 +74,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../otlp/internal/retry
|
||||
|
@ -29,6 +29,11 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
|
4
go.mod
4
go.mod
@ -3,6 +3,8 @@ module go.opentelemetry.io/otel
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.2.1
|
||||
github.com/go-logr/stdr v1.2.0
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opentelemetry.io/otel/trace v1.2.0
|
||||
@ -71,3 +73,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ./bridge/opencensus/t
|
||||
replace go.opentelemetry.io/otel/example/fib => ./example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ./schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ./exporters/otlp/internal/retry
|
||||
|
5
go.sum
5
go.sum
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
63
internal/global/internal_logging.go
Normal file
63
internal/global/internal_logging.go
Normal file
@ -0,0 +1,63 @@
|
||||
// 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/global"
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/go-logr/stdr"
|
||||
)
|
||||
|
||||
// globalLogger is the logging interface used within the otel api and sdk provide deatails of the internals.
|
||||
//
|
||||
// The default logger uses stdr which is backed by the standard `log.Logger`
|
||||
// interface. This logger will only show messages at the Error Level.
|
||||
var globalLogger logr.Logger = stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))
|
||||
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`
|
||||
func SetLogger(l logr.Logger) {
|
||||
globalLoggerLock.Lock()
|
||||
defer globalLoggerLock.Unlock()
|
||||
globalLogger = l
|
||||
}
|
||||
|
||||
// Info prints messages about the general state of the API or SDK.
|
||||
// This should usually be less then 5 messages a minute
|
||||
func Info(msg string, keysAndValues ...interface{}) {
|
||||
globalLoggerLock.RLock()
|
||||
defer globalLoggerLock.RUnlock()
|
||||
globalLogger.V(1).Info(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Error prints messages about exceptional states of the API or SDK.
|
||||
func Error(err error, msg string, keysAndValues ...interface{}) {
|
||||
globalLoggerLock.RLock()
|
||||
defer globalLoggerLock.RUnlock()
|
||||
globalLogger.Error(err, msg, keysAndValues...)
|
||||
}
|
||||
|
||||
// Debug prints messages about all internal changes in the API or SDK.
|
||||
func Debug(msg string, keysAndValues ...interface{}) {
|
||||
globalLoggerLock.RLock()
|
||||
defer globalLoggerLock.RUnlock()
|
||||
globalLogger.V(5).Info(msg, keysAndValues...)
|
||||
}
|
@ -12,27 +12,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package connection
|
||||
package global
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
ottest "go.opentelemetry.io/otel/internal/internaltest"
|
||||
"github.com/go-logr/stdr"
|
||||
)
|
||||
|
||||
// Ensure struct alignment prior to running tests.
|
||||
func TestMain(m *testing.M) {
|
||||
fields := []ottest.FieldOffset{
|
||||
{
|
||||
Name: "Connection.lastConnectErrPtr",
|
||||
Offset: unsafe.Offsetof(Connection{}.lastConnectErrPtr),
|
||||
},
|
||||
}
|
||||
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
func TestRace(t *testing.T) {
|
||||
go SetLogger(stdr.New(log.New(os.Stderr, "", 0)))
|
||||
go Info("")
|
||||
}
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -6,13 +6,12 @@ require (
|
||||
github.com/client9/misspell v0.3.4
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golangci/golangci-lint v1.43.0
|
||||
github.com/itchyny/gojq v0.12.5
|
||||
github.com/itchyny/gojq v0.12.6
|
||||
github.com/jcchavezs/porto v0.4.0
|
||||
github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad
|
||||
go.opentelemetry.io/build-tools/multimod v0.0.0-20210920164323-2ceabab23375
|
||||
go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210920164323-2ceabab23375
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/tools v0.1.7
|
||||
golang.org/x/tools v0.1.8
|
||||
)
|
||||
|
||||
replace go.opentelemetry.io/otel => ../..
|
||||
@ -78,3 +77,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -434,9 +434,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA=
|
||||
github.com/itchyny/gojq v0.12.5 h1:6SJ1BQ1VAwJAlIvLSIZmqHP/RUEq3qfVWvsRxrqhsD0=
|
||||
github.com/itchyny/gojq v0.12.5/go.mod h1:3e1hZXv+Kwvdp6V9HXpVrvddiHVApi5EDZwS+zLFeiE=
|
||||
github.com/itchyny/gojq v0.12.6 h1:VjaFn59Em2wTxDNGcrRkDK9ZHMNa8IksOgL13sLL4d0=
|
||||
github.com/itchyny/gojq v0.12.6/go.mod h1:ZHrkfu7A+RbZLy5J1/JKpS4poEqrzItSTGDItqsfP0A=
|
||||
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
|
||||
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
@ -535,7 +534,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
@ -806,6 +804,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k=
|
||||
@ -946,8 +945,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1051,12 +1051,13 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -1175,8 +1176,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
26
internal_logging.go
Normal file
26
internal_logging.go
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 otel // import "go.opentelemetry.io/otel"
|
||||
|
||||
import (
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// SetLogger configures the logger used internally to opentelemetry.
|
||||
func SetLogger(logger logr.Logger) {
|
||||
global.SetLogger(logger)
|
||||
}
|
29
internal_logging_test.go
Normal file
29
internal_logging_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 otel_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/go-logr/stdr"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
)
|
||||
|
||||
func ExampleSetLogger() {
|
||||
logger := stdr.New(log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile))
|
||||
otel.SetLogger(logger)
|
||||
}
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/
|
||||
replace go.opentelemetry.io/otel/example/fib => ../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/sdk/export/metric => ../sdk/export/metric
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../exporters/otlp/internal/retry
|
||||
|
@ -84,15 +84,6 @@ type (
|
||||
Sum() (number.Number, error)
|
||||
Histogram() (Buckets, error)
|
||||
}
|
||||
|
||||
// MinMaxSumCount supports the Min, Max, Sum, and Count interfaces.
|
||||
MinMaxSumCount interface {
|
||||
Aggregation
|
||||
Min() (number.Number, error)
|
||||
Max() (number.Number, error)
|
||||
Sum() (number.Number, error)
|
||||
Count() (uint64, error)
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
@ -106,18 +97,16 @@ type (
|
||||
// deciding how to expose metric data. This enables
|
||||
// user-supplied Aggregators to replace builtin Aggregators.
|
||||
//
|
||||
// For example, test for a Distribution before testing for a
|
||||
// MinMaxSumCount, test for a Histogram before testing for a
|
||||
// For example, test for a Histogram before testing for a
|
||||
// Sum, and so on.
|
||||
Kind string
|
||||
)
|
||||
|
||||
// Kind description constants.
|
||||
const (
|
||||
SumKind Kind = "Sum"
|
||||
MinMaxSumCountKind Kind = "MinMaxSumCount"
|
||||
HistogramKind Kind = "Histogram"
|
||||
LastValueKind Kind = "Lastvalue"
|
||||
SumKind Kind = "Sum"
|
||||
HistogramKind Kind = "Histogram"
|
||||
LastValueKind Kind = "Lastvalue"
|
||||
)
|
||||
|
||||
// Sentinel errors for Aggregation interface.
|
||||
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/openc
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -139,8 +139,7 @@ type CheckpointerFactory interface {
|
||||
//
|
||||
// Note that any Aggregator may be attached to any instrument--this is
|
||||
// the result of the OpenTelemetry API/SDK separation. It is possible
|
||||
// to attach a Sum aggregator to a Histogram instrument or a
|
||||
// MinMaxSumCount aggregator to a Counter instrument.
|
||||
// to attach a Sum aggregator to a Histogram instrument.
|
||||
type Aggregator interface {
|
||||
// Aggregation returns an Aggregation interface to access the
|
||||
// current state of this Aggregator. The caller is
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/
|
||||
replace go.opentelemetry.io/otel/example/fib => ../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../exporters/otlp/internal/retry
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -1,165 +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 minmaxsumcount // import "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/metric/number"
|
||||
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator"
|
||||
)
|
||||
|
||||
type (
|
||||
// Aggregator aggregates events that form a distribution,
|
||||
// keeping only the min, max, sum, and count.
|
||||
Aggregator struct {
|
||||
lock sync.Mutex
|
||||
kind number.Kind
|
||||
state
|
||||
}
|
||||
|
||||
state struct {
|
||||
sum number.Number
|
||||
min number.Number
|
||||
max number.Number
|
||||
count uint64
|
||||
}
|
||||
)
|
||||
|
||||
var _ export.Aggregator = &Aggregator{}
|
||||
var _ aggregation.MinMaxSumCount = &Aggregator{}
|
||||
|
||||
// New returns a new aggregator for computing the min, max, sum, and
|
||||
// count.
|
||||
//
|
||||
// This type uses a mutex for Update() and SynchronizedMove() concurrency.
|
||||
func New(cnt int, desc *sdkapi.Descriptor) []Aggregator {
|
||||
kind := desc.NumberKind()
|
||||
aggs := make([]Aggregator, cnt)
|
||||
for i := range aggs {
|
||||
aggs[i] = Aggregator{
|
||||
kind: kind,
|
||||
state: emptyState(kind),
|
||||
}
|
||||
}
|
||||
return aggs
|
||||
}
|
||||
|
||||
// Aggregation returns an interface for reading the state of this aggregator.
|
||||
func (c *Aggregator) Aggregation() aggregation.Aggregation {
|
||||
return c
|
||||
}
|
||||
|
||||
// Kind returns aggregation.MinMaxSumCountKind.
|
||||
func (c *Aggregator) Kind() aggregation.Kind {
|
||||
return aggregation.MinMaxSumCountKind
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in the checkpoint.
|
||||
func (c *Aggregator) Sum() (number.Number, error) {
|
||||
return c.sum, nil
|
||||
}
|
||||
|
||||
// Count returns the number of values in the checkpoint.
|
||||
func (c *Aggregator) Count() (uint64, error) {
|
||||
return c.count, nil
|
||||
}
|
||||
|
||||
// Min returns the minimum value in the checkpoint.
|
||||
// The error value aggregation.ErrNoData will be returned
|
||||
// if there were no measurements recorded during the checkpoint.
|
||||
func (c *Aggregator) Min() (number.Number, error) {
|
||||
if c.count == 0 {
|
||||
return 0, aggregation.ErrNoData
|
||||
}
|
||||
return c.min, nil
|
||||
}
|
||||
|
||||
// Max returns the maximum value in the checkpoint.
|
||||
// The error value aggregation.ErrNoData will be returned
|
||||
// if there were no measurements recorded during the checkpoint.
|
||||
func (c *Aggregator) Max() (number.Number, error) {
|
||||
if c.count == 0 {
|
||||
return 0, aggregation.ErrNoData
|
||||
}
|
||||
return c.max, nil
|
||||
}
|
||||
|
||||
// SynchronizedMove saves the current state into oa and resets the current state to
|
||||
// the empty set.
|
||||
func (c *Aggregator) SynchronizedMove(oa export.Aggregator, desc *sdkapi.Descriptor) error {
|
||||
o, _ := oa.(*Aggregator)
|
||||
|
||||
if oa != nil && o == nil {
|
||||
return aggregator.NewInconsistentAggregatorError(c, oa)
|
||||
}
|
||||
c.lock.Lock()
|
||||
if o != nil {
|
||||
o.state = c.state
|
||||
}
|
||||
c.state = emptyState(c.kind)
|
||||
c.lock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func emptyState(kind number.Kind) state {
|
||||
return state{
|
||||
count: 0,
|
||||
sum: 0,
|
||||
min: kind.Maximum(),
|
||||
max: kind.Minimum(),
|
||||
}
|
||||
}
|
||||
|
||||
// Update adds the recorded measurement to the current data set.
|
||||
func (c *Aggregator) Update(_ context.Context, number number.Number, desc *sdkapi.Descriptor) error {
|
||||
kind := desc.NumberKind()
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.count++
|
||||
c.sum.AddNumber(kind, number)
|
||||
if number.CompareNumber(kind, c.min) < 0 {
|
||||
c.min = number
|
||||
}
|
||||
if number.CompareNumber(kind, c.max) > 0 {
|
||||
c.max = number
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge combines two data sets into one.
|
||||
func (c *Aggregator) Merge(oa export.Aggregator, desc *sdkapi.Descriptor) error {
|
||||
o, _ := oa.(*Aggregator)
|
||||
if o == nil {
|
||||
return aggregator.NewInconsistentAggregatorError(c, oa)
|
||||
}
|
||||
|
||||
c.count += o.count
|
||||
c.sum.AddNumber(desc.NumberKind(), o.sum)
|
||||
|
||||
if c.min.CompareNumber(desc.NumberKind(), o.min) > 0 {
|
||||
c.min.SetNumber(o.min)
|
||||
}
|
||||
if c.max.CompareNumber(desc.NumberKind(), o.max) < 0 {
|
||||
c.max.SetNumber(o.max)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,248 +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 minmaxsumcount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel/metric/number"
|
||||
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
|
||||
)
|
||||
|
||||
const count = 100
|
||||
|
||||
type policy struct {
|
||||
name string
|
||||
absolute bool
|
||||
sign func() int
|
||||
}
|
||||
|
||||
var (
|
||||
positiveOnly = policy{
|
||||
name: "absolute",
|
||||
absolute: true,
|
||||
sign: func() int { return +1 },
|
||||
}
|
||||
negativeOnly = policy{
|
||||
name: "negative",
|
||||
absolute: false,
|
||||
sign: func() int { return -1 },
|
||||
}
|
||||
positiveAndNegative = policy{
|
||||
name: "positiveAndNegative",
|
||||
absolute: false,
|
||||
sign: func() int {
|
||||
if rand.Uint32() > math.MaxUint32/2 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestMinMaxSumCountAbsolute(t *testing.T) {
|
||||
aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) {
|
||||
minMaxSumCount(t, profile, positiveOnly)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMinMaxSumCountNegativeOnly(t *testing.T) {
|
||||
aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) {
|
||||
minMaxSumCount(t, profile, negativeOnly)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMinMaxSumCountPositiveAndNegative(t *testing.T) {
|
||||
aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) {
|
||||
minMaxSumCount(t, profile, positiveAndNegative)
|
||||
})
|
||||
}
|
||||
|
||||
func new2(desc *sdkapi.Descriptor) (_, _ *Aggregator) {
|
||||
alloc := New(2, desc)
|
||||
return &alloc[0], &alloc[1]
|
||||
}
|
||||
|
||||
func new4(desc *sdkapi.Descriptor) (_, _, _, _ *Aggregator) {
|
||||
alloc := New(4, desc)
|
||||
return &alloc[0], &alloc[1], &alloc[2], &alloc[3]
|
||||
}
|
||||
|
||||
func checkZero(t *testing.T, agg *Aggregator, desc *sdkapi.Descriptor) {
|
||||
kind := desc.NumberKind()
|
||||
|
||||
sum, err := agg.Sum()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, kind.Zero(), sum)
|
||||
|
||||
count, err := agg.Count()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), count)
|
||||
|
||||
max, err := agg.Max()
|
||||
require.True(t, errors.Is(err, aggregation.ErrNoData))
|
||||
require.Equal(t, kind.Zero(), max)
|
||||
|
||||
min, err := agg.Min()
|
||||
require.True(t, errors.Is(err, aggregation.ErrNoData))
|
||||
require.Equal(t, kind.Zero(), min)
|
||||
}
|
||||
|
||||
// Validates min, max, sum and count for a given profile and policy
|
||||
func minMaxSumCount(t *testing.T, profile aggregatortest.Profile, policy policy) {
|
||||
descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind)
|
||||
|
||||
agg, ckpt := new2(descriptor)
|
||||
|
||||
all := aggregatortest.NewNumbers(profile.NumberKind)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
x := profile.Random(policy.sign())
|
||||
all.Append(x)
|
||||
aggregatortest.CheckedUpdate(t, agg, x, descriptor)
|
||||
}
|
||||
|
||||
require.NoError(t, agg.SynchronizedMove(ckpt, descriptor))
|
||||
|
||||
checkZero(t, agg, descriptor)
|
||||
|
||||
all.Sort()
|
||||
|
||||
aggSum, err := ckpt.Sum()
|
||||
require.Nil(t, err)
|
||||
allSum := all.Sum()
|
||||
require.InEpsilon(t,
|
||||
(&allSum).CoerceToFloat64(profile.NumberKind),
|
||||
aggSum.CoerceToFloat64(profile.NumberKind),
|
||||
0.000000001,
|
||||
"Same sum - "+policy.name)
|
||||
|
||||
count, err := ckpt.Count()
|
||||
require.Equal(t, all.Count(), count, "Same count -"+policy.name)
|
||||
require.Nil(t, err)
|
||||
|
||||
min, err := ckpt.Min()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t,
|
||||
all.Min(),
|
||||
min,
|
||||
"Same min -"+policy.name)
|
||||
|
||||
max, err := ckpt.Max()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t,
|
||||
all.Max(),
|
||||
max,
|
||||
"Same max -"+policy.name)
|
||||
}
|
||||
|
||||
func TestMinMaxSumCountMerge(t *testing.T) {
|
||||
aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) {
|
||||
descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind)
|
||||
|
||||
agg1, agg2, ckpt1, ckpt2 := new4(descriptor)
|
||||
|
||||
all := aggregatortest.NewNumbers(profile.NumberKind)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
x := profile.Random(+1)
|
||||
all.Append(x)
|
||||
aggregatortest.CheckedUpdate(t, agg1, x, descriptor)
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
x := profile.Random(+1)
|
||||
all.Append(x)
|
||||
aggregatortest.CheckedUpdate(t, agg2, x, descriptor)
|
||||
}
|
||||
|
||||
require.NoError(t, agg1.SynchronizedMove(ckpt1, descriptor))
|
||||
require.NoError(t, agg2.SynchronizedMove(ckpt2, descriptor))
|
||||
|
||||
checkZero(t, agg1, descriptor)
|
||||
checkZero(t, agg2, descriptor)
|
||||
|
||||
aggregatortest.CheckedMerge(t, ckpt1, ckpt2, descriptor)
|
||||
|
||||
all.Sort()
|
||||
|
||||
aggSum, err := ckpt1.Sum()
|
||||
require.Nil(t, err)
|
||||
allSum := all.Sum()
|
||||
require.InEpsilon(t,
|
||||
(&allSum).CoerceToFloat64(profile.NumberKind),
|
||||
aggSum.CoerceToFloat64(profile.NumberKind),
|
||||
0.000000001,
|
||||
"Same sum - absolute")
|
||||
|
||||
count, err := ckpt1.Count()
|
||||
require.Equal(t, all.Count(), count, "Same count - absolute")
|
||||
require.Nil(t, err)
|
||||
|
||||
min, err := ckpt1.Min()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t,
|
||||
all.Min(),
|
||||
min,
|
||||
"Same min - absolute")
|
||||
|
||||
max, err := ckpt1.Max()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t,
|
||||
all.Max(),
|
||||
max,
|
||||
"Same max - absolute")
|
||||
})
|
||||
}
|
||||
|
||||
func TestMaxSumCountNotSet(t *testing.T) {
|
||||
aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) {
|
||||
descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind)
|
||||
|
||||
alloc := New(2, descriptor)
|
||||
agg, ckpt := &alloc[0], &alloc[1]
|
||||
|
||||
require.NoError(t, agg.SynchronizedMove(ckpt, descriptor))
|
||||
|
||||
asum, err := ckpt.Sum()
|
||||
require.Equal(t, number.Number(0), asum, "Empty checkpoint sum = 0")
|
||||
require.Nil(t, err)
|
||||
|
||||
count, err := ckpt.Count()
|
||||
require.Equal(t, uint64(0), count, "Empty checkpoint count = 0")
|
||||
require.Nil(t, err)
|
||||
|
||||
max, err := ckpt.Max()
|
||||
require.Equal(t, aggregation.ErrNoData, err)
|
||||
require.Equal(t, number.Number(0), max)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSynchronizedMoveReset(t *testing.T) {
|
||||
aggregatortest.SynchronizedMoveResetTest(
|
||||
t,
|
||||
sdkapi.HistogramInstrumentKind,
|
||||
func(desc *sdkapi.Descriptor) export.Aggregator {
|
||||
return &New(1, desc)[0]
|
||||
},
|
||||
)
|
||||
}
|
@ -303,16 +303,6 @@ func BenchmarkGaugeObserverObservationFloat64(b *testing.B) {
|
||||
fix.accumulator.Collect(ctx)
|
||||
}
|
||||
|
||||
// MaxSumCount
|
||||
|
||||
func BenchmarkInt64MaxSumCountAdd(b *testing.B) {
|
||||
benchmarkInt64HistogramAdd(b, "int64.minmaxsumcount")
|
||||
}
|
||||
|
||||
func BenchmarkFloat64MaxSumCountAdd(b *testing.B) {
|
||||
benchmarkFloat64HistogramAdd(b, "float64.minmaxsumcount")
|
||||
}
|
||||
|
||||
// Exact
|
||||
|
||||
func BenchmarkInt64ExactAdd(b *testing.B) {
|
||||
|
@ -75,3 +75,5 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
||||
replace go.opentelemetry.io/otel/schema => ../../schema
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry
|
||||
|
@ -2,6 +2,11 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -1,81 +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_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/metric/metrictest"
|
||||
"go.opentelemetry.io/otel/metric/number"
|
||||
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
|
||||
)
|
||||
|
||||
func TestStressInt64MinMaxSumCount(t *testing.T) {
|
||||
desc := metrictest.NewDescriptor("some_metric", sdkapi.HistogramInstrumentKind, number.Int64Kind)
|
||||
alloc := minmaxsumcount.New(2, &desc)
|
||||
mmsc, ckpt := &alloc[0], &alloc[1]
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go func() {
|
||||
rnd := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
v := rnd.Int63() % 103
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
_ = mmsc.Update(ctx, number.NewInt64Number(v), &desc)
|
||||
}
|
||||
v++
|
||||
}
|
||||
}()
|
||||
|
||||
startTime := time.Now()
|
||||
for time.Since(startTime) < time.Second {
|
||||
_ = mmsc.SynchronizedMove(ckpt, &desc)
|
||||
|
||||
s, _ := ckpt.Sum()
|
||||
c, _ := ckpt.Count()
|
||||
min, e1 := ckpt.Min()
|
||||
max, e2 := ckpt.Max()
|
||||
if c == 0 && (e1 == nil || e2 == nil || s.AsInt64() != 0) {
|
||||
t.Fail()
|
||||
}
|
||||
if c != 0 {
|
||||
if e1 != nil || e2 != nil {
|
||||
t.Fail()
|
||||
}
|
||||
lo, hi, sum := min.AsInt64(), max.AsInt64(), s.AsInt64()
|
||||
|
||||
if uint64(hi-lo)+1 != c {
|
||||
t.Fail()
|
||||
}
|
||||
if c == 1 {
|
||||
if lo != hi || lo != sum {
|
||||
t.Fail()
|
||||
}
|
||||
} else {
|
||||
if hi*(hi+1)/2-(lo-1)*lo/2 != sum {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -80,7 +80,6 @@ func TestProcessor(t *testing.T) {
|
||||
t.Run(nc.kind.String(), func(t *testing.T) {
|
||||
for _, ac := range []aggregatorCase{
|
||||
{kind: aggregation.SumKind},
|
||||
{kind: aggregation.MinMaxSumCountKind},
|
||||
{kind: aggregation.HistogramKind},
|
||||
{kind: aggregation.LastValueKind},
|
||||
} {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user