[chore] Bump all otel deps (#3241)
This commit is contained in:
parent
291bb68b47
commit
28d57d1f13
30
go.mod
30
go.mod
|
@ -65,14 +65,14 @@ require (
|
||||||
github.com/uptrace/bun/extra/bunotel v1.2.1
|
github.com/uptrace/bun/extra/bunotel v1.2.1
|
||||||
github.com/wagslane/go-password-validator v0.3.0
|
github.com/wagslane/go-password-validator v0.3.0
|
||||||
github.com/yuin/goldmark v1.7.4
|
github.com/yuin/goldmark v1.7.4
|
||||||
go.opentelemetry.io/otel v1.26.0
|
go.opentelemetry.io/otel v1.29.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.46.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.51.0
|
||||||
go.opentelemetry.io/otel/metric v1.26.0
|
go.opentelemetry.io/otel/metric v1.29.0
|
||||||
go.opentelemetry.io/otel/sdk v1.26.0
|
go.opentelemetry.io/otel/sdk v1.29.0
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.24.0
|
go.opentelemetry.io/otel/sdk/metric v1.29.0
|
||||||
go.opentelemetry.io/otel/trace v1.26.0
|
go.opentelemetry.io/otel/trace v1.29.0
|
||||||
go.uber.org/automaxprocs v1.5.3
|
go.uber.org/automaxprocs v1.5.3
|
||||||
golang.org/x/crypto v0.26.0
|
golang.org/x/crypto v0.26.0
|
||||||
golang.org/x/image v0.19.0
|
golang.org/x/image v0.19.0
|
||||||
|
@ -122,7 +122,7 @@ require (
|
||||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/analysis v0.23.0 // indirect
|
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||||
github.com/go-openapi/errors v0.22.0 // indirect
|
github.com/go-openapi/errors v0.22.0 // indirect
|
||||||
|
@ -148,7 +148,7 @@ require (
|
||||||
github.com/gorilla/handlers v1.5.2 // indirect
|
github.com/gorilla/handlers v1.5.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.2 // indirect
|
github.com/gorilla/sessions v1.2.2 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/huandu/xstrings v1.4.0 // indirect
|
github.com/huandu/xstrings v1.4.0 // indirect
|
||||||
|
@ -209,8 +209,8 @@ require (
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||||
|
@ -218,9 +218,9 @@ require (
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.24.0 // indirect
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.22.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||||
google.golang.org/grpc v1.63.2 // indirect
|
google.golang.org/grpc v1.65.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
|
60
go.sum
60
go.sum
|
@ -212,8 +212,8 @@ github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
|
||||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||||
|
@ -344,8 +344,8 @@ github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8L
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.2 h1:qoW6V1GT3aZxybsbC6oLnailWnB+qTMVwMreOso9XUw=
|
github.com/gorilla/websocket v1.5.2 h1:qoW6V1GT3aZxybsbC6oLnailWnB+qTMVwMreOso9XUw=
|
||||||
github.com/gorilla/websocket v1.5.2/go.mod h1:0n9H61RBAcf5/38py2MCYbxzPIY9rOkpvvMT24Rqs30=
|
github.com/gorilla/websocket v1.5.2/go.mod h1:0n9H61RBAcf5/38py2MCYbxzPIY9rOkpvvMT24Rqs30=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
@ -640,26 +640,26 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 h1:Waw9Wfpo/IXzOI8bCB7DIk+0JZcqqsyn1JFnAc+iam8=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 h1:nSiV3s7wiCam610XcLbYOmMfJxB9gO4uK3Xgv5gmTgg=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0/go.mod h1:hKn/e/Nmd19/x1gvIHwtOwVWM+VhuITSWip3JUDghj0=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ=
|
go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs=
|
go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4=
|
||||||
go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8=
|
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
|
||||||
go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs=
|
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8=
|
go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0=
|
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
|
||||||
go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
@ -927,10 +927,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -943,8 +943,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# A minimal logging API for Go
|
# A minimal logging API for Go
|
||||||
|
|
||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr)
|
[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/go-logr/logr)](https://goreportcard.com/report/github.com/go-logr/logr)
|
||||||
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-logr/logr/badge)](https://securityscorecards.dev/viewer/?platform=github.com&org=go-logr&repo=logr)
|
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-logr/logr/badge)](https://securityscorecards.dev/viewer/?platform=github.com&org=go-logr&repo=logr)
|
||||||
|
|
||||||
logr offers an(other) opinion on how Go programs and libraries can do logging
|
logr offers an(other) opinion on how Go programs and libraries can do logging
|
||||||
|
|
|
@ -240,11 +240,10 @@ type Formatter struct {
|
||||||
prefix string
|
prefix string
|
||||||
values []any
|
values []any
|
||||||
valuesStr string
|
valuesStr string
|
||||||
parentValuesStr string
|
|
||||||
depth int
|
depth int
|
||||||
opts *Options
|
opts *Options
|
||||||
group string // for slog groups
|
groupName string // for slog groups
|
||||||
groupDepth int
|
groups []groupDef
|
||||||
}
|
}
|
||||||
|
|
||||||
// outputFormat indicates which outputFormat to use.
|
// outputFormat indicates which outputFormat to use.
|
||||||
|
@ -257,6 +256,13 @@ const (
|
||||||
outputJSON
|
outputJSON
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// groupDef represents a saved group. The values may be empty, but we don't
|
||||||
|
// know if we need to render the group until the final record is rendered.
|
||||||
|
type groupDef struct {
|
||||||
|
name string
|
||||||
|
values string
|
||||||
|
}
|
||||||
|
|
||||||
// PseudoStruct is a list of key-value pairs that gets logged as a struct.
|
// PseudoStruct is a list of key-value pairs that gets logged as a struct.
|
||||||
type PseudoStruct []any
|
type PseudoStruct []any
|
||||||
|
|
||||||
|
@ -264,76 +270,102 @@ type PseudoStruct []any
|
||||||
func (f Formatter) render(builtins, args []any) string {
|
func (f Formatter) render(builtins, args []any) string {
|
||||||
// Empirically bytes.Buffer is faster than strings.Builder for this.
|
// Empirically bytes.Buffer is faster than strings.Builder for this.
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
|
||||||
if f.outputFormat == outputJSON {
|
if f.outputFormat == outputJSON {
|
||||||
buf.WriteByte('{') // for the whole line
|
buf.WriteByte('{') // for the whole record
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render builtins
|
||||||
vals := builtins
|
vals := builtins
|
||||||
if hook := f.opts.RenderBuiltinsHook; hook != nil {
|
if hook := f.opts.RenderBuiltinsHook; hook != nil {
|
||||||
vals = hook(f.sanitize(vals))
|
vals = hook(f.sanitize(vals))
|
||||||
}
|
}
|
||||||
f.flatten(buf, vals, false, false) // keys are ours, no need to escape
|
f.flatten(buf, vals, false) // keys are ours, no need to escape
|
||||||
continuing := len(builtins) > 0
|
continuing := len(builtins) > 0
|
||||||
|
|
||||||
if f.parentValuesStr != "" {
|
// Turn the inner-most group into a string
|
||||||
if continuing {
|
argsStr := func() string {
|
||||||
buf.WriteByte(f.comma())
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
}
|
|
||||||
buf.WriteString(f.parentValuesStr)
|
|
||||||
continuing = true
|
|
||||||
}
|
|
||||||
|
|
||||||
groupDepth := f.groupDepth
|
|
||||||
if f.group != "" {
|
|
||||||
if f.valuesStr != "" || len(args) != 0 {
|
|
||||||
if continuing {
|
|
||||||
buf.WriteByte(f.comma())
|
|
||||||
}
|
|
||||||
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
|
|
||||||
buf.WriteByte(f.colon())
|
|
||||||
buf.WriteByte('{') // for the group
|
|
||||||
continuing = false
|
|
||||||
} else {
|
|
||||||
// The group was empty
|
|
||||||
groupDepth--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.valuesStr != "" {
|
|
||||||
if continuing {
|
|
||||||
buf.WriteByte(f.comma())
|
|
||||||
}
|
|
||||||
buf.WriteString(f.valuesStr)
|
|
||||||
continuing = true
|
|
||||||
}
|
|
||||||
|
|
||||||
vals = args
|
vals = args
|
||||||
if hook := f.opts.RenderArgsHook; hook != nil {
|
if hook := f.opts.RenderArgsHook; hook != nil {
|
||||||
vals = hook(f.sanitize(vals))
|
vals = hook(f.sanitize(vals))
|
||||||
}
|
}
|
||||||
f.flatten(buf, vals, continuing, true) // escape user-provided keys
|
f.flatten(buf, vals, true) // escape user-provided keys
|
||||||
|
|
||||||
for i := 0; i < groupDepth; i++ {
|
return buf.String()
|
||||||
buf.WriteByte('}') // for the groups
|
}()
|
||||||
|
|
||||||
|
// Render the stack of groups from the inside out.
|
||||||
|
bodyStr := f.renderGroup(f.groupName, f.valuesStr, argsStr)
|
||||||
|
for i := len(f.groups) - 1; i >= 0; i-- {
|
||||||
|
grp := &f.groups[i]
|
||||||
|
if grp.values == "" && bodyStr == "" {
|
||||||
|
// no contents, so we must elide the whole group
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bodyStr = f.renderGroup(grp.name, grp.values, bodyStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bodyStr != "" {
|
||||||
|
if continuing {
|
||||||
|
buf.WriteByte(f.comma())
|
||||||
|
}
|
||||||
|
buf.WriteString(bodyStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.outputFormat == outputJSON {
|
if f.outputFormat == outputJSON {
|
||||||
buf.WriteByte('}') // for the whole line
|
buf.WriteByte('}') // for the whole record
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// flatten renders a list of key-value pairs into a buffer. If continuing is
|
// renderGroup returns a string representation of the named group with rendered
|
||||||
// true, it assumes that the buffer has previous values and will emit a
|
// values and args. If the name is empty, this will return the values and args,
|
||||||
// separator (which depends on the output format) before the first pair it
|
// joined. If the name is not empty, this will return a single key-value pair,
|
||||||
// writes. If escapeKeys is true, the keys are assumed to have
|
// where the value is a grouping of the values and args. If the values and
|
||||||
// non-JSON-compatible characters in them and must be evaluated for escapes.
|
// args are both empty, this will return an empty string, even if the name was
|
||||||
|
// specified.
|
||||||
|
func (f Formatter) renderGroup(name string, values string, args string) string {
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
|
||||||
|
needClosingBrace := false
|
||||||
|
if name != "" && (values != "" || args != "") {
|
||||||
|
buf.WriteString(f.quoted(name, true)) // escape user-provided keys
|
||||||
|
buf.WriteByte(f.colon())
|
||||||
|
buf.WriteByte('{')
|
||||||
|
needClosingBrace = true
|
||||||
|
}
|
||||||
|
|
||||||
|
continuing := false
|
||||||
|
if values != "" {
|
||||||
|
buf.WriteString(values)
|
||||||
|
continuing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if args != "" {
|
||||||
|
if continuing {
|
||||||
|
buf.WriteByte(f.comma())
|
||||||
|
}
|
||||||
|
buf.WriteString(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if needClosingBrace {
|
||||||
|
buf.WriteByte('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flatten renders a list of key-value pairs into a buffer. If escapeKeys is
|
||||||
|
// true, the keys are assumed to have non-JSON-compatible characters in them
|
||||||
|
// and must be evaluated for escapes.
|
||||||
//
|
//
|
||||||
// This function returns a potentially modified version of kvList, which
|
// This function returns a potentially modified version of kvList, which
|
||||||
// ensures that there is a value for every key (adding a value if needed) and
|
// ensures that there is a value for every key (adding a value if needed) and
|
||||||
// that each key is a string (substituting a key if needed).
|
// that each key is a string (substituting a key if needed).
|
||||||
func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, escapeKeys bool) []any {
|
func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, escapeKeys bool) []any {
|
||||||
// This logic overlaps with sanitize() but saves one type-cast per key,
|
// This logic overlaps with sanitize() but saves one type-cast per key,
|
||||||
// which can be measurable.
|
// which can be measurable.
|
||||||
if len(kvList)%2 != 0 {
|
if len(kvList)%2 != 0 {
|
||||||
|
@ -354,7 +386,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
|
||||||
}
|
}
|
||||||
v := kvList[i+1]
|
v := kvList[i+1]
|
||||||
|
|
||||||
if i > 0 || continuing {
|
if i > 0 {
|
||||||
if f.outputFormat == outputJSON {
|
if f.outputFormat == outputJSON {
|
||||||
buf.WriteByte(f.comma())
|
buf.WriteByte(f.comma())
|
||||||
} else {
|
} else {
|
||||||
|
@ -766,46 +798,17 @@ func (f Formatter) sanitize(kvList []any) []any {
|
||||||
// startGroup opens a new group scope (basically a sub-struct), which locks all
|
// startGroup opens a new group scope (basically a sub-struct), which locks all
|
||||||
// the current saved values and starts them anew. This is needed to satisfy
|
// the current saved values and starts them anew. This is needed to satisfy
|
||||||
// slog.
|
// slog.
|
||||||
func (f *Formatter) startGroup(group string) {
|
func (f *Formatter) startGroup(name string) {
|
||||||
// Unnamed groups are just inlined.
|
// Unnamed groups are just inlined.
|
||||||
if group == "" {
|
if name == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any saved values can no longer be changed.
|
n := len(f.groups)
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
f.groups = append(f.groups[:n:n], groupDef{f.groupName, f.valuesStr})
|
||||||
continuing := false
|
|
||||||
|
|
||||||
if f.parentValuesStr != "" {
|
|
||||||
buf.WriteString(f.parentValuesStr)
|
|
||||||
continuing = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.group != "" && f.valuesStr != "" {
|
|
||||||
if continuing {
|
|
||||||
buf.WriteByte(f.comma())
|
|
||||||
}
|
|
||||||
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
|
|
||||||
buf.WriteByte(f.colon())
|
|
||||||
buf.WriteByte('{') // for the group
|
|
||||||
continuing = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.valuesStr != "" {
|
|
||||||
if continuing {
|
|
||||||
buf.WriteByte(f.comma())
|
|
||||||
}
|
|
||||||
buf.WriteString(f.valuesStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: We don't close the scope here - that's done later, when a log line
|
|
||||||
// is actually rendered (because we have N scopes to close).
|
|
||||||
|
|
||||||
f.parentValuesStr = buf.String()
|
|
||||||
|
|
||||||
// Start collecting new values.
|
// Start collecting new values.
|
||||||
f.group = group
|
f.groupName = name
|
||||||
f.groupDepth++
|
|
||||||
f.valuesStr = ""
|
f.valuesStr = ""
|
||||||
f.values = nil
|
f.values = nil
|
||||||
}
|
}
|
||||||
|
@ -900,7 +903,7 @@ func (f *Formatter) AddValues(kvList []any) {
|
||||||
|
|
||||||
// Pre-render values, so we don't have to do it on each Info/Error call.
|
// Pre-render values, so we don't have to do it on each Info/Error call.
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
f.flatten(buf, vals, false, true) // escape user-provided keys
|
f.flatten(buf, vals, true) // escape user-provided keys
|
||||||
f.valuesStr = buf.String()
|
f.valuesStr = buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ go_test(
|
||||||
"@org_golang_google_genproto_googleapis_api//httpbody",
|
"@org_golang_google_genproto_googleapis_api//httpbody",
|
||||||
"@org_golang_google_genproto_googleapis_rpc//errdetails",
|
"@org_golang_google_genproto_googleapis_rpc//errdetails",
|
||||||
"@org_golang_google_genproto_googleapis_rpc//status",
|
"@org_golang_google_genproto_googleapis_rpc//status",
|
||||||
"@org_golang_google_grpc//:go_default_library",
|
"@org_golang_google_grpc//:grpc",
|
||||||
"@org_golang_google_grpc//codes",
|
"@org_golang_google_grpc//codes",
|
||||||
"@org_golang_google_grpc//health/grpc_health_v1",
|
"@org_golang_google_grpc//health/grpc_health_v1",
|
||||||
"@org_golang_google_grpc//metadata",
|
"@org_golang_google_grpc//metadata",
|
||||||
|
|
|
@ -49,6 +49,7 @@ var malformedHTTPHeaders = map[string]struct{}{
|
||||||
type (
|
type (
|
||||||
rpcMethodKey struct{}
|
rpcMethodKey struct{}
|
||||||
httpPathPatternKey struct{}
|
httpPathPatternKey struct{}
|
||||||
|
httpPatternKey struct{}
|
||||||
|
|
||||||
AnnotateContextOption func(ctx context.Context) context.Context
|
AnnotateContextOption func(ctx context.Context) context.Context
|
||||||
)
|
)
|
||||||
|
@ -148,6 +149,12 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
|
||||||
var pairs []string
|
var pairs []string
|
||||||
for key, vals := range req.Header {
|
for key, vals := range req.Header {
|
||||||
key = textproto.CanonicalMIMEHeaderKey(key)
|
key = textproto.CanonicalMIMEHeaderKey(key)
|
||||||
|
switch key {
|
||||||
|
case xForwardedFor, xForwardedHost:
|
||||||
|
// Handled separately below
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
// For backwards-compatibility, pass through 'authorization' header with no prefix.
|
// For backwards-compatibility, pass through 'authorization' header with no prefix.
|
||||||
if key == "Authorization" {
|
if key == "Authorization" {
|
||||||
|
@ -181,18 +188,17 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
|
||||||
pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host)
|
pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xff := req.Header.Values(xForwardedFor)
|
||||||
if addr := req.RemoteAddr; addr != "" {
|
if addr := req.RemoteAddr; addr != "" {
|
||||||
if remoteIP, _, err := net.SplitHostPort(addr); err == nil {
|
if remoteIP, _, err := net.SplitHostPort(addr); err == nil {
|
||||||
if fwd := req.Header.Get(xForwardedFor); fwd == "" {
|
xff = append(xff, remoteIP)
|
||||||
pairs = append(pairs, strings.ToLower(xForwardedFor), remoteIP)
|
|
||||||
} else {
|
|
||||||
pairs = append(pairs, strings.ToLower(xForwardedFor), fmt.Sprintf("%s, %s", fwd, remoteIP))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(xff) > 0 {
|
||||||
|
pairs = append(pairs, strings.ToLower(xForwardedFor), strings.Join(xff, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
if timeout != 0 {
|
if timeout != 0 {
|
||||||
//nolint:govet // The context outlives this function
|
|
||||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||||
}
|
}
|
||||||
if len(pairs) == 0 {
|
if len(pairs) == 0 {
|
||||||
|
@ -399,3 +405,13 @@ func HTTPPathPattern(ctx context.Context) (string, bool) {
|
||||||
func withHTTPPathPattern(ctx context.Context, httpPathPattern string) context.Context {
|
func withHTTPPathPattern(ctx context.Context, httpPathPattern string) context.Context {
|
||||||
return context.WithValue(ctx, httpPathPatternKey{}, httpPathPattern)
|
return context.WithValue(ctx, httpPathPatternKey{}, httpPathPattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTPPattern returns the HTTP path pattern struct relating to the HTTP handler, if one exists.
|
||||||
|
func HTTPPattern(ctx context.Context) (Pattern, bool) {
|
||||||
|
v, ok := ctx.Value(httpPatternKey{}).(Pattern)
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func withHTTPPattern(ctx context.Context, httpPattern Pattern) context.Context {
|
||||||
|
return context.WithValue(ctx, httpPatternKey{}, httpPattern)
|
||||||
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ func HTTPStatusFromCode(code codes.Code) int {
|
||||||
case codes.DataLoss:
|
case codes.DataLoss:
|
||||||
return http.StatusInternalServerError
|
return http.StatusInternalServerError
|
||||||
default:
|
default:
|
||||||
grpclog.Infof("Unknown gRPC error code: %v", code)
|
grpclog.Warningf("Unknown gRPC error code: %v", code)
|
||||||
return http.StatusInternalServerError
|
return http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ func HTTPError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.R
|
||||||
func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) {
|
func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) {
|
||||||
// return Internal when Marshal failed
|
// return Internal when Marshal failed
|
||||||
const fallback = `{"code": 13, "message": "failed to marshal error message"}`
|
const fallback = `{"code": 13, "message": "failed to marshal error message"}`
|
||||||
|
const fallbackRewriter = `{"code": 13, "message": "failed to rewrite error message"}`
|
||||||
|
|
||||||
var customStatus *HTTPStatusError
|
var customStatus *HTTPStatusError
|
||||||
if errors.As(err, &customStatus) {
|
if errors.As(err, &customStatus) {
|
||||||
|
@ -100,31 +101,40 @@ func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marsh
|
||||||
}
|
}
|
||||||
|
|
||||||
s := status.Convert(err)
|
s := status.Convert(err)
|
||||||
pb := s.Proto()
|
|
||||||
|
|
||||||
w.Header().Del("Trailer")
|
w.Header().Del("Trailer")
|
||||||
w.Header().Del("Transfer-Encoding")
|
w.Header().Del("Transfer-Encoding")
|
||||||
|
|
||||||
contentType := marshaler.ContentType(pb)
|
respRw, err := mux.forwardResponseRewriter(ctx, s.Proto())
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Errorf("Failed to rewrite error message %q: %v", s, err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
if _, err := io.WriteString(w, fallbackRewriter); err != nil {
|
||||||
|
grpclog.Errorf("Failed to write response: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := marshaler.ContentType(respRw)
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
if s.Code() == codes.Unauthenticated {
|
if s.Code() == codes.Unauthenticated {
|
||||||
w.Header().Set("WWW-Authenticate", s.Message())
|
w.Header().Set("WWW-Authenticate", s.Message())
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, merr := marshaler.Marshal(pb)
|
buf, merr := marshaler.Marshal(respRw)
|
||||||
if merr != nil {
|
if merr != nil {
|
||||||
grpclog.Infof("Failed to marshal error message %q: %v", s, merr)
|
grpclog.Errorf("Failed to marshal error message %q: %v", s, merr)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
if _, err := io.WriteString(w, fallback); err != nil {
|
if _, err := io.WriteString(w, fallback); err != nil {
|
||||||
grpclog.Infof("Failed to write response: %v", err)
|
grpclog.Errorf("Failed to write response: %v", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
md, ok := ServerMetadataFromContext(ctx)
|
md, ok := ServerMetadataFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Infof("Failed to extract ServerMetadata from context")
|
grpclog.Error("Failed to extract ServerMetadata from context")
|
||||||
}
|
}
|
||||||
|
|
||||||
handleForwardResponseServerMetadata(w, mux, md)
|
handleForwardResponseServerMetadata(w, mux, md)
|
||||||
|
@ -148,7 +158,7 @@ func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marsh
|
||||||
|
|
||||||
w.WriteHeader(st)
|
w.WriteHeader(st)
|
||||||
if _, err := w.Write(buf); err != nil {
|
if _, err := w.Write(buf); err != nil {
|
||||||
grpclog.Infof("Failed to write response: %v", err)
|
grpclog.Errorf("Failed to write response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if doForwardTrailers {
|
if doForwardTrailers {
|
||||||
|
|
|
@ -3,9 +3,11 @@ package runtime
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/genproto/googleapis/api/httpbody"
|
"google.golang.org/genproto/googleapis/api/httpbody"
|
||||||
|
@ -17,16 +19,10 @@ import (
|
||||||
|
|
||||||
// ForwardResponseStream forwards the stream from gRPC server to REST client.
|
// ForwardResponseStream forwards the stream from gRPC server to REST client.
|
||||||
func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||||
f, ok := w.(http.Flusher)
|
rc := http.NewResponseController(w)
|
||||||
if !ok {
|
|
||||||
grpclog.Infof("Flush not supported in %T", w)
|
|
||||||
http.Error(w, "unexpected type of web server", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
md, ok := ServerMetadataFromContext(ctx)
|
md, ok := ServerMetadataFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Infof("Failed to extract ServerMetadata from context")
|
grpclog.Error("Failed to extract ServerMetadata from context")
|
||||||
http.Error(w, "unexpected error", http.StatusInternalServerError)
|
http.Error(w, "unexpected error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -60,20 +56,27 @@ func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshal
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
respRw, err := mux.forwardResponseRewriter(ctx, resp)
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Errorf("Rewrite error: %v", err)
|
||||||
|
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err, delimiter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !wroteHeader {
|
if !wroteHeader {
|
||||||
w.Header().Set("Content-Type", marshaler.ContentType(resp))
|
w.Header().Set("Content-Type", marshaler.ContentType(respRw))
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf []byte
|
var buf []byte
|
||||||
httpBody, isHTTPBody := resp.(*httpbody.HttpBody)
|
httpBody, isHTTPBody := respRw.(*httpbody.HttpBody)
|
||||||
switch {
|
switch {
|
||||||
case resp == nil:
|
case respRw == nil:
|
||||||
buf, err = marshaler.Marshal(errorChunk(status.New(codes.Internal, "empty response")))
|
buf, err = marshaler.Marshal(errorChunk(status.New(codes.Internal, "empty response")))
|
||||||
case isHTTPBody:
|
case isHTTPBody:
|
||||||
buf = httpBody.GetData()
|
buf = httpBody.GetData()
|
||||||
default:
|
default:
|
||||||
result := map[string]interface{}{"result": resp}
|
result := map[string]interface{}{"result": respRw}
|
||||||
if rb, ok := resp.(responseBody); ok {
|
if rb, ok := respRw.(responseBody); ok {
|
||||||
result["result"] = rb.XXX_ResponseBody()
|
result["result"] = rb.XXX_ResponseBody()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,20 +84,29 @@ func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshal
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("Failed to marshal response chunk: %v", err)
|
grpclog.Errorf("Failed to marshal response chunk: %v", err)
|
||||||
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err, delimiter)
|
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err, delimiter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := w.Write(buf); err != nil {
|
if _, err := w.Write(buf); err != nil {
|
||||||
grpclog.Infof("Failed to send response chunk: %v", err)
|
grpclog.Errorf("Failed to send response chunk: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wroteHeader = true
|
wroteHeader = true
|
||||||
if _, err := w.Write(delimiter); err != nil {
|
if _, err := w.Write(delimiter); err != nil {
|
||||||
grpclog.Infof("Failed to send delimiter chunk: %v", err)
|
grpclog.Errorf("Failed to send delimiter chunk: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = rc.Flush()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, http.ErrNotSupported) {
|
||||||
|
grpclog.Errorf("Flush not supported in %T", w)
|
||||||
|
http.Error(w, "unexpected type of web server", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
grpclog.Errorf("Failed to flush response to client: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.Flush()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +148,7 @@ type responseBody interface {
|
||||||
func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||||
md, ok := ServerMetadataFromContext(ctx)
|
md, ok := ServerMetadataFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Infof("Failed to extract ServerMetadata from context")
|
grpclog.Error("Failed to extract ServerMetadata from context")
|
||||||
}
|
}
|
||||||
|
|
||||||
handleForwardResponseServerMetadata(w, mux, md)
|
handleForwardResponseServerMetadata(w, mux, md)
|
||||||
|
@ -160,21 +172,30 @@ func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marsha
|
||||||
HTTPError(ctx, mux, marshaler, w, req, err)
|
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
respRw, err := mux.forwardResponseRewriter(ctx, resp)
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Errorf("Rewrite error: %v", err)
|
||||||
|
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
var buf []byte
|
var buf []byte
|
||||||
var err error
|
if rb, ok := respRw.(responseBody); ok {
|
||||||
if rb, ok := resp.(responseBody); ok {
|
|
||||||
buf, err = marshaler.Marshal(rb.XXX_ResponseBody())
|
buf, err = marshaler.Marshal(rb.XXX_ResponseBody())
|
||||||
} else {
|
} else {
|
||||||
buf, err = marshaler.Marshal(resp)
|
buf, err = marshaler.Marshal(respRw)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("Marshal error: %v", err)
|
grpclog.Errorf("Marshal error: %v", err)
|
||||||
HTTPError(ctx, mux, marshaler, w, req, err)
|
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !doForwardTrailers {
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(len(buf)))
|
||||||
|
}
|
||||||
|
|
||||||
if _, err = w.Write(buf); err != nil {
|
if _, err = w.Write(buf); err != nil {
|
||||||
grpclog.Infof("Failed to write response: %v", err)
|
grpclog.Errorf("Failed to write response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if doForwardTrailers {
|
if doForwardTrailers {
|
||||||
|
@ -193,8 +214,7 @@ func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, re
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if err := opt(ctx, w, resp); err != nil {
|
if err := opt(ctx, w, resp); err != nil {
|
||||||
grpclog.Infof("Error handling ForwardResponseOptions: %v", err)
|
return fmt.Errorf("error handling ForwardResponseOptions: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -209,15 +229,15 @@ func handleForwardResponseStreamError(ctx context.Context, wroteHeader bool, mar
|
||||||
}
|
}
|
||||||
buf, err := marshaler.Marshal(msg)
|
buf, err := marshaler.Marshal(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("Failed to marshal an error: %v", err)
|
grpclog.Errorf("Failed to marshal an error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := w.Write(buf); err != nil {
|
if _, err := w.Write(buf); err != nil {
|
||||||
grpclog.Infof("Failed to notify error to client: %v", err)
|
grpclog.Errorf("Failed to notify error to client: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := w.Write(delimiter); err != nil {
|
if _, err := w.Write(delimiter); err != nil {
|
||||||
grpclog.Infof("Failed to send delimiter chunk: %v", err)
|
grpclog.Errorf("Failed to send delimiter chunk: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,11 @@ func (j *JSONBuiltin) Marshal(v interface{}) ([]byte, error) {
|
||||||
return json.Marshal(v)
|
return json.Marshal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalIndent is like Marshal but applies Indent to format the output
|
||||||
|
func (j *JSONBuiltin) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
return json.MarshalIndent(v, prefix, indent)
|
||||||
|
}
|
||||||
|
|
||||||
// Unmarshal unmarshals JSON data into "v".
|
// Unmarshal unmarshals JSON data into "v".
|
||||||
func (j *JSONBuiltin) Unmarshal(data []byte, v interface{}) error {
|
func (j *JSONBuiltin) Unmarshal(data []byte, v interface{}) error {
|
||||||
return json.Unmarshal(data, v)
|
return json.Unmarshal(data, v)
|
||||||
|
|
|
@ -30,10 +30,6 @@ func (*JSONPb) ContentType(_ interface{}) string {
|
||||||
|
|
||||||
// Marshal marshals "v" into JSON.
|
// Marshal marshals "v" into JSON.
|
||||||
func (j *JSONPb) Marshal(v interface{}) ([]byte, error) {
|
func (j *JSONPb) Marshal(v interface{}) ([]byte, error) {
|
||||||
if _, ok := v.(proto.Message); !ok {
|
|
||||||
return j.marshalNonProtoField(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := j.marshalTo(&buf, v); err != nil {
|
if err := j.marshalTo(&buf, v); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -48,9 +44,17 @@ func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if j.Indent != "" {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if err := json.Indent(b, buf, "", j.Indent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = b.Bytes()
|
||||||
|
}
|
||||||
_, err = w.Write(buf)
|
_, err = w.Write(buf)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := j.MarshalOptions.Marshal(p)
|
b, err := j.MarshalOptions.Marshal(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -150,9 +154,6 @@ func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
|
||||||
}
|
}
|
||||||
m[fmt.Sprintf("%v", k.Interface())] = (*json.RawMessage)(&buf)
|
m[fmt.Sprintf("%v", k.Interface())] = (*json.RawMessage)(&buf)
|
||||||
}
|
}
|
||||||
if j.Indent != "" {
|
|
||||||
return json.MarshalIndent(m, "", j.Indent)
|
|
||||||
}
|
|
||||||
return json.Marshal(m)
|
return json.Marshal(m)
|
||||||
}
|
}
|
||||||
if enum, ok := rv.Interface().(protoEnum); ok && !j.UseEnumNumbers {
|
if enum, ok := rv.Interface().(protoEnum); ok && !j.UseEnumNumbers {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func MarshalerForRequest(mux *ServeMux, r *http.Request) (inbound Marshaler, out
|
||||||
for _, contentTypeVal := range r.Header[contentTypeHeader] {
|
for _, contentTypeVal := range r.Header[contentTypeHeader] {
|
||||||
contentType, _, err := mime.ParseMediaType(contentTypeVal)
|
contentType, _, err := mime.ParseMediaType(contentTypeVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("Failed to parse Content-Type %s: %v", contentTypeVal, err)
|
grpclog.Errorf("Failed to parse Content-Type %s: %v", contentTypeVal, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if m, ok := mux.marshalers.mimeMap[contentType]; ok {
|
if m, ok := mux.marshalers.mimeMap[contentType]; ok {
|
||||||
|
|
|
@ -48,12 +48,19 @@ var encodedPathSplitter = regexp.MustCompile("(/|%2F)")
|
||||||
// A HandlerFunc handles a specific pair of path pattern and HTTP method.
|
// A HandlerFunc handles a specific pair of path pattern and HTTP method.
|
||||||
type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string)
|
type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string)
|
||||||
|
|
||||||
|
// A Middleware handler wraps another HandlerFunc to do some pre- and/or post-processing of the request. This is used as an alternative to gRPC interceptors when using the direct-to-implementation
|
||||||
|
// registration methods. It is generally recommended to use gRPC client or server interceptors instead
|
||||||
|
// where possible.
|
||||||
|
type Middleware func(HandlerFunc) HandlerFunc
|
||||||
|
|
||||||
// ServeMux is a request multiplexer for grpc-gateway.
|
// ServeMux is a request multiplexer for grpc-gateway.
|
||||||
// It matches http requests to patterns and invokes the corresponding handler.
|
// It matches http requests to patterns and invokes the corresponding handler.
|
||||||
type ServeMux struct {
|
type ServeMux struct {
|
||||||
// handlers maps HTTP method to a list of handlers.
|
// handlers maps HTTP method to a list of handlers.
|
||||||
handlers map[string][]handler
|
handlers map[string][]handler
|
||||||
|
middlewares []Middleware
|
||||||
forwardResponseOptions []func(context.Context, http.ResponseWriter, proto.Message) error
|
forwardResponseOptions []func(context.Context, http.ResponseWriter, proto.Message) error
|
||||||
|
forwardResponseRewriter ForwardResponseRewriter
|
||||||
marshalers marshalerRegistry
|
marshalers marshalerRegistry
|
||||||
incomingHeaderMatcher HeaderMatcherFunc
|
incomingHeaderMatcher HeaderMatcherFunc
|
||||||
outgoingHeaderMatcher HeaderMatcherFunc
|
outgoingHeaderMatcher HeaderMatcherFunc
|
||||||
|
@ -69,6 +76,24 @@ type ServeMux struct {
|
||||||
// ServeMuxOption is an option that can be given to a ServeMux on construction.
|
// ServeMuxOption is an option that can be given to a ServeMux on construction.
|
||||||
type ServeMuxOption func(*ServeMux)
|
type ServeMuxOption func(*ServeMux)
|
||||||
|
|
||||||
|
// ForwardResponseRewriter is the signature of a function that is capable of rewriting messages
|
||||||
|
// before they are forwarded in a unary, stream, or error response.
|
||||||
|
type ForwardResponseRewriter func(ctx context.Context, response proto.Message) (any, error)
|
||||||
|
|
||||||
|
// WithForwardResponseRewriter returns a ServeMuxOption that allows for implementers to insert logic
|
||||||
|
// that can rewrite the final response before it is forwarded.
|
||||||
|
//
|
||||||
|
// The response rewriter function is called during unary message forwarding, stream message
|
||||||
|
// forwarding and when errors are being forwarded.
|
||||||
|
//
|
||||||
|
// NOTE: Using this option will likely make what is generated by `protoc-gen-openapiv2` incorrect.
|
||||||
|
// Since this option involves making runtime changes to the response shape or type.
|
||||||
|
func WithForwardResponseRewriter(fwdResponseRewriter ForwardResponseRewriter) ServeMuxOption {
|
||||||
|
return func(sm *ServeMux) {
|
||||||
|
sm.forwardResponseRewriter = fwdResponseRewriter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithForwardResponseOption returns a ServeMuxOption representing the forwardResponseOption.
|
// WithForwardResponseOption returns a ServeMuxOption representing the forwardResponseOption.
|
||||||
//
|
//
|
||||||
// forwardResponseOption is an option that will be called on the relevant context.Context,
|
// forwardResponseOption is an option that will be called on the relevant context.Context,
|
||||||
|
@ -89,6 +114,15 @@ func WithUnescapingMode(mode UnescapingMode) ServeMuxOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithMiddlewares sets server middleware for all handlers. This is useful as an alternative to gRPC
|
||||||
|
// interceptors when using the direct-to-implementation registration methods and cannot rely
|
||||||
|
// on gRPC interceptors. It's recommended to use gRPC interceptors instead if possible.
|
||||||
|
func WithMiddlewares(middlewares ...Middleware) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.middlewares = append(serveMux.middlewares, middlewares...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SetQueryParameterParser sets the query parameter parser, used to populate message from query parameters.
|
// SetQueryParameterParser sets the query parameter parser, used to populate message from query parameters.
|
||||||
// Configuring this will mean the generated OpenAPI output is no longer correct, and it should be
|
// Configuring this will mean the generated OpenAPI output is no longer correct, and it should be
|
||||||
// done with careful consideration.
|
// done with careful consideration.
|
||||||
|
@ -279,6 +313,7 @@ func NewServeMux(opts ...ServeMuxOption) *ServeMux {
|
||||||
serveMux := &ServeMux{
|
serveMux := &ServeMux{
|
||||||
handlers: make(map[string][]handler),
|
handlers: make(map[string][]handler),
|
||||||
forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0),
|
forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0),
|
||||||
|
forwardResponseRewriter: func(ctx context.Context, response proto.Message) (any, error) { return response, nil },
|
||||||
marshalers: makeMarshalerMIMERegistry(),
|
marshalers: makeMarshalerMIMERegistry(),
|
||||||
errorHandler: DefaultHTTPErrorHandler,
|
errorHandler: DefaultHTTPErrorHandler,
|
||||||
streamErrorHandler: DefaultStreamErrorHandler,
|
streamErrorHandler: DefaultStreamErrorHandler,
|
||||||
|
@ -305,6 +340,9 @@ func NewServeMux(opts ...ServeMuxOption) *ServeMux {
|
||||||
|
|
||||||
// Handle associates "h" to the pair of HTTP method and path pattern.
|
// Handle associates "h" to the pair of HTTP method and path pattern.
|
||||||
func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) {
|
func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) {
|
||||||
|
if len(s.middlewares) > 0 {
|
||||||
|
h = chainMiddlewares(s.middlewares)(h)
|
||||||
|
}
|
||||||
s.handlers[meth] = append([]handler{{pat: pat, h: h}}, s.handlers[meth]...)
|
s.handlers[meth] = append([]handler{{pat: pat, h: h}}, s.handlers[meth]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +443,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
h.h(w, r, pathParams)
|
s.handleHandler(h, w, r, pathParams)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +496,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.errorHandler(ctx, s, outboundMarshaler, w, r, sterr)
|
s.errorHandler(ctx, s, outboundMarshaler, w, r, sterr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.h(w, r, pathParams)
|
s.handleHandler(h, w, r, pathParams)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, outboundMarshaler := MarshalerForRequest(s, r)
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
@ -484,3 +522,16 @@ type handler struct {
|
||||||
pat Pattern
|
pat Pattern
|
||||||
h HandlerFunc
|
h HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServeMux) handleHandler(h handler, w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
|
||||||
|
h.h(w, r.WithContext(withHTTPPattern(r.Context(), h.pat)), pathParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func chainMiddlewares(mws []Middleware) Middleware {
|
||||||
|
return func(next HandlerFunc) HandlerFunc {
|
||||||
|
for i := len(mws); i > 0; i-- {
|
||||||
|
next = mws[i-1](next)
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,13 +52,13 @@ type Pattern struct {
|
||||||
// It returns an error if the given definition is invalid.
|
// It returns an error if the given definition is invalid.
|
||||||
func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) {
|
func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) {
|
||||||
if version != 1 {
|
if version != 1 {
|
||||||
grpclog.Infof("unsupported version: %d", version)
|
grpclog.Errorf("unsupported version: %d", version)
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
l := len(ops)
|
l := len(ops)
|
||||||
if l%2 != 0 {
|
if l%2 != 0 {
|
||||||
grpclog.Infof("odd number of ops codes: %d", l)
|
grpclog.Errorf("odd number of ops codes: %d", l)
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,14 +81,14 @@ func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, er
|
||||||
stack++
|
stack++
|
||||||
case utilities.OpPushM:
|
case utilities.OpPushM:
|
||||||
if pushMSeen {
|
if pushMSeen {
|
||||||
grpclog.Infof("pushM appears twice")
|
grpclog.Error("pushM appears twice")
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
pushMSeen = true
|
pushMSeen = true
|
||||||
stack++
|
stack++
|
||||||
case utilities.OpLitPush:
|
case utilities.OpLitPush:
|
||||||
if op.operand < 0 || len(pool) <= op.operand {
|
if op.operand < 0 || len(pool) <= op.operand {
|
||||||
grpclog.Infof("negative literal index: %d", op.operand)
|
grpclog.Errorf("negative literal index: %d", op.operand)
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
if pushMSeen {
|
if pushMSeen {
|
||||||
|
@ -97,18 +97,18 @@ func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, er
|
||||||
stack++
|
stack++
|
||||||
case utilities.OpConcatN:
|
case utilities.OpConcatN:
|
||||||
if op.operand <= 0 {
|
if op.operand <= 0 {
|
||||||
grpclog.Infof("negative concat size: %d", op.operand)
|
grpclog.Errorf("negative concat size: %d", op.operand)
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
stack -= op.operand
|
stack -= op.operand
|
||||||
if stack < 0 {
|
if stack < 0 {
|
||||||
grpclog.Info("stack underflow")
|
grpclog.Error("stack underflow")
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
stack++
|
stack++
|
||||||
case utilities.OpCapture:
|
case utilities.OpCapture:
|
||||||
if op.operand < 0 || len(pool) <= op.operand {
|
if op.operand < 0 || len(pool) <= op.operand {
|
||||||
grpclog.Infof("variable name index out of bound: %d", op.operand)
|
grpclog.Errorf("variable name index out of bound: %d", op.operand)
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
v := pool[op.operand]
|
v := pool[op.operand]
|
||||||
|
@ -116,11 +116,11 @@ func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, er
|
||||||
vars = append(vars, v)
|
vars = append(vars, v)
|
||||||
stack--
|
stack--
|
||||||
if stack < 0 {
|
if stack < 0 {
|
||||||
grpclog.Infof("stack underflow")
|
grpclog.Error("stack underflow")
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
grpclog.Infof("invalid opcode: %d", op.code)
|
grpclog.Errorf("invalid opcode: %d", op.code)
|
||||||
return Pattern{}, ErrInvalidPattern
|
return Pattern{}, ErrInvalidPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,5 @@ collison
|
||||||
consequentially
|
consequentially
|
||||||
ans
|
ans
|
||||||
nam
|
nam
|
||||||
|
valu
|
||||||
|
thirdparty
|
||||||
|
|
|
@ -5,6 +5,6 @@ check-filenames =
|
||||||
check-hidden =
|
check-hidden =
|
||||||
ignore-words = .codespellignore
|
ignore-words = .codespellignore
|
||||||
interactive = 1
|
interactive = 1
|
||||||
skip = .git,go.mod,go.sum,semconv,venv,.tools
|
skip = .git,go.mod,go.sum,go.work,go.work.sum,semconv,venv,.tools
|
||||||
uri-ignore-words-list = *
|
uri-ignore-words-list = *
|
||||||
write =
|
write =
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "opentelemetry-proto"]
|
|
||||||
path = exporters/otlp/internal/opentelemetry-proto
|
|
||||||
url = https://github.com/open-telemetry/opentelemetry-proto
|
|
|
@ -9,8 +9,11 @@ linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
# Specifically enable linters we want to use.
|
# Specifically enable linters we want to use.
|
||||||
enable:
|
enable:
|
||||||
|
- asasalint
|
||||||
|
- bodyclose
|
||||||
- depguard
|
- depguard
|
||||||
- errcheck
|
- errcheck
|
||||||
|
- errorlint
|
||||||
- godot
|
- godot
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- goimports
|
- goimports
|
||||||
|
@ -21,8 +24,11 @@ linters:
|
||||||
- misspell
|
- misspell
|
||||||
- revive
|
- revive
|
||||||
- staticcheck
|
- staticcheck
|
||||||
|
- tenv
|
||||||
- typecheck
|
- typecheck
|
||||||
|
- unconvert
|
||||||
- unused
|
- unused
|
||||||
|
- unparam
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
# Maximum issues count per one linter.
|
# Maximum issues count per one linter.
|
||||||
|
@ -124,6 +130,8 @@ linters-settings:
|
||||||
- "**/example/**/*.go"
|
- "**/example/**/*.go"
|
||||||
- "**/trace/*.go"
|
- "**/trace/*.go"
|
||||||
- "**/trace/**/*.go"
|
- "**/trace/**/*.go"
|
||||||
|
- "**/log/*.go"
|
||||||
|
- "**/log/**/*.go"
|
||||||
deny:
|
deny:
|
||||||
- pkg: "go.opentelemetry.io/otel/internal$"
|
- pkg: "go.opentelemetry.io/otel/internal$"
|
||||||
desc: Do not use cross-module internal packages.
|
desc: Do not use cross-module internal packages.
|
||||||
|
|
|
@ -8,6 +8,142 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
<!-- Released section -->
|
||||||
|
<!-- Don't change this section unless doing release -->
|
||||||
|
|
||||||
|
## [1.29.0/0.51.0/0.5.0] 2024-08-23
|
||||||
|
|
||||||
|
This release is the last to support [Go 1.21].
|
||||||
|
The next release will require at least [Go 1.22].
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add MacOS ARM64 platform to the compatibility testing suite. (#5577)
|
||||||
|
- Add `InstrumentationScope` field to `SpanStub` in `go.opentelemetry.io/otel/sdk/trace/tracetest`, as a replacement for the deprecated `InstrumentationLibrary`. (#5627)
|
||||||
|
- Make the initial release of `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`.
|
||||||
|
This new module contains an OTLP exporter that transmits log telemetry using gRPC.
|
||||||
|
This module is unstable and breaking changes may be introduced.
|
||||||
|
See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. (#5629)
|
||||||
|
- Add `Walk` function to `TraceState` in `go.opentelemetry.io/otel/trace` to iterate all the key-value pairs. (#5651)
|
||||||
|
- Bridge the trace state in `go.opentelemetry.io/otel/bridge/opencensus`. (#5651)
|
||||||
|
- Zero value of `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` no longer panics. (#5665)
|
||||||
|
- The `FilterProcessor` interface type is added in `go.opentelemetry.io/otel/sdk/log/internal/x`.
|
||||||
|
This is an optional and experimental interface that log `Processor`s can implement to instruct the `Logger` if a `Record` will be processed or not.
|
||||||
|
It replaces the existing `Enabled` method that is removed from the `Processor` interface itself.
|
||||||
|
It does not fall within the scope of the OpenTelemetry Go versioning and stability [policy](./VERSIONING.md) and it may be changed in backwards incompatible ways or removed in feature releases. (#5692)
|
||||||
|
- Support [Go 1.23]. (#5720)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `NewMemberRaw`, `NewKeyProperty` and `NewKeyValuePropertyRaw` in `go.opentelemetry.io/otel/baggage` allow UTF-8 string in key. (#5132)
|
||||||
|
- `Processor.OnEmit` in `go.opentelemetry.io/otel/sdk/log` now accepts a pointer to `Record` instead of a value so that the record modifications done in a processor are propagated to subsequent registered processors. (#5636)
|
||||||
|
- `SimpleProcessor.Enabled` in `go.opentelemetry.io/otel/sdk/log` now returns `false` if the exporter is `nil`. (#5665)
|
||||||
|
- Update the concurrency requirements of `Exporter` in `go.opentelemetry.io/otel/sdk/log`. (#5666)
|
||||||
|
- `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` synchronizes `OnEmit` calls. (#5666)
|
||||||
|
- The `Processor` interface in `go.opentelemetry.io/otel/sdk/log` no longer includes the `Enabled` method.
|
||||||
|
See the `FilterProcessor` interface type added in `go.opentelemetry.io/otel/sdk/log/internal/x` to continue providing this functionality. (#5692)
|
||||||
|
- The `SimpleProcessor` type in `go.opentelemetry.io/otel/sdk/log` is no longer comparable. (#5693)
|
||||||
|
- The `BatchProcessor` type in `go.opentelemetry.io/otel/sdk/log` is no longer comparable. (#5693)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5584)
|
||||||
|
- Pass the underlying error rather than a generic retry-able failure in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` and `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5541)
|
||||||
|
- Correct the `Tracer`, `Meter`, and `Logger` names used in `go.opentelemetry.io/otel/example/dice`. (#5612)
|
||||||
|
- Correct the `Tracer` names used in `go.opentelemetry.io/otel/example/namedtracer`. (#5612)
|
||||||
|
- Correct the `Tracer` name used in `go.opentelemetry.io/otel/example/opencensus`. (#5612)
|
||||||
|
- Correct the `Tracer` and `Meter` names used in `go.opentelemetry.io/otel/example/otel-collector`. (#5612)
|
||||||
|
- Correct the `Tracer` names used in `go.opentelemetry.io/otel/example/passthrough`. (#5612)
|
||||||
|
- Correct the `Meter` name used in `go.opentelemetry.io/otel/example/prometheus`. (#5612)
|
||||||
|
- Correct the `Tracer` names used in `go.opentelemetry.io/otel/example/zipkin`. (#5612)
|
||||||
|
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#5641)
|
||||||
|
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5650)
|
||||||
|
- Stop percent encoding header environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` (#5705)
|
||||||
|
- Remove invalid environment variable header keys in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` (#5705)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- The `Enabled` method of the `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` is removed. (#5692)
|
||||||
|
- The `Enabled` method of the `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` is removed. (#5692)
|
||||||
|
|
||||||
|
## [1.28.0/0.50.0/0.4.0] 2024-07-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- The `IsEmpty` method is added to the `Instrument` type in `go.opentelemetry.io/otel/sdk/metric`.
|
||||||
|
This method is used to check if an `Instrument` instance is a zero-value. (#5431)
|
||||||
|
- Store and provide the emitted `context.Context` in `ScopeRecords` of `go.opentelemetry.io/otel/sdk/log/logtest`. (#5468)
|
||||||
|
- The `go.opentelemetry.io/otel/semconv/v1.26.0` package.
|
||||||
|
The package contains semantic conventions from the `v1.26.0` version of the OpenTelemetry Semantic Conventions. (#5476)
|
||||||
|
- The `AssertRecordEqual` method to `go.opentelemetry.io/otel/log/logtest` to allow comparison of two log records in tests. (#5499)
|
||||||
|
- The `WithHeaders` option to `go.opentelemetry.io/otel/exporters/zipkin` to allow configuring custom http headers while exporting spans. (#5530)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `Tracer.Start` in `go.opentelemetry.io/otel/trace/noop` no longer allocates a span for empty span context. (#5457)
|
||||||
|
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/example/otel-collector`. (#5490)
|
||||||
|
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/example/zipkin`. (#5490)
|
||||||
|
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/exporters/zipkin`. (#5490)
|
||||||
|
- The exporter no longer exports the deprecated "otel.library.name" or "otel.library.version" attributes.
|
||||||
|
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/resource`. (#5490)
|
||||||
|
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/trace`. (#5490)
|
||||||
|
- `SimpleProcessor.OnEmit` in `go.opentelemetry.io/otel/sdk/log` no longer allocates a slice which makes it possible to have a zero-allocation log processing using `SimpleProcessor`. (#5493)
|
||||||
|
- Use non-generic functions in the `Start` method of `"go.opentelemetry.io/otel/sdk/trace".Trace` to reduce memory allocation. (#5497)
|
||||||
|
- `service.instance.id` is populated for a `Resource` created with `"go.opentelemetry.io/otel/sdk/resource".Default` with a default value when `OTEL_GO_X_RESOURCE` is set. (#5520)
|
||||||
|
- Improve performance of metric instruments in `go.opentelemetry.io/otel/sdk/metric` by removing unnecessary calls to `time.Now`. (#5545)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Log a warning to the OpenTelemetry internal logger when a `Record` in `go.opentelemetry.io/otel/sdk/log` drops an attribute due to a limit being reached. (#5376)
|
||||||
|
- Identify the `Tracer` returned from the global `TracerProvider` in `go.opentelemetry.io/otel/global` with its schema URL. (#5426)
|
||||||
|
- Identify the `Meter` returned from the global `MeterProvider` in `go.opentelemetry.io/otel/global` with its schema URL. (#5426)
|
||||||
|
- Log a warning to the OpenTelemetry internal logger when a `Span` in `go.opentelemetry.io/otel/sdk/trace` drops an attribute, event, or link due to a limit being reached. (#5434)
|
||||||
|
- Document instrument name requirements in `go.opentelemetry.io/otel/metric`. (#5435)
|
||||||
|
- Prevent random number generation data-race for experimental rand exemplars in `go.opentelemetry.io/otel/sdk/metric`. (#5456)
|
||||||
|
- Fix counting number of dropped attributes of `Record` in `go.opentelemetry.io/otel/sdk/log`. (#5464)
|
||||||
|
- Fix panic in baggage creation when a member contains `0x80` char in key or value. (#5494)
|
||||||
|
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#5508)
|
||||||
|
- Retry trace and span ID generation if it generated an invalid one in `go.opentelemetry.io/otel/sdk/trace`. (#5514)
|
||||||
|
- Fix stale timestamps reported by the last-value aggregation. (#5517)
|
||||||
|
- Indicate the `Exporter` in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` must be created by the `New` method. (#5521)
|
||||||
|
- Improved performance in all `{Bool,Int64,Float64,String}SliceValue` functions of `go.opentelemetry.io/attributes` by reducing the number of allocations. (#5549)
|
||||||
|
- Replace invalid percent-encoded octet sequences with replacement char in `go.opentelemetry.io/otel/baggage`. (#5528)
|
||||||
|
|
||||||
|
## [1.27.0/0.49.0/0.3.0] 2024-05-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add example for `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#5242)
|
||||||
|
- Add `RecordFactory` in `go.opentelemetry.io/otel/sdk/log/logtest` to facilitate testing exporter and processor implementations. (#5258)
|
||||||
|
- Add `RecordFactory` in `go.opentelemetry.io/otel/log/logtest` to facilitate testing bridge implementations. (#5263)
|
||||||
|
- The count of dropped records from the `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` is logged. (#5276)
|
||||||
|
- Add metrics in the `otel-collector` example. (#5283)
|
||||||
|
- Add the synchronous gauge instrument to `go.opentelemetry.io/otel/metric`. (#5304)
|
||||||
|
- An `int64` or `float64` synchronous gauge instrument can now be created from a `Meter`.
|
||||||
|
- All implementations of the API (`go.opentelemetry.io/otel/metric/noop`, `go.opentelemetry.io/otel/sdk/metric`) are updated to support this instrument.
|
||||||
|
- Add logs to `go.opentelemetry.io/otel/example/dice`. (#5349)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The `Shutdown` method of `Exporter` in `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` ignores the context cancellation and always returns `nil`. (#5189)
|
||||||
|
- The `ForceFlush` and `Shutdown` methods of the exporter returned by `New` in `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` ignore the context cancellation and always return `nil`. (#5189)
|
||||||
|
- Apply the value length limits to `Record` attributes in `go.opentelemetry.io/otel/sdk/log`. (#5230)
|
||||||
|
- De-duplicate map attributes added to a `Record` in `go.opentelemetry.io/otel/sdk/log`. (#5230)
|
||||||
|
- `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` won't print timestamps when `WithoutTimestamps` option is set. (#5241)
|
||||||
|
- The `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` exporter won't print `AttributeValueLengthLimit` and `AttributeCountLimit` fields now, instead it prints the `DroppedAttributes` field. (#5272)
|
||||||
|
- Improved performance in the `Stringer` implementation of `go.opentelemetry.io/otel/baggage.Member` by reducing the number of allocations. (#5286)
|
||||||
|
- Set the start time for last-value aggregates in `go.opentelemetry.io/otel/sdk/metric`. (#5305)
|
||||||
|
- The `Span` in `go.opentelemetry.io/otel/sdk/trace` will record links without span context if either non-empty `TraceState` or attributes are provided. (#5315)
|
||||||
|
- Upgrade all dependencies of `go.opentelemetry.io/otel/semconv/v1.24.0` to `go.opentelemetry.io/otel/semconv/v1.25.0`. (#5374)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Comparison of unordered maps for `go.opentelemetry.io/otel/log.KeyValue` and `go.opentelemetry.io/otel/log.Value`. (#5306)
|
||||||
|
- Fix the empty output of `go.opentelemetry.io/otel/log.Value` in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#5311)
|
||||||
|
- Split the behavior of `Recorder` in `go.opentelemetry.io/otel/log/logtest` so it behaves as a `LoggerProvider` only. (#5365)
|
||||||
|
- Fix wrong package name of the error message when parsing endpoint URL in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5371)
|
||||||
|
- Identify the `Logger` returned from the global `LoggerProvider` in `go.opentelemetry.io/otel/log/global` with its schema URL. (#5375)
|
||||||
|
|
||||||
## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24
|
## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -33,6 +169,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||||
|
|
||||||
- Update `go.opentelemetry.io/proto/otlp` from v1.1.0 to v1.2.0. (#5177)
|
- Update `go.opentelemetry.io/proto/otlp` from v1.1.0 to v1.2.0. (#5177)
|
||||||
- Improve performance of baggage member character validation in `go.opentelemetry.io/otel/baggage`. (#5214)
|
- Improve performance of baggage member character validation in `go.opentelemetry.io/otel/baggage`. (#5214)
|
||||||
|
- The `otel-collector` example now uses docker compose to bring up services instead of kubernetes. (#5244)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Slice attribute values in `go.opentelemetry.io/otel/attribute` are now emitted as their JSON representation. (#5159)
|
||||||
|
|
||||||
## [1.25.0/0.47.0/0.0.8/0.1.0-alpha] 2024-04-05
|
## [1.25.0/0.47.0/0.0.8/0.1.0-alpha] 2024-04-05
|
||||||
|
|
||||||
|
@ -93,7 +234,7 @@ The next release will require at least [Go 1.21].
|
||||||
This module includes OpenTelemetry Go's implementation of the Logs Bridge API.
|
This module includes OpenTelemetry Go's implementation of the Logs Bridge API.
|
||||||
This module is in an alpha state, it is subject to breaking changes.
|
This module is in an alpha state, it is subject to breaking changes.
|
||||||
See our [versioning policy](./VERSIONING.md) for more info. (#4961)
|
See our [versioning policy](./VERSIONING.md) for more info. (#4961)
|
||||||
- ARM64 platform to the compatibility testing suite. (#4994)
|
- Add ARM64 platform to the compatibility testing suite. (#4994)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -210,7 +351,7 @@ See our [versioning policy](VERSIONING.md) for more information about these stab
|
||||||
|
|
||||||
## [1.20.0/0.43.0] 2023-11-10
|
## [1.20.0/0.43.0] 2023-11-10
|
||||||
|
|
||||||
This release brings a breaking change for custom trace API implementations. Some interfaces (`TracerProvider`, `Tracer`, `Span`) now embed the `go.opentelemetry.io/otel/trace/embedded` types. Implementors need to update their implementations based on what they want the default behavior to be. See the "API Implementations" section of the [trace API] package documentation for more information about how to accomplish this.
|
This release brings a breaking change for custom trace API implementations. Some interfaces (`TracerProvider`, `Tracer`, `Span`) now embed the `go.opentelemetry.io/otel/trace/embedded` types. Implementers need to update their implementations based on what they want the default behavior to be. See the "API Implementations" section of the [trace API] package documentation for more information about how to accomplish this.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -242,15 +383,15 @@ This release brings a breaking change for custom trace API implementations. Some
|
||||||
- `go.opentelemetry.io/otel/bridge/opencensus.NewMetricProducer` returns a `*MetricProducer` struct instead of the metric.Producer interface. (#4583)
|
- `go.opentelemetry.io/otel/bridge/opencensus.NewMetricProducer` returns a `*MetricProducer` struct instead of the metric.Producer interface. (#4583)
|
||||||
- The `TracerProvider` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.TracerProvider` type.
|
- The `TracerProvider` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.TracerProvider` type.
|
||||||
This extends the `TracerProvider` interface and is is a breaking change for any existing implementation.
|
This extends the `TracerProvider` interface and is is a breaking change for any existing implementation.
|
||||||
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
|
Implementers need to update their implementations based on what they want the default behavior of the interface to be.
|
||||||
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
|
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
|
||||||
- The `Tracer` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Tracer` type.
|
- The `Tracer` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Tracer` type.
|
||||||
This extends the `Tracer` interface and is is a breaking change for any existing implementation.
|
This extends the `Tracer` interface and is is a breaking change for any existing implementation.
|
||||||
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
|
Implementers need to update their implementations based on what they want the default behavior of the interface to be.
|
||||||
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
|
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
|
||||||
- The `Span` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Span` type.
|
- The `Span` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Span` type.
|
||||||
This extends the `Span` interface and is is a breaking change for any existing implementation.
|
This extends the `Span` interface and is is a breaking change for any existing implementation.
|
||||||
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
|
Implementers need to update their implementations based on what they want the default behavior of the interface to be.
|
||||||
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
|
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
|
||||||
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
|
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
|
||||||
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
|
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
|
||||||
|
@ -886,7 +1027,7 @@ The next release will require at least [Go 1.19].
|
||||||
- Exported `Status` codes in the `go.opentelemetry.io/otel/exporters/zipkin` exporter are now exported as all upper case values. (#3340)
|
- Exported `Status` codes in the `go.opentelemetry.io/otel/exporters/zipkin` exporter are now exported as all upper case values. (#3340)
|
||||||
- `Aggregation`s from `go.opentelemetry.io/otel/sdk/metric` with no data are not exported. (#3394, #3436)
|
- `Aggregation`s from `go.opentelemetry.io/otel/sdk/metric` with no data are not exported. (#3394, #3436)
|
||||||
- Re-enabled Attribute Filters in the Metric SDK. (#3396)
|
- Re-enabled Attribute Filters in the Metric SDK. (#3396)
|
||||||
- Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggragation. (#3408)
|
- Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggregation. (#3408)
|
||||||
- Do not report empty partial-success responses in the `go.opentelemetry.io/otel/exporters/otlp` exporters. (#3438, #3432)
|
- Do not report empty partial-success responses in the `go.opentelemetry.io/otel/exporters/otlp` exporters. (#3438, #3432)
|
||||||
- Handle partial success responses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` exporters. (#3162, #3440)
|
- Handle partial success responses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` exporters. (#3162, #3440)
|
||||||
- Prevent duplicate Prometheus description, unit, and type. (#3469)
|
- Prevent duplicate Prometheus description, unit, and type. (#3469)
|
||||||
|
@ -1931,7 +2072,7 @@ with major version 0.
|
||||||
- `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369)
|
- `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369)
|
||||||
- Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369)
|
- Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369)
|
||||||
- Unify endpoint API that related to OTel exporter. (#1401)
|
- Unify endpoint API that related to OTel exporter. (#1401)
|
||||||
- Optimize metric histogram aggregator to re-use its slice of buckets. (#1435)
|
- Optimize metric histogram aggregator to reuse its slice of buckets. (#1435)
|
||||||
- Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430)
|
- Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430)
|
||||||
- Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434)
|
- Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434)
|
||||||
- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432)
|
- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432)
|
||||||
|
@ -2921,7 +3062,10 @@ It contains api and sdk for trace and meter.
|
||||||
- CircleCI build CI manifest files.
|
- CircleCI build CI manifest files.
|
||||||
- CODEOWNERS file to track owners of this project.
|
- CODEOWNERS file to track owners of this project.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...HEAD
|
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.29.0...HEAD
|
||||||
|
[1.29.0/0.51.0/0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.29.0
|
||||||
|
[1.28.0/0.50.0/0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.28.0
|
||||||
|
[1.27.0/0.49.0/0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.27.0
|
||||||
[1.26.0/0.48.0/0.2.0-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.26.0
|
[1.26.0/0.48.0/0.2.0-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.26.0
|
||||||
[1.25.0/0.47.0/0.0.8/0.1.0-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.25.0
|
[1.25.0/0.47.0/0.0.8/0.1.0-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.25.0
|
||||||
[1.24.0/0.46.0/0.0.1-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.24.0
|
[1.24.0/0.46.0/0.0.1-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.24.0
|
||||||
|
@ -3002,6 +3146,9 @@ It contains api and sdk for trace and meter.
|
||||||
[0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1
|
[0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1
|
||||||
[0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0
|
[0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0
|
||||||
|
|
||||||
|
<!-- Released section ended -->
|
||||||
|
|
||||||
|
[Go 1.23]: https://go.dev/doc/go1.23
|
||||||
[Go 1.22]: https://go.dev/doc/go1.22
|
[Go 1.22]: https://go.dev/doc/go1.22
|
||||||
[Go 1.21]: https://go.dev/doc/go1.21
|
[Go 1.21]: https://go.dev/doc/go1.21
|
||||||
[Go 1.20]: https://go.dev/doc/go1.20
|
[Go 1.20]: https://go.dev/doc/go1.20
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
#####################################################
|
#####################################################
|
||||||
#
|
#
|
||||||
# Learn about membership in OpenTelemetry community:
|
# Learn about membership in OpenTelemetry community:
|
||||||
# https://github.com/open-telemetry/community/blob/main/community-membership.md
|
# https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Learn about CODEOWNERS file format:
|
# Learn about CODEOWNERS file format:
|
||||||
# https://help.github.com/en/articles/about-code-owners
|
# https://help.github.com/en/articles/about-code-owners
|
||||||
#
|
#
|
||||||
|
|
||||||
* @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu
|
* @MrAlias @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu
|
||||||
|
|
||||||
CODEOWNERS @MrAlias @MadVikingGod @pellared @dashpole
|
CODEOWNERS @MrAlias @MadVikingGod @pellared @dashpole @XSAM @dmathieu
|
||||||
|
|
|
@ -570,6 +570,9 @@ functionality should be added, each one will need their own super-set
|
||||||
interfaces and will duplicate the pattern. For this reason, the simple targeted
|
interfaces and will duplicate the pattern. For this reason, the simple targeted
|
||||||
interface that defines the specific functionality should be preferred.
|
interface that defines the specific functionality should be preferred.
|
||||||
|
|
||||||
|
See also:
|
||||||
|
[Keeping Your Modules Compatible: Working with interfaces](https://go.dev/blog/module-compatibility#working-with-interfaces).
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
The tests should never leak goroutines.
|
The tests should never leak goroutines.
|
||||||
|
@ -625,17 +628,15 @@ should be canceled.
|
||||||
|
|
||||||
### Approvers
|
### Approvers
|
||||||
|
|
||||||
- [Evan Torrie](https://github.com/evantorrie), Verizon Media
|
|
||||||
- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics
|
|
||||||
- [Chester Cheung](https://github.com/hanyuancheung), Tencent
|
- [Chester Cheung](https://github.com/hanyuancheung), Tencent
|
||||||
- [Damien Mathieu](https://github.com/dmathieu), Elastic
|
|
||||||
- [Anthony Mirabella](https://github.com/Aneurysm9), AWS
|
|
||||||
|
|
||||||
### Maintainers
|
### Maintainers
|
||||||
|
|
||||||
- [David Ashpole](https://github.com/dashpole), Google
|
|
||||||
- [Aaron Clawson](https://github.com/MadVikingGod), LightStep
|
- [Aaron Clawson](https://github.com/MadVikingGod), LightStep
|
||||||
|
- [Damien Mathieu](https://github.com/dmathieu), Elastic
|
||||||
|
- [David Ashpole](https://github.com/dashpole), Google
|
||||||
- [Robert Pająk](https://github.com/pellared), Splunk
|
- [Robert Pająk](https://github.com/pellared), Splunk
|
||||||
|
- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics
|
||||||
- [Tyler Yahn](https://github.com/MrAlias), Splunk
|
- [Tyler Yahn](https://github.com/MrAlias), Splunk
|
||||||
|
|
||||||
### Emeritus
|
### Emeritus
|
||||||
|
@ -643,11 +644,13 @@ should be canceled.
|
||||||
- [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb
|
- [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb
|
||||||
- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep
|
- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep
|
||||||
- [Josh MacDonald](https://github.com/jmacd), LightStep
|
- [Josh MacDonald](https://github.com/jmacd), LightStep
|
||||||
|
- [Anthony Mirabella](https://github.com/Aneurysm9), AWS
|
||||||
|
- [Evan Torrie](https://github.com/evantorrie), Yahoo
|
||||||
|
|
||||||
### Become an Approver or a Maintainer
|
### Become an Approver or a Maintainer
|
||||||
|
|
||||||
See the [community membership document in OpenTelemetry community
|
See the [community membership document in OpenTelemetry community
|
||||||
repo](https://github.com/open-telemetry/community/blob/main/community-membership.md).
|
repo](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md).
|
||||||
|
|
||||||
[Approver]: #approvers
|
[Approver]: #approvers
|
||||||
[Maintainer]: #maintainers
|
[Maintainer]: #maintainers
|
||||||
|
|
|
@ -14,8 +14,8 @@ TIMEOUT = 60
|
||||||
.DEFAULT_GOAL := precommit
|
.DEFAULT_GOAL := precommit
|
||||||
|
|
||||||
.PHONY: precommit ci
|
.PHONY: precommit ci
|
||||||
precommit: generate dependabot-generate license-check misspell go-mod-tidy golangci-lint-fix verify-readmes test-default
|
precommit: generate license-check misspell go-mod-tidy golangci-lint-fix verify-readmes verify-mods test-default
|
||||||
ci: generate dependabot-check license-check lint vanity-import-check verify-readmes build test-default check-clean-work-tree test-coverage
|
ci: generate license-check lint vanity-import-check verify-readmes verify-mods build test-default check-clean-work-tree test-coverage
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
|
|
||||||
|
@ -39,9 +39,6 @@ $(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink
|
||||||
SEMCONVKIT = $(TOOLS)/semconvkit
|
SEMCONVKIT = $(TOOLS)/semconvkit
|
||||||
$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit
|
$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit
|
||||||
|
|
||||||
DBOTCONF = $(TOOLS)/dbotconf
|
|
||||||
$(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/build-tools/dbotconf
|
|
||||||
|
|
||||||
GOLANGCI_LINT = $(TOOLS)/golangci-lint
|
GOLANGCI_LINT = $(TOOLS)/golangci-lint
|
||||||
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint
|
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||||
|
|
||||||
|
@ -70,7 +67,7 @@ GOVULNCHECK = $(TOOLS)/govulncheck
|
||||||
$(TOOLS)/govulncheck: PACKAGE=golang.org/x/vuln/cmd/govulncheck
|
$(TOOLS)/govulncheck: PACKAGE=golang.org/x/vuln/cmd/govulncheck
|
||||||
|
|
||||||
.PHONY: tools
|
.PHONY: tools
|
||||||
tools: $(CROSSLINK) $(DBOTCONF) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
|
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
|
||||||
|
|
||||||
# Virtualized python tools via docker
|
# Virtualized python tools via docker
|
||||||
|
|
||||||
|
@ -181,17 +178,14 @@ test-coverage: $(GOCOVMERGE)
|
||||||
done; \
|
done; \
|
||||||
$(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt
|
$(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt
|
||||||
|
|
||||||
# Adding a directory will include all benchmarks in that directory if a filter is not specified.
|
|
||||||
BENCHMARK_TARGETS := sdk/trace
|
|
||||||
.PHONY: benchmark
|
.PHONY: benchmark
|
||||||
benchmark: $(BENCHMARK_TARGETS:%=benchmark/%)
|
benchmark: $(OTEL_GO_MOD_DIRS:%=benchmark/%)
|
||||||
BENCHMARK_FILTER = .
|
|
||||||
# You can override the filter for a particular directory by adding a rule here.
|
|
||||||
benchmark/sdk/trace: BENCHMARK_FILTER = SpanWithAttributes_8/AlwaysSample
|
|
||||||
benchmark/%:
|
benchmark/%:
|
||||||
@echo "$(GO) test -timeout $(TIMEOUT)s -run=xxxxxMatchNothingxxxxx -bench=$(BENCHMARK_FILTER) $*..." \
|
@echo "$(GO) test -run=xxxxxMatchNothingxxxxx -bench=. $*..." \
|
||||||
&& cd $* \
|
&& cd $* \
|
||||||
$(foreach filter, $(BENCHMARK_FILTER), && $(GO) test -timeout $(TIMEOUT)s -run=xxxxxMatchNothingxxxxx -bench=$(filter))
|
&& $(GO) list ./... \
|
||||||
|
| grep -v third_party \
|
||||||
|
| xargs $(GO) test -run=xxxxxMatchNothingxxxxx -bench=.
|
||||||
|
|
||||||
.PHONY: golangci-lint golangci-lint-fix
|
.PHONY: golangci-lint golangci-lint-fix
|
||||||
golangci-lint-fix: ARGS=--fix
|
golangci-lint-fix: ARGS=--fix
|
||||||
|
@ -252,15 +246,6 @@ license-check:
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEPENDABOT_CONFIG = .github/dependabot.yml
|
|
||||||
.PHONY: dependabot-check
|
|
||||||
dependabot-check: $(DBOTCONF)
|
|
||||||
@$(DBOTCONF) verify $(DEPENDABOT_CONFIG) || ( echo "(run: make dependabot-generate)"; exit 1 )
|
|
||||||
|
|
||||||
.PHONY: dependabot-generate
|
|
||||||
dependabot-generate: $(DBOTCONF)
|
|
||||||
@$(DBOTCONF) generate > $(DEPENDABOT_CONFIG)
|
|
||||||
|
|
||||||
.PHONY: check-clean-work-tree
|
.PHONY: check-clean-work-tree
|
||||||
check-clean-work-tree:
|
check-clean-work-tree:
|
||||||
@if ! git diff --quiet; then \
|
@if ! git diff --quiet; then \
|
||||||
|
@ -276,10 +261,7 @@ SEMCONVPKG ?= "semconv/"
|
||||||
semconv-generate: $(SEMCONVGEN) $(SEMCONVKIT)
|
semconv-generate: $(SEMCONVGEN) $(SEMCONVKIT)
|
||||||
[ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 )
|
[ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 )
|
||||||
[ "$(OTEL_SEMCONV_REPO)" ] || ( echo "OTEL_SEMCONV_REPO unset: missing path to opentelemetry semantic-conventions repo"; exit 1 )
|
[ "$(OTEL_SEMCONV_REPO)" ] || ( echo "OTEL_SEMCONV_REPO unset: missing path to opentelemetry semantic-conventions repo"; exit 1 )
|
||||||
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=span -p conventionType=trace -f trace.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
|
|
||||||
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=attribute_group -p conventionType=trace -f attribute_group.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
|
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=attribute_group -p conventionType=trace -f attribute_group.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
|
||||||
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=event -p conventionType=event -f event.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
|
|
||||||
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=resource -p conventionType=resource -f resource.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
|
|
||||||
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=metric -f metric.go -t "$(SEMCONVPKG)/metric_template.j2" -s "$(TAG)"
|
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=metric -f metric.go -t "$(SEMCONVPKG)/metric_template.j2" -s "$(TAG)"
|
||||||
$(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)"
|
$(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)"
|
||||||
|
|
||||||
|
@ -292,16 +274,20 @@ gorelease/%:| $(GORELEASE)
|
||||||
&& $(GORELEASE) \
|
&& $(GORELEASE) \
|
||||||
|| echo ""
|
|| echo ""
|
||||||
|
|
||||||
|
.PHONY: verify-mods
|
||||||
|
verify-mods: $(MULTIMOD)
|
||||||
|
$(MULTIMOD) verify
|
||||||
|
|
||||||
.PHONY: prerelease
|
.PHONY: prerelease
|
||||||
prerelease: $(MULTIMOD)
|
prerelease: verify-mods
|
||||||
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
|
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
|
||||||
$(MULTIMOD) verify && $(MULTIMOD) prerelease -m ${MODSET}
|
$(MULTIMOD) prerelease -m ${MODSET}
|
||||||
|
|
||||||
COMMIT ?= "HEAD"
|
COMMIT ?= "HEAD"
|
||||||
.PHONY: add-tags
|
.PHONY: add-tags
|
||||||
add-tags: $(MULTIMOD)
|
add-tags: verify-mods
|
||||||
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
|
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
|
||||||
$(MULTIMOD) verify && $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT}
|
$(MULTIMOD) tag -m ${MODSET} -c ${COMMIT}
|
||||||
|
|
||||||
.PHONY: lint-markdown
|
.PHONY: lint-markdown
|
||||||
lint-markdown:
|
lint-markdown:
|
||||||
|
|
|
@ -15,7 +15,7 @@ It provides a set of APIs to directly measure performance and behavior of your s
|
||||||
|---------|--------------------|
|
|---------|--------------------|
|
||||||
| Traces | Stable |
|
| Traces | Stable |
|
||||||
| Metrics | Stable |
|
| Metrics | Stable |
|
||||||
| Logs | In development[^1] |
|
| Logs | Beta[^1] |
|
||||||
|
|
||||||
Progress and status specific to this repository is tracked in our
|
Progress and status specific to this repository is tracked in our
|
||||||
[project boards](https://github.com/open-telemetry/opentelemetry-go/projects)
|
[project boards](https://github.com/open-telemetry/opentelemetry-go/projects)
|
||||||
|
@ -48,17 +48,26 @@ stop ensuring compatibility with these versions in the following manner:
|
||||||
Currently, this project supports the following environments.
|
Currently, this project supports the following environments.
|
||||||
|
|
||||||
| OS | Go Version | Architecture |
|
| OS | Go Version | Architecture |
|
||||||
|---------|------------|--------------|
|
|----------|------------|--------------|
|
||||||
|
| Ubuntu | 1.23 | amd64 |
|
||||||
| Ubuntu | 1.22 | amd64 |
|
| Ubuntu | 1.22 | amd64 |
|
||||||
| Ubuntu | 1.21 | amd64 |
|
| Ubuntu | 1.21 | amd64 |
|
||||||
|
| Ubuntu | 1.23 | 386 |
|
||||||
| Ubuntu | 1.22 | 386 |
|
| Ubuntu | 1.22 | 386 |
|
||||||
| Ubuntu | 1.21 | 386 |
|
| Ubuntu | 1.21 | 386 |
|
||||||
|
| Linux | 1.23 | arm64 |
|
||||||
| Linux | 1.22 | arm64 |
|
| Linux | 1.22 | arm64 |
|
||||||
| Linux | 1.21 | arm64 |
|
| Linux | 1.21 | arm64 |
|
||||||
| MacOS | 1.22 | amd64 |
|
| macOS 13 | 1.23 | amd64 |
|
||||||
| MacOS | 1.21 | amd64 |
|
| macOS 13 | 1.22 | amd64 |
|
||||||
|
| macOS 13 | 1.21 | amd64 |
|
||||||
|
| macOS | 1.23 | arm64 |
|
||||||
|
| macOS | 1.22 | arm64 |
|
||||||
|
| macOS | 1.21 | arm64 |
|
||||||
|
| Windows | 1.23 | amd64 |
|
||||||
| Windows | 1.22 | amd64 |
|
| Windows | 1.22 | amd64 |
|
||||||
| Windows | 1.21 | amd64 |
|
| Windows | 1.21 | amd64 |
|
||||||
|
| Windows | 1.23 | 386 |
|
||||||
| Windows | 1.22 | 386 |
|
| Windows | 1.22 | 386 |
|
||||||
| Windows | 1.21 | 386 |
|
| Windows | 1.21 | 386 |
|
||||||
|
|
||||||
|
@ -97,12 +106,12 @@ export pipeline to send that telemetry to an observability platform.
|
||||||
|
|
||||||
All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters).
|
All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters).
|
||||||
|
|
||||||
| Exporter | Metrics | Traces |
|
| Exporter | Logs | Metrics | Traces |
|
||||||
|---------------------------------------|:-------:|:------:|
|
|---------------------------------------|:----:|:-------:|:------:|
|
||||||
| [OTLP](./exporters/otlp/) | ✓ | ✓ |
|
| [OTLP](./exporters/otlp/) | ✓ | ✓ | ✓ |
|
||||||
| [Prometheus](./exporters/prometheus/) | ✓ | |
|
| [Prometheus](./exporters/prometheus/) | | ✓ | |
|
||||||
| [stdout](./exporters/stdout/) | ✓ | ✓ |
|
| [stdout](./exporters/stdout/) | ✓ | ✓ | ✓ |
|
||||||
| [Zipkin](./exporters/zipkin/) | | ✓ |
|
| [Zipkin](./exporters/zipkin/) | | | ✓ |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,12 @@ You can run `make gorelease` that runs [gorelease](https://pkg.go.dev/golang.org
|
||||||
|
|
||||||
You can check/report problems with `gorelease` [here](https://golang.org/issues/26420).
|
You can check/report problems with `gorelease` [here](https://golang.org/issues/26420).
|
||||||
|
|
||||||
|
## Verify changes for contrib repository
|
||||||
|
|
||||||
|
If the changes in the main repository are going to affect the contrib repository, it is important to verify that the changes are compatible with the contrib repository.
|
||||||
|
|
||||||
|
Follow [the steps](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md#verify-otel-changes) in the contrib repository to verify OTel changes.
|
||||||
|
|
||||||
## Pre-Release
|
## Pre-Release
|
||||||
|
|
||||||
First, decide which module sets will be released and update their versions
|
First, decide which module sets will be released and update their versions
|
||||||
|
@ -63,6 +69,7 @@ Update go.mod for submodules to depend on the new release which will happen in t
|
||||||
```
|
```
|
||||||
|
|
||||||
- Move all the `Unreleased` changes into a new section following the title scheme (`[<new tag>] - <date of release>`).
|
- Move all the `Unreleased` changes into a new section following the title scheme (`[<new tag>] - <date of release>`).
|
||||||
|
- Make sure the new section is under the comment for released section, like `<!-- Released section -->`, so it is protected from being overwritten in the future.
|
||||||
- Update all the appropriate links at the bottom.
|
- Update all the appropriate links at the bottom.
|
||||||
|
|
||||||
4. Push the changes to upstream and create a Pull Request on GitHub.
|
4. Push the changes to upstream and create a Pull Request on GitHub.
|
||||||
|
|
|
@ -231,15 +231,27 @@ func (v Value) Emit() string {
|
||||||
case BOOL:
|
case BOOL:
|
||||||
return strconv.FormatBool(v.AsBool())
|
return strconv.FormatBool(v.AsBool())
|
||||||
case INT64SLICE:
|
case INT64SLICE:
|
||||||
return fmt.Sprint(v.asInt64Slice())
|
j, err := json.Marshal(v.asInt64Slice())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("invalid: %v", v.asInt64Slice())
|
||||||
|
}
|
||||||
|
return string(j)
|
||||||
case INT64:
|
case INT64:
|
||||||
return strconv.FormatInt(v.AsInt64(), 10)
|
return strconv.FormatInt(v.AsInt64(), 10)
|
||||||
case FLOAT64SLICE:
|
case FLOAT64SLICE:
|
||||||
return fmt.Sprint(v.asFloat64Slice())
|
j, err := json.Marshal(v.asFloat64Slice())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("invalid: %v", v.asFloat64Slice())
|
||||||
|
}
|
||||||
|
return string(j)
|
||||||
case FLOAT64:
|
case FLOAT64:
|
||||||
return fmt.Sprint(v.AsFloat64())
|
return fmt.Sprint(v.AsFloat64())
|
||||||
case STRINGSLICE:
|
case STRINGSLICE:
|
||||||
return fmt.Sprint(v.asStringSlice())
|
j, err := json.Marshal(v.asStringSlice())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("invalid: %v", v.asStringSlice())
|
||||||
|
}
|
||||||
|
return string(j)
|
||||||
case STRING:
|
case STRING:
|
||||||
return v.stringly
|
return v.stringly
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -44,9 +44,15 @@ type Property struct {
|
||||||
|
|
||||||
// NewKeyProperty returns a new Property for key.
|
// NewKeyProperty returns a new Property for key.
|
||||||
//
|
//
|
||||||
|
// The passed key must be valid, non-empty UTF-8 string.
|
||||||
// If key is invalid, an error will be returned.
|
// If key is invalid, an error will be returned.
|
||||||
|
// However, the specific Propagators that are used to transmit baggage entries across
|
||||||
|
// component boundaries may impose their own restrictions on Property key.
|
||||||
|
// For example, the W3C Baggage specification restricts the Property keys to strings that
|
||||||
|
// satisfy the token definition from RFC7230, Section 3.2.6.
|
||||||
|
// For maximum compatibility, alpha-numeric value are strongly recommended to be used as Property key.
|
||||||
func NewKeyProperty(key string) (Property, error) {
|
func NewKeyProperty(key string) (Property, error) {
|
||||||
if !validateKey(key) {
|
if !validateBaggageName(key) {
|
||||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +68,10 @@ func NewKeyProperty(key string) (Property, error) {
|
||||||
// Notice: Consider using [NewKeyValuePropertyRaw] instead
|
// Notice: Consider using [NewKeyValuePropertyRaw] instead
|
||||||
// that does not require percent-encoding of the value.
|
// that does not require percent-encoding of the value.
|
||||||
func NewKeyValueProperty(key, value string) (Property, error) {
|
func NewKeyValueProperty(key, value string) (Property, error) {
|
||||||
|
if !validateKey(key) {
|
||||||
|
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
|
}
|
||||||
|
|
||||||
if !validateValue(value) {
|
if !validateValue(value) {
|
||||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||||
}
|
}
|
||||||
|
@ -74,11 +84,20 @@ func NewKeyValueProperty(key, value string) (Property, error) {
|
||||||
|
|
||||||
// NewKeyValuePropertyRaw returns a new Property for key with value.
|
// NewKeyValuePropertyRaw returns a new Property for key with value.
|
||||||
//
|
//
|
||||||
// The passed key must be compliant with W3C Baggage specification.
|
// The passed key must be valid, non-empty UTF-8 string.
|
||||||
|
// The passed value must be valid UTF-8 string.
|
||||||
|
// However, the specific Propagators that are used to transmit baggage entries across
|
||||||
|
// component boundaries may impose their own restrictions on Property key.
|
||||||
|
// For example, the W3C Baggage specification restricts the Property keys to strings that
|
||||||
|
// satisfy the token definition from RFC7230, Section 3.2.6.
|
||||||
|
// For maximum compatibility, alpha-numeric value are strongly recommended to be used as Property key.
|
||||||
func NewKeyValuePropertyRaw(key, value string) (Property, error) {
|
func NewKeyValuePropertyRaw(key, value string) (Property, error) {
|
||||||
if !validateKey(key) {
|
if !validateBaggageName(key) {
|
||||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
}
|
}
|
||||||
|
if !validateBaggageValue(value) {
|
||||||
|
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||||
|
}
|
||||||
|
|
||||||
p := Property{
|
p := Property{
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -115,12 +134,15 @@ func (p Property) validate() error {
|
||||||
return fmt.Errorf("invalid property: %w", err)
|
return fmt.Errorf("invalid property: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !validateKey(p.key) {
|
if !validateBaggageName(p.key) {
|
||||||
return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
|
return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
|
||||||
}
|
}
|
||||||
if !p.hasValue && p.value != "" {
|
if !p.hasValue && p.value != "" {
|
||||||
return errFunc(errors.New("inconsistent value"))
|
return errFunc(errors.New("inconsistent value"))
|
||||||
}
|
}
|
||||||
|
if p.hasValue && !validateBaggageValue(p.value) {
|
||||||
|
return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +160,15 @@ func (p Property) Value() (string, bool) {
|
||||||
|
|
||||||
// String encodes Property into a header string compliant with the W3C Baggage
|
// String encodes Property into a header string compliant with the W3C Baggage
|
||||||
// specification.
|
// specification.
|
||||||
|
// It would return empty string if the key is invalid with the W3C Baggage
|
||||||
|
// specification. This could happen for a UTF-8 key, as it may contain
|
||||||
|
// invalid characters.
|
||||||
func (p Property) String() string {
|
func (p Property) String() string {
|
||||||
|
// W3C Baggage specification does not allow percent-encoded keys.
|
||||||
|
if !validateKey(p.key) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
if p.hasValue {
|
if p.hasValue {
|
||||||
return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, valueEscape(p.value))
|
return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, valueEscape(p.value))
|
||||||
}
|
}
|
||||||
|
@ -203,9 +233,14 @@ func (p properties) validate() error {
|
||||||
// String encodes properties into a header string compliant with the W3C Baggage
|
// String encodes properties into a header string compliant with the W3C Baggage
|
||||||
// specification.
|
// specification.
|
||||||
func (p properties) String() string {
|
func (p properties) String() string {
|
||||||
props := make([]string, len(p))
|
props := make([]string, 0, len(p))
|
||||||
for i, prop := range p {
|
for _, prop := range p {
|
||||||
props[i] = prop.String()
|
s := prop.String()
|
||||||
|
|
||||||
|
// Ignored empty properties.
|
||||||
|
if s != "" {
|
||||||
|
props = append(props, s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(props, propertyDelimiter)
|
return strings.Join(props, propertyDelimiter)
|
||||||
}
|
}
|
||||||
|
@ -230,6 +265,10 @@ type Member struct {
|
||||||
// Notice: Consider using [NewMemberRaw] instead
|
// Notice: Consider using [NewMemberRaw] instead
|
||||||
// that does not require percent-encoding of the value.
|
// that does not require percent-encoding of the value.
|
||||||
func NewMember(key, value string, props ...Property) (Member, error) {
|
func NewMember(key, value string, props ...Property) (Member, error) {
|
||||||
|
if !validateKey(key) {
|
||||||
|
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
|
}
|
||||||
|
|
||||||
if !validateValue(value) {
|
if !validateValue(value) {
|
||||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||||
}
|
}
|
||||||
|
@ -242,7 +281,13 @@ func NewMember(key, value string, props ...Property) (Member, error) {
|
||||||
|
|
||||||
// NewMemberRaw returns a new Member from the passed arguments.
|
// NewMemberRaw returns a new Member from the passed arguments.
|
||||||
//
|
//
|
||||||
// The passed key must be compliant with W3C Baggage specification.
|
// The passed key must be valid, non-empty UTF-8 string.
|
||||||
|
// The passed value must be valid UTF-8 string.
|
||||||
|
// However, the specific Propagators that are used to transmit baggage entries across
|
||||||
|
// component boundaries may impose their own restrictions on baggage key.
|
||||||
|
// For example, the W3C Baggage specification restricts the baggage keys to strings that
|
||||||
|
// satisfy the token definition from RFC7230, Section 3.2.6.
|
||||||
|
// For maximum compatibility, alpha-numeric value are strongly recommended to be used as baggage key.
|
||||||
func NewMemberRaw(key, value string, props ...Property) (Member, error) {
|
func NewMemberRaw(key, value string, props ...Property) (Member, error) {
|
||||||
m := Member{
|
m := Member{
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -294,19 +339,45 @@ func parseMember(member string) (Member, error) {
|
||||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
val := strings.TrimSpace(v)
|
rawVal := strings.TrimSpace(v)
|
||||||
if !validateValue(val) {
|
if !validateValue(rawVal) {
|
||||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, v)
|
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode a percent-encoded value.
|
// Decode a percent-encoded value.
|
||||||
value, err := url.PathUnescape(val)
|
unescapeVal, err := url.PathUnescape(rawVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newInvalidMember(), fmt.Errorf("%w: %v", errInvalidValue, err)
|
return newInvalidMember(), fmt.Errorf("%w: %w", errInvalidValue, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
|
||||||
return Member{key: key, value: value, properties: props, hasData: true}, nil
|
return Member{key: key, value: value, properties: props, hasData: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replaceInvalidUTF8Sequences replaces invalid UTF-8 sequences with '<27>'.
|
||||||
|
func replaceInvalidUTF8Sequences(cap int, unescapeVal string) string {
|
||||||
|
if utf8.ValidString(unescapeVal) {
|
||||||
|
return unescapeVal
|
||||||
|
}
|
||||||
|
// W3C baggage spec:
|
||||||
|
// https://github.com/w3c/baggage/blob/8c215efbeebd3fa4b1aceb937a747e56444f22f3/baggage/HTTP_HEADER_FORMAT.md?plain=1#L69
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
b.Grow(cap)
|
||||||
|
for i := 0; i < len(unescapeVal); {
|
||||||
|
r, size := utf8.DecodeRuneInString(unescapeVal[i:])
|
||||||
|
if r == utf8.RuneError && size == 1 {
|
||||||
|
// Invalid UTF-8 sequence found, replace it with '<27>'
|
||||||
|
_, _ = b.WriteString("<22>")
|
||||||
|
} else {
|
||||||
|
_, _ = b.WriteRune(r)
|
||||||
|
}
|
||||||
|
i += size
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
// validate ensures m conforms to the W3C Baggage specification.
|
// validate ensures m conforms to the W3C Baggage specification.
|
||||||
// A key must be an ASCII string, returning an error otherwise.
|
// A key must be an ASCII string, returning an error otherwise.
|
||||||
func (m Member) validate() error {
|
func (m Member) validate() error {
|
||||||
|
@ -314,9 +385,12 @@ func (m Member) validate() error {
|
||||||
return fmt.Errorf("%w: %q", errInvalidMember, m)
|
return fmt.Errorf("%w: %q", errInvalidMember, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !validateKey(m.key) {
|
if !validateBaggageName(m.key) {
|
||||||
return fmt.Errorf("%w: %q", errInvalidKey, m.key)
|
return fmt.Errorf("%w: %q", errInvalidKey, m.key)
|
||||||
}
|
}
|
||||||
|
if !validateBaggageValue(m.value) {
|
||||||
|
return fmt.Errorf("%w: %q", errInvalidValue, m.value)
|
||||||
|
}
|
||||||
return m.properties.validate()
|
return m.properties.validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,13 +405,18 @@ func (m Member) Properties() []Property { return m.properties.Copy() }
|
||||||
|
|
||||||
// String encodes Member into a header string compliant with the W3C Baggage
|
// String encodes Member into a header string compliant with the W3C Baggage
|
||||||
// specification.
|
// specification.
|
||||||
|
// It would return empty string if the key is invalid with the W3C Baggage
|
||||||
|
// specification. This could happen for a UTF-8 key, as it may contain
|
||||||
|
// invalid characters.
|
||||||
func (m Member) String() string {
|
func (m Member) String() string {
|
||||||
// A key is just an ASCII string. A value is restricted to be
|
// W3C Baggage specification does not allow percent-encoded keys.
|
||||||
// US-ASCII characters excluding CTLs, whitespace,
|
if !validateKey(m.key) {
|
||||||
// DQUOTE, comma, semicolon, and backslash.
|
return ""
|
||||||
s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, valueEscape(m.value))
|
}
|
||||||
|
|
||||||
|
s := m.key + keyValueDelimiter + valueEscape(m.value)
|
||||||
if len(m.properties) > 0 {
|
if len(m.properties) > 0 {
|
||||||
s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String())
|
s += propertyDelimiter + m.properties.String()
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -448,7 +527,7 @@ func (b Baggage) Member(key string) Member {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Members returns all the baggage list-members.
|
// Members returns all the baggage list-members.
|
||||||
// The order of the returned list-members does not have significance.
|
// The order of the returned list-members is not significant.
|
||||||
//
|
//
|
||||||
// The returned members are not validated, as we assume the validation happened
|
// The returned members are not validated, as we assume the validation happened
|
||||||
// when they were added to the Baggage.
|
// when they were added to the Baggage.
|
||||||
|
@ -469,8 +548,8 @@ func (b Baggage) Members() []Member {
|
||||||
return members
|
return members
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMember returns a copy the Baggage with the member included. If the
|
// SetMember returns a copy of the Baggage with the member included. If the
|
||||||
// baggage contains a Member with the same key the existing Member is
|
// baggage contains a Member with the same key, the existing Member is
|
||||||
// replaced.
|
// replaced.
|
||||||
//
|
//
|
||||||
// If member is invalid according to the W3C Baggage specification, an error
|
// If member is invalid according to the W3C Baggage specification, an error
|
||||||
|
@ -528,14 +607,22 @@ func (b Baggage) Len() int {
|
||||||
|
|
||||||
// String encodes Baggage into a header string compliant with the W3C Baggage
|
// String encodes Baggage into a header string compliant with the W3C Baggage
|
||||||
// specification.
|
// specification.
|
||||||
|
// It would ignore members where the member key is invalid with the W3C Baggage
|
||||||
|
// specification. This could happen for a UTF-8 key, as it may contain
|
||||||
|
// invalid characters.
|
||||||
func (b Baggage) String() string {
|
func (b Baggage) String() string {
|
||||||
members := make([]string, 0, len(b.list))
|
members := make([]string, 0, len(b.list))
|
||||||
for k, v := range b.list {
|
for k, v := range b.list {
|
||||||
members = append(members, Member{
|
s := Member{
|
||||||
key: k,
|
key: k,
|
||||||
value: v.Value,
|
value: v.Value,
|
||||||
properties: fromInternalProperties(v.Properties),
|
properties: fromInternalProperties(v.Properties),
|
||||||
}.String())
|
}.String()
|
||||||
|
|
||||||
|
// Ignored empty members.
|
||||||
|
if s != "" {
|
||||||
|
members = append(members, s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(members, listDelimiter)
|
return strings.Join(members, listDelimiter)
|
||||||
}
|
}
|
||||||
|
@ -607,10 +694,12 @@ func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode a percent-encoded value.
|
// Decode a percent-encoded value.
|
||||||
value, err := url.PathUnescape(s[valueStart:valueEnd])
|
rawVal := s[valueStart:valueEnd]
|
||||||
|
unescapeVal, err := url.PathUnescape(rawVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
|
||||||
|
|
||||||
ok = true
|
ok = true
|
||||||
p.key = s[keyStart:keyEnd]
|
p.key = s[keyStart:keyEnd]
|
||||||
|
@ -720,6 +809,24 @@ var safeKeyCharset = [utf8.RuneSelf]bool{
|
||||||
'~': true,
|
'~': true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateBaggageName checks if the string is a valid OpenTelemetry Baggage name.
|
||||||
|
// Baggage name is a valid, non-empty UTF-8 string.
|
||||||
|
func validateBaggageName(s string) bool {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return utf8.ValidString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateBaggageValue checks if the string is a valid OpenTelemetry Baggage value.
|
||||||
|
// Baggage value is a valid UTF-8 strings.
|
||||||
|
// Empty string is also a valid UTF-8 string.
|
||||||
|
func validateBaggageValue(s string) bool {
|
||||||
|
return utf8.ValidString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateKey checks if the string is a valid W3C Baggage key.
|
||||||
func validateKey(s string) bool {
|
func validateKey(s string) bool {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -735,9 +842,10 @@ func validateKey(s string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateKeyChar(c int32) bool {
|
func validateKeyChar(c int32) bool {
|
||||||
return c >= 0 && c <= int32(utf8.RuneSelf) && safeKeyCharset[c]
|
return c >= 0 && c < int32(utf8.RuneSelf) && safeKeyCharset[c]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateValue checks if the string is a valid W3C Baggage value.
|
||||||
func validateValue(s string) bool {
|
func validateValue(s string) bool {
|
||||||
for _, c := range s {
|
for _, c := range s {
|
||||||
if !validateValueChar(c) {
|
if !validateValueChar(c) {
|
||||||
|
@ -850,7 +958,7 @@ var safeValueCharset = [utf8.RuneSelf]bool{
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateValueChar(c int32) bool {
|
func validateValueChar(c int32) bool {
|
||||||
return c >= 0 && c <= int32(utf8.RuneSelf) && safeValueCharset[c]
|
return c >= 0 && c < int32(utf8.RuneSelf) && safeValueCharset[c]
|
||||||
}
|
}
|
||||||
|
|
||||||
// valueEscape escapes the string so it can be safely placed inside a baggage value,
|
// valueEscape escapes the string so it can be safely placed inside a baggage value,
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (c *Code) UnmarshalJSON(b []byte) error {
|
||||||
return fmt.Errorf("invalid code: %q", ci)
|
return fmt.Errorf("invalid code: %q", ci)
|
||||||
}
|
}
|
||||||
|
|
||||||
*c = Code(ci)
|
*c = Code(ci) // nolint: gosec // Bit size of 32 check above.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("invalid code: %q", string(b))
|
return fmt.Errorf("invalid code: %q", string(b))
|
||||||
|
|
|
@ -17,6 +17,8 @@ To read more about tracing, see go.opentelemetry.io/otel/trace.
|
||||||
|
|
||||||
To read more about metrics, see go.opentelemetry.io/otel/metric.
|
To read more about metrics, see go.opentelemetry.io/otel/metric.
|
||||||
|
|
||||||
|
To read more about logs, see go.opentelemetry.io/otel/log.
|
||||||
|
|
||||||
To read more about propagation, see go.opentelemetry.io/otel/propagation and
|
To read more about propagation, see go.opentelemetry.io/otel/propagation and
|
||||||
go.opentelemetry.io/otel/baggage.
|
go.opentelemetry.io/otel/baggage.
|
||||||
*/
|
*/
|
||||||
|
|
22
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go
generated
vendored
22
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go
generated
vendored
|
@ -4,6 +4,8 @@
|
||||||
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
|
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||||
|
@ -102,9 +104,9 @@ func span(sd tracesdk.ReadOnlySpan) *tracepb.Span {
|
||||||
Name: sd.Name(),
|
Name: sd.Name(),
|
||||||
Attributes: KeyValues(sd.Attributes()),
|
Attributes: KeyValues(sd.Attributes()),
|
||||||
Events: spanEvents(sd.Events()),
|
Events: spanEvents(sd.Events()),
|
||||||
DroppedAttributesCount: uint32(sd.DroppedAttributes()),
|
DroppedAttributesCount: clampUint32(sd.DroppedAttributes()),
|
||||||
DroppedEventsCount: uint32(sd.DroppedEvents()),
|
DroppedEventsCount: clampUint32(sd.DroppedEvents()),
|
||||||
DroppedLinksCount: uint32(sd.DroppedLinks()),
|
DroppedLinksCount: clampUint32(sd.DroppedLinks()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if psid := sd.Parent().SpanID(); psid.IsValid() {
|
if psid := sd.Parent().SpanID(); psid.IsValid() {
|
||||||
|
@ -115,6 +117,16 @@ func span(sd tracesdk.ReadOnlySpan) *tracepb.Span {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clampUint32(v int) uint32 {
|
||||||
|
if v < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if int64(v) > math.MaxUint32 {
|
||||||
|
return math.MaxUint32
|
||||||
|
}
|
||||||
|
return uint32(v) // nolint: gosec // Overflow/Underflow checked.
|
||||||
|
}
|
||||||
|
|
||||||
// status transform a span code and message into an OTLP span status.
|
// status transform a span code and message into an OTLP span status.
|
||||||
func status(status codes.Code, message string) *tracepb.Status {
|
func status(status codes.Code, message string) *tracepb.Status {
|
||||||
var c tracepb.Status_StatusCode
|
var c tracepb.Status_StatusCode
|
||||||
|
@ -153,7 +165,7 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link {
|
||||||
TraceId: tid[:],
|
TraceId: tid[:],
|
||||||
SpanId: sid[:],
|
SpanId: sid[:],
|
||||||
Attributes: KeyValues(otLink.Attributes),
|
Attributes: KeyValues(otLink.Attributes),
|
||||||
DroppedAttributesCount: uint32(otLink.DroppedAttributeCount),
|
DroppedAttributesCount: clampUint32(otLink.DroppedAttributeCount),
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -182,7 +194,7 @@ func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event {
|
||||||
Name: es[i].Name,
|
Name: es[i].Name,
|
||||||
TimeUnixNano: uint64(es[i].Time.UnixNano()),
|
TimeUnixNano: uint64(es[i].Time.UnixNano()),
|
||||||
Attributes: KeyValues(es[i].Attributes),
|
Attributes: KeyValues(es[i].Attributes),
|
||||||
DroppedAttributesCount: uint32(es[i].DroppedAttributeCount),
|
DroppedAttributesCount: clampUint32(es[i].DroppedAttributeCount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return events
|
return events
|
||||||
|
|
|
@ -12,9 +12,8 @@ The environment variables described below can be used for configuration.
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (default: "https://localhost:4317") -
|
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (default: "https://localhost:4317") -
|
||||||
target to which the exporter sends telemetry.
|
target to which the exporter sends telemetry.
|
||||||
The target syntax is defined in https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
The target syntax is defined in https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
||||||
The value must contain a host.
|
The value must contain a scheme ("http" or "https") and host.
|
||||||
The value may additionally a port, a scheme, and a path.
|
The value may additionally contain a port, and a path.
|
||||||
The value accepts "http" and "https" scheme.
|
|
||||||
The value should not contain a query string or fragment.
|
The value should not contain a query string or fragment.
|
||||||
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT.
|
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT.
|
||||||
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithGRPCConn] options.
|
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithGRPCConn] options.
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/internal/global"
|
"go.opentelemetry.io/otel/internal/global"
|
||||||
)
|
)
|
||||||
|
@ -163,12 +164,16 @@ func stringToHeader(value string) map[string]string {
|
||||||
global.Error(errors.New("missing '="), "parse headers", "input", header)
|
global.Error(errors.New("missing '="), "parse headers", "input", header)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name, err := url.PathUnescape(n)
|
|
||||||
if err != nil {
|
trimmedName := strings.TrimSpace(n)
|
||||||
global.Error(err, "escape header key", "key", n)
|
|
||||||
|
// Validate the key.
|
||||||
|
if !isValidHeaderKey(trimmedName) {
|
||||||
|
global.Error(errors.New("invalid header key"), "parse headers", "key", trimmedName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
trimmedName := strings.TrimSpace(name)
|
|
||||||
|
// Only decode the value.
|
||||||
value, err := url.PathUnescape(v)
|
value, err := url.PathUnescape(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Error(err, "escape header value", "value", v)
|
global.Error(err, "escape header value", "value", v)
|
||||||
|
@ -189,3 +194,22 @@ func createCertPool(certBytes []byte) (*x509.CertPool, error) {
|
||||||
}
|
}
|
||||||
return cp, nil
|
return cp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValidHeaderKey(key string) bool {
|
||||||
|
if key == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range key {
|
||||||
|
if !isTokenChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTokenChar(c rune) bool {
|
||||||
|
return c <= unicode.MaxASCII && (unicode.IsLetter(c) ||
|
||||||
|
unicode.IsDigit(c) ||
|
||||||
|
c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' ||
|
||||||
|
c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~')
|
||||||
|
}
|
||||||
|
|
|
@ -256,6 +256,9 @@ func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
|
||||||
|
|
||||||
// Generic Options
|
// Generic Options
|
||||||
|
|
||||||
|
// WithEndpoint configures the trace host and port only; endpoint should
|
||||||
|
// resemble "example.com" or "localhost:4317". To configure the scheme and path,
|
||||||
|
// use WithEndpointURL.
|
||||||
func WithEndpoint(endpoint string) GenericOption {
|
func WithEndpoint(endpoint string) GenericOption {
|
||||||
return newGenericOption(func(cfg Config) Config {
|
return newGenericOption(func(cfg Config) Config {
|
||||||
cfg.Traces.Endpoint = endpoint
|
cfg.Traces.Endpoint = endpoint
|
||||||
|
@ -263,6 +266,8 @@ func WithEndpoint(endpoint string) GenericOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithEndpointURL configures the trace scheme, host, port, and path; the
|
||||||
|
// provided value should resemble "https://example.com:4318/v1/traces".
|
||||||
func WithEndpointURL(v string) GenericOption {
|
func WithEndpointURL(v string) GenericOption {
|
||||||
return newGenericOption(func(cfg Config) Config {
|
return newGenericOption(func(cfg Config) Config {
|
||||||
u, err := url.Parse(v)
|
u, err := url.Parse(v)
|
||||||
|
|
|
@ -112,7 +112,7 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
||||||
return fmt.Errorf("%w: %s", ctxErr, err)
|
return fmt.Errorf("%w: %w", ctxErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go
generated
vendored
24
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go
generated
vendored
|
@ -53,12 +53,15 @@ func WithInsecure() Option {
|
||||||
return wrappedOption{otlpconfig.WithInsecure()}
|
return wrappedOption{otlpconfig.WithInsecure()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEndpoint sets the target endpoint the Exporter will connect to.
|
// WithEndpoint sets the target endpoint (host and port) the Exporter will
|
||||||
|
// connect to. The provided endpoint should resemble "example.com:4317" (no
|
||||||
|
// scheme or path).
|
||||||
//
|
//
|
||||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
||||||
// environment variable is set, and this option is not passed, that variable
|
// environment variable is set, and this option is not passed, that variable
|
||||||
// value will be used. If both are set, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
// value will be used. If both environment variables are set,
|
||||||
// will take precedence.
|
// OTEL_EXPORTER_OTLP_TRACES_ENDPOINT will take precedence. If an environment
|
||||||
|
// variable is set, and this option is passed, this option will take precedence.
|
||||||
//
|
//
|
||||||
// If both this option and WithEndpointURL are used, the last used option will
|
// If both this option and WithEndpointURL are used, the last used option will
|
||||||
// take precedence.
|
// take precedence.
|
||||||
|
@ -71,12 +74,15 @@ func WithEndpoint(endpoint string) Option {
|
||||||
return wrappedOption{otlpconfig.WithEndpoint(endpoint)}
|
return wrappedOption{otlpconfig.WithEndpoint(endpoint)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
|
// WithEndpointURL sets the target endpoint URL (scheme, host, port, path)
|
||||||
|
// the Exporter will connect to. The provided endpoint URL should resemble
|
||||||
|
// "https://example.com:4318/v1/traces".
|
||||||
//
|
//
|
||||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
||||||
// environment variable is set, and this option is not passed, that variable
|
// environment variable is set, and this option is not passed, that variable
|
||||||
// value will be used. If both are set, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
// value will be used. If both environment variables are set,
|
||||||
// will take precedence.
|
// OTEL_EXPORTER_OTLP_TRACES_ENDPOINT will take precedence. If an environment
|
||||||
|
// variable is set, and this option is passed, this option will take precedence.
|
||||||
//
|
//
|
||||||
// If both this option and WithEndpoint are used, the last used option will
|
// If both this option and WithEndpoint are used, the last used option will
|
||||||
// take precedence.
|
// take precedence.
|
||||||
|
@ -84,7 +90,7 @@ func WithEndpoint(endpoint string) Option {
|
||||||
// If an invalid URL is provided, the default value will be kept.
|
// If an invalid URL is provided, the default value will be kept.
|
||||||
//
|
//
|
||||||
// By default, if an environment variable is not set, and this option is not
|
// By default, if an environment variable is not set, and this option is not
|
||||||
// passed, "localhost:4317" will be used.
|
// passed, "https://localhost:4317/v1/traces" will be used.
|
||||||
//
|
//
|
||||||
// This option has no effect if WithGRPCConn is used.
|
// This option has no effect if WithGRPCConn is used.
|
||||||
func WithEndpointURL(u string) Option {
|
func WithEndpointURL(u string) Option {
|
||||||
|
|
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# OTLP Trace HTTP Exporter
|
||||||
|
|
||||||
|
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp)](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp)
|
90
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client.go
generated
vendored
90
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client.go
generated
vendored
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
package otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||||
|
|
||||||
|
@ -25,6 +14,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -85,10 +75,17 @@ func NewClient(opts ...Option) otlptrace.Client {
|
||||||
Transport: ourTransport,
|
Transport: ourTransport,
|
||||||
Timeout: cfg.Traces.Timeout,
|
Timeout: cfg.Traces.Timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Traces.TLSCfg != nil || cfg.Traces.Proxy != nil {
|
||||||
|
clonedTransport := ourTransport.Clone()
|
||||||
|
httpClient.Transport = clonedTransport
|
||||||
|
|
||||||
if cfg.Traces.TLSCfg != nil {
|
if cfg.Traces.TLSCfg != nil {
|
||||||
transport := ourTransport.Clone()
|
clonedTransport.TLSClientConfig = cfg.Traces.TLSCfg
|
||||||
transport.TLSClientConfig = cfg.Traces.TLSCfg
|
}
|
||||||
httpClient.Transport = transport
|
if cfg.Traces.Proxy != nil {
|
||||||
|
clonedTransport.Proxy = cfg.Traces.Proxy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
|
@ -155,7 +152,7 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
|
||||||
resp, err := d.client.Do(request.Request)
|
resp, err := d.client.Do(request.Request)
|
||||||
var urlErr *url.Error
|
var urlErr *url.Error
|
||||||
if errors.As(err, &urlErr) && urlErr.Temporary() {
|
if errors.As(err, &urlErr) && urlErr.Temporary() {
|
||||||
return newResponseError(http.Header{})
|
return newResponseError(http.Header{}, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -202,11 +199,27 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
|
||||||
sc == http.StatusBadGateway,
|
sc == http.StatusBadGateway,
|
||||||
sc == http.StatusServiceUnavailable,
|
sc == http.StatusServiceUnavailable,
|
||||||
sc == http.StatusGatewayTimeout:
|
sc == http.StatusGatewayTimeout:
|
||||||
// Retry-able failures. Drain the body to reuse the connection.
|
// Retry-able failures.
|
||||||
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
|
rErr := newResponseError(resp.Header, nil)
|
||||||
otel.Handle(err)
|
|
||||||
|
// server may return a message with the response
|
||||||
|
// body, so we read it to include in the error
|
||||||
|
// message to be returned. It will help in
|
||||||
|
// debugging the actual issue.
|
||||||
|
var respData bytes.Buffer
|
||||||
|
if _, err := io.Copy(&respData, resp.Body); err != nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return newResponseError(resp.Header)
|
|
||||||
|
// overwrite the error message with the response body
|
||||||
|
// if it is not empty
|
||||||
|
if respStr := strings.TrimSpace(respData.String()); respStr != "" {
|
||||||
|
// Include response for context.
|
||||||
|
e := errors.New(respStr)
|
||||||
|
rErr = newResponseError(resp.Header, e)
|
||||||
|
}
|
||||||
|
return rErr
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("failed to send to %s: %s", request.URL, resp.Status)
|
return fmt.Errorf("failed to send to %s: %s", request.URL, resp.Status)
|
||||||
}
|
}
|
||||||
|
@ -247,7 +260,7 @@ func (d *client) newRequest(body []byte) (request, error) {
|
||||||
if _, err := gz.Write(body); err != nil {
|
if _, err := gz.Write(body); err != nil {
|
||||||
return req, err
|
return req, err
|
||||||
}
|
}
|
||||||
// Close needs to be called to ensure body if fully written.
|
// Close needs to be called to ensure body is fully written.
|
||||||
if err := gz.Close(); err != nil {
|
if err := gz.Close(); err != nil {
|
||||||
return req, err
|
return req, err
|
||||||
}
|
}
|
||||||
|
@ -295,24 +308,50 @@ func (r *request) reset(ctx context.Context) {
|
||||||
// retryableError represents a request failure that can be retried.
|
// retryableError represents a request failure that can be retried.
|
||||||
type retryableError struct {
|
type retryableError struct {
|
||||||
throttle int64
|
throttle int64
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// newResponseError returns a retryableError and will extract any explicit
|
// newResponseError returns a retryableError and will extract any explicit
|
||||||
// throttle delay contained in headers.
|
// throttle delay contained in headers. The returned error wraps wrapped
|
||||||
func newResponseError(header http.Header) error {
|
// if it is not nil.
|
||||||
|
func newResponseError(header http.Header, wrapped error) error {
|
||||||
var rErr retryableError
|
var rErr retryableError
|
||||||
if s, ok := header["Retry-After"]; ok {
|
if s, ok := header["Retry-After"]; ok {
|
||||||
if t, err := strconv.ParseInt(s[0], 10, 64); err == nil {
|
if t, err := strconv.ParseInt(s[0], 10, 64); err == nil {
|
||||||
rErr.throttle = t
|
rErr.throttle = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rErr.err = wrapped
|
||||||
return rErr
|
return rErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e retryableError) Error() string {
|
func (e retryableError) Error() string {
|
||||||
|
if e.err != nil {
|
||||||
|
return fmt.Sprintf("retry-able request failure: %s", e.err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return "retry-able request failure"
|
return "retry-able request failure"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e retryableError) Unwrap() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e retryableError) As(target interface{}) bool {
|
||||||
|
if e.err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := target.(type) {
|
||||||
|
case **retryableError:
|
||||||
|
*v = &e
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// evaluate returns if err is retry-able. If it is and it includes an explicit
|
// evaluate returns if err is retry-able. If it is and it includes an explicit
|
||||||
// throttling delay, that delay is also returned.
|
// throttling delay, that delay is also returned.
|
||||||
func evaluate(err error) (bool, time.Duration) {
|
func evaluate(err error) (bool, time.Duration) {
|
||||||
|
@ -320,7 +359,10 @@ func evaluate(err error) (bool, time.Duration) {
|
||||||
return false, 0
|
return false, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
rErr, ok := err.(retryableError)
|
// Do not use errors.As here, this should only be flattened one layer. If
|
||||||
|
// there are several chained errors, all the errors above it will be
|
||||||
|
// discarded if errors.As is used instead.
|
||||||
|
rErr, ok := err.(retryableError) //nolint:errorlint
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, 0
|
return false, 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlptracehttp provides an OTLP span exporter using HTTP with protobuf payloads.
|
Package otlptracehttp provides an OTLP span exporter using HTTP with protobuf payloads.
|
||||||
|
@ -37,7 +26,7 @@ The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WitnI
|
||||||
|
|
||||||
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS (default: none) -
|
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS (default: none) -
|
||||||
key-value pairs used as headers associated with HTTP requests.
|
key-value pairs used as headers associated with HTTP requests.
|
||||||
The value is expected to be represented in a format matching to the [W3C Baggage HTTP Header Content Format],
|
The value is expected to be represented in a format matching the [W3C Baggage HTTP Header Content Format],
|
||||||
except that additional semi-colon delimited metadata is not supported.
|
except that additional semi-colon delimited metadata is not supported.
|
||||||
Example value: "key1=value1,key2=value2".
|
Example value: "key1=value1,key2=value2".
|
||||||
OTEL_EXPORTER_OTLP_TRACES_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
|
OTEL_EXPORTER_OTLP_TRACES_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
|
||||||
|
@ -60,12 +49,12 @@ OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_C
|
||||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||||
|
|
||||||
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE (default: none) -
|
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE (default: none) -
|
||||||
the filepath to the client certificate/chain trust for clients private key to use in mTLS communication in PEM format.
|
the filepath to the client certificate/chain trust for client's private key to use in mTLS communication in PEM format.
|
||||||
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
|
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
|
||||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||||
|
|
||||||
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY (default: none) -
|
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY (default: none) -
|
||||||
the filepath to the clients private key to use in mTLS communication in PEM format.
|
the filepath to the client's private key to use in mTLS communication in PEM format.
|
||||||
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
|
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
|
||||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||||
|
|
||||||
|
|
13
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/exporter.go
generated
vendored
13
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/exporter.go
generated
vendored
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
package otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,7 @@
|
||||||
// source: internal/shared/otlp/envconfig/envconfig.go.tmpl
|
// source: internal/shared/otlp/envconfig/envconfig.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package envconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/envconfig"
|
package envconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/envconfig"
|
||||||
|
|
||||||
|
@ -26,6 +15,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/internal/global"
|
"go.opentelemetry.io/otel/internal/global"
|
||||||
)
|
)
|
||||||
|
@ -174,12 +164,16 @@ func stringToHeader(value string) map[string]string {
|
||||||
global.Error(errors.New("missing '="), "parse headers", "input", header)
|
global.Error(errors.New("missing '="), "parse headers", "input", header)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name, err := url.PathUnescape(n)
|
|
||||||
if err != nil {
|
trimmedName := strings.TrimSpace(n)
|
||||||
global.Error(err, "escape header key", "key", n)
|
|
||||||
|
// Validate the key.
|
||||||
|
if !isValidHeaderKey(trimmedName) {
|
||||||
|
global.Error(errors.New("invalid header key"), "parse headers", "key", trimmedName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
trimmedName := strings.TrimSpace(name)
|
|
||||||
|
// Only decode the value.
|
||||||
value, err := url.PathUnescape(v)
|
value, err := url.PathUnescape(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Error(err, "escape header value", "value", v)
|
global.Error(err, "escape header value", "value", v)
|
||||||
|
@ -200,3 +194,22 @@ func createCertPool(certBytes []byte) (*x509.CertPool, error) {
|
||||||
}
|
}
|
||||||
return cp, nil
|
return cp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValidHeaderKey(key string) bool {
|
||||||
|
if key == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range key {
|
||||||
|
if !isTokenChar(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTokenChar(c rune) bool {
|
||||||
|
return c <= unicode.MaxASCII && (unicode.IsLetter(c) ||
|
||||||
|
unicode.IsDigit(c) ||
|
||||||
|
c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' ||
|
||||||
|
c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~')
|
||||||
|
}
|
||||||
|
|
13
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/gen.go
generated
vendored
13
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/gen.go
generated
vendored
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal"
|
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal"
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,7 @@
|
||||||
// source: internal/shared/otlp/otlptrace/otlpconfig/envconfig.go.tmpl
|
// source: internal/shared/otlp/otlptrace/otlpconfig/envconfig.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
||||||
|
|
||||||
|
|
|
@ -2,24 +2,14 @@
|
||||||
// source: internal/shared/otlp/otlptrace/otlpconfig/options.go.tmpl
|
// source: internal/shared/otlp/otlptrace/otlpconfig/options.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -46,6 +36,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy for a given request.
|
||||||
|
// This type is compatible with `http.Transport.Proxy` and can be used to set a custom proxy function to the OTLP HTTP client.
|
||||||
|
HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||||
|
|
||||||
SignalConfig struct {
|
SignalConfig struct {
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Insecure bool
|
Insecure bool
|
||||||
|
@ -57,6 +51,8 @@ type (
|
||||||
|
|
||||||
// gRPC configurations
|
// gRPC configurations
|
||||||
GRPCCredentials credentials.TransportCredentials
|
GRPCCredentials credentials.TransportCredentials
|
||||||
|
|
||||||
|
Proxy HTTPTransportProxyFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
Config struct {
|
Config struct {
|
||||||
|
@ -260,6 +256,9 @@ func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
|
||||||
|
|
||||||
// Generic Options
|
// Generic Options
|
||||||
|
|
||||||
|
// WithEndpoint configures the trace host and port only; endpoint should
|
||||||
|
// resemble "example.com" or "localhost:4317". To configure the scheme and path,
|
||||||
|
// use WithEndpointURL.
|
||||||
func WithEndpoint(endpoint string) GenericOption {
|
func WithEndpoint(endpoint string) GenericOption {
|
||||||
return newGenericOption(func(cfg Config) Config {
|
return newGenericOption(func(cfg Config) Config {
|
||||||
cfg.Traces.Endpoint = endpoint
|
cfg.Traces.Endpoint = endpoint
|
||||||
|
@ -267,6 +266,8 @@ func WithEndpoint(endpoint string) GenericOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithEndpointURL configures the trace scheme, host, port, and path; the
|
||||||
|
// provided value should resemble "https://example.com:4318/v1/traces".
|
||||||
func WithEndpointURL(v string) GenericOption {
|
func WithEndpointURL(v string) GenericOption {
|
||||||
return newGenericOption(func(cfg Config) Config {
|
return newGenericOption(func(cfg Config) Config {
|
||||||
u, err := url.Parse(v)
|
u, err := url.Parse(v)
|
||||||
|
@ -343,3 +344,10 @@ func WithTimeout(duration time.Duration) GenericOption {
|
||||||
return cfg
|
return cfg
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithProxy(pf HTTPTransportProxyFunc) GenericOption {
|
||||||
|
return newGenericOption(func(cfg Config) Config {
|
||||||
|
cfg.Traces.Proxy = pf
|
||||||
|
return cfg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,18 +2,7 @@
|
||||||
// source: internal/shared/otlp/otlptrace/otlpconfig/optiontypes.go.tmpl
|
// source: internal/shared/otlp/otlptrace/otlpconfig/optiontypes.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,7 @@
|
||||||
// source: internal/shared/otlp/otlptrace/otlpconfig/tls.go.tmpl
|
// source: internal/shared/otlp/otlptrace/otlpconfig/tls.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,7 @@
|
||||||
// source: internal/shared/otlp/partialsuccess.go
|
// source: internal/shared/otlp/partialsuccess.go
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal"
|
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal"
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,7 @@
|
||||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||||
|
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 retry provides request retry functionality that can perform
|
// Package retry provides request retry functionality that can perform
|
||||||
// configurable exponential backoff for transient errors and honor any
|
// configurable exponential backoff for transient errors and honor any
|
||||||
|
@ -123,7 +112,7 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
||||||
return fmt.Errorf("%w: %s", ctxErr, err)
|
return fmt.Errorf("%w: %w", ctxErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go
generated
vendored
54
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go
generated
vendored
|
@ -1,21 +1,12 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
package otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
|
||||||
|
@ -26,6 +17,11 @@ import (
|
||||||
// collector.
|
// collector.
|
||||||
type Compression otlpconfig.Compression
|
type Compression otlpconfig.Compression
|
||||||
|
|
||||||
|
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy for a given request.
|
||||||
|
// This type is compatible with http.Transport.Proxy and can be used to set a custom proxy function
|
||||||
|
// to the OTLP HTTP client.
|
||||||
|
type HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// NoCompression tells the driver to send payloads without
|
// NoCompression tells the driver to send payloads without
|
||||||
// compression.
|
// compression.
|
||||||
|
@ -60,30 +56,37 @@ func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config
|
||||||
return w.ApplyHTTPOption(cfg)
|
return w.ApplyHTTPOption(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEndpoint sets the target endpoint the Exporter will connect to.
|
// WithEndpoint sets the target endpoint (host and port) the Exporter will
|
||||||
|
// connect to. The provided endpoint should resemble "example.com:4318" (no
|
||||||
|
// scheme or path).
|
||||||
//
|
//
|
||||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
||||||
// environment variable is set, and this option is not passed, that variable
|
// environment variable is set, and this option is not passed, that variable
|
||||||
// value will be used. If both are set, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
// value will be used. If both environment variables are set,
|
||||||
// will take precedence.
|
// OTEL_EXPORTER_OTLP_TRACES_ENDPOINT will take precedence. If an environment
|
||||||
|
// variable is set, and this option is passed, this option will take precedence.
|
||||||
|
// Note, both environment variables include the full
|
||||||
|
// scheme and path, while WithEndpoint sets only the host and port.
|
||||||
//
|
//
|
||||||
// If both this option and WithEndpointURL are used, the last used option will
|
// If both this option and WithEndpointURL are used, the last used option will
|
||||||
// take precedence.
|
// take precedence.
|
||||||
//
|
//
|
||||||
// By default, if an environment variable is not set, and this option is not
|
// By default, if an environment variable is not set, and this option is not
|
||||||
// passed, "localhost:4317" will be used.
|
// passed, "localhost:4318" will be used.
|
||||||
//
|
//
|
||||||
// This option has no effect if WithGRPCConn is used.
|
// This option has no effect if WithGRPCConn is used.
|
||||||
func WithEndpoint(endpoint string) Option {
|
func WithEndpoint(endpoint string) Option {
|
||||||
return wrappedOption{otlpconfig.WithEndpoint(endpoint)}
|
return wrappedOption{otlpconfig.WithEndpoint(endpoint)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
|
// WithEndpointURL sets the target endpoint URL (scheme, host, port, path) the
|
||||||
|
// Exporter will connect to.
|
||||||
//
|
//
|
||||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
||||||
// environment variable is set, and this option is not passed, that variable
|
// environment variable is set, and this option is not passed, that variable
|
||||||
// value will be used. If both are set, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
|
// value will be used. If both environment variables are set,
|
||||||
// will take precedence.
|
// OTEL_EXPORTER_OTLP_TRACES_ENDPOINT will take precedence. If an environment
|
||||||
|
// variable is set, and this option is passed, this option will take precedence.
|
||||||
//
|
//
|
||||||
// If both this option and WithEndpoint are used, the last used option will
|
// If both this option and WithEndpoint are used, the last used option will
|
||||||
// take precedence.
|
// take precedence.
|
||||||
|
@ -91,7 +94,7 @@ func WithEndpoint(endpoint string) Option {
|
||||||
// If an invalid URL is provided, the default value will be kept.
|
// If an invalid URL is provided, the default value will be kept.
|
||||||
//
|
//
|
||||||
// By default, if an environment variable is not set, and this option is not
|
// By default, if an environment variable is not set, and this option is not
|
||||||
// passed, "localhost:4317" will be used.
|
// passed, "localhost:4318" will be used.
|
||||||
//
|
//
|
||||||
// This option has no effect if WithGRPCConn is used.
|
// This option has no effect if WithGRPCConn is used.
|
||||||
func WithEndpointURL(u string) Option {
|
func WithEndpointURL(u string) Option {
|
||||||
|
@ -143,3 +146,10 @@ func WithTimeout(duration time.Duration) Option {
|
||||||
func WithRetry(rc RetryConfig) Option {
|
func WithRetry(rc RetryConfig) Option {
|
||||||
return wrappedOption{otlpconfig.WithRetry(retry.Config(rc))}
|
return wrappedOption{otlpconfig.WithRetry(retry.Config(rc))}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithProxy sets the Proxy function the client will use to determine the
|
||||||
|
// proxy to use for an HTTP request. If this option is not used, the client
|
||||||
|
// will use [http.ProxyFromEnvironment].
|
||||||
|
func WithProxy(pf HTTPTransportProxyFunc) Option {
|
||||||
|
return wrappedOption{otlpconfig.WithProxy(otlpconfig.HTTPTransportProxyFunc(pf))}
|
||||||
|
}
|
||||||
|
|
|
@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||||
|
|
||||||
// Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
|
// Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return "1.26.0"
|
return "1.29.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Prometheus Exporter
|
||||||
|
|
||||||
|
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/exporters/prometheus)](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/prometheus)
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 prometheus // import "go.opentelemetry.io/otel/exporters/prometheus"
|
package prometheus // import "go.opentelemetry.io/otel/exporters/prometheus"
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 prometheus provides a Prometheus Exporter that converts
|
// Package prometheus provides a Prometheus Exporter that converts
|
||||||
// OTLP metrics into the Prometheus exposition format and implements
|
// OTLP metrics into the Prometheus exposition format and implements
|
||||||
|
|
|
@ -1,24 +1,14 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 prometheus // import "go.opentelemetry.io/otel/exporters/prometheus"
|
package prometheus // import "go.opentelemetry.io/otel/exporters/prometheus"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
@ -43,6 +33,9 @@ const (
|
||||||
|
|
||||||
scopeInfoMetricName = "otel_scope_info"
|
scopeInfoMetricName = "otel_scope_info"
|
||||||
scopeInfoDescription = "Instrumentation Scope metadata"
|
scopeInfoDescription = "Instrumentation Scope metadata"
|
||||||
|
|
||||||
|
traceIDExemplarKey = "trace_id"
|
||||||
|
spanIDExemplarKey = "span_id"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -199,7 +192,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
|
||||||
if !c.disableScopeInfo {
|
if !c.disableScopeInfo {
|
||||||
scopeInfo, err := c.scopeInfo(scopeMetrics.Scope)
|
scopeInfo, err := c.scopeInfo(scopeMetrics.Scope)
|
||||||
if err == errScopeInvalid {
|
if errors.Is(err, errScopeInvalid) {
|
||||||
// Do not report the same error multiple times.
|
// Do not report the same error multiple times.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -249,7 +242,6 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addHistogramMetric[N int64 | float64](ch chan<- prometheus.Metric, histogram metricdata.Histogram[N], m metricdata.Metrics, ks, vs [2]string, name string, resourceKV keyVals) {
|
func addHistogramMetric[N int64 | float64](ch chan<- prometheus.Metric, histogram metricdata.Histogram[N], m metricdata.Metrics, ks, vs [2]string, name string, resourceKV keyVals) {
|
||||||
// TODO(https://github.com/open-telemetry/opentelemetry-go/issues/3163): support exemplars
|
|
||||||
for _, dp := range histogram.DataPoints {
|
for _, dp := range histogram.DataPoints {
|
||||||
keys, values := getAttrs(dp.Attributes, ks, vs, resourceKV)
|
keys, values := getAttrs(dp.Attributes, ks, vs, resourceKV)
|
||||||
|
|
||||||
|
@ -266,6 +258,7 @@ func addHistogramMetric[N int64 | float64](ch chan<- prometheus.Metric, histogra
|
||||||
otel.Handle(err)
|
otel.Handle(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
m = addExemplars(m, dp.Exemplars)
|
||||||
ch <- m
|
ch <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,6 +278,7 @@ func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata
|
||||||
otel.Handle(err)
|
otel.Handle(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
m = addExemplars(m, dp.Exemplars)
|
||||||
ch <- m
|
ch <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,9 +318,7 @@ func getAttrs(attrs attribute.Set, ks, vs [2]string, resourceKV keyVals) ([]stri
|
||||||
values := make([]string, 0, attrs.Len())
|
values := make([]string, 0, attrs.Len())
|
||||||
for key, vals := range keysMap {
|
for key, vals := range keysMap {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
sort.Slice(vals, func(i, j int) bool {
|
slices.Sort(vals)
|
||||||
return i < j
|
|
||||||
})
|
|
||||||
values = append(values, strings.Join(vals, ";"))
|
values = append(values, strings.Join(vals, ";"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,3 +554,37 @@ func (c *collector) validateMetrics(name, description string, metricType *dto.Me
|
||||||
|
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addExemplars[N int64 | float64](m prometheus.Metric, exemplars []metricdata.Exemplar[N]) prometheus.Metric {
|
||||||
|
if len(exemplars) == 0 {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
promExemplars := make([]prometheus.Exemplar, len(exemplars))
|
||||||
|
for i, exemplar := range exemplars {
|
||||||
|
labels := attributesToLabels(exemplar.FilteredAttributes)
|
||||||
|
// Overwrite any existing trace ID or span ID attributes
|
||||||
|
labels[traceIDExemplarKey] = hex.EncodeToString(exemplar.TraceID[:])
|
||||||
|
labels[spanIDExemplarKey] = hex.EncodeToString(exemplar.SpanID[:])
|
||||||
|
promExemplars[i] = prometheus.Exemplar{
|
||||||
|
Value: float64(exemplar.Value),
|
||||||
|
Timestamp: exemplar.Time,
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metricWithExemplar, err := prometheus.NewMetricWithExemplars(m, promExemplars...)
|
||||||
|
if err != nil {
|
||||||
|
// If there are errors creating the metric with exemplars, just warn
|
||||||
|
// and return the metric without exemplars.
|
||||||
|
otel.Handle(err)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return metricWithExemplar
|
||||||
|
}
|
||||||
|
|
||||||
|
func attributesToLabels(attrs []attribute.KeyValue) prometheus.Labels {
|
||||||
|
labels := make(map[string]string)
|
||||||
|
for _, attr := range attrs {
|
||||||
|
labels[string(attr.Key)] = attr.Value.Emit()
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
|
@ -14,33 +14,33 @@ import (
|
||||||
// BoolSliceValue converts a bool slice into an array with same elements as slice.
|
// BoolSliceValue converts a bool slice into an array with same elements as slice.
|
||||||
func BoolSliceValue(v []bool) interface{} {
|
func BoolSliceValue(v []bool) interface{} {
|
||||||
var zero bool
|
var zero bool
|
||||||
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
|
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
|
||||||
copy(cp.Elem().Slice(0, len(v)).Interface().([]bool), v)
|
reflect.Copy(cp, reflect.ValueOf(v))
|
||||||
return cp.Elem().Interface()
|
return cp.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64SliceValue converts an int64 slice into an array with same elements as slice.
|
// Int64SliceValue converts an int64 slice into an array with same elements as slice.
|
||||||
func Int64SliceValue(v []int64) interface{} {
|
func Int64SliceValue(v []int64) interface{} {
|
||||||
var zero int64
|
var zero int64
|
||||||
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
|
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
|
||||||
copy(cp.Elem().Slice(0, len(v)).Interface().([]int64), v)
|
reflect.Copy(cp, reflect.ValueOf(v))
|
||||||
return cp.Elem().Interface()
|
return cp.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64SliceValue converts a float64 slice into an array with same elements as slice.
|
// Float64SliceValue converts a float64 slice into an array with same elements as slice.
|
||||||
func Float64SliceValue(v []float64) interface{} {
|
func Float64SliceValue(v []float64) interface{} {
|
||||||
var zero float64
|
var zero float64
|
||||||
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
|
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
|
||||||
copy(cp.Elem().Slice(0, len(v)).Interface().([]float64), v)
|
reflect.Copy(cp, reflect.ValueOf(v))
|
||||||
return cp.Elem().Interface()
|
return cp.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringSliceValue converts a string slice into an array with same elements as slice.
|
// StringSliceValue converts a string slice into an array with same elements as slice.
|
||||||
func StringSliceValue(v []string) interface{} {
|
func StringSliceValue(v []string) interface{} {
|
||||||
var zero string
|
var zero string
|
||||||
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
|
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
|
||||||
copy(cp.Elem().Slice(0, len(v)).Interface().([]string), v)
|
reflect.Copy(cp, reflect.ValueOf(v))
|
||||||
return cp.Elem().Interface()
|
return cp.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsBoolSlice converts a bool array into a slice into with same elements as array.
|
// AsBoolSlice converts a bool array into a slice into with same elements as array.
|
||||||
|
|
|
@ -281,6 +281,32 @@ func (i *sfHistogram) Record(ctx context.Context, x float64, opts ...metric.Reco
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sfGauge struct {
|
||||||
|
embedded.Float64Gauge
|
||||||
|
|
||||||
|
name string
|
||||||
|
opts []metric.Float64GaugeOption
|
||||||
|
|
||||||
|
delegate atomic.Value // metric.Float64Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ metric.Float64Gauge = (*sfGauge)(nil)
|
||||||
|
|
||||||
|
func (i *sfGauge) setDelegate(m metric.Meter) {
|
||||||
|
ctr, err := m.Float64Gauge(i.name, i.opts...)
|
||||||
|
if err != nil {
|
||||||
|
GetErrorHandler().Handle(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.delegate.Store(ctr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *sfGauge) Record(ctx context.Context, x float64, opts ...metric.RecordOption) {
|
||||||
|
if ctr := i.delegate.Load(); ctr != nil {
|
||||||
|
ctr.(metric.Float64Gauge).Record(ctx, x, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type siCounter struct {
|
type siCounter struct {
|
||||||
embedded.Int64Counter
|
embedded.Int64Counter
|
||||||
|
|
||||||
|
@ -358,3 +384,29 @@ func (i *siHistogram) Record(ctx context.Context, x int64, opts ...metric.Record
|
||||||
ctr.(metric.Int64Histogram).Record(ctx, x, opts...)
|
ctr.(metric.Int64Histogram).Record(ctx, x, opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type siGauge struct {
|
||||||
|
embedded.Int64Gauge
|
||||||
|
|
||||||
|
name string
|
||||||
|
opts []metric.Int64GaugeOption
|
||||||
|
|
||||||
|
delegate atomic.Value // metric.Int64Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ metric.Int64Gauge = (*siGauge)(nil)
|
||||||
|
|
||||||
|
func (i *siGauge) setDelegate(m metric.Meter) {
|
||||||
|
ctr, err := m.Int64Gauge(i.name, i.opts...)
|
||||||
|
if err != nil {
|
||||||
|
GetErrorHandler().Handle(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.delegate.Store(ctr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *siGauge) Record(ctx context.Context, x int64, opts ...metric.RecordOption) {
|
||||||
|
if ctr := i.delegate.Load(); ctr != nil {
|
||||||
|
ctr.(metric.Int64Gauge).Record(ctx, x, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ func (p *meterProvider) Meter(name string, opts ...metric.MeterOption) metric.Me
|
||||||
key := il{
|
key := il{
|
||||||
name: name,
|
name: name,
|
||||||
version: c.InstrumentationVersion(),
|
version: c.InstrumentationVersion(),
|
||||||
|
schema: c.SchemaURL(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.meters == nil {
|
if p.meters == nil {
|
||||||
|
@ -164,6 +165,17 @@ func (m *meter) Int64Histogram(name string, options ...metric.Int64HistogramOpti
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *meter) Int64Gauge(name string, options ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
|
||||||
|
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
||||||
|
return del.Int64Gauge(name, options...)
|
||||||
|
}
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
i := &siGauge{name: name, opts: options}
|
||||||
|
m.instruments = append(m.instruments, i)
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *meter) Int64ObservableCounter(name string, options ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
|
func (m *meter) Int64ObservableCounter(name string, options ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
|
||||||
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
||||||
return del.Int64ObservableCounter(name, options...)
|
return del.Int64ObservableCounter(name, options...)
|
||||||
|
@ -230,6 +242,17 @@ func (m *meter) Float64Histogram(name string, options ...metric.Float64Histogram
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *meter) Float64Gauge(name string, options ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
|
||||||
|
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
||||||
|
return del.Float64Gauge(name, options...)
|
||||||
|
}
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
i := &sfGauge{name: name, opts: options}
|
||||||
|
m.instruments = append(m.instruments, i)
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *meter) Float64ObservableCounter(name string, options ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
|
func (m *meter) Float64ObservableCounter(name string, options ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
|
||||||
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
if del, ok := m.delegate.Load().(metric.Meter); ok {
|
||||||
return del.Float64ObservableCounter(name, options...)
|
return del.Float64ObservableCounter(name, options...)
|
||||||
|
|
|
@ -86,6 +86,7 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||||
key := il{
|
key := il{
|
||||||
name: name,
|
name: name,
|
||||||
version: c.InstrumentationVersion(),
|
version: c.InstrumentationVersion(),
|
||||||
|
schema: c.SchemaURL(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.tracers == nil {
|
if p.tracers == nil {
|
||||||
|
@ -101,10 +102,7 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
type il struct {
|
type il struct{ name, version, schema string }
|
||||||
name string
|
|
||||||
version string
|
|
||||||
}
|
|
||||||
|
|
||||||
// tracer is a placeholder for a trace.Tracer.
|
// tracer is a placeholder for a trace.Tracer.
|
||||||
//
|
//
|
||||||
|
|
|
@ -24,7 +24,8 @@ func Int64ToRaw(i int64) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RawToInt64(r uint64) int64 {
|
func RawToInt64(r uint64) int64 {
|
||||||
return int64(r)
|
// Assumes original was a valid int64 (overflow not checked).
|
||||||
|
return int64(r) // nolint: gosec
|
||||||
}
|
}
|
||||||
|
|
||||||
func Float64ToRaw(f float64) uint64 {
|
func Float64ToRaw(f float64) uint64 {
|
||||||
|
@ -36,9 +37,11 @@ func RawToFloat64(r uint64) float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RawPtrToFloat64Ptr(r *uint64) *float64 {
|
func RawPtrToFloat64Ptr(r *uint64) *float64 {
|
||||||
return (*float64)(unsafe.Pointer(r))
|
// Assumes original was a valid *float64 (overflow not checked).
|
||||||
|
return (*float64)(unsafe.Pointer(r)) // nolint: gosec
|
||||||
}
|
}
|
||||||
|
|
||||||
func RawPtrToInt64Ptr(r *uint64) *int64 {
|
func RawPtrToInt64Ptr(r *uint64) *int64 {
|
||||||
return (*int64)(unsafe.Pointer(r))
|
// Assumes original was a valid *int64 (overflow not checked).
|
||||||
|
return (*int64)(unsafe.Pointer(r)) // nolint: gosec
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Float64ObservableCounter interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64ObservableCounterConfig contains options for asynchronous counter
|
// Float64ObservableCounterConfig contains options for asynchronous counter
|
||||||
// instruments that record int64 values.
|
// instruments that record float64 values.
|
||||||
type Float64ObservableCounterConfig struct {
|
type Float64ObservableCounterConfig struct {
|
||||||
description string
|
description string
|
||||||
unit string
|
unit string
|
||||||
|
@ -97,7 +97,7 @@ type Float64ObservableUpDownCounter interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64ObservableUpDownCounterConfig contains options for asynchronous
|
// Float64ObservableUpDownCounterConfig contains options for asynchronous
|
||||||
// counter instruments that record int64 values.
|
// counter instruments that record float64 values.
|
||||||
type Float64ObservableUpDownCounterConfig struct {
|
type Float64ObservableUpDownCounterConfig struct {
|
||||||
description string
|
description string
|
||||||
unit string
|
unit string
|
||||||
|
@ -154,7 +154,7 @@ type Float64ObservableGauge interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64ObservableGaugeConfig contains options for asynchronous counter
|
// Float64ObservableGaugeConfig contains options for asynchronous counter
|
||||||
// instruments that record int64 values.
|
// instruments that record float64 values.
|
||||||
type Float64ObservableGaugeConfig struct {
|
type Float64ObservableGaugeConfig struct {
|
||||||
description string
|
description string
|
||||||
unit string
|
unit string
|
||||||
|
|
|
@ -57,6 +57,23 @@ asynchronous measurement, a Gauge ([Int64ObservableGauge] and
|
||||||
See the [OpenTelemetry documentation] for more information about instruments
|
See the [OpenTelemetry documentation] for more information about instruments
|
||||||
and their intended use.
|
and their intended use.
|
||||||
|
|
||||||
|
# Instrument Name
|
||||||
|
|
||||||
|
OpenTelemetry defines an [instrument name syntax] that restricts what
|
||||||
|
instrument names are allowed.
|
||||||
|
|
||||||
|
Instrument names should ...
|
||||||
|
|
||||||
|
- Not be empty.
|
||||||
|
- Have an alphabetic character as their first letter.
|
||||||
|
- Have any letter after the first be an alphanumeric character, ‘_’, ‘.’,
|
||||||
|
‘-’, or ‘/’.
|
||||||
|
- Have a maximum length of 255 letters.
|
||||||
|
|
||||||
|
To ensure compatibility with observability platforms, all instruments created
|
||||||
|
need to conform to this syntax. Not all implementations of the API will validate
|
||||||
|
these names, it is the callers responsibility to ensure compliance.
|
||||||
|
|
||||||
# Measurements
|
# Measurements
|
||||||
|
|
||||||
Measurements are made by recording values and information about the values with
|
Measurements are made by recording values and information about the values with
|
||||||
|
@ -153,6 +170,7 @@ It is strongly recommended that authors only embed
|
||||||
That implementation is the only one OpenTelemetry authors can guarantee will
|
That implementation is the only one OpenTelemetry authors can guarantee will
|
||||||
fully implement all the API interfaces when a user updates their API.
|
fully implement all the API interfaces when a user updates their API.
|
||||||
|
|
||||||
|
[instrument name syntax]: https://opentelemetry.io/docs/specs/otel/metrics/api/#instrument-name-syntax
|
||||||
[OpenTelemetry documentation]: https://opentelemetry.io/docs/concepts/signals/metrics/
|
[OpenTelemetry documentation]: https://opentelemetry.io/docs/concepts/signals/metrics/
|
||||||
[GetMeterProvider]: https://pkg.go.dev/go.opentelemetry.io/otel#GetMeterProvider
|
[GetMeterProvider]: https://pkg.go.dev/go.opentelemetry.io/otel#GetMeterProvider
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -102,6 +102,16 @@ type Float64Counter interface{ float64Counter() }
|
||||||
// the API package).
|
// the API package).
|
||||||
type Float64Histogram interface{ float64Histogram() }
|
type Float64Histogram interface{ float64Histogram() }
|
||||||
|
|
||||||
|
// Float64Gauge is embedded in [go.opentelemetry.io/otel/metric.Float64Gauge].
|
||||||
|
//
|
||||||
|
// Embed this interface in your implementation of the
|
||||||
|
// [go.opentelemetry.io/otel/metric.Float64Gauge] if you want users to
|
||||||
|
// experience a compilation error, signaling they need to update to your latest
|
||||||
|
// implementation, when the [go.opentelemetry.io/otel/metric.Float64Gauge]
|
||||||
|
// interface is extended (which is something that can happen without a major
|
||||||
|
// version bump of the API package).
|
||||||
|
type Float64Gauge interface{ float64Gauge() }
|
||||||
|
|
||||||
// Float64ObservableCounter is embedded in
|
// Float64ObservableCounter is embedded in
|
||||||
// [go.opentelemetry.io/otel/metric.Float64ObservableCounter].
|
// [go.opentelemetry.io/otel/metric.Float64ObservableCounter].
|
||||||
//
|
//
|
||||||
|
@ -174,6 +184,16 @@ type Int64Counter interface{ int64Counter() }
|
||||||
// the API package).
|
// the API package).
|
||||||
type Int64Histogram interface{ int64Histogram() }
|
type Int64Histogram interface{ int64Histogram() }
|
||||||
|
|
||||||
|
// Int64Gauge is embedded in [go.opentelemetry.io/otel/metric.Int64Gauge].
|
||||||
|
//
|
||||||
|
// Embed this interface in your implementation of the
|
||||||
|
// [go.opentelemetry.io/otel/metric.Int64Gauge] if you want users to experience
|
||||||
|
// a compilation error, signaling they need to update to your latest
|
||||||
|
// implementation, when the [go.opentelemetry.io/otel/metric.Int64Gauge]
|
||||||
|
// interface is extended (which is something that can happen without a major
|
||||||
|
// version bump of the API package).
|
||||||
|
type Int64Gauge interface{ int64Gauge() }
|
||||||
|
|
||||||
// Int64ObservableCounter is embedded in
|
// Int64ObservableCounter is embedded in
|
||||||
// [go.opentelemetry.io/otel/metric.Int64ObservableCounter].
|
// [go.opentelemetry.io/otel/metric.Int64ObservableCounter].
|
||||||
//
|
//
|
||||||
|
|
|
@ -16,6 +16,7 @@ type InstrumentOption interface {
|
||||||
Int64CounterOption
|
Int64CounterOption
|
||||||
Int64UpDownCounterOption
|
Int64UpDownCounterOption
|
||||||
Int64HistogramOption
|
Int64HistogramOption
|
||||||
|
Int64GaugeOption
|
||||||
Int64ObservableCounterOption
|
Int64ObservableCounterOption
|
||||||
Int64ObservableUpDownCounterOption
|
Int64ObservableUpDownCounterOption
|
||||||
Int64ObservableGaugeOption
|
Int64ObservableGaugeOption
|
||||||
|
@ -23,6 +24,7 @@ type InstrumentOption interface {
|
||||||
Float64CounterOption
|
Float64CounterOption
|
||||||
Float64UpDownCounterOption
|
Float64UpDownCounterOption
|
||||||
Float64HistogramOption
|
Float64HistogramOption
|
||||||
|
Float64GaugeOption
|
||||||
Float64ObservableCounterOption
|
Float64ObservableCounterOption
|
||||||
Float64ObservableUpDownCounterOption
|
Float64ObservableUpDownCounterOption
|
||||||
Float64ObservableGaugeOption
|
Float64ObservableGaugeOption
|
||||||
|
@ -51,6 +53,11 @@ func (o descOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64Histogra
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o descOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
|
||||||
|
c.description = string(o)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
|
func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
|
||||||
c.description = string(o)
|
c.description = string(o)
|
||||||
return c
|
return c
|
||||||
|
@ -81,6 +88,11 @@ func (o descOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfi
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o descOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
|
||||||
|
c.description = string(o)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
|
func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
|
||||||
c.description = string(o)
|
c.description = string(o)
|
||||||
return c
|
return c
|
||||||
|
@ -116,6 +128,11 @@ func (o unitOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64Histogra
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o unitOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
|
||||||
|
c.unit = string(o)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
|
func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
|
||||||
c.unit = string(o)
|
c.unit = string(o)
|
||||||
return c
|
return c
|
||||||
|
@ -146,6 +163,11 @@ func (o unitOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfi
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o unitOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
|
||||||
|
c.unit = string(o)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
|
func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
|
||||||
c.unit = string(o)
|
c.unit = string(o)
|
||||||
return c
|
return c
|
||||||
|
|
|
@ -47,17 +47,41 @@ type Meter interface {
|
||||||
// Int64Counter returns a new Int64Counter instrument identified by name
|
// Int64Counter returns a new Int64Counter instrument identified by name
|
||||||
// and configured with options. The instrument is used to synchronously
|
// and configured with options. The instrument is used to synchronously
|
||||||
// record increasing int64 measurements during a computational operation.
|
// record increasing int64 measurements during a computational operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Int64Counter(name string, options ...Int64CounterOption) (Int64Counter, error)
|
Int64Counter(name string, options ...Int64CounterOption) (Int64Counter, error)
|
||||||
|
|
||||||
// Int64UpDownCounter returns a new Int64UpDownCounter instrument
|
// Int64UpDownCounter returns a new Int64UpDownCounter instrument
|
||||||
// identified by name and configured with options. The instrument is used
|
// identified by name and configured with options. The instrument is used
|
||||||
// to synchronously record int64 measurements during a computational
|
// to synchronously record int64 measurements during a computational
|
||||||
// operation.
|
// operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Int64UpDownCounter(name string, options ...Int64UpDownCounterOption) (Int64UpDownCounter, error)
|
Int64UpDownCounter(name string, options ...Int64UpDownCounterOption) (Int64UpDownCounter, error)
|
||||||
|
|
||||||
// Int64Histogram returns a new Int64Histogram instrument identified by
|
// Int64Histogram returns a new Int64Histogram instrument identified by
|
||||||
// name and configured with options. The instrument is used to
|
// name and configured with options. The instrument is used to
|
||||||
// synchronously record the distribution of int64 measurements during a
|
// synchronously record the distribution of int64 measurements during a
|
||||||
// computational operation.
|
// computational operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error)
|
Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error)
|
||||||
|
|
||||||
|
// Int64Gauge returns a new Int64Gauge instrument identified by name and
|
||||||
|
// configured with options. The instrument is used to synchronously record
|
||||||
|
// instantaneous int64 measurements during a computational operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
|
Int64Gauge(name string, options ...Int64GaugeOption) (Int64Gauge, error)
|
||||||
|
|
||||||
// Int64ObservableCounter returns a new Int64ObservableCounter identified
|
// Int64ObservableCounter returns a new Int64ObservableCounter identified
|
||||||
// by name and configured with options. The instrument is used to
|
// by name and configured with options. The instrument is used to
|
||||||
// asynchronously record increasing int64 measurements once per a
|
// asynchronously record increasing int64 measurements once per a
|
||||||
|
@ -67,7 +91,12 @@ type Meter interface {
|
||||||
// the WithInt64Callback option to register the callback here, or use the
|
// the WithInt64Callback option to register the callback here, or use the
|
||||||
// RegisterCallback method of this Meter to register one later. See the
|
// RegisterCallback method of this Meter to register one later. See the
|
||||||
// Measurements section of the package documentation for more information.
|
// Measurements section of the package documentation for more information.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Int64ObservableCounter(name string, options ...Int64ObservableCounterOption) (Int64ObservableCounter, error)
|
Int64ObservableCounter(name string, options ...Int64ObservableCounterOption) (Int64ObservableCounter, error)
|
||||||
|
|
||||||
// Int64ObservableUpDownCounter returns a new Int64ObservableUpDownCounter
|
// Int64ObservableUpDownCounter returns a new Int64ObservableUpDownCounter
|
||||||
// instrument identified by name and configured with options. The
|
// instrument identified by name and configured with options. The
|
||||||
// instrument is used to asynchronously record int64 measurements once per
|
// instrument is used to asynchronously record int64 measurements once per
|
||||||
|
@ -77,7 +106,12 @@ type Meter interface {
|
||||||
// the WithInt64Callback option to register the callback here, or use the
|
// the WithInt64Callback option to register the callback here, or use the
|
||||||
// RegisterCallback method of this Meter to register one later. See the
|
// RegisterCallback method of this Meter to register one later. See the
|
||||||
// Measurements section of the package documentation for more information.
|
// Measurements section of the package documentation for more information.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Int64ObservableUpDownCounter(name string, options ...Int64ObservableUpDownCounterOption) (Int64ObservableUpDownCounter, error)
|
Int64ObservableUpDownCounter(name string, options ...Int64ObservableUpDownCounterOption) (Int64ObservableUpDownCounter, error)
|
||||||
|
|
||||||
// Int64ObservableGauge returns a new Int64ObservableGauge instrument
|
// Int64ObservableGauge returns a new Int64ObservableGauge instrument
|
||||||
// identified by name and configured with options. The instrument is used
|
// identified by name and configured with options. The instrument is used
|
||||||
// to asynchronously record instantaneous int64 measurements once per a
|
// to asynchronously record instantaneous int64 measurements once per a
|
||||||
|
@ -87,23 +121,51 @@ type Meter interface {
|
||||||
// the WithInt64Callback option to register the callback here, or use the
|
// the WithInt64Callback option to register the callback here, or use the
|
||||||
// RegisterCallback method of this Meter to register one later. See the
|
// RegisterCallback method of this Meter to register one later. See the
|
||||||
// Measurements section of the package documentation for more information.
|
// Measurements section of the package documentation for more information.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Int64ObservableGauge(name string, options ...Int64ObservableGaugeOption) (Int64ObservableGauge, error)
|
Int64ObservableGauge(name string, options ...Int64ObservableGaugeOption) (Int64ObservableGauge, error)
|
||||||
|
|
||||||
// Float64Counter returns a new Float64Counter instrument identified by
|
// Float64Counter returns a new Float64Counter instrument identified by
|
||||||
// name and configured with options. The instrument is used to
|
// name and configured with options. The instrument is used to
|
||||||
// synchronously record increasing float64 measurements during a
|
// synchronously record increasing float64 measurements during a
|
||||||
// computational operation.
|
// computational operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Float64Counter(name string, options ...Float64CounterOption) (Float64Counter, error)
|
Float64Counter(name string, options ...Float64CounterOption) (Float64Counter, error)
|
||||||
|
|
||||||
// Float64UpDownCounter returns a new Float64UpDownCounter instrument
|
// Float64UpDownCounter returns a new Float64UpDownCounter instrument
|
||||||
// identified by name and configured with options. The instrument is used
|
// identified by name and configured with options. The instrument is used
|
||||||
// to synchronously record float64 measurements during a computational
|
// to synchronously record float64 measurements during a computational
|
||||||
// operation.
|
// operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Float64UpDownCounter(name string, options ...Float64UpDownCounterOption) (Float64UpDownCounter, error)
|
Float64UpDownCounter(name string, options ...Float64UpDownCounterOption) (Float64UpDownCounter, error)
|
||||||
|
|
||||||
// Float64Histogram returns a new Float64Histogram instrument identified by
|
// Float64Histogram returns a new Float64Histogram instrument identified by
|
||||||
// name and configured with options. The instrument is used to
|
// name and configured with options. The instrument is used to
|
||||||
// synchronously record the distribution of float64 measurements during a
|
// synchronously record the distribution of float64 measurements during a
|
||||||
// computational operation.
|
// computational operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error)
|
Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error)
|
||||||
|
|
||||||
|
// Float64Gauge returns a new Float64Gauge instrument identified by name and
|
||||||
|
// configured with options. The instrument is used to synchronously record
|
||||||
|
// instantaneous float64 measurements during a computational operation.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
|
Float64Gauge(name string, options ...Float64GaugeOption) (Float64Gauge, error)
|
||||||
|
|
||||||
// Float64ObservableCounter returns a new Float64ObservableCounter
|
// Float64ObservableCounter returns a new Float64ObservableCounter
|
||||||
// instrument identified by name and configured with options. The
|
// instrument identified by name and configured with options. The
|
||||||
// instrument is used to asynchronously record increasing float64
|
// instrument is used to asynchronously record increasing float64
|
||||||
|
@ -113,7 +175,12 @@ type Meter interface {
|
||||||
// the WithFloat64Callback option to register the callback here, or use the
|
// the WithFloat64Callback option to register the callback here, or use the
|
||||||
// RegisterCallback method of this Meter to register one later. See the
|
// RegisterCallback method of this Meter to register one later. See the
|
||||||
// Measurements section of the package documentation for more information.
|
// Measurements section of the package documentation for more information.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Float64ObservableCounter(name string, options ...Float64ObservableCounterOption) (Float64ObservableCounter, error)
|
Float64ObservableCounter(name string, options ...Float64ObservableCounterOption) (Float64ObservableCounter, error)
|
||||||
|
|
||||||
// Float64ObservableUpDownCounter returns a new
|
// Float64ObservableUpDownCounter returns a new
|
||||||
// Float64ObservableUpDownCounter instrument identified by name and
|
// Float64ObservableUpDownCounter instrument identified by name and
|
||||||
// configured with options. The instrument is used to asynchronously record
|
// configured with options. The instrument is used to asynchronously record
|
||||||
|
@ -123,7 +190,12 @@ type Meter interface {
|
||||||
// the WithFloat64Callback option to register the callback here, or use the
|
// the WithFloat64Callback option to register the callback here, or use the
|
||||||
// RegisterCallback method of this Meter to register one later. See the
|
// RegisterCallback method of this Meter to register one later. See the
|
||||||
// Measurements section of the package documentation for more information.
|
// Measurements section of the package documentation for more information.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Float64ObservableUpDownCounter(name string, options ...Float64ObservableUpDownCounterOption) (Float64ObservableUpDownCounter, error)
|
Float64ObservableUpDownCounter(name string, options ...Float64ObservableUpDownCounterOption) (Float64ObservableUpDownCounter, error)
|
||||||
|
|
||||||
// Float64ObservableGauge returns a new Float64ObservableGauge instrument
|
// Float64ObservableGauge returns a new Float64ObservableGauge instrument
|
||||||
// identified by name and configured with options. The instrument is used
|
// identified by name and configured with options. The instrument is used
|
||||||
// to asynchronously record instantaneous float64 measurements once per a
|
// to asynchronously record instantaneous float64 measurements once per a
|
||||||
|
@ -133,6 +205,10 @@ type Meter interface {
|
||||||
// the WithFloat64Callback option to register the callback here, or use the
|
// the WithFloat64Callback option to register the callback here, or use the
|
||||||
// RegisterCallback method of this Meter to register one later. See the
|
// RegisterCallback method of this Meter to register one later. See the
|
||||||
// Measurements section of the package documentation for more information.
|
// Measurements section of the package documentation for more information.
|
||||||
|
//
|
||||||
|
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||||
|
// See the Instrument Name section of the package documentation for more
|
||||||
|
// information.
|
||||||
Float64ObservableGauge(name string, options ...Float64ObservableGaugeOption) (Float64ObservableGauge, error)
|
Float64ObservableGauge(name string, options ...Float64ObservableGaugeOption) (Float64ObservableGauge, error)
|
||||||
|
|
||||||
// RegisterCallback registers f to be called during the collection of a
|
// RegisterCallback registers f to be called during the collection of a
|
||||||
|
@ -178,6 +254,7 @@ type Observer interface {
|
||||||
|
|
||||||
// ObserveFloat64 records the float64 value for obsrv.
|
// ObserveFloat64 records the float64 value for obsrv.
|
||||||
ObserveFloat64(obsrv Float64Observable, value float64, opts ...ObserveOption)
|
ObserveFloat64(obsrv Float64Observable, value float64, opts ...ObserveOption)
|
||||||
|
|
||||||
// ObserveInt64 records the int64 value for obsrv.
|
// ObserveInt64 records the int64 value for obsrv.
|
||||||
ObserveInt64(obsrv Int64Observable, value int64, opts ...ObserveOption)
|
ObserveInt64(obsrv Int64Observable, value int64, opts ...ObserveOption)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ var (
|
||||||
_ metric.Float64UpDownCounter = Float64UpDownCounter{}
|
_ metric.Float64UpDownCounter = Float64UpDownCounter{}
|
||||||
_ metric.Int64Histogram = Int64Histogram{}
|
_ metric.Int64Histogram = Int64Histogram{}
|
||||||
_ metric.Float64Histogram = Float64Histogram{}
|
_ metric.Float64Histogram = Float64Histogram{}
|
||||||
|
_ metric.Int64Gauge = Int64Gauge{}
|
||||||
|
_ metric.Float64Gauge = Float64Gauge{}
|
||||||
_ metric.Int64ObservableCounter = Int64ObservableCounter{}
|
_ metric.Int64ObservableCounter = Int64ObservableCounter{}
|
||||||
_ metric.Float64ObservableCounter = Float64ObservableCounter{}
|
_ metric.Float64ObservableCounter = Float64ObservableCounter{}
|
||||||
_ metric.Int64ObservableGauge = Int64ObservableGauge{}
|
_ metric.Int64ObservableGauge = Int64ObservableGauge{}
|
||||||
|
@ -76,6 +78,12 @@ func (Meter) Int64Histogram(string, ...metric.Int64HistogramOption) (metric.Int6
|
||||||
return Int64Histogram{}, nil
|
return Int64Histogram{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Int64Gauge returns a Gauge used to record int64 measurements that
|
||||||
|
// produces no telemetry.
|
||||||
|
func (Meter) Int64Gauge(string, ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
|
||||||
|
return Int64Gauge{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Int64ObservableCounter returns an ObservableCounter used to record int64
|
// Int64ObservableCounter returns an ObservableCounter used to record int64
|
||||||
// measurements that produces no telemetry.
|
// measurements that produces no telemetry.
|
||||||
func (Meter) Int64ObservableCounter(string, ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
|
func (Meter) Int64ObservableCounter(string, ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
|
||||||
|
@ -112,6 +120,12 @@ func (Meter) Float64Histogram(string, ...metric.Float64HistogramOption) (metric.
|
||||||
return Float64Histogram{}, nil
|
return Float64Histogram{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Float64Gauge returns a Gauge used to record float64 measurements that
|
||||||
|
// produces no telemetry.
|
||||||
|
func (Meter) Float64Gauge(string, ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
|
||||||
|
return Float64Gauge{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Float64ObservableCounter returns an ObservableCounter used to record int64
|
// Float64ObservableCounter returns an ObservableCounter used to record int64
|
||||||
// measurements that produces no telemetry.
|
// measurements that produces no telemetry.
|
||||||
func (Meter) Float64ObservableCounter(string, ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
|
func (Meter) Float64ObservableCounter(string, ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
|
||||||
|
@ -197,6 +211,20 @@ type Float64Histogram struct{ embedded.Float64Histogram }
|
||||||
// Record performs no operation.
|
// Record performs no operation.
|
||||||
func (Float64Histogram) Record(context.Context, float64, ...metric.RecordOption) {}
|
func (Float64Histogram) Record(context.Context, float64, ...metric.RecordOption) {}
|
||||||
|
|
||||||
|
// Int64Gauge is an OpenTelemetry Gauge used to record instantaneous int64
|
||||||
|
// measurements. It produces no telemetry.
|
||||||
|
type Int64Gauge struct{ embedded.Int64Gauge }
|
||||||
|
|
||||||
|
// Record performs no operation.
|
||||||
|
func (Int64Gauge) Record(context.Context, int64, ...metric.RecordOption) {}
|
||||||
|
|
||||||
|
// Float64Gauge is an OpenTelemetry Gauge used to record instantaneous float64
|
||||||
|
// measurements. It produces no telemetry.
|
||||||
|
type Float64Gauge struct{ embedded.Float64Gauge }
|
||||||
|
|
||||||
|
// Record performs no operation.
|
||||||
|
func (Float64Gauge) Record(context.Context, float64, ...metric.RecordOption) {}
|
||||||
|
|
||||||
// Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record
|
// Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record
|
||||||
// int64 measurements. It produces no telemetry.
|
// int64 measurements. It produces no telemetry.
|
||||||
type Int64ObservableCounter struct {
|
type Int64ObservableCounter struct {
|
||||||
|
|
|
@ -28,7 +28,7 @@ type Float64Counter interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64CounterConfig contains options for synchronous counter instruments that
|
// Float64CounterConfig contains options for synchronous counter instruments that
|
||||||
// record int64 values.
|
// record float64 values.
|
||||||
type Float64CounterConfig struct {
|
type Float64CounterConfig struct {
|
||||||
description string
|
description string
|
||||||
unit string
|
unit string
|
||||||
|
@ -81,7 +81,7 @@ type Float64UpDownCounter interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64UpDownCounterConfig contains options for synchronous counter
|
// Float64UpDownCounterConfig contains options for synchronous counter
|
||||||
// instruments that record int64 values.
|
// instruments that record float64 values.
|
||||||
type Float64UpDownCounterConfig struct {
|
type Float64UpDownCounterConfig struct {
|
||||||
description string
|
description string
|
||||||
unit string
|
unit string
|
||||||
|
@ -133,8 +133,8 @@ type Float64Histogram interface {
|
||||||
Record(ctx context.Context, incr float64, options ...RecordOption)
|
Record(ctx context.Context, incr float64, options ...RecordOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64HistogramConfig contains options for synchronous counter instruments
|
// Float64HistogramConfig contains options for synchronous histogram
|
||||||
// that record int64 values.
|
// instruments that record float64 values.
|
||||||
type Float64HistogramConfig struct {
|
type Float64HistogramConfig struct {
|
||||||
description string
|
description string
|
||||||
unit string
|
unit string
|
||||||
|
@ -172,3 +172,55 @@ func (c Float64HistogramConfig) ExplicitBucketBoundaries() []float64 {
|
||||||
type Float64HistogramOption interface {
|
type Float64HistogramOption interface {
|
||||||
applyFloat64Histogram(Float64HistogramConfig) Float64HistogramConfig
|
applyFloat64Histogram(Float64HistogramConfig) Float64HistogramConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Float64Gauge is an instrument that records instantaneous float64 values.
|
||||||
|
//
|
||||||
|
// Warning: Methods may be added to this interface in minor releases. See
|
||||||
|
// package documentation on API implementation for information on how to set
|
||||||
|
// default behavior for unimplemented methods.
|
||||||
|
type Float64Gauge interface {
|
||||||
|
// Users of the interface can ignore this. This embedded type is only used
|
||||||
|
// by implementations of this interface. See the "API Implementations"
|
||||||
|
// section of the package documentation for more information.
|
||||||
|
embedded.Float64Gauge
|
||||||
|
|
||||||
|
// Record records the instantaneous value.
|
||||||
|
//
|
||||||
|
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||||
|
// the WithAttributes) option to include measurement attributes.
|
||||||
|
Record(ctx context.Context, value float64, options ...RecordOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64GaugeConfig contains options for synchronous gauge instruments that
|
||||||
|
// record float64 values.
|
||||||
|
type Float64GaugeConfig struct {
|
||||||
|
description string
|
||||||
|
unit string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64GaugeConfig returns a new [Float64GaugeConfig] with all opts
|
||||||
|
// applied.
|
||||||
|
func NewFloat64GaugeConfig(opts ...Float64GaugeOption) Float64GaugeConfig {
|
||||||
|
var config Float64GaugeConfig
|
||||||
|
for _, o := range opts {
|
||||||
|
config = o.applyFloat64Gauge(config)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description returns the configured description.
|
||||||
|
func (c Float64GaugeConfig) Description() string {
|
||||||
|
return c.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit returns the configured unit.
|
||||||
|
func (c Float64GaugeConfig) Unit() string {
|
||||||
|
return c.unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64GaugeOption applies options to a [Float64GaugeConfig]. See
|
||||||
|
// [InstrumentOption] for other options that can be used as a
|
||||||
|
// Float64GaugeOption.
|
||||||
|
type Float64GaugeOption interface {
|
||||||
|
applyFloat64Gauge(Float64GaugeConfig) Float64GaugeConfig
|
||||||
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ type Int64Histogram interface {
|
||||||
Record(ctx context.Context, incr int64, options ...RecordOption)
|
Record(ctx context.Context, incr int64, options ...RecordOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64HistogramConfig contains options for synchronous counter instruments
|
// Int64HistogramConfig contains options for synchronous histogram instruments
|
||||||
// that record int64 values.
|
// that record int64 values.
|
||||||
type Int64HistogramConfig struct {
|
type Int64HistogramConfig struct {
|
||||||
description string
|
description string
|
||||||
|
@ -172,3 +172,55 @@ func (c Int64HistogramConfig) ExplicitBucketBoundaries() []float64 {
|
||||||
type Int64HistogramOption interface {
|
type Int64HistogramOption interface {
|
||||||
applyInt64Histogram(Int64HistogramConfig) Int64HistogramConfig
|
applyInt64Histogram(Int64HistogramConfig) Int64HistogramConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Int64Gauge is an instrument that records instantaneous int64 values.
|
||||||
|
//
|
||||||
|
// Warning: Methods may be added to this interface in minor releases. See
|
||||||
|
// package documentation on API implementation for information on how to set
|
||||||
|
// default behavior for unimplemented methods.
|
||||||
|
type Int64Gauge interface {
|
||||||
|
// Users of the interface can ignore this. This embedded type is only used
|
||||||
|
// by implementations of this interface. See the "API Implementations"
|
||||||
|
// section of the package documentation for more information.
|
||||||
|
embedded.Int64Gauge
|
||||||
|
|
||||||
|
// Record records the instantaneous value.
|
||||||
|
//
|
||||||
|
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||||
|
// the WithAttributes) option to include measurement attributes.
|
||||||
|
Record(ctx context.Context, value int64, options ...RecordOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64GaugeConfig contains options for synchronous gauge instruments that
|
||||||
|
// record int64 values.
|
||||||
|
type Int64GaugeConfig struct {
|
||||||
|
description string
|
||||||
|
unit string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64GaugeConfig returns a new [Int64GaugeConfig] with all opts
|
||||||
|
// applied.
|
||||||
|
func NewInt64GaugeConfig(opts ...Int64GaugeOption) Int64GaugeConfig {
|
||||||
|
var config Int64GaugeConfig
|
||||||
|
for _, o := range opts {
|
||||||
|
config = o.applyInt64Gauge(config)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description returns the configured description.
|
||||||
|
func (c Int64GaugeConfig) Description() string {
|
||||||
|
return c.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit returns the configured unit.
|
||||||
|
func (c Int64GaugeConfig) Unit() string {
|
||||||
|
return c.unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64GaugeOption applies options to a [Int64GaugeConfig]. See
|
||||||
|
// [InstrumentOption] for other options that can be used as a
|
||||||
|
// Int64GaugeOption.
|
||||||
|
type Int64GaugeOption interface {
|
||||||
|
applyInt64Gauge(Int64GaugeConfig) Int64GaugeConfig
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:recommended"
|
||||||
|
],
|
||||||
|
"ignorePaths": [],
|
||||||
|
"labels": ["Skip Changelog", "dependencies"],
|
||||||
|
"postUpdateOptions" : [
|
||||||
|
"gomodTidy"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchManagers": ["gomod"],
|
||||||
|
"matchDepTypes": ["indirect"],
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchFileNames": ["internal/tools/**"],
|
||||||
|
"matchManagers": ["gomod"],
|
||||||
|
"matchDepTypes": ["indirect"],
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
codespell==2.2.6
|
codespell==2.3.0
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"
|
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"
|
||||||
|
|
||||||
// Library represents the instrumentation library.
|
// Library represents the instrumentation library.
|
||||||
// Deprecated: please use Scope instead.
|
//
|
||||||
|
// Deprecated: use [Scope] instead.
|
||||||
type Library = Scope
|
type Library = Scope
|
||||||
|
|
|
@ -22,7 +22,7 @@ const (
|
||||||
BatchSpanProcessorMaxQueueSizeKey = "OTEL_BSP_MAX_QUEUE_SIZE"
|
BatchSpanProcessorMaxQueueSizeKey = "OTEL_BSP_MAX_QUEUE_SIZE"
|
||||||
// BatchSpanProcessorMaxExportBatchSizeKey is the maximum batch size (i.e.
|
// BatchSpanProcessorMaxExportBatchSizeKey is the maximum batch size (i.e.
|
||||||
// 512). Note: it must be less than or equal to
|
// 512). Note: it must be less than or equal to
|
||||||
// EnvBatchSpanProcessorMaxQueueSize.
|
// BatchSpanProcessorMaxQueueSize.
|
||||||
BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE"
|
BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE"
|
||||||
|
|
||||||
// AttributeValueLengthKey is the maximum allowed attribute value size.
|
// AttributeValueLengthKey is the maximum allowed attribute value size.
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package internal // import "go.opentelemetry.io/otel/sdk/internal"
|
|
||||||
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go
|
|
||||||
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/sdk/internal/matchers\"}" --out=internaltest/harness.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go
|
|
||||||
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package internal // import "go.opentelemetry.io/otel/sdk/internal"
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// MonotonicEndTime returns the end time at present
|
|
||||||
// but offset from start, monotonically.
|
|
||||||
//
|
|
||||||
// The monotonic clock is used in subtractions hence
|
|
||||||
// the duration since start added back to start gives
|
|
||||||
// end as a monotonic time.
|
|
||||||
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
|
|
||||||
func MonotonicEndTime(start time.Time) time.Time {
|
|
||||||
return start.Add(time.Since(start))
|
|
||||||
}
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Experimental Features
|
||||||
|
|
||||||
|
The SDK contains features that have not yet stabilized in the OpenTelemetry specification.
|
||||||
|
These features are added to the OpenTelemetry Go SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
|
||||||
|
|
||||||
|
These feature may change in backwards incompatible ways as feedback is applied.
|
||||||
|
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- [Resource](#resource)
|
||||||
|
|
||||||
|
### Resource
|
||||||
|
|
||||||
|
[OpenTelemetry resource semantic conventions] include many attribute definitions that are defined as experimental.
|
||||||
|
To have experimental semantic conventions be added by [resource detectors] set the `OTEL_GO_X_RESOURCE` environment variable.
|
||||||
|
The value set must be the case-insensitive string of `"true"` to enable the feature.
|
||||||
|
All other values are ignored.
|
||||||
|
|
||||||
|
<!-- TODO: document what attributes are added by which detector -->
|
||||||
|
|
||||||
|
[OpenTelemetry resource semantic conventions]: https://opentelemetry.io/docs/specs/semconv/resource/
|
||||||
|
[resource detectors]: https://pkg.go.dev/go.opentelemetry.io/otel/sdk/resource#Detector
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
Enable experimental resource semantic conventions.
|
||||||
|
|
||||||
|
```console
|
||||||
|
export OTEL_GO_X_RESOURCE=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Disable experimental resource semantic conventions.
|
||||||
|
|
||||||
|
```console
|
||||||
|
unset OTEL_GO_X_RESOURCE
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compatibility and Stability
|
||||||
|
|
||||||
|
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../VERSIONING.md).
|
||||||
|
These features may be removed or modified in successive version releases, including patch versions.
|
||||||
|
|
||||||
|
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
|
||||||
|
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
|
||||||
|
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package x contains support for OTel SDK experimental features.
|
||||||
|
//
|
||||||
|
// This package should only be used for features defined in the specification.
|
||||||
|
// It should not be used for experiments or new project ideas.
|
||||||
|
package x // import "go.opentelemetry.io/otel/sdk/internal/x"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Resource is an experimental feature flag that defines if resource detectors
|
||||||
|
// should be included experimental semantic conventions.
|
||||||
|
//
|
||||||
|
// To enable this feature set the OTEL_GO_X_RESOURCE environment variable
|
||||||
|
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||||
|
// will also enable this).
|
||||||
|
var Resource = newFeature("RESOURCE", func(v string) (string, bool) {
|
||||||
|
if strings.ToLower(v) == "true" {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Feature is an experimental feature control flag. It provides a uniform way
|
||||||
|
// to interact with these feature flags and parse their values.
|
||||||
|
type Feature[T any] struct {
|
||||||
|
key string
|
||||||
|
parse func(v string) (T, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
|
||||||
|
const envKeyRoot = "OTEL_GO_X_"
|
||||||
|
return Feature[T]{
|
||||||
|
key: envKeyRoot + suffix,
|
||||||
|
parse: parse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the environment variable key that needs to be set to enable the
|
||||||
|
// feature.
|
||||||
|
func (f Feature[T]) Key() string { return f.key }
|
||||||
|
|
||||||
|
// Lookup returns the user configured value for the feature and true if the
|
||||||
|
// user has enabled the feature. Otherwise, if the feature is not enabled, a
|
||||||
|
// zero-value and false are returned.
|
||||||
|
func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
|
||||||
|
//
|
||||||
|
// > The SDK MUST interpret an empty value of an environment variable the
|
||||||
|
// > same way as when the variable is unset.
|
||||||
|
vRaw := os.Getenv(f.key)
|
||||||
|
if vRaw == "" {
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
return f.parse(vRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled returns if the feature is enabled.
|
||||||
|
func (f Feature[T]) Enabled() bool {
|
||||||
|
_, ok := f.Lookup()
|
||||||
|
return ok
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Metric SDK
|
||||||
|
|
||||||
|
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/sdk/metric)](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric)
|
|
@ -1,22 +1,12 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errAgg is wrapped by misconfigured aggregations.
|
// errAgg is wrapped by misconfigured aggregations.
|
||||||
|
@ -141,10 +131,8 @@ func (h AggregationExplicitBucketHistogram) err() error {
|
||||||
|
|
||||||
// copy returns a deep copy of h.
|
// copy returns a deep copy of h.
|
||||||
func (h AggregationExplicitBucketHistogram) copy() Aggregation {
|
func (h AggregationExplicitBucketHistogram) copy() Aggregation {
|
||||||
b := make([]float64, len(h.Boundaries))
|
|
||||||
copy(b, h.Boundaries)
|
|
||||||
return AggregationExplicitBucketHistogram{
|
return AggregationExplicitBucketHistogram{
|
||||||
Boundaries: b,
|
Boundaries: slices.Clone(h.Boundaries),
|
||||||
NoMinMax: h.NoMinMax,
|
NoMinMax: h.NoMinMax,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
|
@ -133,7 +122,7 @@ func WithReader(r Reader) Option {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithView associates views a MeterProvider.
|
// WithView associates views with a MeterProvider.
|
||||||
//
|
//
|
||||||
// Views are appended to existing ones in a MeterProvider if this option is
|
// Views are appended to existing ones in a MeterProvider if this option is
|
||||||
// used multiple times.
|
// used multiple times.
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 provides an implementation of the OpenTelemetry metrics SDK.
|
// Package metric provides an implementation of the OpenTelemetry metrics SDK.
|
||||||
//
|
//
|
||||||
|
@ -42,6 +31,14 @@
|
||||||
// is being run on. That way when multiple instances of the code are collected
|
// is being run on. That way when multiple instances of the code are collected
|
||||||
// at a single endpoint their origin is decipherable.
|
// at a single endpoint their origin is decipherable.
|
||||||
//
|
//
|
||||||
|
// To avoid leaking memory, the SDK returns the same instrument for calls to
|
||||||
|
// create new instruments with the same Name, Unit, and Description.
|
||||||
|
// Importantly, callbacks provided using metric.WithFloat64Callback or
|
||||||
|
// metric.WithInt64Callback will only apply for the first instrument created
|
||||||
|
// with a given Name, Unit, and Description. Instead, use
|
||||||
|
// Meter.RegisterCallback and Registration.Unregister to add and remove
|
||||||
|
// callbacks without leaking memory.
|
||||||
|
//
|
||||||
// See [go.opentelemetry.io/otel/metric] for more information about
|
// See [go.opentelemetry.io/otel/metric] for more information about
|
||||||
// the metric API.
|
// the metric API.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
"go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/internal/x"
|
"go.opentelemetry.io/otel/sdk/metric/internal/x"
|
||||||
|
@ -29,22 +19,35 @@ import (
|
||||||
// Note: This will only return non-nil values when the experimental exemplar
|
// Note: This will only return non-nil values when the experimental exemplar
|
||||||
// feature is enabled and the OTEL_METRICS_EXEMPLAR_FILTER environment variable
|
// feature is enabled and the OTEL_METRICS_EXEMPLAR_FILTER environment variable
|
||||||
// is not set to always_off.
|
// is not set to always_off.
|
||||||
func reservoirFunc[N int64 | float64](agg Aggregation) func() exemplar.Reservoir[N] {
|
func reservoirFunc[N int64 | float64](agg Aggregation) func() exemplar.FilteredReservoir[N] {
|
||||||
if !x.Exemplars.Enabled() {
|
if !x.Exemplars.Enabled() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/d4b241f451674e8f611bb589477680341006ad2b/specification/configuration/sdk-environment-variables.md#exemplar
|
||||||
|
const filterEnvKey = "OTEL_METRICS_EXEMPLAR_FILTER"
|
||||||
|
|
||||||
|
var filter exemplar.Filter
|
||||||
|
|
||||||
|
switch os.Getenv(filterEnvKey) {
|
||||||
|
case "always_on":
|
||||||
|
filter = exemplar.AlwaysOnFilter
|
||||||
|
case "always_off":
|
||||||
|
return exemplar.Drop
|
||||||
|
case "trace_based":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
filter = exemplar.SampledFilter
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/d4b241f451674e8f611bb589477680341006ad2b/specification/metrics/sdk.md#exemplar-defaults
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/d4b241f451674e8f611bb589477680341006ad2b/specification/metrics/sdk.md#exemplar-defaults
|
||||||
resF := func() func() exemplar.Reservoir[N] {
|
|
||||||
// Explicit bucket histogram aggregation with more than 1 bucket will
|
// Explicit bucket histogram aggregation with more than 1 bucket will
|
||||||
// use AlignedHistogramBucketExemplarReservoir.
|
// use AlignedHistogramBucketExemplarReservoir.
|
||||||
a, ok := agg.(AggregationExplicitBucketHistogram)
|
a, ok := agg.(AggregationExplicitBucketHistogram)
|
||||||
if ok && len(a.Boundaries) > 0 {
|
if ok && len(a.Boundaries) > 0 {
|
||||||
cp := make([]float64, len(a.Boundaries))
|
cp := slices.Clone(a.Boundaries)
|
||||||
copy(cp, a.Boundaries)
|
return func() exemplar.FilteredReservoir[N] {
|
||||||
return func() exemplar.Reservoir[N] {
|
|
||||||
bounds := cp
|
bounds := cp
|
||||||
return exemplar.Histogram[N](bounds)
|
return exemplar.NewFilteredReservoir[N](filter, exemplar.Histogram(bounds))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,25 +75,7 @@ func reservoirFunc[N int64 | float64](agg Aggregation) func() exemplar.Reservoir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return func() exemplar.Reservoir[N] {
|
return func() exemplar.FilteredReservoir[N] {
|
||||||
return exemplar.FixedSize[N](n)
|
return exemplar.NewFilteredReservoir[N](filter, exemplar.FixedSize(n))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/d4b241f451674e8f611bb589477680341006ad2b/specification/configuration/sdk-environment-variables.md#exemplar
|
|
||||||
const filterEnvKey = "OTEL_METRICS_EXEMPLAR_FILTER"
|
|
||||||
|
|
||||||
switch os.Getenv(filterEnvKey) {
|
|
||||||
case "always_on":
|
|
||||||
return resF()
|
|
||||||
case "always_off":
|
|
||||||
return exemplar.Drop[N]
|
|
||||||
case "trace_based":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
newR := resF()
|
|
||||||
return func() exemplar.Reservoir[N] {
|
|
||||||
return exemplar.SampledFilter(newR())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//go:generate stringer -type=InstrumentKind -trimprefix=InstrumentKind
|
//go:generate stringer -type=InstrumentKind -trimprefix=InstrumentKind
|
||||||
|
|
||||||
|
@ -29,10 +18,7 @@ import (
|
||||||
"go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
"go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var zeroScope instrumentation.Scope
|
||||||
zeroInstrumentKind InstrumentKind
|
|
||||||
zeroScope instrumentation.Scope
|
|
||||||
)
|
|
||||||
|
|
||||||
// InstrumentKind is the identifier of a group of instruments that all
|
// InstrumentKind is the identifier of a group of instruments that all
|
||||||
// performing the same function.
|
// performing the same function.
|
||||||
|
@ -41,28 +27,32 @@ type InstrumentKind uint8
|
||||||
const (
|
const (
|
||||||
// instrumentKindUndefined is an undefined instrument kind, it should not
|
// instrumentKindUndefined is an undefined instrument kind, it should not
|
||||||
// be used by any initialized type.
|
// be used by any initialized type.
|
||||||
instrumentKindUndefined InstrumentKind = iota // nolint:deadcode,varcheck,unused
|
instrumentKindUndefined InstrumentKind = 0 // nolint:deadcode,varcheck,unused
|
||||||
// InstrumentKindCounter identifies a group of instruments that record
|
// InstrumentKindCounter identifies a group of instruments that record
|
||||||
// increasing values synchronously with the code path they are measuring.
|
// increasing values synchronously with the code path they are measuring.
|
||||||
InstrumentKindCounter
|
InstrumentKindCounter InstrumentKind = 1
|
||||||
// InstrumentKindUpDownCounter identifies a group of instruments that
|
// InstrumentKindUpDownCounter identifies a group of instruments that
|
||||||
// record increasing and decreasing values synchronously with the code path
|
// record increasing and decreasing values synchronously with the code path
|
||||||
// they are measuring.
|
// they are measuring.
|
||||||
InstrumentKindUpDownCounter
|
InstrumentKindUpDownCounter InstrumentKind = 2
|
||||||
// InstrumentKindHistogram identifies a group of instruments that record a
|
// InstrumentKindHistogram identifies a group of instruments that record a
|
||||||
// distribution of values synchronously with the code path they are
|
// distribution of values synchronously with the code path they are
|
||||||
// measuring.
|
// measuring.
|
||||||
InstrumentKindHistogram
|
InstrumentKindHistogram InstrumentKind = 3
|
||||||
// InstrumentKindObservableCounter identifies a group of instruments that
|
// InstrumentKindObservableCounter identifies a group of instruments that
|
||||||
// record increasing values in an asynchronous callback.
|
// record increasing values in an asynchronous callback.
|
||||||
InstrumentKindObservableCounter
|
InstrumentKindObservableCounter InstrumentKind = 4
|
||||||
// InstrumentKindObservableUpDownCounter identifies a group of instruments
|
// InstrumentKindObservableUpDownCounter identifies a group of instruments
|
||||||
// that record increasing and decreasing values in an asynchronous
|
// that record increasing and decreasing values in an asynchronous
|
||||||
// callback.
|
// callback.
|
||||||
InstrumentKindObservableUpDownCounter
|
InstrumentKindObservableUpDownCounter InstrumentKind = 5
|
||||||
// InstrumentKindObservableGauge identifies a group of instruments that
|
// InstrumentKindObservableGauge identifies a group of instruments that
|
||||||
// record current values in an asynchronous callback.
|
// record current values in an asynchronous callback.
|
||||||
InstrumentKindObservableGauge
|
InstrumentKindObservableGauge InstrumentKind = 6
|
||||||
|
// InstrumentKindGauge identifies a group of instruments that record
|
||||||
|
// instantaneous values synchronously with the code path they are
|
||||||
|
// measuring.
|
||||||
|
InstrumentKindGauge InstrumentKind = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
type nonComparable [0]func() // nolint: unused // This is indeed used.
|
type nonComparable [0]func() // nolint: unused // This is indeed used.
|
||||||
|
@ -84,11 +74,11 @@ type Instrument struct {
|
||||||
nonComparable // nolint: unused
|
nonComparable // nolint: unused
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty returns if all fields of i are their zero-value.
|
// IsEmpty returns if all Instrument fields are their zero-value.
|
||||||
func (i Instrument) empty() bool {
|
func (i Instrument) IsEmpty() bool {
|
||||||
return i.Name == "" &&
|
return i.Name == "" &&
|
||||||
i.Description == "" &&
|
i.Description == "" &&
|
||||||
i.Kind == zeroInstrumentKind &&
|
i.Kind == instrumentKindUndefined &&
|
||||||
i.Unit == "" &&
|
i.Unit == "" &&
|
||||||
i.Scope == zeroScope
|
i.Scope == zeroScope
|
||||||
}
|
}
|
||||||
|
@ -119,7 +109,7 @@ func (i Instrument) matchesDescription(other Instrument) bool {
|
||||||
// matchesKind returns true if the Kind of i is its zero-value or it equals the
|
// matchesKind returns true if the Kind of i is its zero-value or it equals the
|
||||||
// Kind of other, otherwise false.
|
// Kind of other, otherwise false.
|
||||||
func (i Instrument) matchesKind(other Instrument) bool {
|
func (i Instrument) matchesKind(other Instrument) bool {
|
||||||
return i.Kind == zeroInstrumentKind || i.Kind == other.Kind
|
return i.Kind == instrumentKindUndefined || i.Kind == other.Kind
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchesUnit returns true if the Unit of i is its zero-value or it equals the
|
// matchesUnit returns true if the Unit of i is its zero-value or it equals the
|
||||||
|
@ -186,12 +176,14 @@ type int64Inst struct {
|
||||||
embedded.Int64Counter
|
embedded.Int64Counter
|
||||||
embedded.Int64UpDownCounter
|
embedded.Int64UpDownCounter
|
||||||
embedded.Int64Histogram
|
embedded.Int64Histogram
|
||||||
|
embedded.Int64Gauge
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ metric.Int64Counter = (*int64Inst)(nil)
|
_ metric.Int64Counter = (*int64Inst)(nil)
|
||||||
_ metric.Int64UpDownCounter = (*int64Inst)(nil)
|
_ metric.Int64UpDownCounter = (*int64Inst)(nil)
|
||||||
_ metric.Int64Histogram = (*int64Inst)(nil)
|
_ metric.Int64Histogram = (*int64Inst)(nil)
|
||||||
|
_ metric.Int64Gauge = (*int64Inst)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *int64Inst) Add(ctx context.Context, val int64, opts ...metric.AddOption) {
|
func (i *int64Inst) Add(ctx context.Context, val int64, opts ...metric.AddOption) {
|
||||||
|
@ -216,12 +208,14 @@ type float64Inst struct {
|
||||||
embedded.Float64Counter
|
embedded.Float64Counter
|
||||||
embedded.Float64UpDownCounter
|
embedded.Float64UpDownCounter
|
||||||
embedded.Float64Histogram
|
embedded.Float64Histogram
|
||||||
|
embedded.Float64Gauge
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ metric.Float64Counter = (*float64Inst)(nil)
|
_ metric.Float64Counter = (*float64Inst)(nil)
|
||||||
_ metric.Float64UpDownCounter = (*float64Inst)(nil)
|
_ metric.Float64UpDownCounter = (*float64Inst)(nil)
|
||||||
_ metric.Float64Histogram = (*float64Inst)(nil)
|
_ metric.Float64Histogram = (*float64Inst)(nil)
|
||||||
|
_ metric.Float64Gauge = (*float64Inst)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *float64Inst) Add(ctx context.Context, val float64, opts ...metric.AddOption) {
|
func (i *float64Inst) Add(ctx context.Context, val float64, opts ...metric.AddOption) {
|
||||||
|
|
|
@ -15,11 +15,12 @@ func _() {
|
||||||
_ = x[InstrumentKindObservableCounter-4]
|
_ = x[InstrumentKindObservableCounter-4]
|
||||||
_ = x[InstrumentKindObservableUpDownCounter-5]
|
_ = x[InstrumentKindObservableUpDownCounter-5]
|
||||||
_ = x[InstrumentKindObservableGauge-6]
|
_ = x[InstrumentKindObservableGauge-6]
|
||||||
|
_ = x[InstrumentKindGauge-7]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _InstrumentKind_name = "instrumentKindUndefinedCounterUpDownCounterHistogramObservableCounterObservableUpDownCounterObservableGauge"
|
const _InstrumentKind_name = "instrumentKindUndefinedCounterUpDownCounterHistogramObservableCounterObservableUpDownCounterObservableGaugeGauge"
|
||||||
|
|
||||||
var _InstrumentKind_index = [...]uint8{0, 23, 30, 43, 52, 69, 92, 107}
|
var _InstrumentKind_index = [...]uint8{0, 23, 30, 43, 52, 69, 92, 107, 112}
|
||||||
|
|
||||||
func (i InstrumentKind) String() string {
|
func (i InstrumentKind) String() string {
|
||||||
if i >= InstrumentKind(len(_InstrumentKind_index)-1) {
|
if i >= InstrumentKind(len(_InstrumentKind_index)-1) {
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
|
@ -50,7 +39,7 @@ type Builder[N int64 | float64] struct {
|
||||||
//
|
//
|
||||||
// If this is not provided a default factory function that returns an
|
// If this is not provided a default factory function that returns an
|
||||||
// exemplar.Drop reservoir will be used.
|
// exemplar.Drop reservoir will be used.
|
||||||
ReservoirFunc func() exemplar.Reservoir[N]
|
ReservoirFunc func() exemplar.FilteredReservoir[N]
|
||||||
// AggregationLimit is the cardinality limit of measurement attributes. Any
|
// AggregationLimit is the cardinality limit of measurement attributes. Any
|
||||||
// measurement for new attributes once the limit has been reached will be
|
// measurement for new attributes once the limit has been reached will be
|
||||||
// aggregated into a single aggregate for the "otel.metric.overflow"
|
// aggregated into a single aggregate for the "otel.metric.overflow"
|
||||||
|
@ -61,12 +50,12 @@ type Builder[N int64 | float64] struct {
|
||||||
AggregationLimit int
|
AggregationLimit int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Builder[N]) resFunc() func() exemplar.Reservoir[N] {
|
func (b Builder[N]) resFunc() func() exemplar.FilteredReservoir[N] {
|
||||||
if b.ReservoirFunc != nil {
|
if b.ReservoirFunc != nil {
|
||||||
return b.ReservoirFunc
|
return b.ReservoirFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
return exemplar.Drop[N]
|
return exemplar.Drop
|
||||||
}
|
}
|
||||||
|
|
||||||
type fltrMeasure[N int64 | float64] func(ctx context.Context, value N, fltrAttr attribute.Set, droppedAttr []attribute.KeyValue)
|
type fltrMeasure[N int64 | float64] func(ctx context.Context, value N, fltrAttr attribute.Set, droppedAttr []attribute.KeyValue)
|
||||||
|
@ -85,21 +74,26 @@ func (b Builder[N]) filter(f fltrMeasure[N]) Measure[N] {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastValue returns a last-value aggregate function input and output.
|
// LastValue returns a last-value aggregate function input and output.
|
||||||
//
|
|
||||||
// The Builder.Temporality is ignored and delta is use always.
|
|
||||||
func (b Builder[N]) LastValue() (Measure[N], ComputeAggregation) {
|
func (b Builder[N]) LastValue() (Measure[N], ComputeAggregation) {
|
||||||
// Delta temporality is the only temporality that makes semantic sense for
|
|
||||||
// a last-value aggregate.
|
|
||||||
lv := newLastValue[N](b.AggregationLimit, b.resFunc())
|
lv := newLastValue[N](b.AggregationLimit, b.resFunc())
|
||||||
|
switch b.Temporality {
|
||||||
|
case metricdata.DeltaTemporality:
|
||||||
|
return b.filter(lv.measure), lv.delta
|
||||||
|
default:
|
||||||
|
return b.filter(lv.measure), lv.cumulative
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return b.filter(lv.measure), func(dest *metricdata.Aggregation) int {
|
// PrecomputedLastValue returns a last-value aggregate function input and
|
||||||
// Ignore if dest is not a metricdata.Gauge. The chance for memory
|
// output. The aggregation returned from the returned ComputeAggregation
|
||||||
// reuse of the DataPoints is missed (better luck next time).
|
// function will always only return values from the previous collection cycle.
|
||||||
gData, _ := (*dest).(metricdata.Gauge[N])
|
func (b Builder[N]) PrecomputedLastValue() (Measure[N], ComputeAggregation) {
|
||||||
lv.computeAggregation(&gData.DataPoints)
|
lv := newPrecomputedLastValue[N](b.AggregationLimit, b.resFunc())
|
||||||
*dest = gData
|
switch b.Temporality {
|
||||||
|
case metricdata.DeltaTemporality:
|
||||||
return len(gData.DataPoints)
|
return b.filter(lv.measure), lv.delta
|
||||||
|
default:
|
||||||
|
return b.filter(lv.measure), lv.cumulative
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate provides aggregate types used compute aggregations and
|
// Package aggregate provides aggregate types used compute aggregations and
|
||||||
// cycle the state of metric measurements made by the SDK. These types and
|
// cycle the state of metric measurements made by the SDK. These types and
|
||||||
|
|
42
vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exemplar.go
generated
vendored
Normal file
42
vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exemplar.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||||
|
)
|
||||||
|
|
||||||
|
var exemplarPool = sync.Pool{
|
||||||
|
New: func() any { return new([]exemplar.Exemplar) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectExemplars[N int64 | float64](out *[]metricdata.Exemplar[N], f func(*[]exemplar.Exemplar)) {
|
||||||
|
dest := exemplarPool.Get().(*[]exemplar.Exemplar)
|
||||||
|
defer func() {
|
||||||
|
*dest = (*dest)[:0]
|
||||||
|
exemplarPool.Put(dest)
|
||||||
|
}()
|
||||||
|
|
||||||
|
*dest = reset(*dest, len(*out), cap(*out))
|
||||||
|
|
||||||
|
f(dest)
|
||||||
|
|
||||||
|
*out = reset(*out, len(*dest), cap(*dest))
|
||||||
|
for i, e := range *dest {
|
||||||
|
(*out)[i].FilteredAttributes = e.FilteredAttributes
|
||||||
|
(*out)[i].Time = e.Time
|
||||||
|
(*out)[i].SpanID = e.SpanID
|
||||||
|
(*out)[i].TraceID = e.TraceID
|
||||||
|
|
||||||
|
switch e.Value.Type() {
|
||||||
|
case exemplar.Int64ValueType:
|
||||||
|
(*out)[i].Value = N(e.Value.Int64())
|
||||||
|
case exemplar.Float64ValueType:
|
||||||
|
(*out)[i].Value = N(e.Value.Float64())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exponential_histogram.go
generated
vendored
165
vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exponential_histogram.go
generated
vendored
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
|
@ -41,7 +30,8 @@ const (
|
||||||
|
|
||||||
// expoHistogramDataPoint is a single data point in an exponential histogram.
|
// expoHistogramDataPoint is a single data point in an exponential histogram.
|
||||||
type expoHistogramDataPoint[N int64 | float64] struct {
|
type expoHistogramDataPoint[N int64 | float64] struct {
|
||||||
res exemplar.Reservoir[N]
|
attrs attribute.Set
|
||||||
|
res exemplar.FilteredReservoir[N]
|
||||||
|
|
||||||
count uint64
|
count uint64
|
||||||
min N
|
min N
|
||||||
|
@ -52,14 +42,14 @@ type expoHistogramDataPoint[N int64 | float64] struct {
|
||||||
noMinMax bool
|
noMinMax bool
|
||||||
noSum bool
|
noSum bool
|
||||||
|
|
||||||
scale int
|
scale int32
|
||||||
|
|
||||||
posBuckets expoBuckets
|
posBuckets expoBuckets
|
||||||
negBuckets expoBuckets
|
negBuckets expoBuckets
|
||||||
zeroCount uint64
|
zeroCount uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExpoHistogramDataPoint[N int64 | float64](maxSize, maxScale int, noMinMax, noSum bool) *expoHistogramDataPoint[N] {
|
func newExpoHistogramDataPoint[N int64 | float64](attrs attribute.Set, maxSize int, maxScale int32, noMinMax, noSum bool) *expoHistogramDataPoint[N] {
|
||||||
f := math.MaxFloat64
|
f := math.MaxFloat64
|
||||||
max := N(f) // if N is int64, max will overflow to -9223372036854775808
|
max := N(f) // if N is int64, max will overflow to -9223372036854775808
|
||||||
min := N(-f)
|
min := N(-f)
|
||||||
|
@ -68,6 +58,7 @@ func newExpoHistogramDataPoint[N int64 | float64](maxSize, maxScale int, noMinMa
|
||||||
min = N(minInt64)
|
min = N(minInt64)
|
||||||
}
|
}
|
||||||
return &expoHistogramDataPoint[N]{
|
return &expoHistogramDataPoint[N]{
|
||||||
|
attrs: attrs,
|
||||||
min: max,
|
min: max,
|
||||||
max: min,
|
max: min,
|
||||||
maxSize: maxSize,
|
maxSize: maxSize,
|
||||||
|
@ -128,11 +119,13 @@ func (p *expoHistogramDataPoint[N]) record(v N) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBin returns the bin v should be recorded into.
|
// getBin returns the bin v should be recorded into.
|
||||||
func (p *expoHistogramDataPoint[N]) getBin(v float64) int {
|
func (p *expoHistogramDataPoint[N]) getBin(v float64) int32 {
|
||||||
frac, exp := math.Frexp(v)
|
frac, expInt := math.Frexp(v)
|
||||||
|
// 11-bit exponential.
|
||||||
|
exp := int32(expInt) // nolint: gosec
|
||||||
if p.scale <= 0 {
|
if p.scale <= 0 {
|
||||||
// Because of the choice of fraction is always 1 power of two higher than we want.
|
// Because of the choice of fraction is always 1 power of two higher than we want.
|
||||||
correction := 1
|
var correction int32 = 1
|
||||||
if frac == .5 {
|
if frac == .5 {
|
||||||
// If v is an exact power of two the frac will be .5 and the exp
|
// If v is an exact power of two the frac will be .5 and the exp
|
||||||
// will be one higher than we want.
|
// will be one higher than we want.
|
||||||
|
@ -140,7 +133,7 @@ func (p *expoHistogramDataPoint[N]) getBin(v float64) int {
|
||||||
}
|
}
|
||||||
return (exp - correction) >> (-p.scale)
|
return (exp - correction) >> (-p.scale)
|
||||||
}
|
}
|
||||||
return exp<<p.scale + int(math.Log(frac)*scaleFactors[p.scale]) - 1
|
return exp<<p.scale + int32(math.Log(frac)*scaleFactors[p.scale]) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// scaleFactors are constants used in calculating the logarithm index. They are
|
// scaleFactors are constants used in calculating the logarithm index. They are
|
||||||
|
@ -171,20 +164,20 @@ var scaleFactors = [21]float64{
|
||||||
|
|
||||||
// scaleChange returns the magnitude of the scale change needed to fit bin in
|
// scaleChange returns the magnitude of the scale change needed to fit bin in
|
||||||
// the bucket. If no scale change is needed 0 is returned.
|
// the bucket. If no scale change is needed 0 is returned.
|
||||||
func (p *expoHistogramDataPoint[N]) scaleChange(bin, startBin, length int) int {
|
func (p *expoHistogramDataPoint[N]) scaleChange(bin, startBin int32, length int) int32 {
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
// No need to rescale if there are no buckets.
|
// No need to rescale if there are no buckets.
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
low := startBin
|
low := int(startBin)
|
||||||
high := bin
|
high := int(bin)
|
||||||
if startBin >= bin {
|
if startBin >= bin {
|
||||||
low = bin
|
low = int(bin)
|
||||||
high = startBin + length - 1
|
high = int(startBin) + length - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
var count int32
|
||||||
for high-low >= p.maxSize {
|
for high-low >= p.maxSize {
|
||||||
low = low >> 1
|
low = low >> 1
|
||||||
high = high >> 1
|
high = high >> 1
|
||||||
|
@ -198,39 +191,39 @@ func (p *expoHistogramDataPoint[N]) scaleChange(bin, startBin, length int) int {
|
||||||
|
|
||||||
// expoBuckets is a set of buckets in an exponential histogram.
|
// expoBuckets is a set of buckets in an exponential histogram.
|
||||||
type expoBuckets struct {
|
type expoBuckets struct {
|
||||||
startBin int
|
startBin int32
|
||||||
counts []uint64
|
counts []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// record increments the count for the given bin, and expands the buckets if needed.
|
// record increments the count for the given bin, and expands the buckets if needed.
|
||||||
// Size changes must be done before calling this function.
|
// Size changes must be done before calling this function.
|
||||||
func (b *expoBuckets) record(bin int) {
|
func (b *expoBuckets) record(bin int32) {
|
||||||
if len(b.counts) == 0 {
|
if len(b.counts) == 0 {
|
||||||
b.counts = []uint64{1}
|
b.counts = []uint64{1}
|
||||||
b.startBin = bin
|
b.startBin = bin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
endBin := b.startBin + len(b.counts) - 1
|
endBin := int(b.startBin) + len(b.counts) - 1
|
||||||
|
|
||||||
// if the new bin is inside the current range
|
// if the new bin is inside the current range
|
||||||
if bin >= b.startBin && bin <= endBin {
|
if bin >= b.startBin && int(bin) <= endBin {
|
||||||
b.counts[bin-b.startBin]++
|
b.counts[bin-b.startBin]++
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if the new bin is before the current start add spaces to the counts
|
// if the new bin is before the current start add spaces to the counts
|
||||||
if bin < b.startBin {
|
if bin < b.startBin {
|
||||||
origLen := len(b.counts)
|
origLen := len(b.counts)
|
||||||
newLength := endBin - bin + 1
|
newLength := endBin - int(bin) + 1
|
||||||
shift := b.startBin - bin
|
shift := b.startBin - bin
|
||||||
|
|
||||||
if newLength > cap(b.counts) {
|
if newLength > cap(b.counts) {
|
||||||
b.counts = append(b.counts, make([]uint64, newLength-len(b.counts))...)
|
b.counts = append(b.counts, make([]uint64, newLength-len(b.counts))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(b.counts[shift:origLen+shift], b.counts[:])
|
copy(b.counts[shift:origLen+int(shift)], b.counts[:])
|
||||||
b.counts = b.counts[:newLength]
|
b.counts = b.counts[:newLength]
|
||||||
for i := 1; i < shift; i++ {
|
for i := 1; i < int(shift); i++ {
|
||||||
b.counts[i] = 0
|
b.counts[i] = 0
|
||||||
}
|
}
|
||||||
b.startBin = bin
|
b.startBin = bin
|
||||||
|
@ -238,17 +231,17 @@ func (b *expoBuckets) record(bin int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if the new is after the end add spaces to the end
|
// if the new is after the end add spaces to the end
|
||||||
if bin > endBin {
|
if int(bin) > endBin {
|
||||||
if bin-b.startBin < cap(b.counts) {
|
if int(bin-b.startBin) < cap(b.counts) {
|
||||||
b.counts = b.counts[:bin-b.startBin+1]
|
b.counts = b.counts[:bin-b.startBin+1]
|
||||||
for i := endBin + 1 - b.startBin; i < len(b.counts); i++ {
|
for i := endBin + 1 - int(b.startBin); i < len(b.counts); i++ {
|
||||||
b.counts[i] = 0
|
b.counts[i] = 0
|
||||||
}
|
}
|
||||||
b.counts[bin-b.startBin] = 1
|
b.counts[bin-b.startBin] = 1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
end := make([]uint64, bin-b.startBin-len(b.counts)+1)
|
end := make([]uint64, int(bin-b.startBin)-len(b.counts)+1)
|
||||||
b.counts = append(b.counts, end...)
|
b.counts = append(b.counts, end...)
|
||||||
b.counts[bin-b.startBin] = 1
|
b.counts[bin-b.startBin] = 1
|
||||||
}
|
}
|
||||||
|
@ -256,7 +249,7 @@ func (b *expoBuckets) record(bin int) {
|
||||||
|
|
||||||
// downscale shrinks a bucket by a factor of 2*s. It will sum counts into the
|
// downscale shrinks a bucket by a factor of 2*s. It will sum counts into the
|
||||||
// correct lower resolution bucket.
|
// correct lower resolution bucket.
|
||||||
func (b *expoBuckets) downscale(delta int) {
|
func (b *expoBuckets) downscale(delta int32) {
|
||||||
// Example
|
// Example
|
||||||
// delta = 2
|
// delta = 2
|
||||||
// Original offset: -6
|
// Original offset: -6
|
||||||
|
@ -271,19 +264,19 @@ func (b *expoBuckets) downscale(delta int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
steps := 1 << delta
|
steps := int32(1) << delta
|
||||||
offset := b.startBin % steps
|
offset := b.startBin % steps
|
||||||
offset = (offset + steps) % steps // to make offset positive
|
offset = (offset + steps) % steps // to make offset positive
|
||||||
for i := 1; i < len(b.counts); i++ {
|
for i := 1; i < len(b.counts); i++ {
|
||||||
idx := i + offset
|
idx := i + int(offset)
|
||||||
if idx%steps == 0 {
|
if idx%int(steps) == 0 {
|
||||||
b.counts[idx/steps] = b.counts[i]
|
b.counts[idx/int(steps)] = b.counts[i]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.counts[idx/steps] += b.counts[i]
|
b.counts[idx/int(steps)] += b.counts[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
lastIdx := (len(b.counts) - 1 + offset) / steps
|
lastIdx := (len(b.counts) - 1 + int(offset)) / int(steps)
|
||||||
b.counts = b.counts[:lastIdx+1]
|
b.counts = b.counts[:lastIdx+1]
|
||||||
b.startBin = b.startBin >> delta
|
b.startBin = b.startBin >> delta
|
||||||
}
|
}
|
||||||
|
@ -291,16 +284,16 @@ func (b *expoBuckets) downscale(delta int) {
|
||||||
// newExponentialHistogram returns an Aggregator that summarizes a set of
|
// newExponentialHistogram returns an Aggregator that summarizes a set of
|
||||||
// measurements as an exponential histogram. Each histogram is scoped by attributes
|
// measurements as an exponential histogram. Each histogram is scoped by attributes
|
||||||
// and the aggregation cycle the measurements were made in.
|
// and the aggregation cycle the measurements were made in.
|
||||||
func newExponentialHistogram[N int64 | float64](maxSize, maxScale int32, noMinMax, noSum bool, limit int, r func() exemplar.Reservoir[N]) *expoHistogram[N] {
|
func newExponentialHistogram[N int64 | float64](maxSize, maxScale int32, noMinMax, noSum bool, limit int, r func() exemplar.FilteredReservoir[N]) *expoHistogram[N] {
|
||||||
return &expoHistogram[N]{
|
return &expoHistogram[N]{
|
||||||
noSum: noSum,
|
noSum: noSum,
|
||||||
noMinMax: noMinMax,
|
noMinMax: noMinMax,
|
||||||
maxSize: int(maxSize),
|
maxSize: int(maxSize),
|
||||||
maxScale: int(maxScale),
|
maxScale: maxScale,
|
||||||
|
|
||||||
newRes: r,
|
newRes: r,
|
||||||
limit: newLimiter[*expoHistogramDataPoint[N]](limit),
|
limit: newLimiter[*expoHistogramDataPoint[N]](limit),
|
||||||
values: make(map[attribute.Set]*expoHistogramDataPoint[N]),
|
values: make(map[attribute.Distinct]*expoHistogramDataPoint[N]),
|
||||||
|
|
||||||
start: now(),
|
start: now(),
|
||||||
}
|
}
|
||||||
|
@ -312,11 +305,11 @@ type expoHistogram[N int64 | float64] struct {
|
||||||
noSum bool
|
noSum bool
|
||||||
noMinMax bool
|
noMinMax bool
|
||||||
maxSize int
|
maxSize int
|
||||||
maxScale int
|
maxScale int32
|
||||||
|
|
||||||
newRes func() exemplar.Reservoir[N]
|
newRes func() exemplar.FilteredReservoir[N]
|
||||||
limit limiter[*expoHistogramDataPoint[N]]
|
limit limiter[*expoHistogramDataPoint[N]]
|
||||||
values map[attribute.Set]*expoHistogramDataPoint[N]
|
values map[attribute.Distinct]*expoHistogramDataPoint[N]
|
||||||
valuesMu sync.Mutex
|
valuesMu sync.Mutex
|
||||||
|
|
||||||
start time.Time
|
start time.Time
|
||||||
|
@ -328,21 +321,19 @@ func (e *expoHistogram[N]) measure(ctx context.Context, value N, fltrAttr attrib
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t := now()
|
|
||||||
|
|
||||||
e.valuesMu.Lock()
|
e.valuesMu.Lock()
|
||||||
defer e.valuesMu.Unlock()
|
defer e.valuesMu.Unlock()
|
||||||
|
|
||||||
attr := e.limit.Attributes(fltrAttr, e.values)
|
attr := e.limit.Attributes(fltrAttr, e.values)
|
||||||
v, ok := e.values[attr]
|
v, ok := e.values[attr.Equivalent()]
|
||||||
if !ok {
|
if !ok {
|
||||||
v = newExpoHistogramDataPoint[N](e.maxSize, e.maxScale, e.noMinMax, e.noSum)
|
v = newExpoHistogramDataPoint[N](attr, e.maxSize, e.maxScale, e.noMinMax, e.noSum)
|
||||||
v.res = e.newRes()
|
v.res = e.newRes()
|
||||||
|
|
||||||
e.values[attr] = v
|
e.values[attr.Equivalent()] = v
|
||||||
}
|
}
|
||||||
v.record(value)
|
v.record(value)
|
||||||
v.res.Offer(ctx, t, value, droppedAttr)
|
v.res.Offer(ctx, value, droppedAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *expoHistogram[N]) delta(dest *metricdata.Aggregation) int {
|
func (e *expoHistogram[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
|
@ -360,36 +351,38 @@ func (e *expoHistogram[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
hDPts := reset(h.DataPoints, n, n)
|
hDPts := reset(h.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for a, b := range e.values {
|
for _, val := range e.values {
|
||||||
hDPts[i].Attributes = a
|
hDPts[i].Attributes = val.attrs
|
||||||
hDPts[i].StartTime = e.start
|
hDPts[i].StartTime = e.start
|
||||||
hDPts[i].Time = t
|
hDPts[i].Time = t
|
||||||
hDPts[i].Count = b.count
|
hDPts[i].Count = val.count
|
||||||
hDPts[i].Scale = int32(b.scale)
|
hDPts[i].Scale = val.scale
|
||||||
hDPts[i].ZeroCount = b.zeroCount
|
hDPts[i].ZeroCount = val.zeroCount
|
||||||
hDPts[i].ZeroThreshold = 0.0
|
hDPts[i].ZeroThreshold = 0.0
|
||||||
|
|
||||||
hDPts[i].PositiveBucket.Offset = int32(b.posBuckets.startBin)
|
hDPts[i].PositiveBucket.Offset = val.posBuckets.startBin
|
||||||
hDPts[i].PositiveBucket.Counts = reset(hDPts[i].PositiveBucket.Counts, len(b.posBuckets.counts), len(b.posBuckets.counts))
|
hDPts[i].PositiveBucket.Counts = reset(hDPts[i].PositiveBucket.Counts, len(val.posBuckets.counts), len(val.posBuckets.counts))
|
||||||
copy(hDPts[i].PositiveBucket.Counts, b.posBuckets.counts)
|
copy(hDPts[i].PositiveBucket.Counts, val.posBuckets.counts)
|
||||||
|
|
||||||
hDPts[i].NegativeBucket.Offset = int32(b.negBuckets.startBin)
|
hDPts[i].NegativeBucket.Offset = val.negBuckets.startBin
|
||||||
hDPts[i].NegativeBucket.Counts = reset(hDPts[i].NegativeBucket.Counts, len(b.negBuckets.counts), len(b.negBuckets.counts))
|
hDPts[i].NegativeBucket.Counts = reset(hDPts[i].NegativeBucket.Counts, len(val.negBuckets.counts), len(val.negBuckets.counts))
|
||||||
copy(hDPts[i].NegativeBucket.Counts, b.negBuckets.counts)
|
copy(hDPts[i].NegativeBucket.Counts, val.negBuckets.counts)
|
||||||
|
|
||||||
if !e.noSum {
|
if !e.noSum {
|
||||||
hDPts[i].Sum = b.sum
|
hDPts[i].Sum = val.sum
|
||||||
}
|
}
|
||||||
if !e.noMinMax {
|
if !e.noMinMax {
|
||||||
hDPts[i].Min = metricdata.NewExtrema(b.min)
|
hDPts[i].Min = metricdata.NewExtrema(val.min)
|
||||||
hDPts[i].Max = metricdata.NewExtrema(b.max)
|
hDPts[i].Max = metricdata.NewExtrema(val.max)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.res.Collect(&hDPts[i].Exemplars)
|
collectExemplars(&hDPts[i].Exemplars, val.res.Collect)
|
||||||
|
|
||||||
delete(e.values, a)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
// Unused attribute sets do not report.
|
||||||
|
clear(e.values)
|
||||||
|
|
||||||
e.start = t
|
e.start = t
|
||||||
h.DataPoints = hDPts
|
h.DataPoints = hDPts
|
||||||
*dest = h
|
*dest = h
|
||||||
|
@ -411,32 +404,32 @@ func (e *expoHistogram[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
hDPts := reset(h.DataPoints, n, n)
|
hDPts := reset(h.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for a, b := range e.values {
|
for _, val := range e.values {
|
||||||
hDPts[i].Attributes = a
|
hDPts[i].Attributes = val.attrs
|
||||||
hDPts[i].StartTime = e.start
|
hDPts[i].StartTime = e.start
|
||||||
hDPts[i].Time = t
|
hDPts[i].Time = t
|
||||||
hDPts[i].Count = b.count
|
hDPts[i].Count = val.count
|
||||||
hDPts[i].Scale = int32(b.scale)
|
hDPts[i].Scale = val.scale
|
||||||
hDPts[i].ZeroCount = b.zeroCount
|
hDPts[i].ZeroCount = val.zeroCount
|
||||||
hDPts[i].ZeroThreshold = 0.0
|
hDPts[i].ZeroThreshold = 0.0
|
||||||
|
|
||||||
hDPts[i].PositiveBucket.Offset = int32(b.posBuckets.startBin)
|
hDPts[i].PositiveBucket.Offset = val.posBuckets.startBin
|
||||||
hDPts[i].PositiveBucket.Counts = reset(hDPts[i].PositiveBucket.Counts, len(b.posBuckets.counts), len(b.posBuckets.counts))
|
hDPts[i].PositiveBucket.Counts = reset(hDPts[i].PositiveBucket.Counts, len(val.posBuckets.counts), len(val.posBuckets.counts))
|
||||||
copy(hDPts[i].PositiveBucket.Counts, b.posBuckets.counts)
|
copy(hDPts[i].PositiveBucket.Counts, val.posBuckets.counts)
|
||||||
|
|
||||||
hDPts[i].NegativeBucket.Offset = int32(b.negBuckets.startBin)
|
hDPts[i].NegativeBucket.Offset = val.negBuckets.startBin
|
||||||
hDPts[i].NegativeBucket.Counts = reset(hDPts[i].NegativeBucket.Counts, len(b.negBuckets.counts), len(b.negBuckets.counts))
|
hDPts[i].NegativeBucket.Counts = reset(hDPts[i].NegativeBucket.Counts, len(val.negBuckets.counts), len(val.negBuckets.counts))
|
||||||
copy(hDPts[i].NegativeBucket.Counts, b.negBuckets.counts)
|
copy(hDPts[i].NegativeBucket.Counts, val.negBuckets.counts)
|
||||||
|
|
||||||
if !e.noSum {
|
if !e.noSum {
|
||||||
hDPts[i].Sum = b.sum
|
hDPts[i].Sum = val.sum
|
||||||
}
|
}
|
||||||
if !e.noMinMax {
|
if !e.noMinMax {
|
||||||
hDPts[i].Min = metricdata.NewExtrema(b.min)
|
hDPts[i].Min = metricdata.NewExtrema(val.min)
|
||||||
hDPts[i].Max = metricdata.NewExtrema(b.max)
|
hDPts[i].Max = metricdata.NewExtrema(val.max)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.res.Collect(&hDPts[i].Exemplars)
|
collectExemplars(&hDPts[i].Exemplars, val.res.Collect)
|
||||||
|
|
||||||
i++
|
i++
|
||||||
// TODO (#3006): This will use an unbounded amount of memory if there
|
// TODO (#3006): This will use an unbounded amount of memory if there
|
||||||
|
|
|
@ -1,21 +1,11 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -26,7 +16,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type buckets[N int64 | float64] struct {
|
type buckets[N int64 | float64] struct {
|
||||||
res exemplar.Reservoir[N]
|
attrs attribute.Set
|
||||||
|
res exemplar.FilteredReservoir[N]
|
||||||
|
|
||||||
counts []uint64
|
counts []uint64
|
||||||
count uint64
|
count uint64
|
||||||
|
@ -35,8 +26,8 @@ type buckets[N int64 | float64] struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBuckets returns buckets with n bins.
|
// newBuckets returns buckets with n bins.
|
||||||
func newBuckets[N int64 | float64](n int) *buckets[N] {
|
func newBuckets[N int64 | float64](attrs attribute.Set, n int) *buckets[N] {
|
||||||
return &buckets[N]{counts: make([]uint64, n)}
|
return &buckets[N]{attrs: attrs, counts: make([]uint64, n)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *buckets[N]) sum(value N) { b.total += value }
|
func (b *buckets[N]) sum(value N) { b.total += value }
|
||||||
|
@ -57,26 +48,25 @@ type histValues[N int64 | float64] struct {
|
||||||
noSum bool
|
noSum bool
|
||||||
bounds []float64
|
bounds []float64
|
||||||
|
|
||||||
newRes func() exemplar.Reservoir[N]
|
newRes func() exemplar.FilteredReservoir[N]
|
||||||
limit limiter[*buckets[N]]
|
limit limiter[*buckets[N]]
|
||||||
values map[attribute.Set]*buckets[N]
|
values map[attribute.Distinct]*buckets[N]
|
||||||
valuesMu sync.Mutex
|
valuesMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHistValues[N int64 | float64](bounds []float64, noSum bool, limit int, r func() exemplar.Reservoir[N]) *histValues[N] {
|
func newHistValues[N int64 | float64](bounds []float64, noSum bool, limit int, r func() exemplar.FilteredReservoir[N]) *histValues[N] {
|
||||||
// The responsibility of keeping all buckets correctly associated with the
|
// The responsibility of keeping all buckets correctly associated with the
|
||||||
// passed boundaries is ultimately this type's responsibility. Make a copy
|
// passed boundaries is ultimately this type's responsibility. Make a copy
|
||||||
// here so we can always guarantee this. Or, in the case of failure, have
|
// here so we can always guarantee this. Or, in the case of failure, have
|
||||||
// complete control over the fix.
|
// complete control over the fix.
|
||||||
b := make([]float64, len(bounds))
|
b := slices.Clone(bounds)
|
||||||
copy(b, bounds)
|
slices.Sort(b)
|
||||||
sort.Float64s(b)
|
|
||||||
return &histValues[N]{
|
return &histValues[N]{
|
||||||
noSum: noSum,
|
noSum: noSum,
|
||||||
bounds: b,
|
bounds: b,
|
||||||
newRes: r,
|
newRes: r,
|
||||||
limit: newLimiter[*buckets[N]](limit),
|
limit: newLimiter[*buckets[N]](limit),
|
||||||
values: make(map[attribute.Set]*buckets[N]),
|
values: make(map[attribute.Distinct]*buckets[N]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,13 +80,11 @@ func (s *histValues[N]) measure(ctx context.Context, value N, fltrAttr attribute
|
||||||
// (s.bounds[len(s.bounds)-1], +∞).
|
// (s.bounds[len(s.bounds)-1], +∞).
|
||||||
idx := sort.SearchFloat64s(s.bounds, float64(value))
|
idx := sort.SearchFloat64s(s.bounds, float64(value))
|
||||||
|
|
||||||
t := now()
|
|
||||||
|
|
||||||
s.valuesMu.Lock()
|
s.valuesMu.Lock()
|
||||||
defer s.valuesMu.Unlock()
|
defer s.valuesMu.Unlock()
|
||||||
|
|
||||||
attr := s.limit.Attributes(fltrAttr, s.values)
|
attr := s.limit.Attributes(fltrAttr, s.values)
|
||||||
b, ok := s.values[attr]
|
b, ok := s.values[attr.Equivalent()]
|
||||||
if !ok {
|
if !ok {
|
||||||
// N+1 buckets. For example:
|
// N+1 buckets. For example:
|
||||||
//
|
//
|
||||||
|
@ -105,23 +93,23 @@ func (s *histValues[N]) measure(ctx context.Context, value N, fltrAttr attribute
|
||||||
// Then,
|
// Then,
|
||||||
//
|
//
|
||||||
// buckets = (-∞, 0], (0, 5.0], (5.0, 10.0], (10.0, +∞)
|
// buckets = (-∞, 0], (0, 5.0], (5.0, 10.0], (10.0, +∞)
|
||||||
b = newBuckets[N](len(s.bounds) + 1)
|
b = newBuckets[N](attr, len(s.bounds)+1)
|
||||||
b.res = s.newRes()
|
b.res = s.newRes()
|
||||||
|
|
||||||
// Ensure min and max are recorded values (not zero), for new buckets.
|
// Ensure min and max are recorded values (not zero), for new buckets.
|
||||||
b.min, b.max = value, value
|
b.min, b.max = value, value
|
||||||
s.values[attr] = b
|
s.values[attr.Equivalent()] = b
|
||||||
}
|
}
|
||||||
b.bin(idx, value)
|
b.bin(idx, value)
|
||||||
if !s.noSum {
|
if !s.noSum {
|
||||||
b.sum(value)
|
b.sum(value)
|
||||||
}
|
}
|
||||||
b.res.Offer(ctx, t, value, droppedAttr)
|
b.res.Offer(ctx, value, droppedAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHistogram returns an Aggregator that summarizes a set of measurements as
|
// newHistogram returns an Aggregator that summarizes a set of measurements as
|
||||||
// an histogram.
|
// an histogram.
|
||||||
func newHistogram[N int64 | float64](boundaries []float64, noMinMax, noSum bool, limit int, r func() exemplar.Reservoir[N]) *histogram[N] {
|
func newHistogram[N int64 | float64](boundaries []float64, noMinMax, noSum bool, limit int, r func() exemplar.FilteredReservoir[N]) *histogram[N] {
|
||||||
return &histogram[N]{
|
return &histogram[N]{
|
||||||
histValues: newHistValues[N](boundaries, noSum, limit, r),
|
histValues: newHistValues[N](boundaries, noSum, limit, r),
|
||||||
noMinMax: noMinMax,
|
noMinMax: noMinMax,
|
||||||
|
@ -150,36 +138,35 @@ func (s *histogram[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
defer s.valuesMu.Unlock()
|
defer s.valuesMu.Unlock()
|
||||||
|
|
||||||
// Do not allow modification of our copy of bounds.
|
// Do not allow modification of our copy of bounds.
|
||||||
bounds := make([]float64, len(s.bounds))
|
bounds := slices.Clone(s.bounds)
|
||||||
copy(bounds, s.bounds)
|
|
||||||
|
|
||||||
n := len(s.values)
|
n := len(s.values)
|
||||||
hDPts := reset(h.DataPoints, n, n)
|
hDPts := reset(h.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for a, b := range s.values {
|
for _, val := range s.values {
|
||||||
hDPts[i].Attributes = a
|
hDPts[i].Attributes = val.attrs
|
||||||
hDPts[i].StartTime = s.start
|
hDPts[i].StartTime = s.start
|
||||||
hDPts[i].Time = t
|
hDPts[i].Time = t
|
||||||
hDPts[i].Count = b.count
|
hDPts[i].Count = val.count
|
||||||
hDPts[i].Bounds = bounds
|
hDPts[i].Bounds = bounds
|
||||||
hDPts[i].BucketCounts = b.counts
|
hDPts[i].BucketCounts = val.counts
|
||||||
|
|
||||||
if !s.noSum {
|
if !s.noSum {
|
||||||
hDPts[i].Sum = b.total
|
hDPts[i].Sum = val.total
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.noMinMax {
|
if !s.noMinMax {
|
||||||
hDPts[i].Min = metricdata.NewExtrema(b.min)
|
hDPts[i].Min = metricdata.NewExtrema(val.min)
|
||||||
hDPts[i].Max = metricdata.NewExtrema(b.max)
|
hDPts[i].Max = metricdata.NewExtrema(val.max)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.res.Collect(&hDPts[i].Exemplars)
|
collectExemplars(&hDPts[i].Exemplars, val.res.Collect)
|
||||||
|
|
||||||
// Unused attribute sets do not report.
|
|
||||||
delete(s.values, a)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
// Unused attribute sets do not report.
|
||||||
|
clear(s.values)
|
||||||
// The delta collection cycle resets.
|
// The delta collection cycle resets.
|
||||||
s.start = t
|
s.start = t
|
||||||
|
|
||||||
|
@ -201,39 +188,36 @@ func (s *histogram[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
defer s.valuesMu.Unlock()
|
defer s.valuesMu.Unlock()
|
||||||
|
|
||||||
// Do not allow modification of our copy of bounds.
|
// Do not allow modification of our copy of bounds.
|
||||||
bounds := make([]float64, len(s.bounds))
|
bounds := slices.Clone(s.bounds)
|
||||||
copy(bounds, s.bounds)
|
|
||||||
|
|
||||||
n := len(s.values)
|
n := len(s.values)
|
||||||
hDPts := reset(h.DataPoints, n, n)
|
hDPts := reset(h.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for a, b := range s.values {
|
for _, val := range s.values {
|
||||||
|
hDPts[i].Attributes = val.attrs
|
||||||
|
hDPts[i].StartTime = s.start
|
||||||
|
hDPts[i].Time = t
|
||||||
|
hDPts[i].Count = val.count
|
||||||
|
hDPts[i].Bounds = bounds
|
||||||
|
|
||||||
// The HistogramDataPoint field values returned need to be copies of
|
// The HistogramDataPoint field values returned need to be copies of
|
||||||
// the buckets value as we will keep updating them.
|
// the buckets value as we will keep updating them.
|
||||||
//
|
//
|
||||||
// TODO (#3047): Making copies for bounds and counts incurs a large
|
// TODO (#3047): Making copies for bounds and counts incurs a large
|
||||||
// memory allocation footprint. Alternatives should be explored.
|
// memory allocation footprint. Alternatives should be explored.
|
||||||
counts := make([]uint64, len(b.counts))
|
hDPts[i].BucketCounts = slices.Clone(val.counts)
|
||||||
copy(counts, b.counts)
|
|
||||||
|
|
||||||
hDPts[i].Attributes = a
|
|
||||||
hDPts[i].StartTime = s.start
|
|
||||||
hDPts[i].Time = t
|
|
||||||
hDPts[i].Count = b.count
|
|
||||||
hDPts[i].Bounds = bounds
|
|
||||||
hDPts[i].BucketCounts = counts
|
|
||||||
|
|
||||||
if !s.noSum {
|
if !s.noSum {
|
||||||
hDPts[i].Sum = b.total
|
hDPts[i].Sum = val.total
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.noMinMax {
|
if !s.noMinMax {
|
||||||
hDPts[i].Min = metricdata.NewExtrema(b.min)
|
hDPts[i].Min = metricdata.NewExtrema(val.min)
|
||||||
hDPts[i].Max = metricdata.NewExtrema(b.max)
|
hDPts[i].Max = metricdata.NewExtrema(val.max)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.res.Collect(&hDPts[i].Exemplars)
|
collectExemplars(&hDPts[i].Exemplars, val.res.Collect)
|
||||||
|
|
||||||
i++
|
i++
|
||||||
// TODO (#3006): This will use an unbounded amount of memory if there
|
// TODO (#3006): This will use an unbounded amount of memory if there
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
|
@ -26,16 +15,17 @@ import (
|
||||||
|
|
||||||
// datapoint is timestamped measurement data.
|
// datapoint is timestamped measurement data.
|
||||||
type datapoint[N int64 | float64] struct {
|
type datapoint[N int64 | float64] struct {
|
||||||
timestamp time.Time
|
attrs attribute.Set
|
||||||
value N
|
value N
|
||||||
res exemplar.Reservoir[N]
|
res exemplar.FilteredReservoir[N]
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLastValue[N int64 | float64](limit int, r func() exemplar.Reservoir[N]) *lastValue[N] {
|
func newLastValue[N int64 | float64](limit int, r func() exemplar.FilteredReservoir[N]) *lastValue[N] {
|
||||||
return &lastValue[N]{
|
return &lastValue[N]{
|
||||||
newRes: r,
|
newRes: r,
|
||||||
limit: newLimiter[datapoint[N]](limit),
|
limit: newLimiter[datapoint[N]](limit),
|
||||||
values: make(map[attribute.Set]datapoint[N]),
|
values: make(map[attribute.Distinct]datapoint[N]),
|
||||||
|
start: now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,47 +33,130 @@ func newLastValue[N int64 | float64](limit int, r func() exemplar.Reservoir[N])
|
||||||
type lastValue[N int64 | float64] struct {
|
type lastValue[N int64 | float64] struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
newRes func() exemplar.Reservoir[N]
|
newRes func() exemplar.FilteredReservoir[N]
|
||||||
limit limiter[datapoint[N]]
|
limit limiter[datapoint[N]]
|
||||||
values map[attribute.Set]datapoint[N]
|
values map[attribute.Distinct]datapoint[N]
|
||||||
|
start time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *lastValue[N]) measure(ctx context.Context, value N, fltrAttr attribute.Set, droppedAttr []attribute.KeyValue) {
|
func (s *lastValue[N]) measure(ctx context.Context, value N, fltrAttr attribute.Set, droppedAttr []attribute.KeyValue) {
|
||||||
t := now()
|
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
attr := s.limit.Attributes(fltrAttr, s.values)
|
attr := s.limit.Attributes(fltrAttr, s.values)
|
||||||
d, ok := s.values[attr]
|
d, ok := s.values[attr.Equivalent()]
|
||||||
if !ok {
|
if !ok {
|
||||||
d.res = s.newRes()
|
d.res = s.newRes()
|
||||||
}
|
}
|
||||||
|
|
||||||
d.timestamp = t
|
d.attrs = attr
|
||||||
d.value = value
|
d.value = value
|
||||||
d.res.Offer(ctx, t, value, droppedAttr)
|
d.res.Offer(ctx, value, droppedAttr)
|
||||||
|
|
||||||
s.values[attr] = d
|
s.values[attr.Equivalent()] = d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *lastValue[N]) computeAggregation(dest *[]metricdata.DataPoint[N]) {
|
func (s *lastValue[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
|
t := now()
|
||||||
|
// Ignore if dest is not a metricdata.Gauge. The chance for memory reuse of
|
||||||
|
// the DataPoints is missed (better luck next time).
|
||||||
|
gData, _ := (*dest).(metricdata.Gauge[N])
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
|
n := s.copyDpts(&gData.DataPoints, t)
|
||||||
|
// Do not report stale values.
|
||||||
|
clear(s.values)
|
||||||
|
// Update start time for delta temporality.
|
||||||
|
s.start = t
|
||||||
|
|
||||||
|
*dest = gData
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *lastValue[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
|
t := now()
|
||||||
|
// Ignore if dest is not a metricdata.Gauge. The chance for memory reuse of
|
||||||
|
// the DataPoints is missed (better luck next time).
|
||||||
|
gData, _ := (*dest).(metricdata.Gauge[N])
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
n := s.copyDpts(&gData.DataPoints, t)
|
||||||
|
// TODO (#3006): This will use an unbounded amount of memory if there
|
||||||
|
// are unbounded number of attribute sets being aggregated. Attribute
|
||||||
|
// sets that become "stale" need to be forgotten so this will not
|
||||||
|
// overload the system.
|
||||||
|
*dest = gData
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyDpts copies the datapoints held by s into dest. The number of datapoints
|
||||||
|
// copied is returned.
|
||||||
|
func (s *lastValue[N]) copyDpts(dest *[]metricdata.DataPoint[N], t time.Time) int {
|
||||||
n := len(s.values)
|
n := len(s.values)
|
||||||
*dest = reset(*dest, n, n)
|
*dest = reset(*dest, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for a, v := range s.values {
|
for _, v := range s.values {
|
||||||
(*dest)[i].Attributes = a
|
(*dest)[i].Attributes = v.attrs
|
||||||
// The event time is the only meaningful timestamp, StartTime is
|
(*dest)[i].StartTime = s.start
|
||||||
// ignored.
|
(*dest)[i].Time = t
|
||||||
(*dest)[i].Time = v.timestamp
|
|
||||||
(*dest)[i].Value = v.value
|
(*dest)[i].Value = v.value
|
||||||
v.res.Collect(&(*dest)[i].Exemplars)
|
collectExemplars(&(*dest)[i].Exemplars, v.res.Collect)
|
||||||
// Do not report stale values.
|
|
||||||
delete(s.values, a)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPrecomputedLastValue returns an aggregator that summarizes a set of
|
||||||
|
// observations as the last one made.
|
||||||
|
func newPrecomputedLastValue[N int64 | float64](limit int, r func() exemplar.FilteredReservoir[N]) *precomputedLastValue[N] {
|
||||||
|
return &precomputedLastValue[N]{lastValue: newLastValue[N](limit, r)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// precomputedLastValue summarizes a set of observations as the last one made.
|
||||||
|
type precomputedLastValue[N int64 | float64] struct {
|
||||||
|
*lastValue[N]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *precomputedLastValue[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
|
t := now()
|
||||||
|
// Ignore if dest is not a metricdata.Gauge. The chance for memory reuse of
|
||||||
|
// the DataPoints is missed (better luck next time).
|
||||||
|
gData, _ := (*dest).(metricdata.Gauge[N])
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
n := s.copyDpts(&gData.DataPoints, t)
|
||||||
|
// Do not report stale values.
|
||||||
|
clear(s.values)
|
||||||
|
// Update start time for delta temporality.
|
||||||
|
s.start = t
|
||||||
|
|
||||||
|
*dest = gData
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *precomputedLastValue[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
|
t := now()
|
||||||
|
// Ignore if dest is not a metricdata.Gauge. The chance for memory reuse of
|
||||||
|
// the DataPoints is missed (better luck next time).
|
||||||
|
gData, _ := (*dest).(metricdata.Gauge[N])
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
n := s.copyDpts(&gData.DataPoints, t)
|
||||||
|
// Do not report stale values.
|
||||||
|
clear(s.values)
|
||||||
|
*dest = gData
|
||||||
|
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
|
@ -41,9 +30,9 @@ func newLimiter[V any](aggregation int) limiter[V] {
|
||||||
// aggregation cardinality limit for the existing measurements. If it will,
|
// aggregation cardinality limit for the existing measurements. If it will,
|
||||||
// overflowSet is returned. Otherwise, if it will not exceed the limit, or the
|
// overflowSet is returned. Otherwise, if it will not exceed the limit, or the
|
||||||
// limit is not set (limit <= 0), attr is returned.
|
// limit is not set (limit <= 0), attr is returned.
|
||||||
func (l limiter[V]) Attributes(attrs attribute.Set, measurements map[attribute.Set]V) attribute.Set {
|
func (l limiter[V]) Attributes(attrs attribute.Set, measurements map[attribute.Distinct]V) attribute.Set {
|
||||||
if l.aggLimit > 0 {
|
if l.aggLimit > 0 {
|
||||||
_, exists := measurements[attrs]
|
_, exists := measurements[attrs.Equivalent()]
|
||||||
if !exists && len(measurements) >= l.aggLimit-1 {
|
if !exists && len(measurements) >= l.aggLimit-1 {
|
||||||
return overflowSet
|
return overflowSet
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
||||||
|
|
||||||
|
@ -26,47 +15,47 @@ import (
|
||||||
|
|
||||||
type sumValue[N int64 | float64] struct {
|
type sumValue[N int64 | float64] struct {
|
||||||
n N
|
n N
|
||||||
res exemplar.Reservoir[N]
|
res exemplar.FilteredReservoir[N]
|
||||||
|
attrs attribute.Set
|
||||||
}
|
}
|
||||||
|
|
||||||
// valueMap is the storage for sums.
|
// valueMap is the storage for sums.
|
||||||
type valueMap[N int64 | float64] struct {
|
type valueMap[N int64 | float64] struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
newRes func() exemplar.Reservoir[N]
|
newRes func() exemplar.FilteredReservoir[N]
|
||||||
limit limiter[sumValue[N]]
|
limit limiter[sumValue[N]]
|
||||||
values map[attribute.Set]sumValue[N]
|
values map[attribute.Distinct]sumValue[N]
|
||||||
}
|
}
|
||||||
|
|
||||||
func newValueMap[N int64 | float64](limit int, r func() exemplar.Reservoir[N]) *valueMap[N] {
|
func newValueMap[N int64 | float64](limit int, r func() exemplar.FilteredReservoir[N]) *valueMap[N] {
|
||||||
return &valueMap[N]{
|
return &valueMap[N]{
|
||||||
newRes: r,
|
newRes: r,
|
||||||
limit: newLimiter[sumValue[N]](limit),
|
limit: newLimiter[sumValue[N]](limit),
|
||||||
values: make(map[attribute.Set]sumValue[N]),
|
values: make(map[attribute.Distinct]sumValue[N]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *valueMap[N]) measure(ctx context.Context, value N, fltrAttr attribute.Set, droppedAttr []attribute.KeyValue) {
|
func (s *valueMap[N]) measure(ctx context.Context, value N, fltrAttr attribute.Set, droppedAttr []attribute.KeyValue) {
|
||||||
t := now()
|
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
attr := s.limit.Attributes(fltrAttr, s.values)
|
attr := s.limit.Attributes(fltrAttr, s.values)
|
||||||
v, ok := s.values[attr]
|
v, ok := s.values[attr.Equivalent()]
|
||||||
if !ok {
|
if !ok {
|
||||||
v.res = s.newRes()
|
v.res = s.newRes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.attrs = attr
|
||||||
v.n += value
|
v.n += value
|
||||||
v.res.Offer(ctx, t, value, droppedAttr)
|
v.res.Offer(ctx, value, droppedAttr)
|
||||||
|
|
||||||
s.values[attr] = v
|
s.values[attr.Equivalent()] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSum returns an aggregator that summarizes a set of measurements as their
|
// newSum returns an aggregator that summarizes a set of measurements as their
|
||||||
// arithmetic sum. Each sum is scoped by attributes and the aggregation cycle
|
// arithmetic sum. Each sum is scoped by attributes and the aggregation cycle
|
||||||
// the measurements were made in.
|
// the measurements were made in.
|
||||||
func newSum[N int64 | float64](monotonic bool, limit int, r func() exemplar.Reservoir[N]) *sum[N] {
|
func newSum[N int64 | float64](monotonic bool, limit int, r func() exemplar.FilteredReservoir[N]) *sum[N] {
|
||||||
return &sum[N]{
|
return &sum[N]{
|
||||||
valueMap: newValueMap[N](limit, r),
|
valueMap: newValueMap[N](limit, r),
|
||||||
monotonic: monotonic,
|
monotonic: monotonic,
|
||||||
|
@ -98,16 +87,16 @@ func (s *sum[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
dPts := reset(sData.DataPoints, n, n)
|
dPts := reset(sData.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for attr, val := range s.values {
|
for _, val := range s.values {
|
||||||
dPts[i].Attributes = attr
|
dPts[i].Attributes = val.attrs
|
||||||
dPts[i].StartTime = s.start
|
dPts[i].StartTime = s.start
|
||||||
dPts[i].Time = t
|
dPts[i].Time = t
|
||||||
dPts[i].Value = val.n
|
dPts[i].Value = val.n
|
||||||
val.res.Collect(&dPts[i].Exemplars)
|
collectExemplars(&dPts[i].Exemplars, val.res.Collect)
|
||||||
// Do not report stale values.
|
|
||||||
delete(s.values, attr)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
// Do not report stale values.
|
||||||
|
clear(s.values)
|
||||||
// The delta collection cycle resets.
|
// The delta collection cycle resets.
|
||||||
s.start = t
|
s.start = t
|
||||||
|
|
||||||
|
@ -133,12 +122,12 @@ func (s *sum[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
dPts := reset(sData.DataPoints, n, n)
|
dPts := reset(sData.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for attr, value := range s.values {
|
for _, value := range s.values {
|
||||||
dPts[i].Attributes = attr
|
dPts[i].Attributes = value.attrs
|
||||||
dPts[i].StartTime = s.start
|
dPts[i].StartTime = s.start
|
||||||
dPts[i].Time = t
|
dPts[i].Time = t
|
||||||
dPts[i].Value = value.n
|
dPts[i].Value = value.n
|
||||||
value.res.Collect(&dPts[i].Exemplars)
|
collectExemplars(&dPts[i].Exemplars, value.res.Collect)
|
||||||
// TODO (#3006): This will use an unbounded amount of memory if there
|
// TODO (#3006): This will use an unbounded amount of memory if there
|
||||||
// are unbounded number of attribute sets being aggregated. Attribute
|
// are unbounded number of attribute sets being aggregated. Attribute
|
||||||
// sets that become "stale" need to be forgotten so this will not
|
// sets that become "stale" need to be forgotten so this will not
|
||||||
|
@ -155,7 +144,7 @@ func (s *sum[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
// newPrecomputedSum returns an aggregator that summarizes a set of
|
// newPrecomputedSum returns an aggregator that summarizes a set of
|
||||||
// observatrions as their arithmetic sum. Each sum is scoped by attributes and
|
// observatrions as their arithmetic sum. Each sum is scoped by attributes and
|
||||||
// the aggregation cycle the measurements were made in.
|
// the aggregation cycle the measurements were made in.
|
||||||
func newPrecomputedSum[N int64 | float64](monotonic bool, limit int, r func() exemplar.Reservoir[N]) *precomputedSum[N] {
|
func newPrecomputedSum[N int64 | float64](monotonic bool, limit int, r func() exemplar.FilteredReservoir[N]) *precomputedSum[N] {
|
||||||
return &precomputedSum[N]{
|
return &precomputedSum[N]{
|
||||||
valueMap: newValueMap[N](limit, r),
|
valueMap: newValueMap[N](limit, r),
|
||||||
monotonic: monotonic,
|
monotonic: monotonic,
|
||||||
|
@ -170,12 +159,12 @@ type precomputedSum[N int64 | float64] struct {
|
||||||
monotonic bool
|
monotonic bool
|
||||||
start time.Time
|
start time.Time
|
||||||
|
|
||||||
reported map[attribute.Set]N
|
reported map[attribute.Distinct]N
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *precomputedSum[N]) delta(dest *metricdata.Aggregation) int {
|
func (s *precomputedSum[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
t := now()
|
t := now()
|
||||||
newReported := make(map[attribute.Set]N)
|
newReported := make(map[attribute.Distinct]N)
|
||||||
|
|
||||||
// If *dest is not a metricdata.Sum, memory reuse is missed. In that case,
|
// If *dest is not a metricdata.Sum, memory reuse is missed. In that case,
|
||||||
// use the zero-value sData and hope for better alignment next cycle.
|
// use the zero-value sData and hope for better alignment next cycle.
|
||||||
|
@ -190,21 +179,20 @@ func (s *precomputedSum[N]) delta(dest *metricdata.Aggregation) int {
|
||||||
dPts := reset(sData.DataPoints, n, n)
|
dPts := reset(sData.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for attr, value := range s.values {
|
for key, value := range s.values {
|
||||||
delta := value.n - s.reported[attr]
|
delta := value.n - s.reported[key]
|
||||||
|
|
||||||
dPts[i].Attributes = attr
|
dPts[i].Attributes = value.attrs
|
||||||
dPts[i].StartTime = s.start
|
dPts[i].StartTime = s.start
|
||||||
dPts[i].Time = t
|
dPts[i].Time = t
|
||||||
dPts[i].Value = delta
|
dPts[i].Value = delta
|
||||||
value.res.Collect(&dPts[i].Exemplars)
|
collectExemplars(&dPts[i].Exemplars, value.res.Collect)
|
||||||
|
|
||||||
newReported[attr] = value.n
|
newReported[key] = value.n
|
||||||
// Unused attribute sets do not report.
|
|
||||||
delete(s.values, attr)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
// Unused attribute sets are forgotten.
|
// Unused attribute sets do not report.
|
||||||
|
clear(s.values)
|
||||||
s.reported = newReported
|
s.reported = newReported
|
||||||
// The delta collection cycle resets.
|
// The delta collection cycle resets.
|
||||||
s.start = t
|
s.start = t
|
||||||
|
@ -231,17 +219,17 @@ func (s *precomputedSum[N]) cumulative(dest *metricdata.Aggregation) int {
|
||||||
dPts := reset(sData.DataPoints, n, n)
|
dPts := reset(sData.DataPoints, n, n)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for attr, val := range s.values {
|
for _, val := range s.values {
|
||||||
dPts[i].Attributes = attr
|
dPts[i].Attributes = val.attrs
|
||||||
dPts[i].StartTime = s.start
|
dPts[i].StartTime = s.start
|
||||||
dPts[i].Time = t
|
dPts[i].Time = t
|
||||||
dPts[i].Value = val.n
|
dPts[i].Value = val.n
|
||||||
val.res.Collect(&dPts[i].Exemplars)
|
collectExemplars(&dPts[i].Exemplars, val.res.Collect)
|
||||||
|
|
||||||
// Unused attribute sets do not report.
|
|
||||||
delete(s.values, attr)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
// Unused attribute sets do not report.
|
||||||
|
clear(s.values)
|
||||||
|
|
||||||
sData.DataPoints = dPts
|
sData.DataPoints = dPts
|
||||||
*dest = sData
|
*dest = sData
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar provides an implementation of the OpenTelemetry exemplar
|
// Package exemplar provides an implementation of the OpenTelemetry exemplar
|
||||||
// reservoir to be used in metric collection pipelines.
|
// reservoir to be used in metric collection pipelines.
|
||||||
|
|
|
@ -1,36 +1,23 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Drop returns a [Reservoir] that drops all measurements it is offered.
|
// Drop returns a [FilteredReservoir] that drops all measurements it is offered.
|
||||||
func Drop[N int64 | float64]() Reservoir[N] { return &dropRes[N]{} }
|
func Drop[N int64 | float64]() FilteredReservoir[N] { return &dropRes[N]{} }
|
||||||
|
|
||||||
type dropRes[N int64 | float64] struct{}
|
type dropRes[N int64 | float64] struct{}
|
||||||
|
|
||||||
// Offer does nothing, all measurements offered will be dropped.
|
// Offer does nothing, all measurements offered will be dropped.
|
||||||
func (r *dropRes[N]) Offer(context.Context, time.Time, N, []attribute.KeyValue) {}
|
func (r *dropRes[N]) Offer(context.Context, N, []attribute.KeyValue) {}
|
||||||
|
|
||||||
// Collect resets dest. No exemplars will ever be returned.
|
// Collect resets dest. No exemplars will ever be returned.
|
||||||
func (r *dropRes[N]) Collect(dest *[]metricdata.Exemplar[N]) {
|
func (r *dropRes[N]) Collect(dest *[]Exemplar) {
|
||||||
*dest = (*dest)[:0]
|
*dest = (*dest)[:0]
|
||||||
}
|
}
|
||||||
|
|
29
vendor/go.opentelemetry.io/otel/sdk/metric/internal/exemplar/exemplar.go
generated
vendored
Normal file
29
vendor/go.opentelemetry.io/otel/sdk/metric/internal/exemplar/exemplar.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exemplar is a measurement sampled from a timeseries providing a typical
|
||||||
|
// example.
|
||||||
|
type Exemplar struct {
|
||||||
|
// FilteredAttributes are the attributes recorded with the measurement but
|
||||||
|
// filtered out of the timeseries' aggregated data.
|
||||||
|
FilteredAttributes []attribute.KeyValue
|
||||||
|
// Time is the time when the measurement was recorded.
|
||||||
|
Time time.Time
|
||||||
|
// Value is the measured value.
|
||||||
|
Value Value
|
||||||
|
// SpanID is the ID of the span that was active during the measurement. If
|
||||||
|
// no span was active or the span was not sampled this will be empty.
|
||||||
|
SpanID []byte `json:",omitempty"`
|
||||||
|
// TraceID is the ID of the trace the active span belonged to during the
|
||||||
|
// measurement. If no span was active or the span was not sampled this will
|
||||||
|
// be empty.
|
||||||
|
TraceID []byte `json:",omitempty"`
|
||||||
|
}
|
|
@ -1,40 +1,29 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SampledFilter returns a [Reservoir] wrapping r that will only offer measurements
|
// Filter determines if a measurement should be offered.
|
||||||
// to r if the passed context associated with the measurement contains a sampled
|
//
|
||||||
|
// The passed ctx needs to contain any baggage or span that were active
|
||||||
|
// when the measurement was made. This information may be used by the
|
||||||
|
// Reservoir in making a sampling decision.
|
||||||
|
type Filter func(context.Context) bool
|
||||||
|
|
||||||
|
// SampledFilter is a [Filter] that will only offer measurements
|
||||||
|
// if the passed context associated with the measurement contains a sampled
|
||||||
// [go.opentelemetry.io/otel/trace.SpanContext].
|
// [go.opentelemetry.io/otel/trace.SpanContext].
|
||||||
func SampledFilter[N int64 | float64](r Reservoir[N]) Reservoir[N] {
|
func SampledFilter(ctx context.Context) bool {
|
||||||
return filtered[N]{Reservoir: r}
|
return trace.SpanContextFromContext(ctx).IsSampled()
|
||||||
}
|
}
|
||||||
|
|
||||||
type filtered[N int64 | float64] struct {
|
// AlwaysOnFilter is a [Filter] that always offers measurements.
|
||||||
Reservoir[N]
|
func AlwaysOnFilter(ctx context.Context) bool {
|
||||||
}
|
return true
|
||||||
|
|
||||||
func (f filtered[N]) Offer(ctx context.Context, t time.Time, n N, a []attribute.KeyValue) {
|
|
||||||
if trace.SpanContextFromContext(ctx).IsSampled() {
|
|
||||||
f.Reservoir.Offer(ctx, t, n, a)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
49
vendor/go.opentelemetry.io/otel/sdk/metric/internal/exemplar/filtered_reservoir.go
generated
vendored
Normal file
49
vendor/go.opentelemetry.io/otel/sdk/metric/internal/exemplar/filtered_reservoir.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilteredReservoir wraps a [Reservoir] with a filter.
|
||||||
|
type FilteredReservoir[N int64 | float64] interface {
|
||||||
|
// Offer accepts the parameters associated with a measurement. The
|
||||||
|
// parameters will be stored as an exemplar if the filter decides to
|
||||||
|
// sample the measurement.
|
||||||
|
//
|
||||||
|
// The passed ctx needs to contain any baggage or span that were active
|
||||||
|
// when the measurement was made. This information may be used by the
|
||||||
|
// Reservoir in making a sampling decision.
|
||||||
|
Offer(ctx context.Context, val N, attr []attribute.KeyValue)
|
||||||
|
// Collect returns all the held exemplars in the reservoir.
|
||||||
|
Collect(dest *[]Exemplar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// filteredReservoir handles the pre-sampled exemplar of measurements made.
|
||||||
|
type filteredReservoir[N int64 | float64] struct {
|
||||||
|
filter Filter
|
||||||
|
reservoir Reservoir
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredReservoir creates a [FilteredReservoir] which only offers values
|
||||||
|
// that are allowed by the filter.
|
||||||
|
func NewFilteredReservoir[N int64 | float64](f Filter, r Reservoir) FilteredReservoir[N] {
|
||||||
|
return &filteredReservoir[N]{
|
||||||
|
filter: f,
|
||||||
|
reservoir: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filteredReservoir[N]) Offer(ctx context.Context, val N, attr []attribute.KeyValue) {
|
||||||
|
if f.filter(ctx) {
|
||||||
|
// only record the current time if we are sampling this measurment.
|
||||||
|
f.reservoir.Offer(ctx, time.Now(), NewValue(val), attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filteredReservoir[N]) Collect(dest *[]Exemplar) { f.reservoir.Collect(dest) }
|
|
@ -1,21 +1,11 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -27,21 +17,30 @@ import (
|
||||||
// by bounds.
|
// by bounds.
|
||||||
//
|
//
|
||||||
// The passed bounds will be sorted by this function.
|
// The passed bounds will be sorted by this function.
|
||||||
func Histogram[N int64 | float64](bounds []float64) Reservoir[N] {
|
func Histogram(bounds []float64) Reservoir {
|
||||||
sort.Float64s(bounds)
|
slices.Sort(bounds)
|
||||||
return &histRes[N]{
|
return &histRes{
|
||||||
bounds: bounds,
|
bounds: bounds,
|
||||||
storage: newStorage[N](len(bounds) + 1),
|
storage: newStorage(len(bounds) + 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type histRes[N int64 | float64] struct {
|
type histRes struct {
|
||||||
*storage[N]
|
*storage
|
||||||
|
|
||||||
// bounds are bucket bounds in ascending order.
|
// bounds are bucket bounds in ascending order.
|
||||||
bounds []float64
|
bounds []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *histRes[N]) Offer(ctx context.Context, t time.Time, n N, a []attribute.KeyValue) {
|
func (r *histRes) Offer(ctx context.Context, t time.Time, v Value, a []attribute.KeyValue) {
|
||||||
r.store[sort.SearchFloat64s(r.bounds, float64(n))] = newMeasurement(ctx, t, n, a)
|
var x float64
|
||||||
|
switch v.Type() {
|
||||||
|
case Int64ValueType:
|
||||||
|
x = float64(v.Int64())
|
||||||
|
case Float64ValueType:
|
||||||
|
x = v.Float64()
|
||||||
|
default:
|
||||||
|
panic("unknown value type")
|
||||||
|
}
|
||||||
|
r.store[sort.SearchFloat64s(r.bounds, x)] = newMeasurement(ctx, t, v, a)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
|
@ -18,17 +7,21 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
// rng is used to make sampling decisions.
|
// rng is used to make sampling decisions.
|
||||||
//
|
//
|
||||||
// Do not use crypto/rand. There is no reason for the decrease in performance
|
// Do not use crypto/rand. There is no reason for the decrease in performance
|
||||||
// given this is not a security sensitive decision.
|
// given this is not a security sensitive decision.
|
||||||
var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
|
rng = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
// Ensure concurrent safe accecess to rng and its underlying source.
|
||||||
|
rngMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
// random returns, as a float64, a uniform pseudo-random number in the open
|
// random returns, as a float64, a uniform pseudo-random number in the open
|
||||||
// interval (0.0,1.0).
|
// interval (0.0,1.0).
|
||||||
|
@ -50,6 +43,9 @@ func random() float64 {
|
||||||
//
|
//
|
||||||
// There are likely many other methods to explore here as well.
|
// There are likely many other methods to explore here as well.
|
||||||
|
|
||||||
|
rngMu.Lock()
|
||||||
|
defer rngMu.Unlock()
|
||||||
|
|
||||||
f := rng.Float64()
|
f := rng.Float64()
|
||||||
for f == 0 {
|
for f == 0 {
|
||||||
f = rng.Float64()
|
f = rng.Float64()
|
||||||
|
@ -61,14 +57,14 @@ func random() float64 {
|
||||||
// are k or less measurements made, the Reservoir will sample each one. If
|
// are k or less measurements made, the Reservoir will sample each one. If
|
||||||
// there are more than k, the Reservoir will then randomly sample all
|
// there are more than k, the Reservoir will then randomly sample all
|
||||||
// additional measurement with a decreasing probability.
|
// additional measurement with a decreasing probability.
|
||||||
func FixedSize[N int64 | float64](k int) Reservoir[N] {
|
func FixedSize(k int) Reservoir {
|
||||||
r := &randRes[N]{storage: newStorage[N](k)}
|
r := &randRes{storage: newStorage(k)}
|
||||||
r.reset()
|
r.reset()
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
type randRes[N int64 | float64] struct {
|
type randRes struct {
|
||||||
*storage[N]
|
*storage
|
||||||
|
|
||||||
// count is the number of measurement seen.
|
// count is the number of measurement seen.
|
||||||
count int64
|
count int64
|
||||||
|
@ -80,7 +76,7 @@ type randRes[N int64 | float64] struct {
|
||||||
w float64
|
w float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *randRes[N]) Offer(ctx context.Context, t time.Time, n N, a []attribute.KeyValue) {
|
func (r *randRes) Offer(ctx context.Context, t time.Time, n Value, a []attribute.KeyValue) {
|
||||||
// The following algorithm is "Algorithm L" from Li, Kim-Hung (4 December
|
// The following algorithm is "Algorithm L" from Li, Kim-Hung (4 December
|
||||||
// 1994). "Reservoir-Sampling Algorithms of Time Complexity
|
// 1994). "Reservoir-Sampling Algorithms of Time Complexity
|
||||||
// O(n(1+log(N/n)))". ACM Transactions on Mathematical Software. 20 (4):
|
// O(n(1+log(N/n)))". ACM Transactions on Mathematical Software. 20 (4):
|
||||||
|
@ -136,7 +132,7 @@ func (r *randRes[N]) Offer(ctx context.Context, t time.Time, n N, a []attribute.
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset resets r to the initial state.
|
// reset resets r to the initial state.
|
||||||
func (r *randRes[N]) reset() {
|
func (r *randRes) reset() {
|
||||||
// This resets the number of exemplars known.
|
// This resets the number of exemplars known.
|
||||||
r.count = 0
|
r.count = 0
|
||||||
// Random index inserts should only happen after the storage is full.
|
// Random index inserts should only happen after the storage is full.
|
||||||
|
@ -158,7 +154,7 @@ func (r *randRes[N]) reset() {
|
||||||
|
|
||||||
// advance updates the count at which the offered measurement will overwrite an
|
// advance updates the count at which the offered measurement will overwrite an
|
||||||
// existing exemplar.
|
// existing exemplar.
|
||||||
func (r *randRes[N]) advance() {
|
func (r *randRes) advance() {
|
||||||
// Calculate the next value in the random number series.
|
// Calculate the next value in the random number series.
|
||||||
//
|
//
|
||||||
// The current value of r.w is based on the max of a distribution of random
|
// The current value of r.w is based on the max of a distribution of random
|
||||||
|
@ -185,7 +181,7 @@ func (r *randRes[N]) advance() {
|
||||||
r.next += int64(math.Log(random())/math.Log(1-r.w)) + 1
|
r.next += int64(math.Log(random())/math.Log(1-r.w)) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *randRes[N]) Collect(dest *[]metricdata.Exemplar[N]) {
|
func (r *randRes) Collect(dest *[]Exemplar) {
|
||||||
r.storage.Collect(dest)
|
r.storage.Collect(dest)
|
||||||
// Call reset here even though it will reset r.count and restart the random
|
// Call reset here even though it will reset r.count and restart the random
|
||||||
// number series. This will persist any old exemplars as long as no new
|
// number series. This will persist any old exemplars as long as no new
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
|
@ -19,11 +8,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reservoir holds the sampled exemplar of measurements made.
|
// Reservoir holds the sampled exemplar of measurements made.
|
||||||
type Reservoir[N int64 | float64] interface {
|
type Reservoir interface {
|
||||||
// Offer accepts the parameters associated with a measurement. The
|
// Offer accepts the parameters associated with a measurement. The
|
||||||
// parameters will be stored as an exemplar if the Reservoir decides to
|
// parameters will be stored as an exemplar if the Reservoir decides to
|
||||||
// sample the measurement.
|
// sample the measurement.
|
||||||
|
@ -35,10 +23,10 @@ type Reservoir[N int64 | float64] interface {
|
||||||
// The time t is the time when the measurement was made. The val and attr
|
// The time t is the time when the measurement was made. The val and attr
|
||||||
// parameters are the value and dropped (filtered) attributes of the
|
// parameters are the value and dropped (filtered) attributes of the
|
||||||
// measurement respectively.
|
// measurement respectively.
|
||||||
Offer(ctx context.Context, t time.Time, val N, attr []attribute.KeyValue)
|
Offer(ctx context.Context, t time.Time, val Value, attr []attribute.KeyValue)
|
||||||
|
|
||||||
// Collect returns all the held exemplars.
|
// Collect returns all the held exemplars.
|
||||||
//
|
//
|
||||||
// The Reservoir state is preserved after this call.
|
// The Reservoir state is preserved after this call.
|
||||||
Collect(dest *[]metricdata.Exemplar[N])
|
Collect(dest *[]Exemplar)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
// Copyright The OpenTelemetry Authors
|
// Copyright The OpenTelemetry Authors
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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 exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
|
@ -19,27 +8,26 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// storage is an exemplar storage for [Reservoir] implementations.
|
// storage is an exemplar storage for [Reservoir] implementations.
|
||||||
type storage[N int64 | float64] struct {
|
type storage struct {
|
||||||
// store are the measurements sampled.
|
// store are the measurements sampled.
|
||||||
//
|
//
|
||||||
// This does not use []metricdata.Exemplar because it potentially would
|
// This does not use []metricdata.Exemplar because it potentially would
|
||||||
// require an allocation for trace and span IDs in the hot path of Offer.
|
// require an allocation for trace and span IDs in the hot path of Offer.
|
||||||
store []measurement[N]
|
store []measurement
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStorage[N int64 | float64](n int) *storage[N] {
|
func newStorage(n int) *storage {
|
||||||
return &storage[N]{store: make([]measurement[N], n)}
|
return &storage{store: make([]measurement, n)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect returns all the held exemplars.
|
// Collect returns all the held exemplars.
|
||||||
//
|
//
|
||||||
// The Reservoir state is preserved after this call.
|
// The Reservoir state is preserved after this call.
|
||||||
func (r *storage[N]) Collect(dest *[]metricdata.Exemplar[N]) {
|
func (r *storage) Collect(dest *[]Exemplar) {
|
||||||
*dest = reset(*dest, len(r.store), len(r.store))
|
*dest = reset(*dest, len(r.store), len(r.store))
|
||||||
var n int
|
var n int
|
||||||
for _, m := range r.store {
|
for _, m := range r.store {
|
||||||
|
@ -54,13 +42,13 @@ func (r *storage[N]) Collect(dest *[]metricdata.Exemplar[N]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// measurement is a measurement made by a telemetry system.
|
// measurement is a measurement made by a telemetry system.
|
||||||
type measurement[N int64 | float64] struct {
|
type measurement struct {
|
||||||
// FilteredAttributes are the attributes dropped during the measurement.
|
// FilteredAttributes are the attributes dropped during the measurement.
|
||||||
FilteredAttributes []attribute.KeyValue
|
FilteredAttributes []attribute.KeyValue
|
||||||
// Time is the time when the measurement was made.
|
// Time is the time when the measurement was made.
|
||||||
Time time.Time
|
Time time.Time
|
||||||
// Value is the value of the measurement.
|
// Value is the value of the measurement.
|
||||||
Value N
|
Value Value
|
||||||
// SpanContext is the SpanContext active when a measurement was made.
|
// SpanContext is the SpanContext active when a measurement was made.
|
||||||
SpanContext trace.SpanContext
|
SpanContext trace.SpanContext
|
||||||
|
|
||||||
|
@ -68,8 +56,8 @@ type measurement[N int64 | float64] struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMeasurement returns a new non-empty Measurement.
|
// newMeasurement returns a new non-empty Measurement.
|
||||||
func newMeasurement[N int64 | float64](ctx context.Context, ts time.Time, v N, droppedAttr []attribute.KeyValue) measurement[N] {
|
func newMeasurement(ctx context.Context, ts time.Time, v Value, droppedAttr []attribute.KeyValue) measurement {
|
||||||
return measurement[N]{
|
return measurement{
|
||||||
FilteredAttributes: droppedAttr,
|
FilteredAttributes: droppedAttr,
|
||||||
Time: ts,
|
Time: ts,
|
||||||
Value: v,
|
Value: v,
|
||||||
|
@ -78,8 +66,8 @@ func newMeasurement[N int64 | float64](ctx context.Context, ts time.Time, v N, d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exemplar returns m as a [metricdata.Exemplar].
|
// Exemplar returns m as an [Exemplar].
|
||||||
func (m measurement[N]) Exemplar(dest *metricdata.Exemplar[N]) {
|
func (m measurement) Exemplar(dest *Exemplar) {
|
||||||
dest.FilteredAttributes = m.FilteredAttributes
|
dest.FilteredAttributes = m.FilteredAttributes
|
||||||
dest.Time = m.Time
|
dest.Time = m.Time
|
||||||
dest.Value = m.Value
|
dest.Value = m.Value
|
||||||
|
|
58
vendor/go.opentelemetry.io/otel/sdk/metric/internal/exemplar/value.go
generated
vendored
Normal file
58
vendor/go.opentelemetry.io/otel/sdk/metric/internal/exemplar/value.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package exemplar // import "go.opentelemetry.io/otel/sdk/metric/internal/exemplar"
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// ValueType identifies the type of value used in exemplar data.
|
||||||
|
type ValueType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnknownValueType should not be used. It represents a misconfigured
|
||||||
|
// Value.
|
||||||
|
UnknownValueType ValueType = 0
|
||||||
|
// Int64ValueType represents a Value with int64 data.
|
||||||
|
Int64ValueType ValueType = 1
|
||||||
|
// Float64ValueType represents a Value with float64 data.
|
||||||
|
Float64ValueType ValueType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value is the value of data held by an exemplar.
|
||||||
|
type Value struct {
|
||||||
|
t ValueType
|
||||||
|
val uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewValue returns a new [Value] for the provided value.
|
||||||
|
func NewValue[N int64 | float64](value N) Value {
|
||||||
|
switch v := any(value).(type) {
|
||||||
|
case int64:
|
||||||
|
return Value{t: Int64ValueType, val: uint64(v)}
|
||||||
|
case float64:
|
||||||
|
return Value{t: Float64ValueType, val: math.Float64bits(v)}
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the [ValueType] of data held by v.
|
||||||
|
func (v Value) Type() ValueType { return v.t }
|
||||||
|
|
||||||
|
// Int64 returns the value of v as an int64. If the ValueType of v is not an
|
||||||
|
// Int64ValueType, 0 is returned.
|
||||||
|
func (v Value) Int64() int64 {
|
||||||
|
if v.t == Int64ValueType {
|
||||||
|
// Assumes the correct int64 was stored in v.val based on type.
|
||||||
|
return int64(v.val) // nolint: gosec
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns the value of v as an float64. If the ValueType of v is not
|
||||||
|
// an Float64ValueType, 0 is returned.
|
||||||
|
func (v Value) Float64() float64 {
|
||||||
|
if v.t == Float64ValueType {
|
||||||
|
return math.Float64frombits(v.val)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue