vendor: go.opentelemetry.io/otel v1.42.0, otel/contrib v1.67.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -55,12 +55,12 @@ require (
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
|
||||
github.com/zclconf/go-cty v1.17.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||
go.opentelemetry.io/otel v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0
|
||||
go.opentelemetry.io/otel/metric v1.40.0
|
||||
go.opentelemetry.io/otel/sdk v1.40.0
|
||||
go.opentelemetry.io/otel/trace v1.40.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
|
||||
go.opentelemetry.io/otel v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0
|
||||
go.opentelemetry.io/otel/metric v1.42.0
|
||||
go.opentelemetry.io/otel/sdk v1.42.0
|
||||
go.opentelemetry.io/otel/trace v1.42.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/mod v0.33.0
|
||||
@@ -68,7 +68,7 @@ require (
|
||||
golang.org/x/sys v0.42.0
|
||||
golang.org/x/term v0.41.0
|
||||
golang.org/x/text v0.34.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171
|
||||
google.golang.org/grpc v1.79.3
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
@@ -149,7 +149,7 @@ require (
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-containerregistry v0.20.7 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hiddeco/sshsig v0.2.0 // indirect
|
||||
github.com/in-toto/attestation v1.1.2 // indirect
|
||||
@@ -214,22 +214,22 @@ require (
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
github.com/yashtewari/glob-intersection v0.2.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.67.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.35.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
|
||||
@@ -322,8 +322,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
@@ -622,34 +622,34 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0/go.mod h1:rjbQTDEPQymPE0YnRQp9/NuPwwtL0sesz/fnqRW/v84=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 h1:NOyNnS19BF2SUDApbOKbDtWZ0IK7b8FJ2uAGdIWOGb0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0/go.mod h1:VL6EgVikRLcJa9ftukrHu/ZkkhFBSo1lzvdBC9CF1ss=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0 h1:9y5sHvAxWzft1WQ4BwqcvA+IFVUJ1Ya75mSAUnFEVwE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0/go.mod h1:eQqT90eR3X5Dbs1g9YSM30RavwLF725Ris5/XSXWvqE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.67.0 h1:c9r/G1CSw4dPI1jaNNG9RnQP+q4SvZnHciDQJVIvchU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.67.0/go.mod h1:gO9smoZe9KnZcJCqcB0lMmQ4Z5VEifYmjMTpnwtTSuQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0 h1:H7O6RlGOMTizyl3R08Kn5pdM06bnH8oscSj7o11tmLA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0/go.mod h1:mBFWu/WOVDkWWsR7Tx7h6EpQB8wsv7P0Yrh0Pb7othc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 h1:uLXP+3mghfMf7XmV4PkGfFhFKuNWoCvvx5wP/wOXo0o=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0/go.mod h1:v0Tj04armyT59mnURNUJf7RCKcKzq+lgJs6QSjHjaTc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.step.sm/crypto v0.74.0 h1:/APBEv45yYR4qQFg47HA8w1nesIGcxh44pGyQNw6JRA=
|
||||
@@ -683,8 +683,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -721,10 +721,10 @@ google.golang.org/api v0.260.0 h1:XbNi5E6bOVEj/uLXQRlt6TKuEzMD7zvW/6tNwltE4P4=
|
||||
google.golang.org/api v0.260.0/go.mod h1:Shj1j0Phr/9sloYrKomICzdYgsSDImpTxME8rGLaZ/o=
|
||||
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934=
|
||||
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
||||
|
||||
+3
-1
@@ -28,7 +28,9 @@ func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshal
|
||||
}
|
||||
handleForwardResponseServerMetadata(w, mux, md)
|
||||
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
if !mux.disableChunkedEncoding {
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
}
|
||||
if err := handleForwardResponseOptions(ctx, w, nil, opts); err != nil {
|
||||
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||
return
|
||||
|
||||
+11
@@ -73,6 +73,7 @@ type ServeMux struct {
|
||||
disablePathLengthFallback bool
|
||||
unescapingMode UnescapingMode
|
||||
writeContentLength bool
|
||||
disableChunkedEncoding bool
|
||||
}
|
||||
|
||||
// ServeMuxOption is an option that can be given to a ServeMux on construction.
|
||||
@@ -125,6 +126,16 @@ func WithMiddlewares(middlewares ...Middleware) ServeMuxOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisableChunkedEncoding disables the Transfer-Encoding: chunked header
|
||||
// for streaming responses. This is useful for streaming implementations that use
|
||||
// Content-Length, which is mutually exclusive with Transfer-Encoding:chunked.
|
||||
// Note that this option will not automatically add Content-Length headers, so it should be used with caution.
|
||||
func WithDisableChunkedEncoding() ServeMuxOption {
|
||||
return func(mux *ServeMux) {
|
||||
mux.disableChunkedEncoding = true
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// done with careful consideration.
|
||||
|
||||
Generated
Vendored
+92
-114
@@ -31,14 +31,16 @@ type Filter func(*stats.RPCTagInfo) bool
|
||||
|
||||
// config is a group of options for this instrumentation.
|
||||
type config struct {
|
||||
Filter Filter
|
||||
InterceptorFilter InterceptorFilter
|
||||
Propagators propagation.TextMapPropagator
|
||||
TracerProvider trace.TracerProvider
|
||||
MeterProvider metric.MeterProvider
|
||||
SpanStartOptions []trace.SpanStartOption
|
||||
SpanAttributes []attribute.KeyValue
|
||||
MetricAttributes []attribute.KeyValue
|
||||
Filter Filter
|
||||
InterceptorFilter InterceptorFilter
|
||||
Propagators propagation.TextMapPropagator
|
||||
TracerProvider trace.TracerProvider
|
||||
MeterProvider metric.MeterProvider
|
||||
SpanKind trace.SpanKind
|
||||
SpanStartOptions []trace.SpanStartOption
|
||||
SpanAttributes []attribute.KeyValue
|
||||
MetricAttributes []attribute.KeyValue
|
||||
MetricAttributesFn func(ctx context.Context) []attribute.KeyValue
|
||||
|
||||
PublicEndpoint bool
|
||||
PublicEndpointFn func(ctx context.Context, info *stats.RPCTagInfo) bool
|
||||
@@ -52,6 +54,12 @@ type Option interface {
|
||||
apply(*config)
|
||||
}
|
||||
|
||||
type optionFunc func(*config)
|
||||
|
||||
func (f optionFunc) apply(c *config) {
|
||||
f(c)
|
||||
}
|
||||
|
||||
// newConfig returns a config configured with all the passed Options.
|
||||
func newConfig(opts []Option) *config {
|
||||
c := &config{
|
||||
@@ -65,27 +73,13 @@ func newConfig(opts []Option) *config {
|
||||
return c
|
||||
}
|
||||
|
||||
type publicEndpointOption struct{ p bool }
|
||||
|
||||
func (o publicEndpointOption) apply(c *config) {
|
||||
c.PublicEndpoint = o.p
|
||||
}
|
||||
|
||||
// WithPublicEndpoint configures the Handler to link the span with an incoming
|
||||
// span context. If this option is not provided, then the association is a child
|
||||
// association instead of a link.
|
||||
func WithPublicEndpoint() Option {
|
||||
return publicEndpointOption{p: true}
|
||||
}
|
||||
|
||||
type publicEndpointFnOption struct {
|
||||
fn func(context.Context, *stats.RPCTagInfo) bool
|
||||
}
|
||||
|
||||
func (o publicEndpointFnOption) apply(c *config) {
|
||||
if o.fn != nil {
|
||||
c.PublicEndpointFn = o.fn
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
c.PublicEndpoint = true
|
||||
})
|
||||
}
|
||||
|
||||
// WithPublicEndpointFn runs with every request, and allows conditionally
|
||||
@@ -94,81 +88,59 @@ func (o publicEndpointFnOption) apply(c *config) {
|
||||
// child association instead of a link.
|
||||
// Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn.
|
||||
func WithPublicEndpointFn(fn func(context.Context, *stats.RPCTagInfo) bool) Option {
|
||||
return publicEndpointFnOption{fn: fn}
|
||||
}
|
||||
|
||||
type propagatorsOption struct{ p propagation.TextMapPropagator }
|
||||
|
||||
func (o propagatorsOption) apply(c *config) {
|
||||
if o.p != nil {
|
||||
c.Propagators = o.p
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
c.PublicEndpointFn = fn
|
||||
})
|
||||
}
|
||||
|
||||
// WithPropagators returns an Option to use the Propagators when extracting
|
||||
// and injecting trace context from requests.
|
||||
func WithPropagators(p propagation.TextMapPropagator) Option {
|
||||
return propagatorsOption{p: p}
|
||||
}
|
||||
|
||||
type tracerProviderOption struct{ tp trace.TracerProvider }
|
||||
|
||||
func (o tracerProviderOption) apply(c *config) {
|
||||
if o.tp != nil {
|
||||
c.TracerProvider = o.tp
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
if p != nil {
|
||||
c.Propagators = p
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithInterceptorFilter returns an Option to use the request filter.
|
||||
//
|
||||
// Deprecated: Use stats handlers instead.
|
||||
func WithInterceptorFilter(f InterceptorFilter) Option {
|
||||
return interceptorFilterOption{f: f}
|
||||
}
|
||||
|
||||
type interceptorFilterOption struct {
|
||||
f InterceptorFilter
|
||||
}
|
||||
|
||||
func (o interceptorFilterOption) apply(c *config) {
|
||||
if o.f != nil {
|
||||
c.InterceptorFilter = o.f
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
if f != nil {
|
||||
c.InterceptorFilter = f
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithFilter returns an Option to use the request filter.
|
||||
func WithFilter(f Filter) Option {
|
||||
return filterOption{f: f}
|
||||
}
|
||||
|
||||
type filterOption struct {
|
||||
f Filter
|
||||
}
|
||||
|
||||
func (o filterOption) apply(c *config) {
|
||||
if o.f != nil {
|
||||
c.Filter = o.f
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
if f != nil {
|
||||
c.Filter = f
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithTracerProvider returns an Option to use the TracerProvider when
|
||||
// creating a Tracer.
|
||||
func WithTracerProvider(tp trace.TracerProvider) Option {
|
||||
return tracerProviderOption{tp: tp}
|
||||
}
|
||||
|
||||
type meterProviderOption struct{ mp metric.MeterProvider }
|
||||
|
||||
func (o meterProviderOption) apply(c *config) {
|
||||
if o.mp != nil {
|
||||
c.MeterProvider = o.mp
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
if tp != nil {
|
||||
c.TracerProvider = tp
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithMeterProvider returns an Option to use the MeterProvider when
|
||||
// creating a Meter. If this option is not provide the global MeterProvider will be used.
|
||||
func WithMeterProvider(mp metric.MeterProvider) Option {
|
||||
return meterProviderOption{mp: mp}
|
||||
return optionFunc(func(c *config) {
|
||||
if mp != nil {
|
||||
c.MeterProvider = mp
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Event type that can be recorded, see WithMessageEvents.
|
||||
@@ -180,21 +152,6 @@ const (
|
||||
SentEvents
|
||||
)
|
||||
|
||||
type messageEventsProviderOption struct {
|
||||
events []Event
|
||||
}
|
||||
|
||||
func (m messageEventsProviderOption) apply(c *config) {
|
||||
for _, e := range m.events {
|
||||
switch e {
|
||||
case ReceivedEvents:
|
||||
c.ReceivedEvent = true
|
||||
case SentEvents:
|
||||
c.SentEvent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessageEvents configures the Handler to record the specified events
|
||||
// (span.AddEvent) on spans. By default only summary attributes are added at the
|
||||
// end of the request.
|
||||
@@ -203,13 +160,16 @@ func (m messageEventsProviderOption) apply(c *config) {
|
||||
// - ReceivedEvents: Record the number of bytes read after every gRPC read operation.
|
||||
// - SentEvents: Record the number of bytes written after every gRPC write operation.
|
||||
func WithMessageEvents(events ...Event) Option {
|
||||
return messageEventsProviderOption{events: events}
|
||||
}
|
||||
|
||||
type spanStartOption struct{ opts []trace.SpanStartOption }
|
||||
|
||||
func (o spanStartOption) apply(c *config) {
|
||||
c.SpanStartOptions = append(c.SpanStartOptions, o.opts...)
|
||||
return optionFunc(func(c *config) {
|
||||
for _, e := range events {
|
||||
switch e {
|
||||
case ReceivedEvents:
|
||||
c.ReceivedEvent = true
|
||||
case SentEvents:
|
||||
c.SentEvent = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpanOptions configures an additional set of
|
||||
@@ -217,31 +177,49 @@ func (o spanStartOption) apply(c *config) {
|
||||
//
|
||||
// Deprecated: It is only used by the deprecated interceptor, and is unused by [NewClientHandler] and [NewServerHandler].
|
||||
func WithSpanOptions(opts ...trace.SpanStartOption) Option {
|
||||
return spanStartOption{opts}
|
||||
return optionFunc(func(c *config) {
|
||||
c.SpanStartOptions = append(c.SpanStartOptions, opts...)
|
||||
})
|
||||
}
|
||||
|
||||
type spanAttributesOption struct{ a []attribute.KeyValue }
|
||||
|
||||
func (o spanAttributesOption) apply(c *config) {
|
||||
if o.a != nil {
|
||||
c.SpanAttributes = o.a
|
||||
}
|
||||
// WithSpanKind returns an Option to set the span kind for spans created by
|
||||
// the handler.
|
||||
//
|
||||
// By default, [NewServerHandler] creates spans with
|
||||
// [trace.SpanKindServer] and [NewClientHandler] creates spans with
|
||||
// [trace.SpanKindClient].
|
||||
func WithSpanKind(sk trace.SpanKind) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.SpanKind = sk
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpanAttributes returns an Option to add custom attributes to the spans.
|
||||
func WithSpanAttributes(a ...attribute.KeyValue) Option {
|
||||
return spanAttributesOption{a: a}
|
||||
}
|
||||
|
||||
type metricAttributesOption struct{ a []attribute.KeyValue }
|
||||
|
||||
func (o metricAttributesOption) apply(c *config) {
|
||||
if o.a != nil {
|
||||
c.MetricAttributes = o.a
|
||||
}
|
||||
return optionFunc(func(c *config) {
|
||||
if a != nil {
|
||||
c.SpanAttributes = a
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithMetricAttributes returns an Option to add custom attributes to the metrics.
|
||||
func WithMetricAttributes(a ...attribute.KeyValue) Option {
|
||||
return metricAttributesOption{a: a}
|
||||
return optionFunc(func(c *config) {
|
||||
if a != nil {
|
||||
c.MetricAttributes = append(c.MetricAttributes, a...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithMetricAttributesFn returns an Option to add dynamic custom attributes to the handler's metrics.
|
||||
// The function is called once per RPC and the returned attributes are applied to all metrics recorded by this handler.
|
||||
//
|
||||
// The context parameter is the standard gRPC request context and provides access to request-scoped data.
|
||||
func WithMetricAttributesFn(fn func(ctx context.Context) []attribute.KeyValue) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
if fn != nil {
|
||||
c.MetricAttributesFn = fn
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
grpc_codes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go
Generated
Vendored
+2
-16
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
// ParseFullMethod returns a span name following the OpenTelemetry semantic
|
||||
@@ -23,19 +23,5 @@ func ParseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
|
||||
return fullMethod, nil
|
||||
}
|
||||
name := fullMethod[1:]
|
||||
pos := strings.LastIndex(name, "/")
|
||||
if pos < 0 {
|
||||
// Invalid format, does not follow `/package.service/method`.
|
||||
return name, nil
|
||||
}
|
||||
service, method := name[:pos], name[pos+1:]
|
||||
|
||||
var attrs []attribute.KeyValue
|
||||
if service != "" {
|
||||
attrs = append(attrs, semconv.RPCService(service))
|
||||
}
|
||||
if method != "" {
|
||||
attrs = append(attrs, semconv.RPCMethod(method))
|
||||
}
|
||||
return name, attrs
|
||||
return name, []attribute.KeyValue{semconv.RPCMethod(name)}
|
||||
}
|
||||
|
||||
Generated
Vendored
+6
-34
@@ -6,9 +6,7 @@ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.g
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
@@ -17,9 +15,9 @@ type metadataSupplier struct {
|
||||
}
|
||||
|
||||
// assert that metadataSupplier implements the TextMapCarrier interface.
|
||||
var _ propagation.TextMapCarrier = &metadataSupplier{}
|
||||
var _ propagation.TextMapCarrier = metadataSupplier{}
|
||||
|
||||
func (s *metadataSupplier) Get(key string) string {
|
||||
func (s metadataSupplier) Get(key string) string {
|
||||
values := s.metadata.Get(key)
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
@@ -27,11 +25,11 @@ func (s *metadataSupplier) Get(key string) string {
|
||||
return values[0]
|
||||
}
|
||||
|
||||
func (s *metadataSupplier) Set(key, value string) {
|
||||
func (s metadataSupplier) Set(key, value string) {
|
||||
s.metadata.Set(key, value)
|
||||
}
|
||||
|
||||
func (s *metadataSupplier) Keys() []string {
|
||||
func (s metadataSupplier) Keys() []string {
|
||||
out := make([]string, 0, len(s.metadata))
|
||||
for key := range s.metadata {
|
||||
out = append(out, key)
|
||||
@@ -39,50 +37,24 @@ func (s *metadataSupplier) Keys() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// Inject injects correlation context and span context into the gRPC
|
||||
// metadata object. This function is meant to be used on outgoing
|
||||
// requests.
|
||||
//
|
||||
// Deprecated: Unnecessary public func.
|
||||
func Inject(ctx context.Context, md *metadata.MD, opts ...Option) {
|
||||
c := newConfig(opts)
|
||||
c.Propagators.Inject(ctx, &metadataSupplier{
|
||||
metadata: *md,
|
||||
})
|
||||
}
|
||||
|
||||
func inject(ctx context.Context, propagators propagation.TextMapPropagator) context.Context {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
md = metadata.MD{}
|
||||
}
|
||||
propagators.Inject(ctx, &metadataSupplier{
|
||||
propagators.Inject(ctx, metadataSupplier{
|
||||
metadata: md,
|
||||
})
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
// Extract returns the correlation context and span context that
|
||||
// another service encoded in the gRPC metadata object with Inject.
|
||||
// This function is meant to be used on incoming requests.
|
||||
//
|
||||
// Deprecated: Unnecessary public func.
|
||||
func Extract(ctx context.Context, md *metadata.MD, opts ...Option) (baggage.Baggage, trace.SpanContext) {
|
||||
c := newConfig(opts)
|
||||
ctx = c.Propagators.Extract(ctx, &metadataSupplier{
|
||||
metadata: *md,
|
||||
})
|
||||
|
||||
return baggage.FromContext(ctx), trace.SpanContextFromContext(ctx)
|
||||
}
|
||||
|
||||
func extract(ctx context.Context, propagators propagation.TextMapPropagator) context.Context {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
md = metadata.MD{}
|
||||
}
|
||||
|
||||
return propagators.Extract(ctx, &metadataSupplier{
|
||||
return propagators.Extract(ctx, metadataSupplier{
|
||||
metadata: md,
|
||||
})
|
||||
}
|
||||
|
||||
Generated
Vendored
+101
-120
@@ -5,15 +5,15 @@ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.g
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/rpcconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
grpc_codes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/peer"
|
||||
@@ -26,8 +26,6 @@ import (
|
||||
type gRPCContextKey struct{}
|
||||
|
||||
type gRPCContext struct {
|
||||
inMessages int64
|
||||
outMessages int64
|
||||
metricAttrs []attribute.KeyValue
|
||||
record bool
|
||||
}
|
||||
@@ -37,51 +35,37 @@ type serverHandler struct {
|
||||
|
||||
tracer trace.Tracer
|
||||
|
||||
duration rpcconv.ServerDuration
|
||||
inSize rpcconv.ServerRequestSize
|
||||
outSize rpcconv.ServerResponseSize
|
||||
inMsg rpcconv.ServerRequestsPerRPC
|
||||
outMsg rpcconv.ServerResponsesPerRPC
|
||||
duration rpcconv.ServerCallDuration
|
||||
}
|
||||
|
||||
// NewServerHandler creates a stats.Handler for a gRPC server.
|
||||
func NewServerHandler(opts ...Option) stats.Handler {
|
||||
c := newConfig(opts)
|
||||
if c.SpanKind == trace.SpanKindUnspecified {
|
||||
c.SpanKind = trace.SpanKindServer
|
||||
}
|
||||
|
||||
h := &serverHandler{config: c}
|
||||
|
||||
h.tracer = c.TracerProvider.Tracer(
|
||||
ScopeName,
|
||||
trace.WithInstrumentationVersion(Version()),
|
||||
trace.WithInstrumentationVersion(Version),
|
||||
)
|
||||
|
||||
meter := c.MeterProvider.Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(Version()),
|
||||
metric.WithInstrumentationVersion(Version),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
h.duration, err = rpcconv.NewServerDuration(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.inSize, err = rpcconv.NewServerRequestSize(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.outSize, err = rpcconv.NewServerResponseSize(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.inMsg, err = rpcconv.NewServerRequestsPerRPC(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.outMsg, err = rpcconv.NewServerResponsesPerRPC(meter)
|
||||
h.duration, err = rpcconv.NewServerCallDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(
|
||||
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
|
||||
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
@@ -103,7 +87,7 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
|
||||
ctx = extract(ctx, h.Propagators)
|
||||
|
||||
name, attrs := internal.ParseFullMethod(info.FullMethodName)
|
||||
attrs = append(attrs, semconv.RPCSystemGRPC)
|
||||
attrs = append(attrs, semconv.RPCSystemNameGRPC)
|
||||
|
||||
record := true
|
||||
if h.Filter != nil {
|
||||
@@ -111,9 +95,12 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
|
||||
}
|
||||
|
||||
if record {
|
||||
// Make a new slice to avoid aliasing into the same attrs slice used by metrics.
|
||||
spanAttributes := make([]attribute.KeyValue, 0, len(attrs)+len(h.SpanAttributes))
|
||||
spanAttributes = append(append(spanAttributes, attrs...), h.SpanAttributes...)
|
||||
opts := []trace.SpanStartOption{
|
||||
trace.WithSpanKind(trace.SpanKindServer),
|
||||
trace.WithAttributes(append(attrs, h.SpanAttributes...)...),
|
||||
trace.WithSpanKind(h.SpanKind),
|
||||
trace.WithAttributes(spanAttributes...),
|
||||
}
|
||||
if h.PublicEndpoint || (h.PublicEndpointFn != nil && h.PublicEndpointFn(ctx, info)) {
|
||||
opts = append(opts, trace.WithNewRoot())
|
||||
@@ -134,6 +121,11 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
|
||||
record: record,
|
||||
}
|
||||
|
||||
if h.MetricAttributesFn != nil {
|
||||
extraAttrs := h.MetricAttributesFn(ctx)
|
||||
gctx.metricAttrs = append(gctx.metricAttrs, extraAttrs...)
|
||||
}
|
||||
|
||||
return context.WithValue(ctx, gRPCContextKey{}, &gctx)
|
||||
}
|
||||
|
||||
@@ -143,10 +135,6 @@ func (h *serverHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
|
||||
ctx,
|
||||
rs,
|
||||
h.duration.Inst(),
|
||||
h.inSize,
|
||||
h.outSize,
|
||||
h.inMsg.Inst(),
|
||||
h.outMsg.Inst(),
|
||||
serverStatus,
|
||||
)
|
||||
}
|
||||
@@ -156,51 +144,37 @@ type clientHandler struct {
|
||||
|
||||
tracer trace.Tracer
|
||||
|
||||
duration rpcconv.ClientDuration
|
||||
inSize rpcconv.ClientResponseSize
|
||||
outSize rpcconv.ClientRequestSize
|
||||
inMsg rpcconv.ClientResponsesPerRPC
|
||||
outMsg rpcconv.ClientRequestsPerRPC
|
||||
duration rpcconv.ClientCallDuration
|
||||
}
|
||||
|
||||
// NewClientHandler creates a stats.Handler for a gRPC client.
|
||||
func NewClientHandler(opts ...Option) stats.Handler {
|
||||
c := newConfig(opts)
|
||||
if c.SpanKind == trace.SpanKindUnspecified {
|
||||
c.SpanKind = trace.SpanKindClient
|
||||
}
|
||||
|
||||
h := &clientHandler{config: c}
|
||||
|
||||
h.tracer = c.TracerProvider.Tracer(
|
||||
ScopeName,
|
||||
trace.WithInstrumentationVersion(Version()),
|
||||
trace.WithInstrumentationVersion(Version),
|
||||
)
|
||||
|
||||
meter := c.MeterProvider.Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(Version()),
|
||||
metric.WithInstrumentationVersion(Version),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
h.duration, err = rpcconv.NewClientDuration(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.inSize, err = rpcconv.NewClientResponseSize(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.outSize, err = rpcconv.NewClientRequestSize(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.inMsg, err = rpcconv.NewClientResponsesPerRPC(meter)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
h.outMsg, err = rpcconv.NewClientRequestsPerRPC(meter)
|
||||
h.duration, err = rpcconv.NewClientCallDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(
|
||||
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
|
||||
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
@@ -211,7 +185,7 @@ func NewClientHandler(opts ...Option) stats.Handler {
|
||||
// TagRPC can attach some information to the given context.
|
||||
func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
|
||||
name, attrs := internal.ParseFullMethod(info.FullMethodName)
|
||||
attrs = append(attrs, semconv.RPCSystemGRPC)
|
||||
attrs = append(attrs, semconv.RPCSystemNameGRPC)
|
||||
|
||||
record := true
|
||||
if h.Filter != nil {
|
||||
@@ -219,11 +193,14 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
|
||||
}
|
||||
|
||||
if record {
|
||||
// Make a new slice to avoid aliasing into the same attrs slice used by metrics.
|
||||
spanAttributes := make([]attribute.KeyValue, 0, len(attrs)+len(h.SpanAttributes))
|
||||
spanAttributes = append(append(spanAttributes, attrs...), h.SpanAttributes...)
|
||||
ctx, _ = h.tracer.Start(
|
||||
ctx,
|
||||
name,
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(append(attrs, h.SpanAttributes...)...),
|
||||
trace.WithSpanKind(h.SpanKind),
|
||||
trace.WithAttributes(spanAttributes...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -232,6 +209,11 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
|
||||
record: record,
|
||||
}
|
||||
|
||||
if h.MetricAttributesFn != nil {
|
||||
extraAttrs := h.MetricAttributesFn(ctx)
|
||||
gctx.metricAttrs = append(gctx.metricAttrs, extraAttrs...)
|
||||
}
|
||||
|
||||
return inject(context.WithValue(ctx, gRPCContextKey{}, &gctx), h.Propagators)
|
||||
}
|
||||
|
||||
@@ -241,10 +223,6 @@ func (h *clientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
|
||||
ctx,
|
||||
rs,
|
||||
h.duration.Inst(),
|
||||
h.inSize,
|
||||
h.outSize,
|
||||
h.inMsg.Inst(),
|
||||
h.outMsg.Inst(),
|
||||
func(s *status.Status) (codes.Code, string) {
|
||||
return codes.Error, s.Message()
|
||||
},
|
||||
@@ -261,16 +239,10 @@ func (*clientHandler) HandleConn(context.Context, stats.ConnStats) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
type int64Hist interface {
|
||||
Record(context.Context, int64, ...attribute.KeyValue)
|
||||
}
|
||||
|
||||
func (c *config) handleRPC(
|
||||
func (*config) handleRPC(
|
||||
ctx context.Context,
|
||||
rs stats.RPCStats,
|
||||
duration metric.Float64Histogram,
|
||||
inSize, outSize int64Hist,
|
||||
inMsg, outMsg metric.Int64Histogram,
|
||||
recordStatus func(*status.Status) (codes.Code, string),
|
||||
) {
|
||||
gctx, _ := ctx.Value(gRPCContextKey{}).(*gRPCContext)
|
||||
@@ -279,42 +251,11 @@ func (c *config) handleRPC(
|
||||
}
|
||||
|
||||
span := trace.SpanFromContext(ctx)
|
||||
var messageId int64
|
||||
|
||||
switch rs := rs.(type) {
|
||||
case *stats.Begin:
|
||||
case *stats.InPayload:
|
||||
if gctx != nil {
|
||||
messageId = atomic.AddInt64(&gctx.inMessages, 1)
|
||||
inSize.Record(ctx, int64(rs.Length), gctx.metricAttrs...)
|
||||
}
|
||||
|
||||
if c.ReceivedEvent && span.IsRecording() {
|
||||
span.AddEvent("message",
|
||||
trace.WithAttributes(
|
||||
semconv.RPCMessageTypeReceived,
|
||||
semconv.RPCMessageIDKey.Int64(messageId),
|
||||
semconv.RPCMessageCompressedSizeKey.Int(rs.CompressedLength),
|
||||
semconv.RPCMessageUncompressedSizeKey.Int(rs.Length),
|
||||
),
|
||||
)
|
||||
}
|
||||
case *stats.OutPayload:
|
||||
if gctx != nil {
|
||||
messageId = atomic.AddInt64(&gctx.outMessages, 1)
|
||||
outSize.Record(ctx, int64(rs.Length), gctx.metricAttrs...)
|
||||
}
|
||||
|
||||
if c.SentEvent && span.IsRecording() {
|
||||
span.AddEvent("message",
|
||||
trace.WithAttributes(
|
||||
semconv.RPCMessageTypeSent,
|
||||
semconv.RPCMessageIDKey.Int64(messageId),
|
||||
semconv.RPCMessageCompressedSizeKey.Int(rs.CompressedLength),
|
||||
semconv.RPCMessageUncompressedSizeKey.Int(rs.Length),
|
||||
),
|
||||
)
|
||||
}
|
||||
case *stats.OutTrailer:
|
||||
case *stats.OutHeader:
|
||||
if span.IsRecording() {
|
||||
@@ -328,9 +269,9 @@ func (c *config) handleRPC(
|
||||
var s *status.Status
|
||||
if rs.Error != nil {
|
||||
s, _ = status.FromError(rs.Error)
|
||||
rpcStatusAttr = semconv.RPCGRPCStatusCodeKey.Int(int(s.Code()))
|
||||
rpcStatusAttr = semconv.RPCResponseStatusCode(canonicalString(s.Code()))
|
||||
} else {
|
||||
rpcStatusAttr = semconv.RPCGRPCStatusCodeKey.Int(int(grpc_codes.OK))
|
||||
rpcStatusAttr = semconv.RPCResponseStatusCode(canonicalString(grpc_codes.OK))
|
||||
}
|
||||
if span.IsRecording() {
|
||||
if s != nil {
|
||||
@@ -343,6 +284,9 @@ func (c *config) handleRPC(
|
||||
|
||||
var metricAttrs []attribute.KeyValue
|
||||
if gctx != nil {
|
||||
// Don't use gctx.metricAttrSet here, because it requires passing
|
||||
// multiple RecordOptions, which would call metric.mergeSets and
|
||||
// allocate a new set for each Record call.
|
||||
metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1)
|
||||
metricAttrs = append(metricAttrs, gctx.metricAttrs...)
|
||||
}
|
||||
@@ -352,14 +296,51 @@ func (c *config) handleRPC(
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
// Measure right before calling Record() to capture as much elapsed time as possible.
|
||||
elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Millisecond)
|
||||
elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Second)
|
||||
|
||||
duration.Record(ctx, elapsedTime, recordOpts...)
|
||||
if gctx != nil {
|
||||
inMsg.Record(ctx, atomic.LoadInt64(&gctx.inMessages), recordOpts...)
|
||||
outMsg.Record(ctx, atomic.LoadInt64(&gctx.outMessages), recordOpts...)
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func canonicalString(code grpc_codes.Code) string {
|
||||
switch code {
|
||||
case grpc_codes.OK:
|
||||
return "OK"
|
||||
case grpc_codes.Canceled:
|
||||
return "CANCELLED"
|
||||
case grpc_codes.Unknown:
|
||||
return "UNKNOWN"
|
||||
case grpc_codes.InvalidArgument:
|
||||
return "INVALID_ARGUMENT"
|
||||
case grpc_codes.DeadlineExceeded:
|
||||
return "DEADLINE_EXCEEDED"
|
||||
case grpc_codes.NotFound:
|
||||
return "NOT_FOUND"
|
||||
case grpc_codes.AlreadyExists:
|
||||
return "ALREADY_EXISTS"
|
||||
case grpc_codes.PermissionDenied:
|
||||
return "PERMISSION_DENIED"
|
||||
case grpc_codes.ResourceExhausted:
|
||||
return "RESOURCE_EXHAUSTED"
|
||||
case grpc_codes.FailedPrecondition:
|
||||
return "FAILED_PRECONDITION"
|
||||
case grpc_codes.Aborted:
|
||||
return "ABORTED"
|
||||
case grpc_codes.OutOfRange:
|
||||
return "OUT_OF_RANGE"
|
||||
case grpc_codes.Unimplemented:
|
||||
return "UNIMPLEMENTED"
|
||||
case grpc_codes.Internal:
|
||||
return "INTERNAL"
|
||||
case grpc_codes.Unavailable:
|
||||
return "UNAVAILABLE"
|
||||
case grpc_codes.DataLoss:
|
||||
return "DATA_LOSS"
|
||||
case grpc_codes.Unauthenticated:
|
||||
return "UNAUTHENTICATED"
|
||||
default:
|
||||
return "CODE(" + strconv.FormatInt(int64(code), 10) + ")"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-4
@@ -4,7 +4,4 @@
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
|
||||
// Version is the current release version of the gRPC instrumentation.
|
||||
func Version() string {
|
||||
return "0.63.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
const Version = "0.67.0"
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -165,7 +165,7 @@ func NewClientTrace(ctx context.Context, opts ...ClientTraceOption) *httptrace.C
|
||||
|
||||
ct.tr = ct.tracerProvider.Tracer(
|
||||
ScopeName,
|
||||
trace.WithInstrumentationVersion(Version()),
|
||||
trace.WithInstrumentationVersion(Version),
|
||||
)
|
||||
|
||||
return &httptrace.ClientTrace{
|
||||
@@ -298,7 +298,7 @@ func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) {
|
||||
}
|
||||
|
||||
func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) {
|
||||
var addrs []string
|
||||
addrs := make([]string, 0, len(info.Addrs))
|
||||
for _, netAddr := range info.Addrs {
|
||||
addrs = append(addrs, netAddr.String())
|
||||
}
|
||||
|
||||
Generated
Vendored
+291
@@ -0,0 +1,291 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/client.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv provides OpenTelemetry semantic convention types and
|
||||
// functionality.
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/httpconv"
|
||||
)
|
||||
|
||||
type HTTPClient struct {
|
||||
requestBodySize httpconv.ClientRequestBodySize
|
||||
requestDuration httpconv.ClientRequestDuration
|
||||
}
|
||||
|
||||
func NewHTTPClient(meter metric.Meter) HTTPClient {
|
||||
client := HTTPClient{}
|
||||
|
||||
var err error
|
||||
client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
client.requestDuration, err = httpconv.NewClientRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func (n HTTPClient) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 400 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
|
||||
func (n HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.request.method
|
||||
- http.request.method.original
|
||||
- url.full
|
||||
- server.address
|
||||
- server.port
|
||||
- network.protocol.name
|
||||
- network.protocol.version
|
||||
*/
|
||||
numOfAttributes := 3 // URL, server address, proto, and method.
|
||||
|
||||
var urlHost string
|
||||
if req.URL != nil {
|
||||
urlHost = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if eligiblePort > 0 {
|
||||
numOfAttributes++
|
||||
}
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
numOfAttributes++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
method, originalMethod := n.method(req.Method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, numOfAttributes)
|
||||
|
||||
attrs = append(attrs, method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, originalMethod)
|
||||
}
|
||||
|
||||
var u string
|
||||
if req.URL != nil {
|
||||
// Remove any username/password info that may be in the URL.
|
||||
userinfo := req.URL.User
|
||||
req.URL.User = nil
|
||||
u = req.URL.String()
|
||||
// Restore any username/password info that was removed.
|
||||
req.URL.User = userinfo
|
||||
}
|
||||
attrs = append(attrs, semconv.URLFull(u))
|
||||
|
||||
attrs = append(attrs, semconv.ServerAddress(requestHost))
|
||||
if eligiblePort > 0 {
|
||||
attrs = append(attrs, semconv.ServerPort(eligiblePort))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
|
||||
func (n HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.response.status_code
|
||||
- error.type
|
||||
*/
|
||||
var count int
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
if resp.StatusCode > 0 {
|
||||
attrs = append(attrs, semconv.HTTPResponseStatusCode(resp.StatusCode))
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
errorType := strconv.Itoa(resp.StatusCode)
|
||||
attrs = append(attrs, semconv.ErrorTypeKey.String(errorType))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconv.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconv.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconv.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n HTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 2
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
semconv.ServerAddress(requestHost),
|
||||
n.scheme(req),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconv.ServerPort(port))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type MetricOpts struct {
|
||||
measurement metric.MeasurementOption
|
||||
addOptions metric.AddOption
|
||||
}
|
||||
|
||||
func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
|
||||
return o.measurement
|
||||
}
|
||||
|
||||
func (o MetricOpts) AddOptions() metric.AddOption {
|
||||
return o.addOptions
|
||||
}
|
||||
|
||||
func (n HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts {
|
||||
attributes := n.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
|
||||
return MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
}
|
||||
|
||||
func (n HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts MetricOpts) {
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}()
|
||||
*recordOpts = append(*recordOpts, opts.MeasurementOption())
|
||||
|
||||
n.requestBodySize.Inst().Record(ctx, md.RequestSize, *recordOpts...)
|
||||
n.requestDuration.Inst().Record(ctx, durationToSeconds(md.RequestDuration), *recordOpts...)
|
||||
}
|
||||
|
||||
// TraceAttributes returns attributes for httptrace.
|
||||
func (n HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
semconv.ServerAddress(host),
|
||||
}
|
||||
}
|
||||
|
||||
func (n HTTPClient) scheme(req *http.Request) attribute.KeyValue {
|
||||
if req.URL != nil && req.URL.Scheme != "" {
|
||||
return semconv.URLScheme(req.URL.Scheme)
|
||||
}
|
||||
if req.TLS != nil {
|
||||
return semconv.URLScheme("https")
|
||||
}
|
||||
return semconv.URLScheme("http")
|
||||
}
|
||||
|
||||
func isErrorStatusCode(code int) bool {
|
||||
return code >= 400 || code < 100
|
||||
}
|
||||
Generated
Vendored
-248
@@ -1,248 +0,0 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/env.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/httpconv"
|
||||
)
|
||||
|
||||
// OTelSemConvStabilityOptIn is an environment variable.
|
||||
// That can be set to "http/dup" to keep getting the old HTTP semantic conventions.
|
||||
const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN"
|
||||
|
||||
type ResponseTelemetry struct {
|
||||
StatusCode int
|
||||
ReadBytes int64
|
||||
ReadError error
|
||||
WriteBytes int64
|
||||
WriteError error
|
||||
}
|
||||
|
||||
type HTTPServer struct {
|
||||
requestBodySizeHistogram httpconv.ServerRequestBodySize
|
||||
responseBodySizeHistogram httpconv.ServerResponseBodySize
|
||||
requestDurationHistogram httpconv.ServerRequestDuration
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
// would be the ServerName directive
|
||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||
// server, and the server_name directive
|
||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||
// nginx server. More generically, the primary server name would be the host
|
||||
// header value that matches the default virtual host of an HTTP server. It
|
||||
// should include the host identifier and if a port is used to route to the
|
||||
// server that port identifier should be included as an appropriate port
|
||||
// suffix.
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
|
||||
return CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)
|
||||
}
|
||||
|
||||
func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
CurrentHTTPServer{}.NetworkTransportAttr(network),
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
|
||||
func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
return CurrentHTTPServer{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (s HTTPServer) Route(route string) attribute.KeyValue {
|
||||
return CurrentHTTPServer{}.Route(route)
|
||||
}
|
||||
|
||||
// Status returns a span status code and message for an HTTP status code
|
||||
// value returned by a server. Status codes in the 400-499 range are not
|
||||
// returned as errors.
|
||||
func (s HTTPServer) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 500 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
type ServerMetricData struct {
|
||||
ServerName string
|
||||
ResponseSize int64
|
||||
|
||||
MetricData
|
||||
MetricAttributes
|
||||
}
|
||||
|
||||
type MetricAttributes struct {
|
||||
Req *http.Request
|
||||
StatusCode int
|
||||
AdditionalAttributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
type MetricData struct {
|
||||
RequestSize int64
|
||||
|
||||
// The request duration, in milliseconds
|
||||
ElapsedTime float64
|
||||
}
|
||||
|
||||
var (
|
||||
metricAddOptionPool = &sync.Pool{
|
||||
New: func() any {
|
||||
return &[]metric.AddOption{}
|
||||
},
|
||||
}
|
||||
|
||||
metricRecordOptionPool = &sync.Pool{
|
||||
New: func() any {
|
||||
return &[]metric.RecordOption{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
|
||||
attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
*recordOpts = append(*recordOpts, o)
|
||||
s.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...)
|
||||
s.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...)
|
||||
s.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o)
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}
|
||||
|
||||
// hasOptIn returns true if the comma-separated version string contains the
|
||||
// exact optIn value.
|
||||
func hasOptIn(version, optIn string) bool {
|
||||
for _, v := range strings.Split(version, ",") {
|
||||
if strings.TrimSpace(v) == optIn {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func NewHTTPServer(meter metric.Meter) HTTPServer {
|
||||
server := HTTPServer{}
|
||||
|
||||
var err error
|
||||
server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.requestDurationHistogram, err = httpconv.NewServerRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(
|
||||
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
|
||||
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
|
||||
),
|
||||
)
|
||||
handleErr(err)
|
||||
return server
|
||||
}
|
||||
|
||||
type HTTPClient struct {
|
||||
requestBodySize httpconv.ClientRequestBodySize
|
||||
requestDuration httpconv.ClientRequestDuration
|
||||
}
|
||||
|
||||
func NewHTTPClient(meter metric.Meter) HTTPClient {
|
||||
client := HTTPClient{}
|
||||
|
||||
var err error
|
||||
client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
client.requestDuration, err = httpconv.NewClientRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.RequestTraceAttrs(req)
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
func (c HTTPClient) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 400 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.ErrorType(err)
|
||||
}
|
||||
|
||||
type MetricOpts struct {
|
||||
measurement metric.MeasurementOption
|
||||
addOptions metric.AddOption
|
||||
}
|
||||
|
||||
func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
|
||||
return o.measurement
|
||||
}
|
||||
|
||||
func (o MetricOpts) AddOptions() metric.AddOption {
|
||||
return o.addOptions
|
||||
}
|
||||
|
||||
func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts {
|
||||
opts := map[string]MetricOpts{}
|
||||
|
||||
attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
opts["new"] = MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) {
|
||||
s.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption())
|
||||
s.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption())
|
||||
}
|
||||
|
||||
func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.TraceAttributes(host)
|
||||
}
|
||||
Generated
Vendored
+4
-4
@@ -6,10 +6,10 @@ package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/
|
||||
// Generate semconv package:
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/bench_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=bench_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/common_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=common_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/env.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=env.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/env_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=env_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/httpconv.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=httpconv.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/httpconv_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=httpconv_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/server.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/server_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/client.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/client_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/httpconvtest_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=httpconvtest_test.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/util.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=util.go
|
||||
//go:generate gotmpl --body=../../../../../../../internal/shared/semconv/util_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\" }" --out=util_test.go
|
||||
|
||||
Generated
Vendored
-517
@@ -1,517 +0,0 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/httpconv.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv provides OpenTelemetry semantic convention types and
|
||||
// functionality.
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
)
|
||||
|
||||
type RequestTraceAttrsOpts struct {
|
||||
// If set, this is used as value for the "http.client_ip" attribute.
|
||||
HTTPClientIP string
|
||||
}
|
||||
|
||||
type CurrentHTTPServer struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
// would be the ServerName directive
|
||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||
// server, and the server_name directive
|
||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||
// nginx server. More generically, the primary server name would be the host
|
||||
// header value that matches the default virtual host of an HTTP server. It
|
||||
// should include the host identifier and if a port is used to route to the
|
||||
// server that port identifier should be included as an appropriate port
|
||||
// suffix.
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
|
||||
count := 3 // ServerAddress, Method, Scheme
|
||||
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
method, methodOriginal := n.method(req.Method)
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
count++
|
||||
}
|
||||
|
||||
scheme := n.scheme(req.TLS != nil)
|
||||
|
||||
peer, peerPort := SplitHostPort(req.RemoteAddr)
|
||||
if peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
count++
|
||||
if peerPort > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
// For client IP, use, in order:
|
||||
// 1. The value passed in the options
|
||||
// 2. The value in the X-Forwarded-For header
|
||||
// 3. The peer address
|
||||
clientIP := opts.HTTPClientIP
|
||||
if clientIP == "" {
|
||||
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
|
||||
if clientIP == "" {
|
||||
clientIP = peer
|
||||
}
|
||||
}
|
||||
if clientIP != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
count++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
route := httpRoute(req.Pattern)
|
||||
if route != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
attrs = append(attrs,
|
||||
semconvNew.ServerAddress(host),
|
||||
method,
|
||||
scheme,
|
||||
)
|
||||
|
||||
if hostPort > 0 {
|
||||
attrs = append(attrs, semconvNew.ServerPort(hostPort))
|
||||
}
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, methodOriginal)
|
||||
}
|
||||
|
||||
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
attrs = append(attrs, semconvNew.NetworkPeerAddress(peer))
|
||||
if peerPort > 0 {
|
||||
attrs = append(attrs, semconvNew.NetworkPeerPort(peerPort))
|
||||
}
|
||||
}
|
||||
|
||||
if useragent != "" {
|
||||
attrs = append(attrs, semconvNew.UserAgentOriginal(useragent))
|
||||
}
|
||||
|
||||
if clientIP != "" {
|
||||
attrs = append(attrs, semconvNew.ClientAddress(clientIP))
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
attrs = append(attrs, semconvNew.URLPath(req.URL.Path))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
attrs = append(attrs, n.Route(route))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
return semconvNew.NetworkTransportTCP
|
||||
case "udp", "udp4", "udp6":
|
||||
return semconvNew.NetworkTransportUDP
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
return semconvNew.NetworkTransportUnix
|
||||
default:
|
||||
return semconvNew.NetworkTransportPipe
|
||||
}
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconvNew.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter
|
||||
if https {
|
||||
return semconvNew.URLScheme("https")
|
||||
}
|
||||
return semconvNew.URLScheme("http")
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP
|
||||
// response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will
|
||||
// be omitted.
|
||||
func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
var count int
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
attributes := make([]attribute.KeyValue, 0, count)
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestBodySize(int(resp.ReadBytes)),
|
||||
)
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPResponseBodySize(int(resp.WriteBytes)),
|
||||
)
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPResponseStatusCode(resp.StatusCode),
|
||||
)
|
||||
}
|
||||
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (n CurrentHTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconvNew.HTTPRoute(route)
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 3
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
num++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
n.scheme(req.TLS != nil),
|
||||
semconvNew.ServerAddress(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attributes = append(attributes, semconvNew.ServerPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type CurrentHTTPClient struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
|
||||
func (n CurrentHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.request.method
|
||||
- http.request.method.original
|
||||
- url.full
|
||||
- server.address
|
||||
- server.port
|
||||
- network.protocol.name
|
||||
- network.protocol.version
|
||||
*/
|
||||
numOfAttributes := 3 // URL, server address, proto, and method.
|
||||
|
||||
var urlHost string
|
||||
if req.URL != nil {
|
||||
urlHost = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if eligiblePort > 0 {
|
||||
numOfAttributes++
|
||||
}
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
numOfAttributes++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
method, originalMethod := n.method(req.Method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, numOfAttributes)
|
||||
|
||||
attrs = append(attrs, method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, originalMethod)
|
||||
}
|
||||
|
||||
var u string
|
||||
if req.URL != nil {
|
||||
// Remove any username/password info that may be in the URL.
|
||||
userinfo := req.URL.User
|
||||
req.URL.User = nil
|
||||
u = req.URL.String()
|
||||
// Restore any username/password info that was removed.
|
||||
req.URL.User = userinfo
|
||||
}
|
||||
attrs = append(attrs, semconvNew.URLFull(u))
|
||||
|
||||
attrs = append(attrs, semconvNew.ServerAddress(requestHost))
|
||||
if eligiblePort > 0 {
|
||||
attrs = append(attrs, semconvNew.ServerPort(eligiblePort))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
|
||||
func (n CurrentHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.response.status_code
|
||||
- error.type
|
||||
*/
|
||||
var count int
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
if resp.StatusCode > 0 {
|
||||
attrs = append(attrs, semconvNew.HTTPResponseStatusCode(resp.StatusCode))
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
errorType := strconv.Itoa(resp.StatusCode)
|
||||
attrs = append(attrs, semconvNew.ErrorTypeKey.String(errorType))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
t := reflect.TypeOf(err)
|
||||
var value string
|
||||
if t.PkgPath() == "" && t.Name() == "" {
|
||||
// Likely a builtin type.
|
||||
value = t.String()
|
||||
} else {
|
||||
value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
return semconvNew.ErrorTypeOther
|
||||
}
|
||||
|
||||
return semconvNew.ErrorTypeKey.String(value)
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconvNew.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 2
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
semconvNew.ServerAddress(requestHost),
|
||||
n.scheme(req),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconvNew.ServerPort(port))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
// TraceAttributes returns attributes for httptrace.
|
||||
func (n CurrentHTTPClient) TraceAttributes(host string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
semconvNew.ServerAddress(host),
|
||||
}
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) scheme(req *http.Request) attribute.KeyValue {
|
||||
if req.URL != nil && req.URL.Scheme != "" {
|
||||
return semconvNew.URLScheme(req.URL.Scheme)
|
||||
}
|
||||
if req.TLS != nil {
|
||||
return semconvNew.URLScheme("https")
|
||||
}
|
||||
return semconvNew.URLScheme("http")
|
||||
}
|
||||
|
||||
func isErrorStatusCode(code int) bool {
|
||||
return code >= 400 || code < 100
|
||||
}
|
||||
Generated
Vendored
+396
@@ -0,0 +1,396 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/server.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv provides OpenTelemetry semantic convention types and
|
||||
// functionality.
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/httpconv"
|
||||
)
|
||||
|
||||
type RequestTraceAttrsOpts struct {
|
||||
// If set, this is used as value for the "http.client_ip" attribute.
|
||||
HTTPClientIP string
|
||||
}
|
||||
|
||||
type ResponseTelemetry struct {
|
||||
StatusCode int
|
||||
ReadBytes int64
|
||||
ReadError error
|
||||
WriteBytes int64
|
||||
WriteError error
|
||||
}
|
||||
|
||||
type HTTPServer struct {
|
||||
requestBodySizeHistogram httpconv.ServerRequestBodySize
|
||||
responseBodySizeHistogram httpconv.ServerResponseBodySize
|
||||
requestDurationHistogram httpconv.ServerRequestDuration
|
||||
}
|
||||
|
||||
func NewHTTPServer(meter metric.Meter) HTTPServer {
|
||||
server := HTTPServer{}
|
||||
|
||||
var err error
|
||||
server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.requestDurationHistogram, err = httpconv.NewServerRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(
|
||||
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
|
||||
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
|
||||
),
|
||||
)
|
||||
handleErr(err)
|
||||
return server
|
||||
}
|
||||
|
||||
// Status returns a span status code and message for an HTTP status code
|
||||
// value returned by a server. Status codes in the 400-499 range are not
|
||||
// returned as errors.
|
||||
func (n HTTPServer) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 500 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
// would be the ServerName directive
|
||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||
// server, and the server_name directive
|
||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||
// nginx server. More generically, the primary server name would be the host
|
||||
// header value that matches the default virtual host of an HTTP server. It
|
||||
// should include the host identifier and if a port is used to route to the
|
||||
// server that port identifier should be included as an appropriate port
|
||||
// suffix.
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (n HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
|
||||
count := 3 // ServerAddress, Method, Scheme
|
||||
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
method, methodOriginal := n.method(req.Method)
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
count++
|
||||
}
|
||||
|
||||
scheme := n.scheme(req.TLS != nil)
|
||||
|
||||
peer, peerPort := SplitHostPort(req.RemoteAddr)
|
||||
if peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
count++
|
||||
if peerPort > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
// For client IP, use, in order:
|
||||
// 1. The value passed in the options
|
||||
// 2. The value in the X-Forwarded-For header
|
||||
// 3. The peer address
|
||||
clientIP := opts.HTTPClientIP
|
||||
if clientIP == "" {
|
||||
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
|
||||
if clientIP == "" {
|
||||
clientIP = peer
|
||||
}
|
||||
}
|
||||
if clientIP != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
count++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
route := httpRoute(req.Pattern)
|
||||
if route != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
attrs = append(attrs,
|
||||
semconv.ServerAddress(host),
|
||||
method,
|
||||
scheme,
|
||||
)
|
||||
|
||||
if hostPort > 0 {
|
||||
attrs = append(attrs, semconv.ServerPort(hostPort))
|
||||
}
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, methodOriginal)
|
||||
}
|
||||
|
||||
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
attrs = append(attrs, semconv.NetworkPeerAddress(peer))
|
||||
if peerPort > 0 {
|
||||
attrs = append(attrs, semconv.NetworkPeerPort(peerPort))
|
||||
}
|
||||
}
|
||||
|
||||
if useragent != "" {
|
||||
attrs = append(attrs, semconv.UserAgentOriginal(useragent))
|
||||
}
|
||||
|
||||
if clientIP != "" {
|
||||
attrs = append(attrs, semconv.ClientAddress(clientIP))
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
attrs = append(attrs, semconv.URLPath(req.URL.Path))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
attrs = append(attrs, n.Route(route))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
|
||||
attr := semconv.NetworkTransportPipe
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
attr = semconv.NetworkTransportTCP
|
||||
case "udp", "udp4", "udp6":
|
||||
attr = semconv.NetworkTransportUDP
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
attr = semconv.NetworkTransportUnix
|
||||
}
|
||||
|
||||
return []attribute.KeyValue{attr}
|
||||
}
|
||||
|
||||
type ServerMetricData struct {
|
||||
ServerName string
|
||||
ResponseSize int64
|
||||
|
||||
MetricData
|
||||
MetricAttributes
|
||||
}
|
||||
|
||||
type MetricAttributes struct {
|
||||
Req *http.Request
|
||||
StatusCode int
|
||||
Route string
|
||||
AdditionalAttributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
type MetricData struct {
|
||||
RequestSize int64
|
||||
RequestDuration time.Duration
|
||||
}
|
||||
|
||||
var (
|
||||
metricRecordOptionPool = &sync.Pool{
|
||||
New: func() any {
|
||||
return &[]metric.RecordOption{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
|
||||
attributes := n.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.Route, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
*recordOpts = append(*recordOpts, o)
|
||||
n.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...)
|
||||
n.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...)
|
||||
n.requestDurationHistogram.Inst().Record(ctx, durationToSeconds(md.RequestDuration), o)
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}
|
||||
|
||||
func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconv.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconv.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconv.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n HTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter
|
||||
if https {
|
||||
return semconv.URLScheme("https")
|
||||
}
|
||||
return semconv.URLScheme("http")
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP
|
||||
// response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will
|
||||
// be omitted.
|
||||
func (n HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
var count int
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
attributes := make([]attribute.KeyValue, 0, count)
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPRequestBodySize(int(resp.ReadBytes)),
|
||||
)
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPResponseBodySize(int(resp.WriteBytes)),
|
||||
)
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPResponseStatusCode(resp.StatusCode),
|
||||
)
|
||||
}
|
||||
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (n HTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconv.HTTPRoute(route)
|
||||
}
|
||||
|
||||
func (n HTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, route string, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 3
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
num++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
n.scheme(req.TLS != nil),
|
||||
semconv.ServerAddress(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attributes = append(attributes, semconv.ServerPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
attributes = append(attributes, semconv.HTTPRoute(route))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
Generated
Vendored
+7
-1
@@ -11,10 +11,11 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
// SplitHostPort splits a network address hostport of the form "host",
|
||||
@@ -125,3 +126,8 @@ func standardizeHTTPMethod(method string) string {
|
||||
}
|
||||
return method
|
||||
}
|
||||
|
||||
func durationToSeconds(d time.Duration) float64 {
|
||||
// Use floating point division here for higher precision (instead of Seconds method).
|
||||
return float64(d) / float64(time.Second)
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-4
@@ -4,7 +4,4 @@
|
||||
package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||
|
||||
// Version is the current release version of the httptrace instrumentation.
|
||||
func Version() string {
|
||||
return "0.63.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
const Version = "0.67.0"
|
||||
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DefaultClient is the default Client and is used by Get, Head, Post and PostForm.
|
||||
// Please be careful of initialization order - for example, if you change
|
||||
// the global propagator, the DefaultClient might still be using the old one.
|
||||
var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)}
|
||||
|
||||
// Get is a convenient replacement for http.Get that adds a span around the request.
|
||||
func Get(ctx context.Context, targetURL string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DefaultClient.Do(req)
|
||||
}
|
||||
|
||||
// Head is a convenient replacement for http.Head that adds a span around the request.
|
||||
func Head(ctx context.Context, targetURL string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, targetURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DefaultClient.Do(req)
|
||||
}
|
||||
|
||||
// Post is a convenient replacement for http.Post that adds a span around the request.
|
||||
func Post(ctx context.Context, targetURL, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
return DefaultClient.Do(req)
|
||||
}
|
||||
|
||||
// PostForm is a convenient replacement for http.PostForm that adds a span around the request.
|
||||
func PostForm(ctx context.Context, targetURL string, data url.Values) (resp *http.Response, err error) {
|
||||
return Post(ctx, targetURL, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||
}
|
||||
+1
-1
@@ -23,5 +23,5 @@ const (
|
||||
type Filter func(*http.Request) bool
|
||||
|
||||
func newTracer(tp trace.TracerProvider) trace.Tracer {
|
||||
return tp.Tracer(ScopeName, trace.WithInstrumentationVersion(Version()))
|
||||
return tp.Tracer(ScopeName, trace.WithInstrumentationVersion(Version))
|
||||
}
|
||||
|
||||
+9
-15
@@ -26,7 +26,6 @@ type config struct {
|
||||
Meter metric.Meter
|
||||
Propagators propagation.TextMapPropagator
|
||||
SpanStartOptions []trace.SpanStartOption
|
||||
PublicEndpoint bool
|
||||
PublicEndpointFn func(*http.Request) bool
|
||||
ReadEvent bool
|
||||
WriteEvent bool
|
||||
@@ -67,7 +66,7 @@ func newConfig(opts ...Option) *config {
|
||||
|
||||
c.Meter = c.MeterProvider.Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(Version()),
|
||||
metric.WithInstrumentationVersion(Version),
|
||||
)
|
||||
|
||||
return c
|
||||
@@ -93,20 +92,10 @@ func WithMeterProvider(provider metric.MeterProvider) Option {
|
||||
})
|
||||
}
|
||||
|
||||
// WithPublicEndpoint configures the Handler to link the span with an incoming
|
||||
// span context. If this option is not provided, then the association is a child
|
||||
// association instead of a link.
|
||||
func WithPublicEndpoint() Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.PublicEndpoint = true
|
||||
})
|
||||
}
|
||||
|
||||
// WithPublicEndpointFn runs with every request, and allows conditionally
|
||||
// configuring the Handler to link the span with an incoming span context. If
|
||||
// this option is not provided or returns false, then the association is a
|
||||
// child association instead of a link.
|
||||
// Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn.
|
||||
func WithPublicEndpointFn(fn func(*http.Request) bool) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.PublicEndpointFn = fn
|
||||
@@ -143,11 +132,13 @@ func WithFilter(f Filter) Option {
|
||||
})
|
||||
}
|
||||
|
||||
type event int
|
||||
// Event represents message event types for [WithMessageEvents].
|
||||
type Event int
|
||||
|
||||
// Different types of events that can be recorded, see WithMessageEvents.
|
||||
const (
|
||||
ReadEvents event = iota
|
||||
unspecifiedEvents Event = iota
|
||||
ReadEvents
|
||||
WriteEvents
|
||||
)
|
||||
|
||||
@@ -160,7 +151,7 @@ const (
|
||||
// using the ReadBytesKey
|
||||
// - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write
|
||||
// using the WriteBytesKey
|
||||
func WithMessageEvents(events ...event) Option {
|
||||
func WithMessageEvents(events ...Event) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
for _, e := range events {
|
||||
switch e {
|
||||
@@ -203,6 +194,9 @@ func WithServerName(server string) Option {
|
||||
|
||||
// WithMetricAttributesFn returns an Option to set a function that maps an HTTP request to a slice of attribute.KeyValue.
|
||||
// These attributes will be included in metrics for every request.
|
||||
//
|
||||
// Deprecated: WithMetricAttributesFn is deprecated and will be removed in a
|
||||
// future release. Use [Labeler] instead.
|
||||
func WithMetricAttributesFn(metricAttributesFn func(r *http.Request) []attribute.KeyValue) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.MetricAttributesFn = metricAttributesFn
|
||||
|
||||
+1
-2
@@ -2,6 +2,5 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package otelhttp provides an http.Handler and functions that are intended
|
||||
// to be used to add tracing by wrapping existing handlers (with Handler) and
|
||||
// routes WithRouteTag.
|
||||
// to be used to add tracing by wrapping existing handlers.
|
||||
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
|
||||
+12
-33
@@ -29,7 +29,6 @@ type middleware struct {
|
||||
writeEvent bool
|
||||
filters []Filter
|
||||
spanNameFormatter func(string, *http.Request) string
|
||||
publicEndpoint bool
|
||||
publicEndpointFn func(*http.Request) bool
|
||||
metricAttributesFn func(*http.Request) []attribute.KeyValue
|
||||
|
||||
@@ -77,7 +76,6 @@ func (h *middleware) configure(c *config) {
|
||||
h.writeEvent = c.WriteEvent
|
||||
h.filters = c.Filters
|
||||
h.spanNameFormatter = c.SpanNameFormatter
|
||||
h.publicEndpoint = c.PublicEndpoint
|
||||
h.publicEndpointFn = c.PublicEndpointFn
|
||||
h.server = c.ServerName
|
||||
h.semconv = semconv.NewHTTPServer(c.Meter)
|
||||
@@ -102,7 +100,7 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
}
|
||||
|
||||
opts = append(opts, h.spanStartOptions...)
|
||||
if h.publicEndpoint || (h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx))) {
|
||||
if h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx)) {
|
||||
opts = append(opts, trace.WithNewRoot())
|
||||
// Linking incoming span context if any for public endpoint.
|
||||
if s := trace.SpanContextFromContext(ctx); s.IsValid() && s.IsRemote() {
|
||||
@@ -186,30 +184,26 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
statusCode := rww.StatusCode()
|
||||
bytesWritten := rww.BytesWritten()
|
||||
span.SetStatus(h.semconv.Status(statusCode))
|
||||
bytesRead := bw.BytesRead()
|
||||
span.SetAttributes(h.semconv.ResponseTraceAttrs(semconv.ResponseTelemetry{
|
||||
StatusCode: statusCode,
|
||||
ReadBytes: bw.BytesRead(),
|
||||
ReadBytes: bytesRead,
|
||||
ReadError: bw.Error(),
|
||||
WriteBytes: bytesWritten,
|
||||
WriteError: rww.Error(),
|
||||
})...)
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||
|
||||
metricAttributes := semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: statusCode,
|
||||
AdditionalAttributes: append(labeler.Get(), h.metricAttributesFromRequest(r)...),
|
||||
}
|
||||
|
||||
h.semconv.RecordMetrics(ctx, semconv.ServerMetricData{
|
||||
ServerName: h.server,
|
||||
ResponseSize: bytesWritten,
|
||||
MetricAttributes: metricAttributes,
|
||||
ServerName: h.server,
|
||||
ResponseSize: bytesWritten,
|
||||
MetricAttributes: semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: statusCode,
|
||||
AdditionalAttributes: append(labeler.Get(), h.metricAttributesFromRequest(r)...),
|
||||
},
|
||||
MetricData: semconv.MetricData{
|
||||
RequestSize: bw.BytesRead(),
|
||||
ElapsedTime: elapsedTime,
|
||||
RequestSize: bytesRead,
|
||||
RequestDuration: time.Since(requestStartTime),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -221,18 +215,3 @@ func (h *middleware) metricAttributesFromRequest(r *http.Request) []attribute.Ke
|
||||
}
|
||||
return attributeForRequest
|
||||
}
|
||||
|
||||
// WithRouteTag annotates spans and metrics with the provided route name
|
||||
// with HTTP route attribute.
|
||||
func WithRouteTag(route string, h http.Handler) http.Handler {
|
||||
attr := semconv.NewHTTPServer(nil).Route(route)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
span := trace.SpanFromContext(r.Context())
|
||||
span.SetAttributes(attr)
|
||||
|
||||
labeler, _ := LabelerFromContext(r.Context())
|
||||
labeler.Add(attr)
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
Generated
Vendored
+8
-1
@@ -61,7 +61,7 @@ func (w *RespWriterWrapper) Write(p []byte) (int, error) {
|
||||
|
||||
// WriteHeader persists initial statusCode for span attribution.
|
||||
// All calls to WriteHeader will be propagated to the underlying ResponseWriter
|
||||
// and will persist the statusCode from the first call.
|
||||
// and will persist the statusCode from the first call (except for informational response status codes).
|
||||
// Blocking consecutive calls to WriteHeader alters expected behavior and will
|
||||
// remove warning logs from net/http where developers will notice incorrect handler implementations.
|
||||
func (w *RespWriterWrapper) WriteHeader(statusCode int) {
|
||||
@@ -77,6 +77,13 @@ func (w *RespWriterWrapper) WriteHeader(statusCode int) {
|
||||
// parent method.
|
||||
func (w *RespWriterWrapper) writeHeader(statusCode int) {
|
||||
if !w.wroteHeader {
|
||||
// Ignore informational response status codes.
|
||||
// Based on https://github.com/golang/go/blob/go1.24.1/src/net/http/server.go#L1216
|
||||
if statusCode >= 100 && statusCode <= 199 && statusCode != http.StatusSwitchingProtocols {
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
|
||||
w.wroteHeader = true
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
|
||||
Generated
Vendored
+291
@@ -0,0 +1,291 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/client.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv provides OpenTelemetry semantic convention types and
|
||||
// functionality.
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/httpconv"
|
||||
)
|
||||
|
||||
type HTTPClient struct {
|
||||
requestBodySize httpconv.ClientRequestBodySize
|
||||
requestDuration httpconv.ClientRequestDuration
|
||||
}
|
||||
|
||||
func NewHTTPClient(meter metric.Meter) HTTPClient {
|
||||
client := HTTPClient{}
|
||||
|
||||
var err error
|
||||
client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
client.requestDuration, err = httpconv.NewClientRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func (n HTTPClient) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 400 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
|
||||
func (n HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.request.method
|
||||
- http.request.method.original
|
||||
- url.full
|
||||
- server.address
|
||||
- server.port
|
||||
- network.protocol.name
|
||||
- network.protocol.version
|
||||
*/
|
||||
numOfAttributes := 3 // URL, server address, proto, and method.
|
||||
|
||||
var urlHost string
|
||||
if req.URL != nil {
|
||||
urlHost = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if eligiblePort > 0 {
|
||||
numOfAttributes++
|
||||
}
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
numOfAttributes++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
method, originalMethod := n.method(req.Method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, numOfAttributes)
|
||||
|
||||
attrs = append(attrs, method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, originalMethod)
|
||||
}
|
||||
|
||||
var u string
|
||||
if req.URL != nil {
|
||||
// Remove any username/password info that may be in the URL.
|
||||
userinfo := req.URL.User
|
||||
req.URL.User = nil
|
||||
u = req.URL.String()
|
||||
// Restore any username/password info that was removed.
|
||||
req.URL.User = userinfo
|
||||
}
|
||||
attrs = append(attrs, semconv.URLFull(u))
|
||||
|
||||
attrs = append(attrs, semconv.ServerAddress(requestHost))
|
||||
if eligiblePort > 0 {
|
||||
attrs = append(attrs, semconv.ServerPort(eligiblePort))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
|
||||
func (n HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.response.status_code
|
||||
- error.type
|
||||
*/
|
||||
var count int
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
if resp.StatusCode > 0 {
|
||||
attrs = append(attrs, semconv.HTTPResponseStatusCode(resp.StatusCode))
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
errorType := strconv.Itoa(resp.StatusCode)
|
||||
attrs = append(attrs, semconv.ErrorTypeKey.String(errorType))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconv.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconv.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconv.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n HTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 2
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
semconv.ServerAddress(requestHost),
|
||||
n.scheme(req),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconv.ServerPort(port))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type MetricOpts struct {
|
||||
measurement metric.MeasurementOption
|
||||
addOptions metric.AddOption
|
||||
}
|
||||
|
||||
func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
|
||||
return o.measurement
|
||||
}
|
||||
|
||||
func (o MetricOpts) AddOptions() metric.AddOption {
|
||||
return o.addOptions
|
||||
}
|
||||
|
||||
func (n HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts {
|
||||
attributes := n.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
|
||||
return MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
}
|
||||
|
||||
func (n HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts MetricOpts) {
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}()
|
||||
*recordOpts = append(*recordOpts, opts.MeasurementOption())
|
||||
|
||||
n.requestBodySize.Inst().Record(ctx, md.RequestSize, *recordOpts...)
|
||||
n.requestDuration.Inst().Record(ctx, durationToSeconds(md.RequestDuration), *recordOpts...)
|
||||
}
|
||||
|
||||
// TraceAttributes returns attributes for httptrace.
|
||||
func (n HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
semconv.ServerAddress(host),
|
||||
}
|
||||
}
|
||||
|
||||
func (n HTTPClient) scheme(req *http.Request) attribute.KeyValue {
|
||||
if req.URL != nil && req.URL.Scheme != "" {
|
||||
return semconv.URLScheme(req.URL.Scheme)
|
||||
}
|
||||
if req.TLS != nil {
|
||||
return semconv.URLScheme("https")
|
||||
}
|
||||
return semconv.URLScheme("http")
|
||||
}
|
||||
|
||||
func isErrorStatusCode(code int) bool {
|
||||
return code >= 400 || code < 100
|
||||
}
|
||||
Generated
Vendored
-248
@@ -1,248 +0,0 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/env.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/httpconv"
|
||||
)
|
||||
|
||||
// OTelSemConvStabilityOptIn is an environment variable.
|
||||
// That can be set to "http/dup" to keep getting the old HTTP semantic conventions.
|
||||
const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN"
|
||||
|
||||
type ResponseTelemetry struct {
|
||||
StatusCode int
|
||||
ReadBytes int64
|
||||
ReadError error
|
||||
WriteBytes int64
|
||||
WriteError error
|
||||
}
|
||||
|
||||
type HTTPServer struct {
|
||||
requestBodySizeHistogram httpconv.ServerRequestBodySize
|
||||
responseBodySizeHistogram httpconv.ServerResponseBodySize
|
||||
requestDurationHistogram httpconv.ServerRequestDuration
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
// would be the ServerName directive
|
||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||
// server, and the server_name directive
|
||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||
// nginx server. More generically, the primary server name would be the host
|
||||
// header value that matches the default virtual host of an HTTP server. It
|
||||
// should include the host identifier and if a port is used to route to the
|
||||
// server that port identifier should be included as an appropriate port
|
||||
// suffix.
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
|
||||
return CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)
|
||||
}
|
||||
|
||||
func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
CurrentHTTPServer{}.NetworkTransportAttr(network),
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
|
||||
func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
return CurrentHTTPServer{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (s HTTPServer) Route(route string) attribute.KeyValue {
|
||||
return CurrentHTTPServer{}.Route(route)
|
||||
}
|
||||
|
||||
// Status returns a span status code and message for an HTTP status code
|
||||
// value returned by a server. Status codes in the 400-499 range are not
|
||||
// returned as errors.
|
||||
func (s HTTPServer) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 500 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
type ServerMetricData struct {
|
||||
ServerName string
|
||||
ResponseSize int64
|
||||
|
||||
MetricData
|
||||
MetricAttributes
|
||||
}
|
||||
|
||||
type MetricAttributes struct {
|
||||
Req *http.Request
|
||||
StatusCode int
|
||||
AdditionalAttributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
type MetricData struct {
|
||||
RequestSize int64
|
||||
|
||||
// The request duration, in milliseconds
|
||||
ElapsedTime float64
|
||||
}
|
||||
|
||||
var (
|
||||
metricAddOptionPool = &sync.Pool{
|
||||
New: func() any {
|
||||
return &[]metric.AddOption{}
|
||||
},
|
||||
}
|
||||
|
||||
metricRecordOptionPool = &sync.Pool{
|
||||
New: func() any {
|
||||
return &[]metric.RecordOption{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
|
||||
attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
*recordOpts = append(*recordOpts, o)
|
||||
s.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...)
|
||||
s.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...)
|
||||
s.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o)
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}
|
||||
|
||||
// hasOptIn returns true if the comma-separated version string contains the
|
||||
// exact optIn value.
|
||||
func hasOptIn(version, optIn string) bool {
|
||||
for _, v := range strings.Split(version, ",") {
|
||||
if strings.TrimSpace(v) == optIn {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func NewHTTPServer(meter metric.Meter) HTTPServer {
|
||||
server := HTTPServer{}
|
||||
|
||||
var err error
|
||||
server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.requestDurationHistogram, err = httpconv.NewServerRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(
|
||||
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
|
||||
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
|
||||
),
|
||||
)
|
||||
handleErr(err)
|
||||
return server
|
||||
}
|
||||
|
||||
type HTTPClient struct {
|
||||
requestBodySize httpconv.ClientRequestBodySize
|
||||
requestDuration httpconv.ClientRequestDuration
|
||||
}
|
||||
|
||||
func NewHTTPClient(meter metric.Meter) HTTPClient {
|
||||
client := HTTPClient{}
|
||||
|
||||
var err error
|
||||
client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
client.requestDuration, err = httpconv.NewClientRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.RequestTraceAttrs(req)
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
func (c HTTPClient) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 400 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.ErrorType(err)
|
||||
}
|
||||
|
||||
type MetricOpts struct {
|
||||
measurement metric.MeasurementOption
|
||||
addOptions metric.AddOption
|
||||
}
|
||||
|
||||
func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
|
||||
return o.measurement
|
||||
}
|
||||
|
||||
func (o MetricOpts) AddOptions() metric.AddOption {
|
||||
return o.addOptions
|
||||
}
|
||||
|
||||
func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts {
|
||||
opts := map[string]MetricOpts{}
|
||||
|
||||
attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
opts["new"] = MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) {
|
||||
s.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption())
|
||||
s.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption())
|
||||
}
|
||||
|
||||
func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
|
||||
return CurrentHTTPClient{}.TraceAttributes(host)
|
||||
}
|
||||
Generated
Vendored
+4
-4
@@ -6,10 +6,10 @@ package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/
|
||||
// Generate semconv package:
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/bench_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=bench_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/common_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=common_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/server.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/server_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/client.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/client_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconvtest_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconvtest_test.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/util.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util.go
|
||||
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/util_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util_test.go
|
||||
|
||||
Generated
Vendored
-517
@@ -1,517 +0,0 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/httpconv.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv provides OpenTelemetry semantic convention types and
|
||||
// functionality.
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
)
|
||||
|
||||
type RequestTraceAttrsOpts struct {
|
||||
// If set, this is used as value for the "http.client_ip" attribute.
|
||||
HTTPClientIP string
|
||||
}
|
||||
|
||||
type CurrentHTTPServer struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
// would be the ServerName directive
|
||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||
// server, and the server_name directive
|
||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||
// nginx server. More generically, the primary server name would be the host
|
||||
// header value that matches the default virtual host of an HTTP server. It
|
||||
// should include the host identifier and if a port is used to route to the
|
||||
// server that port identifier should be included as an appropriate port
|
||||
// suffix.
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
|
||||
count := 3 // ServerAddress, Method, Scheme
|
||||
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
method, methodOriginal := n.method(req.Method)
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
count++
|
||||
}
|
||||
|
||||
scheme := n.scheme(req.TLS != nil)
|
||||
|
||||
peer, peerPort := SplitHostPort(req.RemoteAddr)
|
||||
if peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
count++
|
||||
if peerPort > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
// For client IP, use, in order:
|
||||
// 1. The value passed in the options
|
||||
// 2. The value in the X-Forwarded-For header
|
||||
// 3. The peer address
|
||||
clientIP := opts.HTTPClientIP
|
||||
if clientIP == "" {
|
||||
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
|
||||
if clientIP == "" {
|
||||
clientIP = peer
|
||||
}
|
||||
}
|
||||
if clientIP != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
count++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
route := httpRoute(req.Pattern)
|
||||
if route != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
attrs = append(attrs,
|
||||
semconvNew.ServerAddress(host),
|
||||
method,
|
||||
scheme,
|
||||
)
|
||||
|
||||
if hostPort > 0 {
|
||||
attrs = append(attrs, semconvNew.ServerPort(hostPort))
|
||||
}
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, methodOriginal)
|
||||
}
|
||||
|
||||
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
attrs = append(attrs, semconvNew.NetworkPeerAddress(peer))
|
||||
if peerPort > 0 {
|
||||
attrs = append(attrs, semconvNew.NetworkPeerPort(peerPort))
|
||||
}
|
||||
}
|
||||
|
||||
if useragent != "" {
|
||||
attrs = append(attrs, semconvNew.UserAgentOriginal(useragent))
|
||||
}
|
||||
|
||||
if clientIP != "" {
|
||||
attrs = append(attrs, semconvNew.ClientAddress(clientIP))
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
attrs = append(attrs, semconvNew.URLPath(req.URL.Path))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
attrs = append(attrs, n.Route(route))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
return semconvNew.NetworkTransportTCP
|
||||
case "udp", "udp4", "udp6":
|
||||
return semconvNew.NetworkTransportUDP
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
return semconvNew.NetworkTransportUnix
|
||||
default:
|
||||
return semconvNew.NetworkTransportPipe
|
||||
}
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconvNew.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter
|
||||
if https {
|
||||
return semconvNew.URLScheme("https")
|
||||
}
|
||||
return semconvNew.URLScheme("http")
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP
|
||||
// response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will
|
||||
// be omitted.
|
||||
func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
var count int
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
attributes := make([]attribute.KeyValue, 0, count)
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestBodySize(int(resp.ReadBytes)),
|
||||
)
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPResponseBodySize(int(resp.WriteBytes)),
|
||||
)
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPResponseStatusCode(resp.StatusCode),
|
||||
)
|
||||
}
|
||||
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (n CurrentHTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconvNew.HTTPRoute(route)
|
||||
}
|
||||
|
||||
func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 3
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
num++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
n.scheme(req.TLS != nil),
|
||||
semconvNew.ServerAddress(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attributes = append(attributes, semconvNew.ServerPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type CurrentHTTPClient struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
|
||||
func (n CurrentHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.request.method
|
||||
- http.request.method.original
|
||||
- url.full
|
||||
- server.address
|
||||
- server.port
|
||||
- network.protocol.name
|
||||
- network.protocol.version
|
||||
*/
|
||||
numOfAttributes := 3 // URL, server address, proto, and method.
|
||||
|
||||
var urlHost string
|
||||
if req.URL != nil {
|
||||
urlHost = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if eligiblePort > 0 {
|
||||
numOfAttributes++
|
||||
}
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
numOfAttributes++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
method, originalMethod := n.method(req.Method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, numOfAttributes)
|
||||
|
||||
attrs = append(attrs, method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, originalMethod)
|
||||
}
|
||||
|
||||
var u string
|
||||
if req.URL != nil {
|
||||
// Remove any username/password info that may be in the URL.
|
||||
userinfo := req.URL.User
|
||||
req.URL.User = nil
|
||||
u = req.URL.String()
|
||||
// Restore any username/password info that was removed.
|
||||
req.URL.User = userinfo
|
||||
}
|
||||
attrs = append(attrs, semconvNew.URLFull(u))
|
||||
|
||||
attrs = append(attrs, semconvNew.ServerAddress(requestHost))
|
||||
if eligiblePort > 0 {
|
||||
attrs = append(attrs, semconvNew.ServerPort(eligiblePort))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
|
||||
func (n CurrentHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.response.status_code
|
||||
- error.type
|
||||
*/
|
||||
var count int
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
if resp.StatusCode > 0 {
|
||||
attrs = append(attrs, semconvNew.HTTPResponseStatusCode(resp.StatusCode))
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
errorType := strconv.Itoa(resp.StatusCode)
|
||||
attrs = append(attrs, semconvNew.ErrorTypeKey.String(errorType))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
t := reflect.TypeOf(err)
|
||||
var value string
|
||||
if t.PkgPath() == "" && t.Name() == "" {
|
||||
// Likely a builtin type.
|
||||
value = t.String()
|
||||
} else {
|
||||
value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
return semconvNew.ErrorTypeOther
|
||||
}
|
||||
|
||||
return semconvNew.ErrorTypeKey.String(value)
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconvNew.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 2
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = SplitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
semconvNew.ServerAddress(requestHost),
|
||||
n.scheme(req),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconvNew.ServerPort(port))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
// TraceAttributes returns attributes for httptrace.
|
||||
func (n CurrentHTTPClient) TraceAttributes(host string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
semconvNew.ServerAddress(host),
|
||||
}
|
||||
}
|
||||
|
||||
func (n CurrentHTTPClient) scheme(req *http.Request) attribute.KeyValue {
|
||||
if req.URL != nil && req.URL.Scheme != "" {
|
||||
return semconvNew.URLScheme(req.URL.Scheme)
|
||||
}
|
||||
if req.TLS != nil {
|
||||
return semconvNew.URLScheme("https")
|
||||
}
|
||||
return semconvNew.URLScheme("http")
|
||||
}
|
||||
|
||||
func isErrorStatusCode(code int) bool {
|
||||
return code >= 400 || code < 100
|
||||
}
|
||||
Generated
Vendored
+396
@@ -0,0 +1,396 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/semconv/server.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv provides OpenTelemetry semantic convention types and
|
||||
// functionality.
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/httpconv"
|
||||
)
|
||||
|
||||
type RequestTraceAttrsOpts struct {
|
||||
// If set, this is used as value for the "http.client_ip" attribute.
|
||||
HTTPClientIP string
|
||||
}
|
||||
|
||||
type ResponseTelemetry struct {
|
||||
StatusCode int
|
||||
ReadBytes int64
|
||||
ReadError error
|
||||
WriteBytes int64
|
||||
WriteError error
|
||||
}
|
||||
|
||||
type HTTPServer struct {
|
||||
requestBodySizeHistogram httpconv.ServerRequestBodySize
|
||||
responseBodySizeHistogram httpconv.ServerResponseBodySize
|
||||
requestDurationHistogram httpconv.ServerRequestDuration
|
||||
}
|
||||
|
||||
func NewHTTPServer(meter metric.Meter) HTTPServer {
|
||||
server := HTTPServer{}
|
||||
|
||||
var err error
|
||||
server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter)
|
||||
handleErr(err)
|
||||
|
||||
server.requestDurationHistogram, err = httpconv.NewServerRequestDuration(
|
||||
meter,
|
||||
metric.WithExplicitBucketBoundaries(
|
||||
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
|
||||
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
|
||||
),
|
||||
)
|
||||
handleErr(err)
|
||||
return server
|
||||
}
|
||||
|
||||
// Status returns a span status code and message for an HTTP status code
|
||||
// value returned by a server. Status codes in the 400-499 range are not
|
||||
// returned as errors.
|
||||
func (n HTTPServer) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 500 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
// server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
// would be the ServerName directive
|
||||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache
|
||||
// server, and the server_name directive
|
||||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an
|
||||
// nginx server. More generically, the primary server name would be the host
|
||||
// header value that matches the default virtual host of an HTTP server. It
|
||||
// should include the host identifier and if a port is used to route to the
|
||||
// server that port identifier should be included as an appropriate port
|
||||
// suffix.
|
||||
//
|
||||
// If the primary server name is not known, server should be an empty string.
|
||||
// The req Host will be used to determine the server instead.
|
||||
func (n HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
|
||||
count := 3 // ServerAddress, Method, Scheme
|
||||
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
method, methodOriginal := n.method(req.Method)
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
count++
|
||||
}
|
||||
|
||||
scheme := n.scheme(req.TLS != nil)
|
||||
|
||||
peer, peerPort := SplitHostPort(req.RemoteAddr)
|
||||
if peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
count++
|
||||
if peerPort > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
// For client IP, use, in order:
|
||||
// 1. The value passed in the options
|
||||
// 2. The value in the X-Forwarded-For header
|
||||
// 3. The peer address
|
||||
clientIP := opts.HTTPClientIP
|
||||
if clientIP == "" {
|
||||
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
|
||||
if clientIP == "" {
|
||||
clientIP = peer
|
||||
}
|
||||
}
|
||||
if clientIP != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
count++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
route := httpRoute(req.Pattern)
|
||||
if route != "" {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
attrs = append(attrs,
|
||||
semconv.ServerAddress(host),
|
||||
method,
|
||||
scheme,
|
||||
)
|
||||
|
||||
if hostPort > 0 {
|
||||
attrs = append(attrs, semconv.ServerPort(hostPort))
|
||||
}
|
||||
if methodOriginal != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, methodOriginal)
|
||||
}
|
||||
|
||||
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
|
||||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
|
||||
// file-path that would be interpreted with a sock family.
|
||||
attrs = append(attrs, semconv.NetworkPeerAddress(peer))
|
||||
if peerPort > 0 {
|
||||
attrs = append(attrs, semconv.NetworkPeerPort(peerPort))
|
||||
}
|
||||
}
|
||||
|
||||
if useragent != "" {
|
||||
attrs = append(attrs, semconv.UserAgentOriginal(useragent))
|
||||
}
|
||||
|
||||
if clientIP != "" {
|
||||
attrs = append(attrs, semconv.ClientAddress(clientIP))
|
||||
}
|
||||
|
||||
if req.URL != nil && req.URL.Path != "" {
|
||||
attrs = append(attrs, semconv.URLPath(req.URL.Path))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
attrs = append(attrs, n.Route(route))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
|
||||
attr := semconv.NetworkTransportPipe
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
attr = semconv.NetworkTransportTCP
|
||||
case "udp", "udp4", "udp6":
|
||||
attr = semconv.NetworkTransportUDP
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
attr = semconv.NetworkTransportUnix
|
||||
}
|
||||
|
||||
return []attribute.KeyValue{attr}
|
||||
}
|
||||
|
||||
type ServerMetricData struct {
|
||||
ServerName string
|
||||
ResponseSize int64
|
||||
|
||||
MetricData
|
||||
MetricAttributes
|
||||
}
|
||||
|
||||
type MetricAttributes struct {
|
||||
Req *http.Request
|
||||
StatusCode int
|
||||
Route string
|
||||
AdditionalAttributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
type MetricData struct {
|
||||
RequestSize int64
|
||||
RequestDuration time.Duration
|
||||
}
|
||||
|
||||
var (
|
||||
metricRecordOptionPool = &sync.Pool{
|
||||
New: func() any {
|
||||
return &[]metric.RecordOption{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
|
||||
attributes := n.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.Route, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
|
||||
*recordOpts = append(*recordOpts, o)
|
||||
n.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...)
|
||||
n.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...)
|
||||
n.requestDurationHistogram.Inst().Record(ctx, durationToSeconds(md.RequestDuration), o)
|
||||
*recordOpts = (*recordOpts)[:0]
|
||||
metricRecordOptionPool.Put(recordOpts)
|
||||
}
|
||||
|
||||
func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconv.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconv.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconv.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func (n HTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter
|
||||
if https {
|
||||
return semconv.URLScheme("https")
|
||||
}
|
||||
return semconv.URLScheme("http")
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP
|
||||
// response.
|
||||
//
|
||||
// If any of the fields in the ResponseTelemetry are not set the attribute will
|
||||
// be omitted.
|
||||
func (n HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
|
||||
var count int
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
count++
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
attributes := make([]attribute.KeyValue, 0, count)
|
||||
|
||||
if resp.ReadBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPRequestBodySize(int(resp.ReadBytes)),
|
||||
)
|
||||
}
|
||||
if resp.WriteBytes > 0 {
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPResponseBodySize(int(resp.WriteBytes)),
|
||||
)
|
||||
}
|
||||
if resp.StatusCode > 0 {
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPResponseStatusCode(resp.StatusCode),
|
||||
)
|
||||
}
|
||||
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Route returns the attribute for the route.
|
||||
func (n HTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconv.HTTPRoute(route)
|
||||
}
|
||||
|
||||
func (n HTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, route string, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
num := len(additionalAttributes) + 3
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
host, p = SplitHostPort(req.Host)
|
||||
} else {
|
||||
// Prioritize the primary server name.
|
||||
host, p = SplitHostPort(server)
|
||||
if p < 0 {
|
||||
_, p = SplitHostPort(req.Host)
|
||||
}
|
||||
}
|
||||
hostPort := requiredHTTPPort(req.TLS != nil, p)
|
||||
if hostPort > 0 {
|
||||
num++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
num++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
num++
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
num++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, num)
|
||||
attributes = append(attributes,
|
||||
semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
|
||||
n.scheme(req.TLS != nil),
|
||||
semconv.ServerAddress(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attributes = append(attributes, semconv.ServerPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode))
|
||||
}
|
||||
|
||||
if route != "" {
|
||||
attributes = append(attributes, semconv.HTTPRoute(route))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
Generated
Vendored
+7
-1
@@ -11,10 +11,11 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
// SplitHostPort splits a network address hostport of the form "host",
|
||||
@@ -125,3 +126,8 @@ func standardizeHTTPMethod(method string) string {
|
||||
}
|
||||
return method
|
||||
}
|
||||
|
||||
func durationToSeconds(d time.Duration) float64 {
|
||||
// Use floating point division here for higher precision (instead of Seconds method).
|
||||
return float64(d) / float64(time.Second)
|
||||
}
|
||||
|
||||
Generated
Vendored
+46
-47
@@ -15,6 +15,7 @@ import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
otelsemconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
@@ -102,9 +103,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
}
|
||||
}
|
||||
|
||||
opts := append([]trace.SpanStartOption{}, t.spanStartOptions...) // start with the configured options
|
||||
|
||||
ctx, span := tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...)
|
||||
ctx, span := tracer.Start(r.Context(), t.spanNameFormatter("", r), t.spanStartOptions...)
|
||||
|
||||
if t.clientTrace != nil {
|
||||
ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx))
|
||||
@@ -117,12 +116,26 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
|
||||
r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request.
|
||||
|
||||
// if request body is nil or NoBody, we don't want to mutate the body as it
|
||||
// will affect the identity of it in an unforeseeable way because we assert
|
||||
// ReadCloser fulfills a certain interface and it is indeed nil or NoBody.
|
||||
bw := request.NewBodyWrapper(r.Body, func(int64) {})
|
||||
if r.Body != nil && r.Body != http.NoBody {
|
||||
r.Body = bw
|
||||
var lastBW *request.BodyWrapper // Records the last body wrapper. Can be nil.
|
||||
maybeWrapBody := func(body io.ReadCloser) io.ReadCloser {
|
||||
if body == nil || body == http.NoBody {
|
||||
return body
|
||||
}
|
||||
bw := request.NewBodyWrapper(body, func(int64) {})
|
||||
lastBW = bw
|
||||
return bw
|
||||
}
|
||||
r.Body = maybeWrapBody(r.Body)
|
||||
if r.GetBody != nil {
|
||||
originalGetBody := r.GetBody
|
||||
r.GetBody = func() (io.ReadCloser, error) {
|
||||
b, err := originalGetBody()
|
||||
if err != nil {
|
||||
lastBW = nil // The underlying transport will fail to make a retry request, hence, record no data.
|
||||
return nil, err
|
||||
}
|
||||
return maybeWrapBody(b), nil
|
||||
}
|
||||
}
|
||||
|
||||
span.SetAttributes(t.semconv.RequestTraceAttrs(r)...)
|
||||
@@ -130,52 +143,38 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
|
||||
res, err := t.rt.RoundTrip(r)
|
||||
|
||||
// Defer metrics recording function to record the metrics on error or no error.
|
||||
defer func() {
|
||||
metricAttributes := semconv.MetricAttributes{
|
||||
// Record the metrics on error or no error.
|
||||
statusCode := 0
|
||||
if err == nil {
|
||||
statusCode = res.StatusCode
|
||||
}
|
||||
var requestSize int64
|
||||
if lastBW != nil {
|
||||
requestSize = lastBW.BytesRead()
|
||||
}
|
||||
t.semconv.RecordMetrics(
|
||||
ctx,
|
||||
semconv.MetricData{
|
||||
RequestSize: requestSize,
|
||||
RequestDuration: time.Since(requestStartTime),
|
||||
},
|
||||
t.semconv.MetricOptions(semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: statusCode,
|
||||
AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...),
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
metricAttributes.StatusCode = res.StatusCode
|
||||
}
|
||||
|
||||
metricOpts := t.semconv.MetricOptions(metricAttributes)
|
||||
|
||||
metricData := semconv.MetricData{
|
||||
RequestSize: bw.BytesRead(),
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
readRecordFunc := func(int64) {}
|
||||
res.Body = newWrappedBody(span, readRecordFunc, res.Body)
|
||||
}
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||
|
||||
metricData.ElapsedTime = elapsedTime
|
||||
|
||||
t.semconv.RecordMetrics(ctx, metricData, metricOpts)
|
||||
}()
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// set error type attribute if the error is part of the predefined
|
||||
// error types.
|
||||
// otherwise, record it as an exception
|
||||
if errType := t.semconv.ErrorType(err); errType.Valid() {
|
||||
span.SetAttributes(errType)
|
||||
} else {
|
||||
span.RecordError(err)
|
||||
}
|
||||
|
||||
span.SetAttributes(otelsemconv.ErrorType(err))
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
span.End()
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
readRecordFunc := func(int64) {}
|
||||
res.Body = newWrappedBody(span, readRecordFunc, res.Body)
|
||||
// traces
|
||||
span.SetAttributes(t.semconv.ResponseTraceAttrs(res)...)
|
||||
span.SetStatus(t.semconv.Status(res.StatusCode))
|
||||
@@ -229,7 +228,7 @@ func (wb *wrappedBody) Write(p []byte) (int, error) {
|
||||
// This will not panic given the guard in newWrappedBody.
|
||||
n, err := wb.body.(io.Writer).Write(p)
|
||||
if err != nil {
|
||||
wb.span.RecordError(err)
|
||||
wb.span.SetAttributes(otelsemconv.ErrorType(err))
|
||||
wb.span.SetStatus(codes.Error, err.Error())
|
||||
}
|
||||
return n, err
|
||||
@@ -247,7 +246,7 @@ func (wb *wrappedBody) Read(b []byte) (int, error) {
|
||||
wb.recordBytesRead()
|
||||
wb.span.End()
|
||||
default:
|
||||
wb.span.RecordError(err)
|
||||
wb.span.SetAttributes(otelsemconv.ErrorType(err))
|
||||
wb.span.SetStatus(codes.Error, err.Error())
|
||||
}
|
||||
return n, err
|
||||
|
||||
+1
-4
@@ -4,7 +4,4 @@
|
||||
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
|
||||
// Version is the current release version of the otelhttp instrumentation.
|
||||
func Version() string {
|
||||
return "0.63.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
const Version = "0.67.0"
|
||||
|
||||
+1
@@ -194,6 +194,7 @@ linters:
|
||||
arguments:
|
||||
- ["ID"] # AllowList
|
||||
- ["Otel", "Aws", "Gcp"] # DenyList
|
||||
- - skip-package-name-collision-with-go-std: true
|
||||
- name: waitgroup-by-value
|
||||
testifylint:
|
||||
enable-all: true
|
||||
|
||||
+45
-1
@@ -11,6 +11,47 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [1.42.0/0.64.0/0.18.0/0.0.16] 2026-03-06
|
||||
|
||||
### Added
|
||||
|
||||
- Add `go.opentelemetry.io/otel/semconv/v1.40.0` package.
|
||||
The package contains semantic conventions from the `v1.40.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.40.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.39.0`. (#7985)
|
||||
- Add `Err` and `SetErr` on `Record` in `go.opentelemetry.io/otel/log` to attach an error and set record exception attributes in `go.opentelemetry.io/otel/log/sdk`. (#7924)
|
||||
|
||||
### Changed
|
||||
|
||||
- `TracerProvider.ForceFlush` in `go.opentelemetry.io/otel/sdk/trace` joins errors together and continues iteration through SpanProcessors as opposed to returning the first encountered error without attempting exports on subsequent SpanProcessors. (#7856)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix missing `request.GetBody` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` to correctly handle HTTP2 GOAWAY frame. (#7931)
|
||||
- Fix semconv v1.39.0 generated metric helpers skipping required attributes when extra attributes were empty. (#7964)
|
||||
- Preserve W3C TraceFlags bitmask (including the random Trace ID flag) during trace context extraction and injection in `go.opentelemetry.io/otel/propagation`. (#7834)
|
||||
|
||||
### Removed
|
||||
|
||||
- Drop support for [Go 1.24]. (#7984)
|
||||
|
||||
## [1.41.0/0.63.0/0.17.0/0.0.15] 2026-03-02
|
||||
|
||||
This release is the last to support [Go 1.24].
|
||||
The next release will require at least [Go 1.25].
|
||||
|
||||
### Added
|
||||
|
||||
- Support testing of [Go 1.26]. (#7902)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Update `Baggage` in `go.opentelemetry.io/otel/propagation` and `Parse` and `New` in `go.opentelemetry.io/otel/baggage` to comply with W3C Baggage specification limits.
|
||||
`New` and `Parse` now return partial baggage along with an error when limits are exceeded.
|
||||
Errors from baggage extraction are reported to the global error handler. (#7880)
|
||||
- Return an error when the endpoint is configured as insecure and with TLS configuration in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#7914)
|
||||
- Return an error when the endpoint is configured as insecure and with TLS configuration in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#7914)
|
||||
- Return an error when the endpoint is configured as insecure and with TLS configuration in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#7914)
|
||||
|
||||
## [1.40.0/0.62.0/0.16.0] 2026-02-02
|
||||
|
||||
### Added
|
||||
@@ -3535,7 +3576,9 @@ It contains api and sdk for trace and meter.
|
||||
- CircleCI build CI manifest files.
|
||||
- CODEOWNERS file to track owners of this project.
|
||||
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.40.0...HEAD
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.42.0...HEAD
|
||||
[1.42.0/0.64.0/0.18.0/0.0.16]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.42.0
|
||||
[1.41.0/0.63.0/0.17.0/0.0.15]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.41.0
|
||||
[1.40.0/0.62.0/0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.40.0
|
||||
[1.39.0/0.61.0/0.15.0/0.0.14]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.39.0
|
||||
[1.38.0/0.60.0/0.14.0/0.0.13]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.38.0
|
||||
@@ -3635,6 +3678,7 @@ It contains api and sdk for trace and meter.
|
||||
|
||||
<!-- Released section ended -->
|
||||
|
||||
[Go 1.26]: https://go.dev/doc/go1.26
|
||||
[Go 1.25]: https://go.dev/doc/go1.25
|
||||
[Go 1.24]: https://go.dev/doc/go1.24
|
||||
[Go 1.23]: https://go.dev/doc/go1.23
|
||||
|
||||
+3
-3
@@ -746,8 +746,8 @@ Encapsulate setup in constructor functions, ensuring clear ownership and scope:
|
||||
import (
|
||||
"errors"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
type SDKComponent struct {
|
||||
@@ -1039,7 +1039,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan)
|
||||
|
||||
All observability metrics should follow the [OpenTelemetry Semantic Conventions for SDK metrics](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/otel/sdk-metrics.md).
|
||||
|
||||
Use the metric semantic conventions convenience package [otelconv](./semconv/v1.39.0/otelconv/metric.go).
|
||||
Use the metric semantic conventions convenience package [otelconv](./semconv/v1.40.0/otelconv/metric.go).
|
||||
|
||||
##### Component Identification
|
||||
|
||||
|
||||
+5
-6
@@ -185,11 +185,10 @@ test-coverage: $(GOCOVMERGE)
|
||||
.PHONY: benchmark
|
||||
benchmark: $(OTEL_GO_MOD_DIRS:%=benchmark/%)
|
||||
benchmark/%:
|
||||
@echo "$(GO) test -run=xxxxxMatchNothingxxxxx -bench=. $*..." \
|
||||
&& cd $* \
|
||||
&& $(GO) list ./... \
|
||||
| grep -v third_party \
|
||||
| xargs $(GO) test -run=xxxxxMatchNothingxxxxx -bench=.
|
||||
cd $* && $(GO) test -run='^$$' -bench=. $(ARGS) ./...
|
||||
|
||||
print-sharded-benchmarks:
|
||||
@echo $(OTEL_GO_MOD_DIRS) | jq -cR 'split(" ")'
|
||||
|
||||
.PHONY: golangci-lint golangci-lint-fix
|
||||
golangci-lint-fix: ARGS=--fix
|
||||
@@ -215,7 +214,7 @@ go-mod-tidy/%: crosslink
|
||||
&& $(GO) mod tidy -compat=1.21
|
||||
|
||||
.PHONY: lint
|
||||
lint: misspell go-mod-tidy golangci-lint govulncheck
|
||||
lint: misspell go-mod-tidy golangci-lint
|
||||
|
||||
.PHONY: vanity-import-check
|
||||
vanity-import-check: $(PORTO)
|
||||
|
||||
+7
-7
@@ -53,20 +53,20 @@ Currently, this project supports the following environments.
|
||||
|
||||
| OS | Go Version | Architecture |
|
||||
|----------|------------|--------------|
|
||||
| Ubuntu | 1.26 | amd64 |
|
||||
| Ubuntu | 1.25 | amd64 |
|
||||
| Ubuntu | 1.24 | amd64 |
|
||||
| Ubuntu | 1.26 | 386 |
|
||||
| Ubuntu | 1.25 | 386 |
|
||||
| Ubuntu | 1.24 | 386 |
|
||||
| Ubuntu | 1.26 | arm64 |
|
||||
| Ubuntu | 1.25 | arm64 |
|
||||
| Ubuntu | 1.24 | arm64 |
|
||||
| macOS | 1.26 | amd64 |
|
||||
| macOS | 1.25 | amd64 |
|
||||
| macOS | 1.24 | amd64 |
|
||||
| macOS | 1.26 | arm64 |
|
||||
| macOS | 1.25 | arm64 |
|
||||
| macOS | 1.24 | arm64 |
|
||||
| Windows | 1.26 | amd64 |
|
||||
| Windows | 1.25 | amd64 |
|
||||
| Windows | 1.24 | amd64 |
|
||||
| Windows | 1.26 | 386 |
|
||||
| Windows | 1.25 | 386 |
|
||||
| Windows | 1.24 | 386 |
|
||||
|
||||
While this project should work for other systems, no compatibility guarantees
|
||||
are made for those systems currently.
|
||||
|
||||
+83
-26
@@ -14,8 +14,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxMembers = 180
|
||||
maxBytesPerMembers = 4096
|
||||
maxMembers = 64
|
||||
maxBytesPerBaggageString = 8192
|
||||
|
||||
listDelimiter = ","
|
||||
@@ -29,7 +28,6 @@ var (
|
||||
errInvalidProperty = errors.New("invalid baggage list-member property")
|
||||
errInvalidMember = errors.New("invalid baggage list-member")
|
||||
errMemberNumber = errors.New("too many list-members in baggage-string")
|
||||
errMemberBytes = errors.New("list-member too large")
|
||||
errBaggageBytes = errors.New("baggage-string too large")
|
||||
)
|
||||
|
||||
@@ -309,10 +307,6 @@ func newInvalidMember() Member {
|
||||
// an error if the input is invalid according to the W3C Baggage
|
||||
// specification.
|
||||
func parseMember(member string) (Member, error) {
|
||||
if n := len(member); n > maxBytesPerMembers {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n)
|
||||
}
|
||||
|
||||
var props properties
|
||||
keyValue, properties, found := strings.Cut(member, propertyDelimiter)
|
||||
if found {
|
||||
@@ -430,6 +424,10 @@ type Baggage struct { //nolint:golint
|
||||
// New returns a new valid Baggage. It returns an error if it results in a
|
||||
// Baggage exceeding limits set in that specification.
|
||||
//
|
||||
// If the resulting Baggage exceeds the maximum allowed members or bytes,
|
||||
// members are dropped until the limits are satisfied and an error is returned
|
||||
// along with the partial result.
|
||||
//
|
||||
// It expects all the provided members to have already been validated.
|
||||
func New(members ...Member) (Baggage, error) {
|
||||
if len(members) == 0 {
|
||||
@@ -441,7 +439,6 @@ func New(members ...Member) (Baggage, error) {
|
||||
if !m.hasData {
|
||||
return Baggage{}, errInvalidMember
|
||||
}
|
||||
|
||||
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||
b[m.key] = baggage.Item{
|
||||
Value: m.value,
|
||||
@@ -449,17 +446,42 @@ func New(members ...Member) (Baggage, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check member numbers after deduplication.
|
||||
var truncateErr error
|
||||
|
||||
// Check member count after deduplication.
|
||||
if len(b) > maxMembers {
|
||||
return Baggage{}, errMemberNumber
|
||||
truncateErr = errors.Join(truncateErr, errMemberNumber)
|
||||
for k := range b {
|
||||
if len(b) <= maxMembers {
|
||||
break
|
||||
}
|
||||
delete(b, k)
|
||||
}
|
||||
}
|
||||
|
||||
bag := Baggage{b}
|
||||
if n := len(bag.String()); n > maxBytesPerBaggageString {
|
||||
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||
// Check byte size and drop members if necessary.
|
||||
totalBytes := 0
|
||||
first := true
|
||||
for k := range b {
|
||||
m := Member{
|
||||
key: k,
|
||||
value: b[k].Value,
|
||||
properties: fromInternalProperties(b[k].Properties),
|
||||
}
|
||||
memberSize := len(m.String())
|
||||
if !first {
|
||||
memberSize++ // comma separator
|
||||
}
|
||||
if totalBytes+memberSize > maxBytesPerBaggageString {
|
||||
truncateErr = errors.Join(truncateErr, fmt.Errorf("%w: %d", errBaggageBytes, totalBytes+memberSize))
|
||||
delete(b, k)
|
||||
continue
|
||||
}
|
||||
totalBytes += memberSize
|
||||
first = false
|
||||
}
|
||||
|
||||
return bag, nil
|
||||
return Baggage{b}, truncateErr
|
||||
}
|
||||
|
||||
// Parse attempts to decode a baggage-string from the passed string. It
|
||||
@@ -470,36 +492,71 @@ func New(members ...Member) (Baggage, error) {
|
||||
// defined (reading left-to-right) will be the only one kept. This diverges
|
||||
// from the W3C Baggage specification which allows duplicate list-members, but
|
||||
// conforms to the OpenTelemetry Baggage specification.
|
||||
//
|
||||
// If the baggage-string exceeds the maximum allowed members (64) or bytes
|
||||
// (8192), members are dropped until the limits are satisfied and an error is
|
||||
// returned along with the partial result.
|
||||
//
|
||||
// Invalid members are skipped and the error is returned along with the
|
||||
// partial result containing the valid members.
|
||||
func Parse(bStr string) (Baggage, error) {
|
||||
if bStr == "" {
|
||||
return Baggage{}, nil
|
||||
}
|
||||
|
||||
if n := len(bStr); n > maxBytesPerBaggageString {
|
||||
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||
}
|
||||
|
||||
b := make(baggage.List)
|
||||
sizes := make(map[string]int) // Track per-key byte sizes
|
||||
var totalBytes int
|
||||
var truncateErr error
|
||||
for memberStr := range strings.SplitSeq(bStr, listDelimiter) {
|
||||
// Check member count limit.
|
||||
if len(b) >= maxMembers {
|
||||
truncateErr = errors.Join(truncateErr, errMemberNumber)
|
||||
break
|
||||
}
|
||||
|
||||
m, err := parseMember(memberStr)
|
||||
if err != nil {
|
||||
return Baggage{}, err
|
||||
truncateErr = errors.Join(truncateErr, err)
|
||||
continue // skip invalid member, keep processing
|
||||
}
|
||||
|
||||
// Check byte size limit.
|
||||
// Account for comma separator between members.
|
||||
memberBytes := len(m.String())
|
||||
_, existingKey := b[m.key]
|
||||
if !existingKey && len(b) > 0 {
|
||||
memberBytes++ // comma separator only for new keys
|
||||
}
|
||||
|
||||
// Calculate new totalBytes if we add/overwrite this key
|
||||
var newTotalBytes int
|
||||
if oldSize, exists := sizes[m.key]; exists {
|
||||
// Overwriting existing key: subtract old size, add new size
|
||||
newTotalBytes = totalBytes - oldSize + memberBytes
|
||||
} else {
|
||||
// New key
|
||||
newTotalBytes = totalBytes + memberBytes
|
||||
}
|
||||
|
||||
if newTotalBytes > maxBytesPerBaggageString {
|
||||
truncateErr = errors.Join(truncateErr, errBaggageBytes)
|
||||
break
|
||||
}
|
||||
|
||||
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||
b[m.key] = baggage.Item{
|
||||
Value: m.value,
|
||||
Properties: m.properties.asInternal(),
|
||||
}
|
||||
sizes[m.key] = memberBytes
|
||||
totalBytes = newTotalBytes
|
||||
}
|
||||
|
||||
// OpenTelemetry does not allow for duplicate list-members, but the W3C
|
||||
// specification does. Now that we have deduplicated, ensure the baggage
|
||||
// does not exceed list-member limits.
|
||||
if len(b) > maxMembers {
|
||||
return Baggage{}, errMemberNumber
|
||||
if len(b) == 0 {
|
||||
return Baggage{}, truncateErr
|
||||
}
|
||||
|
||||
return Baggage{b}, nil
|
||||
return Baggage{b}, truncateErr
|
||||
}
|
||||
|
||||
// Member returns the baggage list-member identified by key.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# This is a renovate-friendly source of Docker images.
|
||||
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
|
||||
FROM otel/weaver:v0.20.0@sha256:fa4f1c6954ecea78ab1a4e865bd6f5b4aaba80c1896f9f4a11e2c361d04e197e AS weaver
|
||||
FROM otel/weaver:v0.21.2@sha256:2401de985c38bdb98b43918e2f43aa36b2afed4aa5669ac1c1de0a17301cd36d AS weaver
|
||||
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -151,7 +151,7 @@ func (c *client) exportContext(parent context.Context) (context.Context, context
|
||||
if c.exportTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeoutCause(parent, c.exportTimeout, errors.New("exporter export timeout"))
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(parent)
|
||||
ctx, cancel = context.WithCancel(parent) //nolint:gosec // cancel is handled by the caller.
|
||||
}
|
||||
|
||||
if c.metadata.Len() > 0 {
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -5,5 +5,5 @@ package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpme
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over gRPC metrics exporter in use.
|
||||
func Version() string {
|
||||
return "1.40.0"
|
||||
return "1.42.0"
|
||||
}
|
||||
|
||||
Generated
Vendored
+16
@@ -52,8 +52,14 @@ var ourTransport = &http.Transport{
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
var errInsecureEndpointWithTLS = errors.New("insecure HTTP endpoint cannot use TLS client configuration")
|
||||
|
||||
// newClient creates a new HTTP metric client.
|
||||
func newClient(cfg oconf.Config) (*client, error) {
|
||||
if cfg.Metrics.Insecure && cfg.Metrics.TLSCfg != nil {
|
||||
return nil, errInsecureEndpointWithTLS
|
||||
}
|
||||
|
||||
httpClient := cfg.Metrics.HTTPClient
|
||||
if httpClient == nil {
|
||||
httpClient = &http.Client{
|
||||
@@ -146,6 +152,7 @@ func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.Resou
|
||||
}
|
||||
|
||||
request.reset(iCtx)
|
||||
// nolint:gosec // URL is constructed from validated OTLP endpoint configuration
|
||||
resp, err := c.httpClient.Do(request.Request)
|
||||
var urlErr *url.Error
|
||||
if errors.As(err, &urlErr) && urlErr.Temporary() {
|
||||
@@ -236,6 +243,7 @@ func (c *client) newRequest(ctx context.Context, body []byte) (request, error) {
|
||||
case NoCompression:
|
||||
r.ContentLength = int64(len(body))
|
||||
req.bodyReader = bodyReader(body)
|
||||
req.GetBody = bodyReaderErr(body)
|
||||
case GzipCompression:
|
||||
// Ensure the content length is not used.
|
||||
r.ContentLength = -1
|
||||
@@ -256,6 +264,7 @@ func (c *client) newRequest(ctx context.Context, body []byte) (request, error) {
|
||||
}
|
||||
|
||||
req.bodyReader = bodyReader(b.Bytes())
|
||||
req.GetBody = bodyReaderErr(body)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -268,6 +277,13 @@ func bodyReader(buf []byte) func() io.ReadCloser {
|
||||
}
|
||||
}
|
||||
|
||||
// bodyReaderErr returns a closure returning a new reader for buf.
|
||||
func bodyReaderErr(buf []byte) func() (io.ReadCloser, error) {
|
||||
return func() (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader(buf)), nil
|
||||
}
|
||||
}
|
||||
|
||||
// request wraps an http.Request with a resettable body reader.
|
||||
type request struct {
|
||||
*http.Request
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -5,5 +5,5 @@ package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpme
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf metrics exporter in use.
|
||||
func Version() string {
|
||||
return "1.40.0"
|
||||
return "1.42.0"
|
||||
}
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ func NewClient(opts ...Option) otlptrace.Client {
|
||||
func newClient(opts ...Option) *client {
|
||||
cfg := otlpconfig.NewGRPCConfig(asGRPCOptions(opts)...)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(context.Background()) //nolint:gosec // cancel called in client shutdown.
|
||||
|
||||
c := &client{
|
||||
endpoint: cfg.Traces.Endpoint,
|
||||
@@ -248,7 +248,7 @@ func (c *client) exportContext(parent context.Context) (context.Context, context
|
||||
if c.exportTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeoutCause(parent, c.exportTimeout, errors.New("exporter export timeout"))
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(parent)
|
||||
ctx, cancel = context.WithCancel(parent) //nolint:gosec // cancel called by caller when export is complete.
|
||||
}
|
||||
|
||||
if c.metadata.Len() > 0 {
|
||||
|
||||
Generated
Vendored
+22
-14
@@ -18,8 +18,8 @@ import (
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -208,10 +208,12 @@ func BaseAttrs(id int64, target string) []attribute.KeyValue {
|
||||
func (i *Instrumentation) ExportSpans(ctx context.Context, nSpans int) ExportOp {
|
||||
start := time.Now()
|
||||
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, i.addOpt)
|
||||
i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...)
|
||||
if i.inflightSpans.Enabled(ctx) {
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, i.addOpt)
|
||||
i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...)
|
||||
}
|
||||
|
||||
return ExportOp{
|
||||
ctx: ctx,
|
||||
@@ -244,14 +246,18 @@ func (e ExportOp) End(err error, code codes.Code) {
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, e.inst.addOpt)
|
||||
|
||||
e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...)
|
||||
if e.inst.inflightSpans.Enabled(e.ctx) {
|
||||
e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...)
|
||||
}
|
||||
|
||||
success := successful(e.nSpans, err)
|
||||
// Record successfully exported spans, even if the value is 0 which are
|
||||
// meaningful to distribution aggregations.
|
||||
e.inst.exportedSpans.Add(e.ctx, success, *addOpt...)
|
||||
if e.inst.exportedSpans.Enabled(e.ctx) {
|
||||
e.inst.exportedSpans.Add(e.ctx, success, *addOpt...)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err != nil && e.inst.exportedSpans.Enabled(e.ctx) {
|
||||
attrs := get[attribute.KeyValue](measureAttrsPool)
|
||||
defer put(measureAttrsPool, attrs)
|
||||
*attrs = append(*attrs, e.inst.attrs...)
|
||||
@@ -266,12 +272,14 @@ func (e ExportOp) End(err error, code codes.Code) {
|
||||
e.inst.exportedSpans.Add(e.ctx, e.nSpans-success, *addOpt...)
|
||||
}
|
||||
|
||||
recOpt := get[metric.RecordOption](recordOptPool)
|
||||
defer put(recordOptPool, recOpt)
|
||||
*recOpt = append(*recOpt, e.inst.recordOption(err, code))
|
||||
if e.inst.opDuration.Enabled(e.ctx) {
|
||||
recOpt := get[metric.RecordOption](recordOptPool)
|
||||
defer put(recordOptPool, recOpt)
|
||||
*recOpt = append(*recOpt, e.inst.recordOption(err, code))
|
||||
|
||||
d := time.Since(e.start).Seconds()
|
||||
e.inst.opDuration.Record(e.ctx, d, *recOpt...)
|
||||
d := time.Since(e.start).Seconds()
|
||||
e.inst.opDuration.Record(e.ctx, d, *recOpt...)
|
||||
}
|
||||
}
|
||||
|
||||
// recordOption returns a RecordOption with attributes representing the
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -5,4 +5,4 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP gRPC trace
|
||||
// exporter in use.
|
||||
const Version = "1.40.0"
|
||||
const Version = "1.42.0"
|
||||
|
||||
+7
@@ -56,6 +56,8 @@ var ourTransport = &http.Transport{
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
var errInsecureEndpointWithTLS = errors.New("insecure HTTP endpoint cannot use TLS client configuration")
|
||||
|
||||
type client struct {
|
||||
name string
|
||||
cfg otlpconfig.SignalConfig
|
||||
@@ -110,6 +112,10 @@ func NewClient(opts ...Option) otlptrace.Client {
|
||||
|
||||
// Start does nothing in a HTTP client.
|
||||
func (c *client) Start(ctx context.Context) error {
|
||||
if c.cfg.Insecure && c.cfg.TLSCfg != nil {
|
||||
return errInsecureEndpointWithTLS
|
||||
}
|
||||
|
||||
// Initialize the instrumentation if not already done.
|
||||
//
|
||||
// Initialize here instead of NewClient to allow any errors to be passed
|
||||
@@ -174,6 +180,7 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
|
||||
}
|
||||
|
||||
request.reset(ctx)
|
||||
// nolint:gosec // URL is constructed from validated OTLP endpoint configuration
|
||||
resp, err := d.client.Do(request.Request)
|
||||
var urlErr *url.Error
|
||||
if errors.As(err, &urlErr) && urlErr.Temporary() {
|
||||
|
||||
Generated
Vendored
+22
-14
@@ -23,8 +23,8 @@ import (
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/x"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -261,10 +261,12 @@ func parseIP(ip string) string {
|
||||
func (i *Instrumentation) ExportSpans(ctx context.Context, nSpans int) ExportOp {
|
||||
start := time.Now()
|
||||
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, i.addOpt)
|
||||
i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...)
|
||||
if i.inflightSpans.Enabled(ctx) {
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, i.addOpt)
|
||||
i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...)
|
||||
}
|
||||
|
||||
return ExportOp{
|
||||
ctx: ctx,
|
||||
@@ -299,14 +301,18 @@ func (e ExportOp) End(err error, status int) {
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, e.inst.addOpt)
|
||||
|
||||
e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...)
|
||||
if e.inst.inflightSpans.Enabled(e.ctx) {
|
||||
e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...)
|
||||
}
|
||||
|
||||
success := successful(e.nSpans, err)
|
||||
// Record successfully exported spans, even if the value is 0 which are
|
||||
// meaningful to distribution aggregations.
|
||||
e.inst.exportedSpans.Add(e.ctx, success, *addOpt...)
|
||||
if e.inst.exportedSpans.Enabled(e.ctx) {
|
||||
e.inst.exportedSpans.Add(e.ctx, success, *addOpt...)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err != nil && e.inst.exportedSpans.Enabled(e.ctx) {
|
||||
attrs := get[attribute.KeyValue](measureAttrsPool)
|
||||
defer put(measureAttrsPool, attrs)
|
||||
*attrs = append(*attrs, e.inst.attrs...)
|
||||
@@ -321,12 +327,14 @@ func (e ExportOp) End(err error, status int) {
|
||||
e.inst.exportedSpans.Add(e.ctx, e.nSpans-success, *addOpt...)
|
||||
}
|
||||
|
||||
recOpt := get[metric.RecordOption](recordOptPool)
|
||||
defer put(recordOptPool, recOpt)
|
||||
*recOpt = append(*recOpt, e.inst.recordOption(err, status))
|
||||
if e.inst.opDuration.Enabled(e.ctx) {
|
||||
recOpt := get[metric.RecordOption](recordOptPool)
|
||||
defer put(recordOptPool, recOpt)
|
||||
*recOpt = append(*recOpt, e.inst.recordOption(err, status))
|
||||
|
||||
d := time.Since(e.start).Seconds()
|
||||
e.inst.opDuration.Record(e.ctx, d, *recOpt...)
|
||||
d := time.Since(e.start).Seconds()
|
||||
e.inst.opDuration.Record(e.ctx, d, *recOpt...)
|
||||
}
|
||||
}
|
||||
|
||||
// recordOption returns a RecordOption with attributes representing the
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -5,4 +5,4 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP HTTP trace
|
||||
// exporter in use.
|
||||
const Version = "1.40.0"
|
||||
const Version = "1.42.0"
|
||||
|
||||
+1
-1
@@ -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.
|
||||
func Version() string {
|
||||
return "1.40.0"
|
||||
return "1.42.0"
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package internal provides internal functionality for the stdouttrace
|
||||
// package.
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal"
|
||||
|
||||
//go:generate gotmpl --body=../../../../internal/shared/counter/counter.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/counter\" }" --out=counter/counter.go
|
||||
//go:generate gotmpl --body=../../../../internal/shared/counter/counter_test.go.tmpl "--data={}" --out=counter/counter_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/stdout/stdouttrace\" }" --out=x/x.go
|
||||
//go:generate gotmpl --body=../../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go
|
||||
Generated
Vendored
+230
@@ -0,0 +1,230 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package observ provides experimental observability instrumentation
|
||||
// for the stdout trace exporter.
|
||||
package observ // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/observ"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// ComponentType uniquely identifies the OpenTelemetry Exporter component
|
||||
// being instrumented.
|
||||
//
|
||||
// The STDOUT trace exporter is not a standardized OTel component type, so
|
||||
// it uses the Go package prefixed type name to ensure uniqueness and
|
||||
// identity.
|
||||
ComponentType = "go.opentelemetry.io/otel/exporters/stdout/stdouttrace.Exporter"
|
||||
|
||||
// ScopeName is the unique name of the meter used for instrumentation.
|
||||
ScopeName = "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/observ"
|
||||
|
||||
// SchemaURL is the schema URL of the metrics produced by this
|
||||
// instrumentation.
|
||||
SchemaURL = semconv.SchemaURL
|
||||
|
||||
// Version is the current version of this instrumentation.
|
||||
//
|
||||
// This matches the version of the exporter.
|
||||
Version = internal.Version
|
||||
)
|
||||
|
||||
var (
|
||||
measureAttrsPool = &sync.Pool{
|
||||
New: func() any {
|
||||
// "component.name" + "component.type" + "error.type"
|
||||
const n = 1 + 1 + 1
|
||||
s := make([]attribute.KeyValue, 0, n)
|
||||
// Return a pointer to a slice instead of a slice itself
|
||||
// to avoid allocations on every call.
|
||||
return &s
|
||||
},
|
||||
}
|
||||
|
||||
addOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1 // WithAttributeSet
|
||||
o := make([]metric.AddOption, 0, n)
|
||||
return &o
|
||||
},
|
||||
}
|
||||
|
||||
recordOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1 // WithAttributeSet
|
||||
o := make([]metric.RecordOption, 0, n)
|
||||
return &o
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) }
|
||||
|
||||
func put[T any](p *sync.Pool, s *[]T) {
|
||||
*s = (*s)[:0] // Reset.
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func ComponentName(id int64) string {
|
||||
return fmt.Sprintf("%s/%d", ComponentType, id)
|
||||
}
|
||||
|
||||
// Instrumentation is experimental instrumentation for the exporter.
|
||||
type Instrumentation struct {
|
||||
inflightSpans metric.Int64UpDownCounter
|
||||
exportedSpans metric.Int64Counter
|
||||
opDuration metric.Float64Histogram
|
||||
|
||||
attrs []attribute.KeyValue
|
||||
setOpt metric.MeasurementOption
|
||||
}
|
||||
|
||||
// NewInstrumentation returns instrumentation for a STDOUT trace exporter with
|
||||
// the provided ID using the global MeterProvider.
|
||||
//
|
||||
// If the experimental observability is disabled, nil is returned.
|
||||
func NewInstrumentation(id int64) (*Instrumentation, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
i := &Instrumentation{
|
||||
attrs: []attribute.KeyValue{
|
||||
semconv.OTelComponentName(ComponentName(id)),
|
||||
semconv.OTelComponentTypeKey.String(ComponentType),
|
||||
},
|
||||
}
|
||||
|
||||
s := attribute.NewSet(i.attrs...)
|
||||
i.setOpt = metric.WithAttributeSet(s)
|
||||
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(Version),
|
||||
metric.WithSchemaURL(SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
|
||||
inflightSpans, e := otelconv.NewSDKExporterSpanInflight(m)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create span inflight metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
i.inflightSpans = inflightSpans.Inst()
|
||||
|
||||
exportedSpans, e := otelconv.NewSDKExporterSpanExported(m)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create span exported metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
i.exportedSpans = exportedSpans.Inst()
|
||||
|
||||
opDuration, e := otelconv.NewSDKExporterOperationDuration(m)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create operation duration metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
i.opDuration = opDuration.Inst()
|
||||
|
||||
return i, err
|
||||
}
|
||||
|
||||
// ExportSpans instruments the ExportSpans method of the exporter. It returns a
|
||||
// function that needs to be deferred so it is called when the method returns.
|
||||
func (i *Instrumentation) ExportSpans(ctx context.Context, nSpans int) ExportOp {
|
||||
start := time.Now()
|
||||
|
||||
if i.inflightSpans.Enabled(ctx) {
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, i.setOpt)
|
||||
i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...)
|
||||
}
|
||||
|
||||
return ExportOp{
|
||||
ctx: ctx,
|
||||
start: start,
|
||||
nSpans: int64(nSpans),
|
||||
inst: i,
|
||||
}
|
||||
}
|
||||
|
||||
// ExportOp is an in-progress ExportSpans operation.
|
||||
type ExportOp struct {
|
||||
ctx context.Context
|
||||
start time.Time
|
||||
nSpans int64
|
||||
inst *Instrumentation
|
||||
}
|
||||
|
||||
// End ends the ExportSpans operation, recording its success and duration.
|
||||
//
|
||||
// The success parameter indicates how many spans were successfully exported.
|
||||
// The err parameter indicates whether the operation failed. If err is not nil,
|
||||
// the number of failed spans (nSpans - success) is also recorded.
|
||||
func (e ExportOp) End(success int64, err error) {
|
||||
inflightSpansEnable := e.inst.inflightSpans.Enabled(e.ctx)
|
||||
exportedSpansEnable := e.inst.exportedSpans.Enabled(e.ctx)
|
||||
opDurationEnable := e.inst.opDuration.Enabled(e.ctx)
|
||||
|
||||
if !inflightSpansEnable && !exportedSpansEnable && !opDurationEnable {
|
||||
return
|
||||
}
|
||||
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, e.inst.setOpt)
|
||||
|
||||
if inflightSpansEnable {
|
||||
e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...)
|
||||
}
|
||||
|
||||
// Record the success and duration of the operation.
|
||||
//
|
||||
// Do not exclude 0 values, as they are valid and indicate no spans
|
||||
// were exported which is meaningful for certain aggregations.
|
||||
if exportedSpansEnable {
|
||||
e.inst.exportedSpans.Add(e.ctx, success, *addOpt...)
|
||||
}
|
||||
|
||||
mOpt := e.inst.setOpt
|
||||
if err != nil && exportedSpansEnable {
|
||||
attrs := get[attribute.KeyValue](measureAttrsPool)
|
||||
defer put(measureAttrsPool, attrs)
|
||||
*attrs = append(*attrs, e.inst.attrs...)
|
||||
*attrs = append(*attrs, semconv.ErrorType(err))
|
||||
|
||||
// Do not inefficiently make a copy of attrs by using
|
||||
// WithAttributes instead of WithAttributeSet.
|
||||
set := attribute.NewSet(*attrs...)
|
||||
mOpt = metric.WithAttributeSet(set)
|
||||
|
||||
// Reset addOpt with new attribute set.
|
||||
*addOpt = append((*addOpt)[:0], mOpt)
|
||||
|
||||
e.inst.exportedSpans.Add(e.ctx, e.nSpans-success, *addOpt...)
|
||||
}
|
||||
|
||||
if opDurationEnable {
|
||||
recordOpt := get[metric.RecordOption](recordOptPool)
|
||||
defer put(recordOptPool, recordOpt)
|
||||
*recordOpt = append(*recordOpt, mOpt)
|
||||
e.inst.opDuration.Record(e.ctx, time.Since(e.start).Seconds(), *recordOpt...)
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry stdouttrace
|
||||
// exporter in use.
|
||||
const Version = "1.42.0"
|
||||
Generated
Vendored
+4
-4
@@ -8,13 +8,13 @@ See the [Compatibility and Stability](#compatibility-and-stability) section for
|
||||
|
||||
## Features
|
||||
|
||||
- [Self-Observability](#self-observability)
|
||||
- [Observability](#observability)
|
||||
|
||||
### Self-Observability
|
||||
### Observability
|
||||
|
||||
The `stdouttrace` exporter provides a self-observability feature that allows you to monitor the SDK itself.
|
||||
The `stdouttrace` exporter can be configured to provide observability about itself using OpenTelemetry metrics.
|
||||
|
||||
To opt-in, set the environment variable `OTEL_GO_X_SELF_OBSERVABILITY` to `true`.
|
||||
To opt-in, set the environment variable `OTEL_GO_X_OBSERVABILITY` to `true`.
|
||||
|
||||
When enabled, the SDK will create the following metrics using the global `MeterProvider`:
|
||||
|
||||
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/stdout/stdouttrace].
|
||||
package x // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x"
|
||||
|
||||
import "strings"
|
||||
|
||||
// Observability is an experimental feature flag that determines if exporter
|
||||
// observability metrics are enabled.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var Observability = newFeature(
|
||||
[]string{"OBSERVABILITY", "SELF_OBSERVABILITY"},
|
||||
func(v string) (string, bool) {
|
||||
if strings.EqualFold(v, "true") {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
},
|
||||
)
|
||||
+18
-23
@@ -1,3 +1,6 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -6,40 +9,30 @@ package x // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace/inter
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SelfObservability is an experimental feature flag that determines if SDK
|
||||
// self-observability metrics are enabled.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) {
|
||||
if strings.EqualFold(v, "true") {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
})
|
||||
|
||||
// Feature is an experimental feature control flag. It provides a uniform way
|
||||
// to interact with these feature flags and parse their values.
|
||||
type Feature[T any] struct {
|
||||
key string
|
||||
keys []string
|
||||
parse func(v string) (T, bool)
|
||||
}
|
||||
|
||||
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
|
||||
func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] {
|
||||
const envKeyRoot = "OTEL_GO_X_"
|
||||
keys := make([]string, 0, len(suffix))
|
||||
for _, s := range suffix {
|
||||
keys = append(keys, envKeyRoot+s)
|
||||
}
|
||||
return Feature[T]{
|
||||
key: envKeyRoot + suffix,
|
||||
keys: keys,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the environment variable key that needs to be set to enable the
|
||||
// Keys returns the environment variable keys that can be set to enable the
|
||||
// feature.
|
||||
func (f Feature[T]) Key() string { return f.key }
|
||||
func (f Feature[T]) Keys() []string { return f.keys }
|
||||
|
||||
// Lookup returns the user configured value for the feature and true if the
|
||||
// user has enabled the feature. Otherwise, if the feature is not enabled, a
|
||||
@@ -49,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||
//
|
||||
// > The SDK MUST interpret an empty value of an environment variable the
|
||||
// > same way as when the variable is unset.
|
||||
vRaw := os.Getenv(f.key)
|
||||
if vRaw == "" {
|
||||
return v, ok
|
||||
for _, key := range f.keys {
|
||||
vRaw := os.Getenv(key)
|
||||
if vRaw != "" {
|
||||
return f.parse(vRaw)
|
||||
}
|
||||
}
|
||||
return f.parse(vRaw)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Enabled reports whether the feature is enabled.
|
||||
|
||||
+7
-141
@@ -11,23 +11,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/counter"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/observ"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
|
||||
)
|
||||
|
||||
// otelComponentType is a name identifying the type of the OpenTelemetry
|
||||
// component. It is not a standardized OTel component type, so it uses the
|
||||
// Go package prefixed type name to ensure uniqueness and identity.
|
||||
const otelComponentType = "go.opentelemetry.io/otel/exporters/stdout/stdouttrace.Exporter"
|
||||
|
||||
var zeroTime time.Time
|
||||
|
||||
var _ trace.SpanExporter = &Exporter{}
|
||||
@@ -46,39 +35,8 @@ func New(options ...Option) (*Exporter, error) {
|
||||
timestamps: cfg.Timestamps,
|
||||
}
|
||||
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return exporter, nil
|
||||
}
|
||||
|
||||
exporter.selfObservabilityEnabled = true
|
||||
exporter.selfObservabilityAttrs = []attribute.KeyValue{
|
||||
semconv.OTelComponentName(fmt.Sprintf("%s/%d", otelComponentType, counter.NextExporterID())),
|
||||
semconv.OTelComponentTypeKey.String(otelComponentType),
|
||||
}
|
||||
s := attribute.NewSet(exporter.selfObservabilityAttrs...)
|
||||
exporter.selfObservabilitySetOpt = metric.WithAttributeSet(s)
|
||||
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter(
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err, e error
|
||||
if exporter.spanInflightMetric, e = otelconv.NewSDKExporterSpanInflight(m); e != nil {
|
||||
e = fmt.Errorf("failed to create span inflight metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
if exporter.spanExportedMetric, e = otelconv.NewSDKExporterSpanExported(m); e != nil {
|
||||
e = fmt.Errorf("failed to create span exported metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
if exporter.operationDurationMetric, e = otelconv.NewSDKExporterOperationDuration(m); e != nil {
|
||||
e = fmt.Errorf("failed to create operation duration metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
|
||||
var err error
|
||||
exporter.inst, err = observ.NewInstrumentation(counter.NextExporterID())
|
||||
return exporter, err
|
||||
}
|
||||
|
||||
@@ -91,107 +49,15 @@ type Exporter struct {
|
||||
stoppedMu sync.RWMutex
|
||||
stopped bool
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
selfObservabilityAttrs []attribute.KeyValue // selfObservability common attributes
|
||||
selfObservabilitySetOpt metric.MeasurementOption
|
||||
spanInflightMetric otelconv.SDKExporterSpanInflight
|
||||
spanExportedMetric otelconv.SDKExporterSpanExported
|
||||
operationDurationMetric otelconv.SDKExporterOperationDuration
|
||||
inst *observ.Instrumentation
|
||||
}
|
||||
|
||||
var (
|
||||
measureAttrsPool = sync.Pool{
|
||||
New: func() any {
|
||||
// "component.name" + "component.type" + "error.type"
|
||||
const n = 1 + 1 + 1
|
||||
s := make([]attribute.KeyValue, 0, n)
|
||||
// Return a pointer to a slice instead of a slice itself
|
||||
// to avoid allocations on every call.
|
||||
return &s
|
||||
},
|
||||
}
|
||||
|
||||
addOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1 // WithAttributeSet
|
||||
o := make([]metric.AddOption, 0, n)
|
||||
return &o
|
||||
},
|
||||
}
|
||||
|
||||
recordOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1 // WithAttributeSet
|
||||
o := make([]metric.RecordOption, 0, n)
|
||||
return &o
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// ExportSpans writes spans in json format to stdout.
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) (err error) {
|
||||
var success int64
|
||||
if e.selfObservabilityEnabled {
|
||||
count := int64(len(spans))
|
||||
|
||||
addOpt := addOptPool.Get().(*[]metric.AddOption)
|
||||
defer func() {
|
||||
*addOpt = (*addOpt)[:0]
|
||||
addOptPool.Put(addOpt)
|
||||
}()
|
||||
|
||||
*addOpt = append(*addOpt, e.selfObservabilitySetOpt)
|
||||
|
||||
e.spanInflightMetric.Inst().Add(ctx, count, *addOpt...)
|
||||
defer func(starting time.Time) {
|
||||
e.spanInflightMetric.Inst().Add(ctx, -count, *addOpt...)
|
||||
|
||||
// Record the success and duration of the operation.
|
||||
//
|
||||
// Do not exclude 0 values, as they are valid and indicate no spans
|
||||
// were exported which is meaningful for certain aggregations.
|
||||
e.spanExportedMetric.Inst().Add(ctx, success, *addOpt...)
|
||||
|
||||
mOpt := e.selfObservabilitySetOpt
|
||||
if err != nil {
|
||||
// additional attributes for self-observability,
|
||||
// only spanExportedMetric and operationDurationMetric are supported.
|
||||
attrs := measureAttrsPool.Get().(*[]attribute.KeyValue)
|
||||
defer func() {
|
||||
*attrs = (*attrs)[:0] // reset the slice for reuse
|
||||
measureAttrsPool.Put(attrs)
|
||||
}()
|
||||
*attrs = append(*attrs, e.selfObservabilityAttrs...)
|
||||
*attrs = append(*attrs, semconv.ErrorType(err))
|
||||
|
||||
// Do not inefficiently make a copy of attrs by using
|
||||
// WithAttributes instead of WithAttributeSet.
|
||||
set := attribute.NewSet(*attrs...)
|
||||
mOpt = metric.WithAttributeSet(set)
|
||||
|
||||
// Reset addOpt with new attribute set.
|
||||
*addOpt = append((*addOpt)[:0], mOpt)
|
||||
|
||||
e.spanExportedMetric.Inst().Add(
|
||||
ctx,
|
||||
count-success,
|
||||
*addOpt...,
|
||||
)
|
||||
}
|
||||
|
||||
recordOpt := recordOptPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*recordOpt = (*recordOpt)[:0]
|
||||
recordOptPool.Put(recordOpt)
|
||||
}()
|
||||
|
||||
*recordOpt = append(*recordOpt, mOpt)
|
||||
e.operationDurationMetric.Inst().Record(
|
||||
ctx,
|
||||
time.Since(starting).Seconds(),
|
||||
*recordOpt...,
|
||||
)
|
||||
}(time.Now())
|
||||
if e.inst != nil {
|
||||
op := e.inst.ExportSpans(ctx, len(spans))
|
||||
defer func() { op.End(success, err) }()
|
||||
}
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package errorhandler provides the global error handler for OpenTelemetry.
|
||||
//
|
||||
// This package has no OTel dependencies, allowing it to be imported by any
|
||||
// package in the module without creating import cycles.
|
||||
package errorhandler // import "go.opentelemetry.io/otel/internal/errorhandler"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// ErrorHandler handles irremediable events.
|
||||
type ErrorHandler interface {
|
||||
// Handle handles any error deemed irremediable by an OpenTelemetry
|
||||
// component.
|
||||
Handle(error)
|
||||
}
|
||||
|
||||
type ErrDelegator struct {
|
||||
delegate atomic.Pointer[ErrorHandler]
|
||||
}
|
||||
|
||||
// Compile-time check that delegator implements ErrorHandler.
|
||||
var _ ErrorHandler = (*ErrDelegator)(nil)
|
||||
|
||||
func (d *ErrDelegator) Handle(err error) {
|
||||
if eh := d.delegate.Load(); eh != nil {
|
||||
(*eh).Handle(err)
|
||||
return
|
||||
}
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
// setDelegate sets the ErrorHandler delegate.
|
||||
func (d *ErrDelegator) setDelegate(eh ErrorHandler) {
|
||||
d.delegate.Store(&eh)
|
||||
}
|
||||
|
||||
type errorHandlerHolder struct {
|
||||
eh ErrorHandler
|
||||
}
|
||||
|
||||
var (
|
||||
globalErrorHandler = defaultErrorHandler()
|
||||
delegateErrorHandlerOnce sync.Once
|
||||
)
|
||||
|
||||
// GetErrorHandler returns the global ErrorHandler instance.
|
||||
//
|
||||
// The default ErrorHandler instance returned will log all errors to STDERR
|
||||
// until an override ErrorHandler is set with SetErrorHandler. All
|
||||
// ErrorHandler returned prior to this will automatically forward errors to
|
||||
// the set instance instead of logging.
|
||||
//
|
||||
// Subsequent calls to SetErrorHandler after the first will not forward errors
|
||||
// to the new ErrorHandler for prior returned instances.
|
||||
func GetErrorHandler() ErrorHandler {
|
||||
return globalErrorHandler.Load().(errorHandlerHolder).eh
|
||||
}
|
||||
|
||||
// SetErrorHandler sets the global ErrorHandler to h.
|
||||
//
|
||||
// The first time this is called all ErrorHandler previously returned from
|
||||
// GetErrorHandler will send errors to h instead of the default logging
|
||||
// ErrorHandler. Subsequent calls will set the global ErrorHandler, but not
|
||||
// delegate errors to h.
|
||||
func SetErrorHandler(h ErrorHandler) {
|
||||
current := GetErrorHandler()
|
||||
|
||||
if _, cOk := current.(*ErrDelegator); cOk {
|
||||
if _, ehOk := h.(*ErrDelegator); ehOk && current == h {
|
||||
// Do not assign to the delegate of the default ErrDelegator to be
|
||||
// itself.
|
||||
log.Print(errors.New("no ErrorHandler delegate configured"), " ErrorHandler remains its current value.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
delegateErrorHandlerOnce.Do(func() {
|
||||
if def, ok := current.(*ErrDelegator); ok {
|
||||
def.setDelegate(h)
|
||||
}
|
||||
})
|
||||
globalErrorHandler.Store(errorHandlerHolder{eh: h})
|
||||
}
|
||||
|
||||
func defaultErrorHandler() *atomic.Value {
|
||||
v := &atomic.Value{}
|
||||
v.Store(errorHandlerHolder{eh: &ErrDelegator{}})
|
||||
return v
|
||||
}
|
||||
+7
-27
@@ -5,33 +5,13 @@
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync/atomic"
|
||||
"go.opentelemetry.io/otel/internal/errorhandler"
|
||||
)
|
||||
|
||||
// ErrorHandler handles irremediable events.
|
||||
type ErrorHandler interface {
|
||||
// Handle handles any error deemed irremediable by an OpenTelemetry
|
||||
// component.
|
||||
Handle(error)
|
||||
}
|
||||
// ErrorHandler is an alias for errorhandler.ErrorHandler, kept for backward
|
||||
// compatibility with existing callers of internal/global.
|
||||
type ErrorHandler = errorhandler.ErrorHandler
|
||||
|
||||
type ErrDelegator struct {
|
||||
delegate atomic.Pointer[ErrorHandler]
|
||||
}
|
||||
|
||||
// Compile-time check that delegator implements ErrorHandler.
|
||||
var _ ErrorHandler = (*ErrDelegator)(nil)
|
||||
|
||||
func (d *ErrDelegator) Handle(err error) {
|
||||
if eh := d.delegate.Load(); eh != nil {
|
||||
(*eh).Handle(err)
|
||||
return
|
||||
}
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
// setDelegate sets the ErrorHandler delegate.
|
||||
func (d *ErrDelegator) setDelegate(eh ErrorHandler) {
|
||||
d.delegate.Store(&eh)
|
||||
}
|
||||
// ErrDelegator is an alias for errorhandler.ErrDelegator, kept for backward
|
||||
// compatibility with existing callers of internal/global.
|
||||
type ErrDelegator = errorhandler.ErrDelegator
|
||||
|
||||
+3
-33
@@ -8,16 +8,13 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/errorhandler"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
type (
|
||||
errorHandlerHolder struct {
|
||||
eh ErrorHandler
|
||||
}
|
||||
|
||||
tracerProviderHolder struct {
|
||||
tp trace.TracerProvider
|
||||
}
|
||||
@@ -32,12 +29,10 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
globalErrorHandler = defaultErrorHandler()
|
||||
globalTracer = defaultTracerValue()
|
||||
globalPropagators = defaultPropagatorsValue()
|
||||
globalMeterProvider = defaultMeterProvider()
|
||||
|
||||
delegateErrorHandlerOnce sync.Once
|
||||
delegateTraceOnce sync.Once
|
||||
delegateTextMapPropagatorOnce sync.Once
|
||||
delegateMeterOnce sync.Once
|
||||
@@ -53,7 +48,7 @@ var (
|
||||
// Subsequent calls to SetErrorHandler after the first will not forward errors
|
||||
// to the new ErrorHandler for prior returned instances.
|
||||
func GetErrorHandler() ErrorHandler {
|
||||
return globalErrorHandler.Load().(errorHandlerHolder).eh
|
||||
return errorhandler.GetErrorHandler()
|
||||
}
|
||||
|
||||
// SetErrorHandler sets the global ErrorHandler to h.
|
||||
@@ -63,26 +58,7 @@ func GetErrorHandler() ErrorHandler {
|
||||
// ErrorHandler. Subsequent calls will set the global ErrorHandler, but not
|
||||
// delegate errors to h.
|
||||
func SetErrorHandler(h ErrorHandler) {
|
||||
current := GetErrorHandler()
|
||||
|
||||
if _, cOk := current.(*ErrDelegator); cOk {
|
||||
if _, ehOk := h.(*ErrDelegator); ehOk && current == h {
|
||||
// Do not assign to the delegate of the default ErrDelegator to be
|
||||
// itself.
|
||||
Error(
|
||||
errors.New("no ErrorHandler delegate configured"),
|
||||
"ErrorHandler remains its current value.",
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
delegateErrorHandlerOnce.Do(func() {
|
||||
if def, ok := current.(*ErrDelegator); ok {
|
||||
def.setDelegate(h)
|
||||
}
|
||||
})
|
||||
globalErrorHandler.Store(errorHandlerHolder{eh: h})
|
||||
errorhandler.SetErrorHandler(h)
|
||||
}
|
||||
|
||||
// TracerProvider is the internal implementation for global.TracerProvider.
|
||||
@@ -174,12 +150,6 @@ func SetMeterProvider(mp metric.MeterProvider) {
|
||||
globalMeterProvider.Store(meterProviderHolder{mp: mp})
|
||||
}
|
||||
|
||||
func defaultErrorHandler() *atomic.Value {
|
||||
v := &atomic.Value{}
|
||||
v.Store(errorHandlerHolder{eh: &ErrDelegator{}})
|
||||
return v
|
||||
}
|
||||
|
||||
func defaultTracerValue() *atomic.Value {
|
||||
v := &atomic.Value{}
|
||||
v.Store(tracerProviderHolder{tp: &tracerProvider{}})
|
||||
|
||||
+3
@@ -211,6 +211,9 @@ type Float64Observer interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Observe(value float64, options ...ObserveOption)
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -210,6 +210,9 @@ type Int64Observer interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Observe(value int64, options ...ObserveOption)
|
||||
}
|
||||
|
||||
|
||||
+53
-1
@@ -30,6 +30,9 @@ type MeterProvider interface {
|
||||
//
|
||||
// If the name is empty, then an implementation defined default name will
|
||||
// be used instead.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Meter(name string, opts ...MeterOption) Meter
|
||||
}
|
||||
|
||||
@@ -51,6 +54,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64Counter(name string, options ...Int64CounterOption) (Int64Counter, error)
|
||||
|
||||
// Int64UpDownCounter returns a new Int64UpDownCounter instrument
|
||||
@@ -61,6 +67,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64UpDownCounter(name string, options ...Int64UpDownCounterOption) (Int64UpDownCounter, error)
|
||||
|
||||
// Int64Histogram returns a new Int64Histogram instrument identified by
|
||||
@@ -71,6 +80,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error)
|
||||
|
||||
// Int64Gauge returns a new Int64Gauge instrument identified by name and
|
||||
@@ -80,6 +92,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64Gauge(name string, options ...Int64GaugeOption) (Int64Gauge, error)
|
||||
|
||||
// Int64ObservableCounter returns a new Int64ObservableCounter identified
|
||||
@@ -95,6 +110,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64ObservableCounter(name string, options ...Int64ObservableCounterOption) (Int64ObservableCounter, error)
|
||||
|
||||
// Int64ObservableUpDownCounter returns a new Int64ObservableUpDownCounter
|
||||
@@ -110,6 +128,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64ObservableUpDownCounter(
|
||||
name string,
|
||||
options ...Int64ObservableUpDownCounterOption,
|
||||
@@ -128,6 +149,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Int64ObservableGauge(name string, options ...Int64ObservableGaugeOption) (Int64ObservableGauge, error)
|
||||
|
||||
// Float64Counter returns a new Float64Counter instrument identified by
|
||||
@@ -148,6 +172,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Float64UpDownCounter(name string, options ...Float64UpDownCounterOption) (Float64UpDownCounter, error)
|
||||
|
||||
// Float64Histogram returns a new Float64Histogram instrument identified by
|
||||
@@ -158,6 +185,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error)
|
||||
|
||||
// Float64Gauge returns a new Float64Gauge instrument identified by name and
|
||||
@@ -167,6 +197,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Float64Gauge(name string, options ...Float64GaugeOption) (Float64Gauge, error)
|
||||
|
||||
// Float64ObservableCounter returns a new Float64ObservableCounter
|
||||
@@ -182,6 +215,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Float64ObservableCounter(name string, options ...Float64ObservableCounterOption) (Float64ObservableCounter, error)
|
||||
|
||||
// Float64ObservableUpDownCounter returns a new
|
||||
@@ -197,6 +233,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Float64ObservableUpDownCounter(
|
||||
name string,
|
||||
options ...Float64ObservableUpDownCounterOption,
|
||||
@@ -215,6 +254,9 @@ type Meter interface {
|
||||
// The name needs to conform to the OpenTelemetry instrument name syntax.
|
||||
// See the Instrument Name section of the package documentation for more
|
||||
// information.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Float64ObservableGauge(name string, options ...Float64ObservableGaugeOption) (Float64ObservableGauge, error)
|
||||
|
||||
// RegisterCallback registers f to be called during the collection of a
|
||||
@@ -229,6 +271,9 @@ type Meter interface {
|
||||
// If no instruments are passed, f should not be registered nor called
|
||||
// during collection.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
//
|
||||
// The function f needs to be concurrent safe.
|
||||
RegisterCallback(f Callback, instruments ...Observable) (Registration, error)
|
||||
}
|
||||
@@ -263,9 +308,15 @@ type Observer interface {
|
||||
embedded.Observer
|
||||
|
||||
// ObserveFloat64 records the float64 value for obsrv.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
ObserveFloat64(obsrv Float64Observable, value float64, opts ...ObserveOption)
|
||||
|
||||
// ObserveInt64 records the int64 value for obsrv.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
ObserveInt64(obsrv Int64Observable, value int64, opts ...ObserveOption)
|
||||
}
|
||||
|
||||
@@ -283,6 +334,7 @@ type Registration interface {
|
||||
|
||||
// Unregister removes the callback registration from a Meter.
|
||||
//
|
||||
// This method needs to be idempotent and concurrent safe.
|
||||
// Implementations of this method need to be idempotent and safe for a user
|
||||
// to call concurrently.
|
||||
Unregister() error
|
||||
}
|
||||
|
||||
+24
@@ -24,12 +24,18 @@ type Float64Counter interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Add(ctx context.Context, incr float64, options ...AddOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
@@ -83,12 +89,18 @@ type Float64UpDownCounter interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Add(ctx context.Context, incr float64, options ...AddOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
@@ -142,12 +154,18 @@ type Float64Histogram interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Record(ctx context.Context, incr float64, options ...RecordOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
@@ -206,12 +224,18 @@ type Float64Gauge interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Record(ctx context.Context, value float64, options ...RecordOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -24,12 +24,18 @@ type Int64Counter interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Add(ctx context.Context, incr int64, options ...AddOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
@@ -83,12 +89,18 @@ type Int64UpDownCounter interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Add(ctx context.Context, incr int64, options ...AddOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
@@ -142,12 +154,18 @@ type Int64Histogram interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Record(ctx context.Context, incr int64, options ...RecordOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
@@ -206,12 +224,18 @@ type Int64Gauge interface {
|
||||
//
|
||||
// Use the WithAttributeSet (or, if performance is not a concern,
|
||||
// the WithAttributes) option to include measurement attributes.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Record(ctx context.Context, value int64, options ...RecordOption)
|
||||
|
||||
// Enabled reports whether the instrument will process measurements for the given context.
|
||||
//
|
||||
// This function can be used in places where measuring an instrument
|
||||
// would result in computationally expensive operations.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(context.Context) bool
|
||||
}
|
||||
|
||||
|
||||
+22
-2
@@ -7,9 +7,16 @@ import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/internal/errorhandler"
|
||||
)
|
||||
|
||||
const baggageHeader = "baggage"
|
||||
const (
|
||||
baggageHeader = "baggage"
|
||||
|
||||
// W3C Baggage specification limits.
|
||||
// https://www.w3.org/TR/baggage/#limits
|
||||
maxMembers = 64
|
||||
)
|
||||
|
||||
// Baggage is a propagator that supports the W3C Baggage format.
|
||||
//
|
||||
@@ -50,6 +57,9 @@ func extractSingleBaggage(parent context.Context, carrier TextMapCarrier) contex
|
||||
|
||||
bag, err := baggage.Parse(bStr)
|
||||
if err != nil {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
}
|
||||
if bag.Len() == 0 {
|
||||
return parent
|
||||
}
|
||||
return baggage.ContextWithBaggage(parent, bag)
|
||||
@@ -60,17 +70,27 @@ func extractMultiBaggage(parent context.Context, carrier ValuesGetter) context.C
|
||||
if len(bVals) == 0 {
|
||||
return parent
|
||||
}
|
||||
|
||||
var members []baggage.Member
|
||||
for _, bStr := range bVals {
|
||||
currBag, err := baggage.Parse(bStr)
|
||||
if err != nil {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
}
|
||||
if currBag.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
members = append(members, currBag.Members()...)
|
||||
if len(members) >= maxMembers {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
b, err := baggage.New(members...)
|
||||
if err != nil || b.Len() == 0 {
|
||||
if err != nil {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
}
|
||||
if b.Len() == 0 {
|
||||
return parent
|
||||
}
|
||||
return baggage.ContextWithBaggage(parent, b)
|
||||
|
||||
+6
-7
@@ -46,8 +46,8 @@ func (TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) {
|
||||
carrier.Set(tracestateHeader, ts)
|
||||
}
|
||||
|
||||
// Clear all flags other than the trace-context supported sampling bit.
|
||||
flags := sc.TraceFlags() & trace.FlagsSampled
|
||||
// Preserve only the spec-defined flags: sampled (0x01) and random (0x02).
|
||||
flags := sc.TraceFlags() & (trace.FlagsSampled | trace.FlagsRandom)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.Grow(2 + 32 + 16 + 2 + 3)
|
||||
@@ -104,14 +104,13 @@ func (TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
|
||||
if !extractPart(opts[:], &h, 2) {
|
||||
return trace.SpanContext{}
|
||||
}
|
||||
if version == 0 && (h != "" || opts[0] > 2) {
|
||||
// version 0 not allow extra
|
||||
// version 0 not allow other flag
|
||||
if version == 0 && (h != "" || opts[0] > 3) {
|
||||
// version 0 does not allow extra fields or reserved flag bits.
|
||||
return trace.SpanContext{}
|
||||
}
|
||||
|
||||
// Clear all flags other than the trace-context supported sampling bit.
|
||||
scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled // nolint:gosec // slice size already checked.
|
||||
scc.TraceFlags = trace.TraceFlags(opts[0]) & //nolint:gosec // slice size already checked.
|
||||
(trace.FlagsSampled | trace.FlagsRandom)
|
||||
|
||||
// Ignore the error returned here. Failure to parse tracestate MUST NOT
|
||||
// affect the parsing of traceparent according to the W3C tracecontext
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
codespell==2.4.1
|
||||
codespell==2.4.2
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import (
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+4
-2
@@ -15,7 +15,7 @@ import (
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric/internal/observ"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
// Default periodic reader timing.
|
||||
@@ -107,7 +107,9 @@ func WithInterval(d time.Duration) PeriodicReaderOption {
|
||||
// exporter. That is left to the user to accomplish.
|
||||
func NewPeriodicReader(exporter Exporter, options ...PeriodicReaderOption) *PeriodicReader {
|
||||
conf := newPeriodicReaderConfig(options)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel( //nolint:gosec // cancel called during PeriodicReader shutdown.
|
||||
context.Background(),
|
||||
)
|
||||
r := &PeriodicReader{
|
||||
interval: conf.interval,
|
||||
timeout: conf.timeout,
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package metric // import "go.opentelemetry.io/otel/sdk/metric"
|
||||
|
||||
// version is the current release version of the metric SDK in use.
|
||||
func version() string {
|
||||
return "1.40.0"
|
||||
return "1.42.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
type containerIDProvider func() (string, error)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
type hostIDProvider func() (string, error)
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
import "os"
|
||||
|
||||
func readFile(filename string) (string, error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
b, err := os.ReadFile(filename) // nolint:gosec // false positive
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
type osDescriptionProvider func() (string, error)
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
+2
-4
@@ -123,12 +123,10 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO
|
||||
otel.Handle(err)
|
||||
}
|
||||
|
||||
bsp.stopWait.Add(1)
|
||||
go func() {
|
||||
defer bsp.stopWait.Done()
|
||||
bsp.stopWait.Go(func() {
|
||||
bsp.processQueue()
|
||||
bsp.drainQueue()
|
||||
}()
|
||||
})
|
||||
|
||||
return bsp
|
||||
}
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -13,8 +13,8 @@ import (
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -13,8 +13,8 @@ import (
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
)
|
||||
|
||||
var measureAttrsPool = sync.Pool{
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/internal/x"
|
||||
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
||||
+4
-4
@@ -5,6 +5,7 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -262,6 +263,7 @@ func (p *TracerProvider) ForceFlush(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, sps := range spss {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -269,11 +271,9 @@ func (p *TracerProvider) ForceFlush(ctx context.Context) error {
|
||||
default:
|
||||
}
|
||||
|
||||
if err := sps.sp.ForceFlush(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
err = errors.Join(err, sps.sp.ForceFlush(ctx))
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown shuts down TracerProvider. All registered span processors are shut down
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry SDK in use.
|
||||
func Version() string {
|
||||
return "1.40.0"
|
||||
return "1.42.0"
|
||||
}
|
||||
|
||||
-2264
File diff suppressed because it is too large
Load Diff
-1010
File diff suppressed because it is too large
Load Diff
-78
@@ -1,78 +0,0 @@
|
||||
<!-- Generated. DO NOT MODIFY. -->
|
||||
# Migration from v1.38.0 to v1.39.0
|
||||
|
||||
The `go.opentelemetry.io/otel/semconv/v1.39.0` package should be a drop-in replacement for `go.opentelemetry.io/otel/semconv/v1.38.0` with the following exceptions.
|
||||
|
||||
## Removed
|
||||
|
||||
The following declarations have been removed.
|
||||
Refer to the [OpenTelemetry Semantic Conventions documentation] for deprecation instructions.
|
||||
|
||||
If the type is not listed in the documentation as deprecated, it has been removed in this version due to lack of applicability or use.
|
||||
If you use any of these non-deprecated declarations in your Go application, please [open an issue] describing your use-case.
|
||||
|
||||
- `LinuxMemorySlabStateKey`
|
||||
- `LinuxMemorySlabStateReclaimable`
|
||||
- `LinuxMemorySlabStateUnreclaimable`
|
||||
- `PeerService`
|
||||
- `PeerServiceKey`
|
||||
- `RPCConnectRPCErrorCodeAborted`
|
||||
- `RPCConnectRPCErrorCodeAlreadyExists`
|
||||
- `RPCConnectRPCErrorCodeCancelled`
|
||||
- `RPCConnectRPCErrorCodeDataLoss`
|
||||
- `RPCConnectRPCErrorCodeDeadlineExceeded`
|
||||
- `RPCConnectRPCErrorCodeFailedPrecondition`
|
||||
- `RPCConnectRPCErrorCodeInternal`
|
||||
- `RPCConnectRPCErrorCodeInvalidArgument`
|
||||
- `RPCConnectRPCErrorCodeKey`
|
||||
- `RPCConnectRPCErrorCodeNotFound`
|
||||
- `RPCConnectRPCErrorCodeOutOfRange`
|
||||
- `RPCConnectRPCErrorCodePermissionDenied`
|
||||
- `RPCConnectRPCErrorCodeResourceExhausted`
|
||||
- `RPCConnectRPCErrorCodeUnauthenticated`
|
||||
- `RPCConnectRPCErrorCodeUnavailable`
|
||||
- `RPCConnectRPCErrorCodeUnimplemented`
|
||||
- `RPCConnectRPCErrorCodeUnknown`
|
||||
- `RPCConnectRPCRequestMetadata`
|
||||
- `RPCConnectRPCResponseMetadata`
|
||||
- `RPCGRPCRequestMetadata`
|
||||
- `RPCGRPCResponseMetadata`
|
||||
- `RPCGRPCStatusCodeAborted`
|
||||
- `RPCGRPCStatusCodeAlreadyExists`
|
||||
- `RPCGRPCStatusCodeCancelled`
|
||||
- `RPCGRPCStatusCodeDataLoss`
|
||||
- `RPCGRPCStatusCodeDeadlineExceeded`
|
||||
- `RPCGRPCStatusCodeFailedPrecondition`
|
||||
- `RPCGRPCStatusCodeInternal`
|
||||
- `RPCGRPCStatusCodeInvalidArgument`
|
||||
- `RPCGRPCStatusCodeKey`
|
||||
- `RPCGRPCStatusCodeNotFound`
|
||||
- `RPCGRPCStatusCodeOk`
|
||||
- `RPCGRPCStatusCodeOutOfRange`
|
||||
- `RPCGRPCStatusCodePermissionDenied`
|
||||
- `RPCGRPCStatusCodeResourceExhausted`
|
||||
- `RPCGRPCStatusCodeUnauthenticated`
|
||||
- `RPCGRPCStatusCodeUnavailable`
|
||||
- `RPCGRPCStatusCodeUnimplemented`
|
||||
- `RPCGRPCStatusCodeUnknown`
|
||||
- `RPCJSONRPCErrorCode`
|
||||
- `RPCJSONRPCErrorCodeKey`
|
||||
- `RPCJSONRPCErrorMessage`
|
||||
- `RPCJSONRPCErrorMessageKey`
|
||||
- `RPCJSONRPCRequestID`
|
||||
- `RPCJSONRPCRequestIDKey`
|
||||
- `RPCJSONRPCVersion`
|
||||
- `RPCJSONRPCVersionKey`
|
||||
- `RPCService`
|
||||
- `RPCServiceKey`
|
||||
- `RPCSystemApacheDubbo`
|
||||
- `RPCSystemConnectRPC`
|
||||
- `RPCSystemDotnetWcf`
|
||||
- `RPCSystemGRPC`
|
||||
- `RPCSystemJSONRPC`
|
||||
- `RPCSystemJavaRmi`
|
||||
- `RPCSystemKey`
|
||||
- `RPCSystemOncRPC`
|
||||
|
||||
[OpenTelemetry Semantic Conventions documentation]: https://github.com/open-telemetry/semantic-conventions
|
||||
[open an issue]: https://github.com/open-telemetry/opentelemetry-go/issues/new?template=Blank+issue
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# Semconv v1.39.0
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.39.0)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<!-- Generated. DO NOT MODIFY. -->
|
||||
# Migration from v1.39.0 to v1.40.0
|
||||
|
||||
The `go.opentelemetry.io/otel/semconv/v1.40.0` package should be a drop-in replacement for `go.opentelemetry.io/otel/semconv/v1.39.0` with the following exceptions.
|
||||
|
||||
## Removed
|
||||
|
||||
The following declarations have been removed.
|
||||
Refer to the [OpenTelemetry Semantic Conventions documentation] for deprecation instructions.
|
||||
|
||||
If the type is not listed in the documentation as deprecated, it has been removed in this version due to lack of applicability or use.
|
||||
If you use any of these non-deprecated declarations in your Go application, please [open an issue] describing your use-case.
|
||||
|
||||
- `ErrorMessage`
|
||||
- `ErrorMessageKey`
|
||||
- `RPCMessageCompressedSize`
|
||||
- `RPCMessageCompressedSizeKey`
|
||||
- `RPCMessageID`
|
||||
- `RPCMessageIDKey`
|
||||
- `RPCMessageTypeKey`
|
||||
- `RPCMessageTypeReceived`
|
||||
- `RPCMessageTypeSent`
|
||||
- `RPCMessageUncompressedSize`
|
||||
- `RPCMessageUncompressedSizeKey`
|
||||
|
||||
[OpenTelemetry Semantic Conventions documentation]: https://github.com/open-telemetry/semantic-conventions
|
||||
[open an issue]: https://github.com/open-telemetry/opentelemetry-go/issues/new?template=Blank+issue
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Semconv v1.40.0
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.40.0)
|
||||
Generated
Vendored
+767
-145
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+2
-2
@@ -4,6 +4,6 @@
|
||||
// Package semconv implements OpenTelemetry semantic conventions.
|
||||
//
|
||||
// OpenTelemetry semantic conventions are agreed standardized naming
|
||||
// patterns for OpenTelemetry things. This package represents the v1.39.0
|
||||
// patterns for OpenTelemetry things. This package represents the v1.40.0
|
||||
// version of the OpenTelemetry semantic conventions.
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
Generated
Vendored
+15
-5
@@ -1,9 +1,10 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -14,12 +15,14 @@ import (
|
||||
// If err is nil, the returned attribute has the default value
|
||||
// [ErrorTypeOther].
|
||||
//
|
||||
// If err's type has the method
|
||||
// If err or one of the errors in its chain has the method
|
||||
//
|
||||
// ErrorType() string
|
||||
//
|
||||
// then the returned attribute has the value of err.ErrorType(). Otherwise, the
|
||||
// returned attribute has a value derived from the concrete type of err.
|
||||
// the returned attribute has that method's return value. If multiple errors in
|
||||
// the chain implement this method, the value from the first match found by
|
||||
// [errors.As] is used. Otherwise, the returned attribute has a value derived
|
||||
// from the concrete type of err.
|
||||
//
|
||||
// The key of the returned attribute is [ErrorTypeKey].
|
||||
func ErrorType(err error) attribute.KeyValue {
|
||||
@@ -33,8 +36,15 @@ func ErrorType(err error) attribute.KeyValue {
|
||||
func errorType(err error) string {
|
||||
var s string
|
||||
if et, ok := err.(interface{ ErrorType() string }); ok {
|
||||
// Prioritize the ErrorType method if available.
|
||||
// Fast path: check the top-level error first.
|
||||
s = et.ErrorType()
|
||||
} else {
|
||||
// Fallback: search the error chain for an ErrorType method.
|
||||
var et interface{ ErrorType() string }
|
||||
if errors.As(err, &et) {
|
||||
// Prioritize the ErrorType method if available.
|
||||
s = et.ErrorType()
|
||||
}
|
||||
}
|
||||
if s == "" {
|
||||
// Fallback to reflection if the ErrorType method is not supported or
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
|
||||
const (
|
||||
// ExceptionEventName is the name of the Span event representing an exception.
|
||||
Generated
Vendored
+65
-26
@@ -67,6 +67,8 @@ var (
|
||||
RequestMethodPut RequestMethodAttr = "PUT"
|
||||
// RequestMethodTrace is the TRACE method.
|
||||
RequestMethodTrace RequestMethodAttr = "TRACE"
|
||||
// RequestMethodQuery is the QUERY method.
|
||||
RequestMethodQuery RequestMethodAttr = "QUERY"
|
||||
// RequestMethodOther is the any HTTP method that the instrumentation has no
|
||||
// prior knowledge of.
|
||||
RequestMethodOther RequestMethodAttr = "_OTHER"
|
||||
@@ -158,7 +160,10 @@ func (m ClientActiveRequests) Add(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64UpDownCounter.Add(ctx, incr)
|
||||
m.Int64UpDownCounter.Add(ctx, incr, metric.WithAttributes(
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -172,7 +177,7 @@ func (m ClientActiveRequests) Add(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
)...,
|
||||
@@ -298,7 +303,10 @@ func (m ClientConnectionDuration) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
m.Float64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -312,7 +320,7 @@ func (m ClientConnectionDuration) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
)...,
|
||||
@@ -441,7 +449,11 @@ func (m ClientOpenConnections) Add(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64UpDownCounter.Add(ctx, incr)
|
||||
m.Int64UpDownCounter.Add(ctx, incr, metric.WithAttributes(
|
||||
attribute.String("http.connection.state", string(connectionState)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -455,7 +467,7 @@ func (m ClientOpenConnections) Add(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.connection.state", string(connectionState)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
@@ -590,7 +602,11 @@ func (m ClientRequestBodySize) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
m.Int64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -604,7 +620,7 @@ func (m ClientRequestBodySize) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
@@ -766,7 +782,11 @@ func (m ClientRequestDuration) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
m.Float64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -780,7 +800,7 @@ func (m ClientRequestDuration) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
@@ -942,7 +962,11 @@ func (m ClientResponseBodySize) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
m.Int64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -956,7 +980,7 @@ func (m ClientResponseBodySize) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("server.address", serverAddress),
|
||||
attribute.Int("server.port", serverPort),
|
||||
@@ -1116,7 +1140,10 @@ func (m ServerActiveRequests) Add(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64UpDownCounter.Add(ctx, incr)
|
||||
m.Int64UpDownCounter.Add(ctx, incr, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1130,7 +1157,7 @@ func (m ServerActiveRequests) Add(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
)...,
|
||||
@@ -1253,7 +1280,10 @@ func (m ServerRequestBodySize) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
m.Int64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1267,7 +1297,7 @@ func (m ServerRequestBodySize) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
)...,
|
||||
@@ -1318,8 +1348,9 @@ func (ServerRequestBodySize) AttrResponseStatusCode(val int) attribute.KeyValue
|
||||
}
|
||||
|
||||
// AttrRoute returns an optional attribute for the "http.route" semantic
|
||||
// convention. It represents the matched route, that is, the path template in the
|
||||
// format used by the respective server framework.
|
||||
// convention. It represents the matched route template for the request. This
|
||||
// MUST be low-cardinality and include all static path segments, with dynamic
|
||||
// path segments represented with placeholders.
|
||||
func (ServerRequestBodySize) AttrRoute(val string) attribute.KeyValue {
|
||||
return attribute.String("http.route", val)
|
||||
}
|
||||
@@ -1436,7 +1467,10 @@ func (m ServerRequestDuration) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
m.Float64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1450,7 +1484,7 @@ func (m ServerRequestDuration) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
)...,
|
||||
@@ -1494,8 +1528,9 @@ func (ServerRequestDuration) AttrResponseStatusCode(val int) attribute.KeyValue
|
||||
}
|
||||
|
||||
// AttrRoute returns an optional attribute for the "http.route" semantic
|
||||
// convention. It represents the matched route, that is, the path template in the
|
||||
// format used by the respective server framework.
|
||||
// convention. It represents the matched route template for the request. This
|
||||
// MUST be low-cardinality and include all static path segments, with dynamic
|
||||
// path segments represented with placeholders.
|
||||
func (ServerRequestDuration) AttrRoute(val string) attribute.KeyValue {
|
||||
return attribute.String("http.route", val)
|
||||
}
|
||||
@@ -1619,7 +1654,10 @@ func (m ServerResponseBodySize) Record(
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Int64Histogram.Record(ctx, val)
|
||||
m.Int64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1633,7 +1671,7 @@ func (m ServerResponseBodySize) Record(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs,
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("http.request.method", string(requestMethod)),
|
||||
attribute.String("url.scheme", urlScheme),
|
||||
)...,
|
||||
@@ -1684,8 +1722,9 @@ func (ServerResponseBodySize) AttrResponseStatusCode(val int) attribute.KeyValue
|
||||
}
|
||||
|
||||
// AttrRoute returns an optional attribute for the "http.route" semantic
|
||||
// convention. It represents the matched route, that is, the path template in the
|
||||
// format used by the respective server framework.
|
||||
// convention. It represents the matched route template for the request. This
|
||||
// MUST be low-cardinality and include all static path segments, with dynamic
|
||||
// path segments represented with placeholders.
|
||||
func (ServerResponseBodySize) AttrRoute(val string) attribute.KeyValue {
|
||||
return attribute.String("http.route", val)
|
||||
}
|
||||
Generated
Vendored
+360
@@ -0,0 +1,360 @@
|
||||
// Code generated from semantic convention specification. DO NOT EDIT.
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package rpcconv provides types and functionality for OpenTelemetry semantic
|
||||
// conventions in the "rpc" namespace.
|
||||
package rpcconv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/noop"
|
||||
)
|
||||
|
||||
var (
|
||||
addOptPool = &sync.Pool{New: func() any { return &[]metric.AddOption{} }}
|
||||
recOptPool = &sync.Pool{New: func() any { return &[]metric.RecordOption{} }}
|
||||
)
|
||||
|
||||
// ErrorTypeAttr is an attribute conforming to the error.type semantic
|
||||
// conventions. It represents the describes a class of error the operation ended
|
||||
// with.
|
||||
type ErrorTypeAttr string
|
||||
|
||||
var (
|
||||
// ErrorTypeOther is a fallback error value to be used when the instrumentation
|
||||
// doesn't define a custom value.
|
||||
ErrorTypeOther ErrorTypeAttr = "_OTHER"
|
||||
)
|
||||
|
||||
// SystemNameAttr is an attribute conforming to the rpc.system.name semantic
|
||||
// conventions. It represents the Remote Procedure Call (RPC) system.
|
||||
type SystemNameAttr string
|
||||
|
||||
var (
|
||||
// SystemNameGRPC is the [gRPC].
|
||||
//
|
||||
// [gRPC]: https://grpc.io/
|
||||
SystemNameGRPC SystemNameAttr = "grpc"
|
||||
// SystemNameDubbo is the [Apache Dubbo].
|
||||
//
|
||||
// [Apache Dubbo]: https://dubbo.apache.org/
|
||||
SystemNameDubbo SystemNameAttr = "dubbo"
|
||||
// SystemNameConnectrpc is the [Connect RPC].
|
||||
//
|
||||
// [Connect RPC]: https://connectrpc.com/
|
||||
SystemNameConnectrpc SystemNameAttr = "connectrpc"
|
||||
// SystemNameJSONRPC is the [JSON-RPC].
|
||||
//
|
||||
// [JSON-RPC]: https://www.jsonrpc.org/
|
||||
SystemNameJSONRPC SystemNameAttr = "jsonrpc"
|
||||
)
|
||||
|
||||
// ClientCallDuration is an instrument used to record metric values conforming to
|
||||
// the "rpc.client.call.duration" semantic conventions. It represents the
|
||||
// measures the duration of an outgoing Remote Procedure Call (RPC).
|
||||
type ClientCallDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newClientCallDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("Measures the duration of an outgoing Remote Procedure Call (RPC)."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewClientCallDuration returns a new ClientCallDuration instrument.
|
||||
func NewClientCallDuration(
|
||||
m metric.Meter,
|
||||
opt ...metric.Float64HistogramOption,
|
||||
) (ClientCallDuration, error) {
|
||||
// Check if the meter is nil.
|
||||
if m == nil {
|
||||
return ClientCallDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newClientCallDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newClientCallDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"rpc.client.call.duration",
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ClientCallDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ClientCallDuration{i}, nil
|
||||
}
|
||||
|
||||
// Inst returns the underlying metric instrument.
|
||||
func (m ClientCallDuration) Inst() metric.Float64Histogram {
|
||||
return m.Float64Histogram
|
||||
}
|
||||
|
||||
// Name returns the semantic convention name of the instrument.
|
||||
func (ClientCallDuration) Name() string {
|
||||
return "rpc.client.call.duration"
|
||||
}
|
||||
|
||||
// Unit returns the semantic convention unit of the instrument
|
||||
func (ClientCallDuration) Unit() string {
|
||||
return "s"
|
||||
}
|
||||
|
||||
// Description returns the semantic convention description of the instrument
|
||||
func (ClientCallDuration) Description() string {
|
||||
return "Measures the duration of an outgoing Remote Procedure Call (RPC)."
|
||||
}
|
||||
|
||||
// Record records val to the current distribution for attrs.
|
||||
//
|
||||
// The systemName is the the Remote Procedure Call (RPC) system.
|
||||
//
|
||||
// All additional attrs passed are included in the recorded value.
|
||||
//
|
||||
// When this metric is reported alongside an RPC client span, the metric value
|
||||
// SHOULD be the same as the RPC client span duration.
|
||||
func (m ClientCallDuration) Record(
|
||||
ctx context.Context,
|
||||
val float64,
|
||||
systemName SystemNameAttr,
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Float64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("rpc.system.name", string(systemName)),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*o = (*o)[:0]
|
||||
recOptPool.Put(o)
|
||||
}()
|
||||
|
||||
*o = append(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("rpc.system.name", string(systemName)),
|
||||
)...,
|
||||
),
|
||||
)
|
||||
|
||||
m.Float64Histogram.Record(ctx, val, *o...)
|
||||
}
|
||||
|
||||
// RecordSet records val to the current distribution for set.
|
||||
//
|
||||
// When this metric is reported alongside an RPC client span, the metric value
|
||||
// SHOULD be the same as the RPC client span duration.
|
||||
func (m ClientCallDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*o = (*o)[:0]
|
||||
recOptPool.Put(o)
|
||||
}()
|
||||
|
||||
*o = append(*o, metric.WithAttributeSet(set))
|
||||
m.Float64Histogram.Record(ctx, val, *o...)
|
||||
}
|
||||
|
||||
// AttrErrorType returns an optional attribute for the "error.type" semantic
|
||||
// convention. It represents the describes a class of error the operation ended
|
||||
// with.
|
||||
func (ClientCallDuration) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue {
|
||||
return attribute.String("error.type", string(val))
|
||||
}
|
||||
|
||||
// AttrMethod returns an optional attribute for the "rpc.method" semantic
|
||||
// convention. It represents the fully-qualified logical name of the method from
|
||||
// the RPC interface perspective.
|
||||
func (ClientCallDuration) AttrMethod(val string) attribute.KeyValue {
|
||||
return attribute.String("rpc.method", val)
|
||||
}
|
||||
|
||||
// AttrResponseStatusCode returns an optional attribute for the
|
||||
// "rpc.response.status_code" semantic convention. It represents the status code
|
||||
// of the RPC returned by the RPC server or generated by the client.
|
||||
func (ClientCallDuration) AttrResponseStatusCode(val string) attribute.KeyValue {
|
||||
return attribute.String("rpc.response.status_code", val)
|
||||
}
|
||||
|
||||
// AttrServerAddress returns an optional attribute for the "server.address"
|
||||
// semantic convention. It represents a string identifying a group of RPC server
|
||||
// instances request is sent to.
|
||||
func (ClientCallDuration) AttrServerAddress(val string) attribute.KeyValue {
|
||||
return attribute.String("server.address", val)
|
||||
}
|
||||
|
||||
// AttrServerPort returns an optional attribute for the "server.port" semantic
|
||||
// convention. It represents the server port number.
|
||||
func (ClientCallDuration) AttrServerPort(val int) attribute.KeyValue {
|
||||
return attribute.Int("server.port", val)
|
||||
}
|
||||
|
||||
// ServerCallDuration is an instrument used to record metric values conforming to
|
||||
// the "rpc.server.call.duration" semantic conventions. It represents the
|
||||
// measures the duration of an incoming Remote Procedure Call (RPC).
|
||||
type ServerCallDuration struct {
|
||||
metric.Float64Histogram
|
||||
}
|
||||
|
||||
var newServerCallDurationOpts = []metric.Float64HistogramOption{
|
||||
metric.WithDescription("Measures the duration of an incoming Remote Procedure Call (RPC)."),
|
||||
metric.WithUnit("s"),
|
||||
}
|
||||
|
||||
// NewServerCallDuration returns a new ServerCallDuration instrument.
|
||||
func NewServerCallDuration(
|
||||
m metric.Meter,
|
||||
opt ...metric.Float64HistogramOption,
|
||||
) (ServerCallDuration, error) {
|
||||
// Check if the meter is nil.
|
||||
if m == nil {
|
||||
return ServerCallDuration{noop.Float64Histogram{}}, nil
|
||||
}
|
||||
|
||||
if len(opt) == 0 {
|
||||
opt = newServerCallDurationOpts
|
||||
} else {
|
||||
opt = append(opt, newServerCallDurationOpts...)
|
||||
}
|
||||
|
||||
i, err := m.Float64Histogram(
|
||||
"rpc.server.call.duration",
|
||||
opt...,
|
||||
)
|
||||
if err != nil {
|
||||
return ServerCallDuration{noop.Float64Histogram{}}, err
|
||||
}
|
||||
return ServerCallDuration{i}, nil
|
||||
}
|
||||
|
||||
// Inst returns the underlying metric instrument.
|
||||
func (m ServerCallDuration) Inst() metric.Float64Histogram {
|
||||
return m.Float64Histogram
|
||||
}
|
||||
|
||||
// Name returns the semantic convention name of the instrument.
|
||||
func (ServerCallDuration) Name() string {
|
||||
return "rpc.server.call.duration"
|
||||
}
|
||||
|
||||
// Unit returns the semantic convention unit of the instrument
|
||||
func (ServerCallDuration) Unit() string {
|
||||
return "s"
|
||||
}
|
||||
|
||||
// Description returns the semantic convention description of the instrument
|
||||
func (ServerCallDuration) Description() string {
|
||||
return "Measures the duration of an incoming Remote Procedure Call (RPC)."
|
||||
}
|
||||
|
||||
// Record records val to the current distribution for attrs.
|
||||
//
|
||||
// The systemName is the the Remote Procedure Call (RPC) system.
|
||||
//
|
||||
// All additional attrs passed are included in the recorded value.
|
||||
//
|
||||
// When this metric is reported alongside an RPC server span, the metric value
|
||||
// SHOULD be the same as the RPC server span duration.
|
||||
func (m ServerCallDuration) Record(
|
||||
ctx context.Context,
|
||||
val float64,
|
||||
systemName SystemNameAttr,
|
||||
attrs ...attribute.KeyValue,
|
||||
) {
|
||||
if len(attrs) == 0 {
|
||||
m.Float64Histogram.Record(ctx, val, metric.WithAttributes(
|
||||
attribute.String("rpc.system.name", string(systemName)),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*o = (*o)[:0]
|
||||
recOptPool.Put(o)
|
||||
}()
|
||||
|
||||
*o = append(
|
||||
*o,
|
||||
metric.WithAttributes(
|
||||
append(
|
||||
attrs[:len(attrs):len(attrs)],
|
||||
attribute.String("rpc.system.name", string(systemName)),
|
||||
)...,
|
||||
),
|
||||
)
|
||||
|
||||
m.Float64Histogram.Record(ctx, val, *o...)
|
||||
}
|
||||
|
||||
// RecordSet records val to the current distribution for set.
|
||||
//
|
||||
// When this metric is reported alongside an RPC server span, the metric value
|
||||
// SHOULD be the same as the RPC server span duration.
|
||||
func (m ServerCallDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
|
||||
if set.Len() == 0 {
|
||||
m.Float64Histogram.Record(ctx, val)
|
||||
return
|
||||
}
|
||||
|
||||
o := recOptPool.Get().(*[]metric.RecordOption)
|
||||
defer func() {
|
||||
*o = (*o)[:0]
|
||||
recOptPool.Put(o)
|
||||
}()
|
||||
|
||||
*o = append(*o, metric.WithAttributeSet(set))
|
||||
m.Float64Histogram.Record(ctx, val, *o...)
|
||||
}
|
||||
|
||||
// AttrErrorType returns an optional attribute for the "error.type" semantic
|
||||
// convention. It represents the describes a class of error the operation ended
|
||||
// with.
|
||||
func (ServerCallDuration) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue {
|
||||
return attribute.String("error.type", string(val))
|
||||
}
|
||||
|
||||
// AttrMethod returns an optional attribute for the "rpc.method" semantic
|
||||
// convention. It represents the fully-qualified logical name of the method from
|
||||
// the RPC interface perspective.
|
||||
func (ServerCallDuration) AttrMethod(val string) attribute.KeyValue {
|
||||
return attribute.String("rpc.method", val)
|
||||
}
|
||||
|
||||
// AttrResponseStatusCode returns an optional attribute for the
|
||||
// "rpc.response.status_code" semantic convention. It represents the status code
|
||||
// of the RPC returned by the RPC server or generated by the client.
|
||||
func (ServerCallDuration) AttrResponseStatusCode(val string) attribute.KeyValue {
|
||||
return attribute.String("rpc.response.status_code", val)
|
||||
}
|
||||
|
||||
// AttrServerAddress returns an optional attribute for the "server.address"
|
||||
// semantic convention. It represents a string identifying a group of RPC server
|
||||
// instances request is sent to.
|
||||
func (ServerCallDuration) AttrServerAddress(val string) attribute.KeyValue {
|
||||
return attribute.String("server.address", val)
|
||||
}
|
||||
|
||||
// AttrServerPort returns an optional attribute for the "server.port" semantic
|
||||
// convention. It represents the server port number.
|
||||
func (ServerCallDuration) AttrServerPort(val int) attribute.KeyValue {
|
||||
return attribute.Int("server.port", val)
|
||||
}
|
||||
Generated
Vendored
+2
-2
@@ -1,9 +1,9 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
|
||||
// SchemaURL is the schema URL that matches the version of the semantic conventions
|
||||
// that this package defines. Semconv packages starting from v1.4.0 must declare
|
||||
// non-empty schema URL in the form https://opentelemetry.io/schemas/<version>
|
||||
const SchemaURL = "https://opentelemetry.io/schemas/1.39.0"
|
||||
const SchemaURL = "https://opentelemetry.io/schemas/1.40.0"
|
||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/internal/telemetry"
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user