Merge pull request #14058 from thaJeztah/bump_otels

chore(deps): go.opentelemetry.io/otel v1.46.0, contrib v0.71.0
This commit is contained in:
Phil Estes authored and GitHub committed 2026-08-28 14:17:21 +00:00
commit bb5988d3d1
44 files changed
+1244 -265

No files matched your search

+13 -13
View File
@@ -66,20 +66,20 @@ require (
github.com/vishvananda/netlink v1.3.1
github.com/vishvananda/netns v0.0.5
go.etcd.io/bbolt v1.5.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0
go.opentelemetry.io/contrib/propagators/envcar v0.70.0
go.opentelemetry.io/otel v1.45.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0
go.opentelemetry.io/otel/sdk v1.45.0
go.opentelemetry.io/otel/trace v1.45.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0
go.opentelemetry.io/contrib/propagators/envcar v0.71.0
go.opentelemetry.io/otel v1.46.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0
go.opentelemetry.io/otel/sdk v1.46.0
go.opentelemetry.io/otel/trace v1.46.0
golang.org/x/mod v0.40.0
golang.org/x/sync v0.22.0
golang.org/x/sys v0.47.0
golang.org/x/time v0.15.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5
google.golang.org/grpc v1.83.2
google.golang.org/protobuf v1.36.12
k8s.io/apimachinery v0.37.0
@@ -115,7 +115,7 @@ require (
github.com/google/deck v0.0.0-20230104221208-105ad94aa8ae // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/knqyf263/go-plugin v0.9.0 // indirect
github.com/mdlayher/socket v0.6.0 // indirect
@@ -137,7 +137,7 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
go.opentelemetry.io/otel/metric v1.46.0 // indirect
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
@@ -146,7 +146,7 @@ require (
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.41.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/api v0.37.0 // indirect
k8s.io/component-base v0.37.0 // indirect
+54 -54
View File
@@ -117,30 +117,30 @@ github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxg
github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
github.com/go-openapi/swag v0.27.1 h1:VotvOLWW8q/EAxB0YdsBBGC8XYyeL1YwBj2ungAGPNg=
github.com/go-openapi/swag v0.27.1/go.mod h1:GTkJPwHfhJp6MWr4/rCh64HVI3Ofu+tcsbfjfHmTxpE=
github.com/go-openapi/swag/cmdutils v0.27.1 h1:I7sYqaWVl5mq0NEmNQkAmFDyNin9ufvMX/p2zwtQaOE=
github.com/go-openapi/swag/cmdutils v0.27.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM=
github.com/go-openapi/swag/conv v0.27.1 h1:8wi9ZG+olmY1wXphl93EWniPtbSPkXM/feH7FgjsvrU=
github.com/go-openapi/swag/conv v0.27.1/go.mod h1:QbqMivkpKhC3g1B1GGGOJ6ANewI3S62dbzYu3Duowqs=
github.com/go-openapi/swag/fileutils v0.27.1 h1:QQqBSoi5mW4XpU85nS0mLcA+zAE6vLzrb0QkmLKf9oM=
github.com/go-openapi/swag/fileutils v0.27.1/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
github.com/go-openapi/swag/jsonutils v0.27.1 h1:SVgK3i4USzCU5mibOOS/l4ea2h9UQXy7J7RNLTjuXjU=
github.com/go-openapi/swag/jsonutils v0.27.1/go.mod h1:tdlEpZqdcQ17uj6J4YdK9vd8It5qWMwjWXOs0tjpRlk=
github.com/go-openapi/swag/loading v0.27.1 h1:/DxUgDXKbBX4bcn7r9uEXfJyzN5XpiJmZplzQTjrRCY=
github.com/go-openapi/swag/loading v0.27.1/go.mod h1:jvGh3iA2+zyUUycB5fgJWzeHnhrpvGnJJM0RVE9ZShE=
github.com/go-openapi/swag/mangling v0.27.1 h1:yC9D0HyUE8gbP+BfmGx9+AA89ikwZTMjESK3OnnoaqA=
github.com/go-openapi/swag/mangling v0.27.1/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w=
github.com/go-openapi/swag/netutils v0.27.1 h1:mICMFoS82F5TZ4Zy3cqmcQk+BFeCp3Uyq3Np7GI0/qU=
github.com/go-openapi/swag/netutils v0.27.1/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ=
github.com/go-openapi/swag/pools v0.27.1 h1:9LeadcMyb2GJCbXX5hVQDbZ2Lq9TL4dCs/nx1j5DO0E=
github.com/go-openapi/swag/pools v0.27.1/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
github.com/go-openapi/swag/stringutils v0.27.1 h1:ZXePZ0r2p1qSjo8tD3Un4vFj8+FqlCkczxDrJIhYUp8=
github.com/go-openapi/swag/stringutils v0.27.1/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/typeutils v0.27.1 h1:KSTdFlfnse4r6dP9IrEnwMldjE+zs71UeEB3//PtVXc=
github.com/go-openapi/swag/typeutils v0.27.1/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/yamlutils v0.27.1 h1:ftxv6xvXb1E3zohUc+okZ9nSqNb9StQX/FXnKZ98sQA=
github.com/go-openapi/swag/yamlutils v0.27.1/go.mod h1:bnxFIB1qewGRiZHypXGZ3fNgf13/0HfRgnS/iZBDrOo=
github.com/go-openapi/swag v0.28.0 h1:xkgbOSKj6DZziNpyqRRAOt3GJGtgjgsd2RoyT30VWuw=
github.com/go-openapi/swag v0.28.0/go.mod h1:4qYnT3Cqr1p1VknOdPo70evN4rgQnAg6jwApHyxSGIg=
github.com/go-openapi/swag/cmdutils v0.28.0 h1:7TOeNtkYru1SG8Y34tDh9WBbLsMqGnptuxWiHREPZ4Q=
github.com/go-openapi/swag/cmdutils v0.28.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM=
github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8=
github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k=
github.com/go-openapi/swag/fileutils v0.28.0 h1:Z04XWQD7R8Eq+7GnOrjovBxPPmZzsS4gt2H2GPGIViU=
github.com/go-openapi/swag/fileutils v0.28.0/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4=
github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg=
github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU=
github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k=
github.com/go-openapi/swag/mangling v0.28.0 h1:pH8eyeNO9SLYsTMWJrurnNfKmDa28XrlA+HePVD53VM=
github.com/go-openapi/swag/mangling v0.28.0/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w=
github.com/go-openapi/swag/netutils v0.28.0 h1:YXN6TALEi2pzts8/8GNm6T61HTAZsieukGZidap989k=
github.com/go-openapi/swag/netutils v0.28.0/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ=
github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU=
github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU=
github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY=
github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY=
github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
@@ -170,8 +170,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY=
github.com/intel/goresctrl v0.13.0 h1:5fhKjNq4V5MYDFHa//6M6x0jP6Iq5EXwZc6/eYxdEtQ=
github.com/intel/goresctrl v0.13.0/go.mod h1:KFHS91JGOmeeuEog+nTQcsGjLC81nRqdsdhcqf69fjU=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
@@ -251,8 +251,8 @@ github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi
github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc=
github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
@@ -290,28 +290,28 @@ go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
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.70.0 h1:oECp5f+hN7nkwjU/8BxQ/q23bGPb8FIrD839owX222E=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0/go.mod h1:DqEFwLumhzMBDQv9PcWbyoDxHI/4lAk6CM4nJBH39sc=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04=
go.opentelemetry.io/contrib/propagators/envcar v0.70.0 h1:ff/FI6f/ZRgcFluGTATyhGjVOuCGeWV2tg35kKvjie0=
go.opentelemetry.io/contrib/propagators/envcar v0.70.0/go.mod h1:5AvLH2mk5oz7vcmpd6LZ4FJs+NGe13hX1ad24c3OxLI=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 h1:QRefszxJmfPdjXUUm3j6iDzY03mTPXMjqErFqQ67vUg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0/go.mod h1:Tiz03lTBVBrm7eWZBOidzEaYaJa8tjwGUGv6d8mlTyk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 h1:fG5MCxGz8+2VtrN/WgqSpJFctVz24gpxj8CxkKmc8Ww=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0/go.mod h1:BmAYTn+3ysbRe+IU2msxmf5Rx3g6DHvex+tWI3LdhYI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 h1:QBajQ2SrwQijzHyZbQlPsuIzpl/ll8DY6wPWsajeGcI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0/go.mod h1:08ZQLjrPLQ6R4kAXvuOvODEer5Yh4CoFvll5qB2BCI8=
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 h1:B2h3uqicet1CT2N5TOFhS+Gq++9i0/CLmaxvhmhtP5s=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0/go.mod h1:dylvB+ZiiwMvsDij9O84Uy7SijLgHMX4mbkncds+4Sw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU=
go.opentelemetry.io/contrib/propagators/envcar v0.71.0 h1:r7TFdi1B3O5ai64tONEuBTyxeQaLKVYf75vLiV7ZsDc=
go.opentelemetry.io/contrib/propagators/envcar v0.71.0/go.mod h1:uAnx0mCFm63a47qI4f/hcoI72gVYrGdxEBL+6c500Ns=
go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc=
go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 h1:OFnwLJr+pF3iHrlGSzbxyuo6/6HyBlnlN1CWEJmBVcw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0/go.mod h1:716wFneO0ov19A2beH5hjfh9AK5z/VWNAtDijp1Y0/g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 h1:w53CDeOA/Kurp7yRsegSr6pbbr759dOvJ+yNmWM6Hxs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0/go.mod h1:BOmGMCbAtvcJiSJ+hLuhgPLdDbimnraSl8irz3iY8sY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0 h1:KrC1YrQeSt46ITMWAbgQx1M1eV1/1TKzttrBzymPmss=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0/go.mod h1:zDSEzoEqsOrgBeGvH66KRgxh90VonFyJqBHA0Pk3+rM=
go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8=
go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o=
go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI=
go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM=
go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE=
go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4=
go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c=
go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI=
go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
@@ -410,10 +410,10 @@ golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc=
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 h1:izFU9hz7aeLI/Mi1J0991ae+xcwRLr7hTqWnB/9aIIU=
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5/go.mod h1:3LhxRw4YYkf+ylAfgaY9JlVLFKhokkCV8duhLLe7+t0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
@@ -407,22 +407,38 @@ func (*config) handleRPC(
// 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 = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+2)
metricAttrs = append(metricAttrs, gctx.metricAttrs...)
}
metricAttrs = append(metricAttrs, rpcStatusAttr)
// Allocate vararg slice once.
recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))}
// error.type is Conditionally Required on the stable rpc.*.call.duration
// metrics when the RPC failed, and SHOULD be set to the returned status
// code. The failure classification is role-specific: clients treat every
// non-OK status as an error, while servers only classify Unknown,
// DeadlineExceeded, Unimplemented, Internal, Unavailable and DataLoss
// as errors. Use recordStatus so the client and server rules are applied
// independently (e.g. a client cancellation is not a server error).
// The legacy v1.37 rpc.*.duration conventions did not define error.type,
// so the compatibility instruments keep the unmodified attribute set.
// Build each record option only in its enabled branch: single-mode
// configurations (rpc, rpc/old) must not allocate the other set, and
// error.type is only appended after the legacy instruments recorded.
// 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.Second)
if durationEnabled {
duration.Record(ctx, elapsedTime, recordOpts...)
}
if oldDurationEnabled {
oldDuration.Record(ctx, elapsedTime*1000.0, recordOpts...)
oldDuration.Record(ctx, elapsedTime*1000.0, metric.WithAttributeSet(attribute.NewSet(metricAttrs...)))
}
if durationEnabled {
if s != nil {
if c, _ := recordStatus(s); c == codes.Error {
metricAttrs = append(metricAttrs, semconv.ErrorTypeKey.String(canonicalString(s.Code())))
}
}
duration.Record(ctx, elapsedTime, metric.WithAttributeSet(attribute.NewSet(metricAttrs...)))
}
}
@@ -4,4 +4,4 @@
package otelgrpc
// Version is the current release version of the gRPC instrumentation.
const Version = "0.70.0"
const Version = "0.71.0"
@@ -4,4 +4,4 @@
package otelhttp
// Version is the current release version of the otelhttp instrumentation.
const Version = "0.70.0"
const Version = "0.71.0"
+1 -1
View File
@@ -5,6 +5,6 @@ package envcar
// Version is the current release version of the envcar propagator.
func Version() string {
return "0.70.0"
return "0.71.0"
// This string is updated by the pre_release.sh script during release
}
+1 -2
View File
@@ -143,8 +143,7 @@ linters:
- name: constant-logical-expr
- name: context-as-argument
arguments:
- allow-types-before: '*testing.T'
disabled: true
- allow-types-before: '*testing.T,*testing.B'
- name: context-keys-type
- name: deep-exit
- name: defer
+31 -1
View File
@@ -11,6 +11,34 @@ 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.46.0/0.68.0/0.22.0/0.0.19] - 2026-08-25
This release is the last to support [Go 1.25].
The next release will require at least [Go 1.26].
### Added
- Support testing of [Go 1.27]. (#8811)
- Support `http/json` protocol in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#8273, #8775, #8831)
- Add `Hasher` struct and methods in `go.opentelemetry.io/otel/attribute` to compute authoritative `Distinct` hashes incrementally for attribute filtering and deduplication. (#8598)
### Changed
- Lazily evaluate filtered and dropped attributes on measurement hot paths in `go.opentelemetry.io/otel/sdk/metric` to avoid unnecessary attribute set allocations. (#8598)
- Add `ErrExporterShutdown` to `go.opentelemetry.io/otel/sdk/log` and return it from the `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`, and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` exporters when `Export` is called after `Shutdown`. (#8773)
- Clarify in `go.opentelemetry.io/otel/log` that calling `Logger.Enabled` is optional and that cached results can become stale. (#8764)
### Fixed
- Export dropped attribute counts in OTLP log records from `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8829)
- Name span events created from OpenTracing logs after the `event` log field, falling back to `log`, instead of always using an empty name in `go.opentelemetry.io/otel/bridge/opentracing`. (#8648)
- Count exception attributes omitted due to the attribute count limit as dropped in `go.opentelemetry.io/otel/sdk/log`. (#8796)
- Prevent log record and instrumentation scope attributes with empty keys from reaching processors and exporters in `go.opentelemetry.io/otel/sdk/log`. (#8797)
- Fix a data race when span attributes are read concurrently in `go.opentelemetry.io/otel/sdk/trace`. (#8706)
- Prevent a panic in `(*Set).Filter` when called on a nil receiver in `go.opentelemetry.io/otel/attribute`. (#8792)
- The simple span and log processors record `otel.sdk.processor.{span,log}.processed` when the record is submitted to the exporter instead of after the export completes, and no longer set `error.type` from the export outcome, in `go.opentelemetry.io/otel/sdk/trace` and `go.opentelemetry.io/otel/sdk/log`. (#8705)
- Prevent `Resource.MarshalLog` from panicking on nil resources in `go.opentelemetry.io/otel/sdk/resource`. (#8758)
## [1.45.0/0.67.0/0.21.0/0.0.18] - 2026-08-03
### Added
@@ -3782,7 +3810,8 @@ 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.45.0...HEAD
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.46.0...HEAD
[1.46.0/0.68.0/0.22.0/0.0.19]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.46.0
[1.45.0/0.67.0/0.21.0/0.0.18]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.45.0
[1.44.0/0.66.0/0.20.0/0.0.17]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.44.0
[1.43.0/0.65.0/0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.43.0
@@ -3887,6 +3916,7 @@ It contains api and sdk for trace and meter.
<!-- Released section ended -->
[Go 1.27]: https://go.dev/doc/go1.27
[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
+24 -6
View File
@@ -69,8 +69,13 @@ $(GORELEASE): PACKAGE=golang.org/x/exp/cmd/gorelease
GOVULNCHECK = $(TOOLS)/govulncheck
$(TOOLS)/govulncheck: PACKAGE=golang.org/x/vuln/cmd/govulncheck
AFFECTEDMODS = $(TOOLS)/affectedmods
AFFECTEDMODS_FILES := $(sort $(shell find $(TOOLS_MOD_DIR)/affectedmods -type f))
$(TOOLS)/affectedmods: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/affectedmods
$(TOOLS)/affectedmods: $(AFFECTEDMODS_FILES)
.PHONY: tools
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(VERIFYREADMES) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(VERIFYREADMES) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE) $(AFFECTEDMODS)
# Virtualized python tools via docker
@@ -125,9 +130,11 @@ go-work: $(CROSSLINK)
# Build
.PHONY: build
.PHONY: build cross-build
build: $(OTEL_GO_MOD_DIRS:%=build/%) $(OTEL_GO_MOD_DIRS:%=build-tests/%)
# Cross-platform builds cannot execute the binaries produced by build-tests (go test), so use a compile-only target.
cross-build: $(OTEL_GO_MOD_DIRS:%=build/%)
build/%: DIR=$*
build/%:
@echo "$(GO) build $(DIR)/..." \
@@ -163,6 +170,9 @@ test/%:
| grep -v third_party \
| xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS)
.PHONY: test-tools
test-tools: test/$(TOOLS_MOD_DIR)
COVERAGE_MODE = atomic
COVERAGE_PROFILE = coverage.out
.PHONY: test-coverage
@@ -187,15 +197,23 @@ benchmark/%:
# sdk/metric is split into two shards to work around CodSpeed limitations.
# See https://github.com/CodSpeedHQ/codspeed-go/issues/56
BENCHMARK_SHARDS := $(filter-out ./sdk/metric,$(OTEL_GO_MOD_DIRS)) ./sdk/metric/root ./sdk/metric/internal
benchmark/./sdk/metric/root:
BENCHMARK_SHARDS := $(filter-out ./sdk/metric,$(OTEL_GO_MOD_DIRS)) ./sdk/metric/. ./sdk/metric/internal
benchmark/./sdk/metric/.:
cd ./sdk/metric && $(GO) test -run='^$$' -bench=. $(ARGS) . ./exemplar/...
benchmark/./sdk/metric/internal:
cd ./sdk/metric && $(GO) test -run='^$$' -bench=. $(ARGS) ./internal/...
print-sharded-benchmarks:
@echo $(BENCHMARK_SHARDS) | jq -cR 'split(" ")'
# Print the JSON list of benchmark shards whose code changed since
# BASE_REF (default: main). Filters print-sharded-benchmarks
# down to shards whose underlying module contains a changed *.go file.
# Non-Go changes (docs, workflows, Makefile, go.mod/go.sum, tooling) emit [].
# Override the diff base via BASE_REF, or pass ARGS=-all to emit the full list.
BASE_REF ?= main
.PHONY: print-affected-benchmarks
print-affected-benchmarks:
@$(MAKE) -s print-sharded-benchmarks | $(GO) -C $(TOOLS_MOD_DIR) run ./affectedmods $(if $(strip $(BASE_REF)),-base=$(BASE_REF),) $(ARGS)
.PHONY: golangci-lint golangci-lint-fix
golangci-lint-fix: ARGS=--fix
golangci-lint-fix: golangci-lint
+7
View File
@@ -53,18 +53,25 @@ Currently, this project supports the following environments.
| OS | Go Version | Architecture |
|----------|------------|--------------|
| Ubuntu | 1.27 | amd64 |
| Ubuntu | 1.26 | amd64 |
| Ubuntu | 1.25 | amd64 |
| Ubuntu | 1.27 | 386 |
| Ubuntu | 1.26 | 386 |
| Ubuntu | 1.25 | 386 |
| Ubuntu | 1.27 | arm64 |
| Ubuntu | 1.26 | arm64 |
| Ubuntu | 1.25 | arm64 |
| macOS | 1.27 | amd64 |
| macOS | 1.26 | amd64 |
| macOS | 1.25 | amd64 |
| macOS | 1.27 | arm64 |
| macOS | 1.26 | arm64 |
| macOS | 1.25 | arm64 |
| Windows | 1.27 | amd64 |
| Windows | 1.26 | amd64 |
| Windows | 1.25 | amd64 |
| Windows | 1.27 | 386 |
| Windows | 1.26 | 386 |
| Windows | 1.25 | 386 |
+6
View File
@@ -12,6 +12,12 @@ is designed so the following goals can be achieved.
* [Semantic import
versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)
will be used.
* Stable module compatibility is understood in terms of the [Go 1
compatibility guidelines](https://go.dev/doc/go1compat). Code that
compiled against an older version of a package should continue to compile
against newer versions of that package, subject to the exceptions in the
Go 1 compatibility guidelines and any additional exceptions documented
below.
* Versions will comply with [semver
2.0](https://semver.org/spec/v2.0.0.html) with the following exceptions.
* New methods may be added to exported API interfaces. All exported
+63 -11
View File
@@ -33,21 +33,73 @@ const (
emptyID uint64 = 7305809155345288421 // "__empty_" (little endian)
)
// hashKVs returns a new xxHash64 hash of kvs.
func hashKVs(kvs []KeyValue) uint64 {
h := xxhash.New()
for _, kv := range kvs {
h = hashKV(h, kv)
}
sum := h.Sum64()
// Remap 0 to a non-zero value for non-empty input because hash == 0 is a reserved sentinel (treated as empty/invalid).
const remappedZeroHash uint64 = 1
if sum == 0 && len(kvs) > 0 {
return remappedZeroHash
// Hasher computes a Distinct value from KeyValue attributes supplied with
// Write.
//
// A Hasher must be obtained from [NewHasher]. The zero value is not usable and
// its methods will panic.
type Hasher struct {
h xxhash.Hash
}
// NewHasher returns a new Hasher.
func NewHasher() *Hasher {
return &Hasher{h: xxhash.New()}
}
// Reset resets h to its initial state so it can be reused.
func (h *Hasher) Reset() {
h.h.Reset()
}
// Write adds kv to the hash.
//
// Write requires attributes to be supplied in ascending key order with no
// duplicate keys. To produce the same Distinct as Set.Equivalent, write
// attributes in ascending key order, with no more than one value for each key.
// If the source contains duplicate keys, retain the last value for each key
// before calling Write.
func (h *Hasher) Write(kv KeyValue) {
// hashKV mutates the digest h.h refers to in place and returns the same
// Hash value it was passed. Discarding the result keeps the digest pointer
// from flowing back into h, which would force the digest to be heap
// allocated for every Hasher. Keeping Write this small also keeps it within
// the inlining budget, which matters because hashKVs calls it per attribute.
_ = hashKV(h.h, kv)
}
// Distinct returns the identifier for the attributes written to h. When Write
// is called as described above, it returns the same value as [Set.Equivalent].
func (h *Hasher) Distinct() Distinct {
// No count of written attributes is needed to detect the empty case. The
// sum of a digest with nothing written to it is emptyHash, which is
// non-zero (0xef46db3751d8e999), so it passes through remapZeroHash
// unchanged and matches emptySet.Equivalent.
return Distinct{hash: remapZeroHash(h.h.Sum64())}
}
// remapZeroHash remaps a 0 sum to a non-zero value, because hash == 0 is a
// reserved sentinel (treated as empty/invalid).
func remapZeroHash(sum uint64) uint64 {
if sum == 0 {
return 1
}
return sum
}
// hashKVs returns a new xxHash64 hash of kvs.
//
// This routes through [Hasher] so that Set hashing and Hasher cannot disagree:
// there is exactly one implementation of how attributes are mixed and how the
// final sum is framed.
func hashKVs(kvs []KeyValue) uint64 {
h := NewHasher()
for _, kv := range kvs {
h.Write(kv)
}
return h.Distinct().hash
}
// hashKV returns the xxHash64 hash of kv with h as the base.
func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash {
h = h.String(string(kv.Key))
+5
View File
@@ -62,3 +62,8 @@ func (h Hash) String(val string) Hash {
func (h Hash) Sum64() uint64 {
return h.d.Sum64()
}
// Reset resets the hash to its initial state.
func (h Hash) Reset() {
h.d.Reset()
}
+3
View File
@@ -313,6 +313,9 @@ func filteredToFront(slice []KeyValue, keep Filter) int {
// Filter returns a filtered copy of this Set. See the documentation for
// NewSetWithSortableFiltered for more details.
func (l *Set) Filter(re Filter) (Set, []KeyValue) {
if l == nil {
return emptySet, nil
}
if re == nil {
return *l, nil
}
@@ -21,13 +21,13 @@ const (
)
// ParseCanonicalTarget parses a target string and returns the extracted host
// (domain address or IP), the target port, or an error.
// (domain name or IP address), the target port, or an error.
//
// If no port is specified, -1 is returned.
//
// If no host is specified, an empty string is returned.
//
// The target string is expected to always have the form
// The target string must have the form
// "<scheme>://[authority]/<endpoint>". For example:
// - "dns:///example.com:42"
// - "dns://8.8.8.8/example.com:42"
@@ -35,8 +35,8 @@ const (
// - "unix-abstract:///socket-name"
// - "passthrough:///192.34.2.1:42"
//
// The target is expected to come from the CanonicalTarget method of a gRPC
// Client.
// The target is expected to be returned by gRPC's ClientConn.CanonicalTarget
// method.
func ParseCanonicalTarget(target string) (string, int, error) {
const sep = "://"
@@ -60,7 +60,7 @@ func ParseCanonicalTarget(target string) (string, int, error) {
return parseEndpoint(endpoint)
}
// parseUnix parses unix socket targets.
// parseUnix parses Unix socket targets.
func parseUnix(endpoint string) (string, int, error) {
// Format: unix[-abstract]://path
//
@@ -70,8 +70,8 @@ func parseUnix(endpoint string) (string, int, error) {
return endpoint, -1, nil
}
// If there's no leading slash, it means there might be an authority
// Check for authority case (should error): "authority/path"
// If there is no leading slash, an authority might be present, which is
// invalid. Check for this case: "authority/path".
if slashIdx := strings.Index(endpoint, "/"); slashIdx > 0 {
return "", -1, fmt.Errorf("invalid (non-empty) authority: %s", endpoint[:slashIdx])
}
@@ -97,8 +97,8 @@ func parseUnix(endpoint string) (string, int, error) {
// - "[ipv6]:port"
// - "[ipv6%zone]:port"
//
// It returns the host or host%zone (domain address or IP), the port (or -1 if
// not specified), or an error if the input is not a valid.
// It returns the host or host%zone (domain name or IP address), the port (or
// -1 if not specified), or an error if the input is invalid.
func parseEndpoint(endpoint string) (string, int, error) {
// First check if the endpoint is just an IP address.
if ip := parseIP(endpoint); ip != "" {
@@ -100,6 +100,8 @@ func getOptionsFromEnv() []GenericOption {
envconfig.WithHeaders("TRACES_HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
WithEnvCompression("COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
WithEnvCompression("TRACES_COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
WithEnvProtocol("PROTOCOL", func(p Protocol) { opts = append(opts, WithProtocol(p)) }),
WithEnvProtocol("TRACES_PROTOCOL", func(p Protocol) { opts = append(opts, WithProtocol(p)) }),
envconfig.WithDuration("TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
envconfig.WithDuration("TRACES_TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
)
@@ -139,6 +141,25 @@ func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOpt
}
}
// WithEnvProtocol retrieves the specified config and passes it to ConfigFn as a Protocol.
func WithEnvProtocol(n string, fn func(Protocol)) func(e *envconfig.EnvOptionsReader) {
return func(e *envconfig.EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
protocol := ProtocolHTTPProtobuf
switch v {
case "grpc":
protocol = ProtocolGRPC
case "http/protobuf":
protocol = ProtocolHTTPProtobuf
case "http/json":
protocol = ProtocolHTTPJSON
}
fn(protocol)
}
}
}
// revive:disable-next-line:flag-parameter
func withInsecure(b bool) GenericOption {
if b {
@@ -50,6 +50,7 @@ type (
TLSCfg *tls.Config
Headers map[string]string
Compression Compression
Protocol Protocol
MaxRequestSize int
Timeout time.Duration
URLPath string
@@ -84,6 +85,7 @@ func NewHTTPConfig(opts ...HTTPOption) Config {
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
URLPath: DefaultTracesPath,
Compression: NoCompression,
Protocol: ProtocolHTTPProtobuf,
MaxRequestSize: DefaultMaxRequestSize,
Timeout: DefaultTimeout,
},
@@ -119,6 +121,7 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
URLPath: DefaultTracesPath,
Compression: NoCompression,
Protocol: ProtocolGRPC,
MaxRequestSize: DefaultMaxRequestSize,
Timeout: DefaultTimeout,
},
@@ -376,3 +379,25 @@ func WithHTTPClient(c *http.Client) GenericOption {
return cfg
})
}
func WithProtocol(protocol Protocol) GenericOption {
return newSplitOption(
// For OTLP/HTTP endpoints, this is the encoding format of the payloads sent to the collector.
func(cfg Config) Config {
if protocol == ProtocolGRPC {
global.Warn("grpc is not a valid protocol for OTLP/HTTP, defaulting to http/protobuf")
protocol = ProtocolHTTPProtobuf
}
cfg.Traces.Protocol = protocol
return cfg
},
// For OTLP/gRPC endpoints, it's always "grpc".
func(cfg Config) Config {
if protocol != ProtocolGRPC {
global.Debug("protocol option is ignored for OTLP/gRPC and is set to grpc")
}
cfg.Traces.Protocol = ProtocolGRPC
return cfg
},
)
}
@@ -38,3 +38,15 @@ const (
// MarshalJSON tells the driver to send using json format.
MarshalJSON
)
// Protocol describes the transport protocol used to send data to the collector.
type Protocol int
const (
// ProtocolGRPC describes the "grpc" protocol.
ProtocolGRPC Protocol = iota
// ProtocolHTTPProtobuf describes the "http/protobuf" protocol.
ProtocolHTTPProtobuf
// ProtocolHTTPJSON describes the "http/json" protocol.
ProtocolHTTPJSON
)
@@ -17,7 +17,7 @@ import (
"github.com/cenkalti/backoff/v5"
)
// DefaultConfig are the recommended defaults to use.
// DefaultConfig is the recommended default configuration.
var DefaultConfig = Config{
Enabled: true,
InitialInterval: 5 * time.Second,
@@ -25,42 +25,40 @@ var DefaultConfig = Config{
MaxElapsedTime: time.Minute,
}
// Config defines configuration for retrying batches in case of export failure
// using an exponential backoff.
// Config defines configuration for retrying batches after an export failure
// using exponential backoff.
type Config struct {
// Enabled indicates whether to not retry sending batches in case of
// export failure.
// Enabled indicates whether to retry sending batches after an export
// failure.
Enabled bool
// InitialInterval the time to wait after the first failure before
// InitialInterval is the time to wait after the first failure before
// retrying.
InitialInterval time.Duration
// MaxInterval is the upper bound on backoff interval. Once this value is
// reached the delay between consecutive retries will always be
// `MaxInterval`.
// MaxInterval is the upper bound on the backoff interval before
// randomization. Once this value is reached, the base interval remains at
// MaxInterval.
MaxInterval time.Duration
// MaxElapsedTime is the maximum amount of time (including retries) spent
// trying to send a request/batch. Once this value is reached, the data
// is discarded.
// trying to send a request/batch. Once this value is reached, retrying stops.
MaxElapsedTime time.Duration
}
// RequestFunc wraps a request with retry logic.
type RequestFunc func(context.Context, func(context.Context) error) error
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
// duration should be honored that was included in the error.
// EvaluateFunc reports whether an error is retryable and returns any explicit
// throttle duration to honor.
//
// The function must return true if the error argument is retry-able,
// otherwise it must return false for the first return parameter.
// The function must return true as its first return value if the error
// argument is retryable; otherwise, it must return false.
//
// The function must return a non-zero time.Duration if the error contains
// explicit throttle duration that should be honored, otherwise it must return
// a zero valued time.Duration.
// The function must return a nonzero time.Duration if the error contains an
// explicit throttle duration that should be honored; otherwise, it must return
// the zero value for time.Duration.
type EvaluateFunc func(error) (bool, time.Duration)
// RequestFunc returns a RequestFunc using the evaluate function to determine
// if requests can be retried and based on the exponential backoff
// configuration of c.
// RequestFunc returns a RequestFunc that uses evaluate to determine whether
// requests can be retried and uses c to configure exponential backoff.
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
if !c.Enabled {
return func(ctx context.Context, fn func(context.Context) error) error {
@@ -96,7 +94,7 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
// Check if context is canceled before attempting to wait and retry.
if ctx.Err() != nil {
return fmt.Errorf("%w: %w", ctx.Err(), err)
return fmt.Errorf("%w: %w", context.Cause(ctx), err)
}
if maxElapsedTime != 0 && time.Since(startTime) > maxElapsedTime {
@@ -119,12 +117,12 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
}
}
// Allow override for testing.
// waitFunc can be overridden for testing.
var waitFunc = wait
// wait takes the caller's context, and the amount of time to wait. It will
// return nil if the timer fires before or at the same time as the context's
// deadline. This indicates that the call can be retried.
// wait blocks until delay elapses or ctx is done. If both happen
// simultaneously, wait favors the elapsed delay and returns nil to indicate
// that the call can be retried.
func wait(ctx context.Context, delay time.Duration) error {
timer := time.NewTimer(delay)
defer timer.Stop()
@@ -5,4 +5,4 @@ package internal
// Version is the current release version of the OpenTelemetry OTLP gRPC trace
// exporter in use.
const Version = "1.45.0"
const Version = "1.46.0"
@@ -34,9 +34,9 @@ func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T]
// feature.
func (f Feature[T]) Keys() []string { return f.keys }
// Lookup returns the user configured value for the feature and true if the
// Lookup returns the user-configured value for the feature and true if the
// user has enabled the feature. Otherwise, if the feature is not enabled, a
// zero-value and false are returned.
// zero value and false are returned.
func (f Feature[T]) Lookup() (v T, ok bool) {
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
//
@@ -27,10 +27,14 @@ import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/counter"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpjson"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/retry"
)
const contentTypeProto = "application/x-protobuf"
const (
contentTypeProto = "application/x-protobuf"
contentTypeJSON = "application/json"
)
// maxResponseBodySize is the maximum number of bytes to read from a response
// body. It is set to 4 MiB per the OTLP specification recommendation to
@@ -160,19 +164,11 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
pbRequest := &coltracepb.ExportTraceServiceRequest{
ResourceSpans: protoSpans,
}
rawRequest, err := proto.Marshal(pbRequest)
if err != nil {
return err
}
ctx, cancel := c.contextWithStop(ctx)
defer cancel()
if maxSize := c.cfg.MaxRequestSize; maxSize > 0 && len(rawRequest) > maxSize {
return fmt.Errorf("request body too large: exceeded %d bytes", maxSize)
}
request, err := c.newRequest(rawRequest)
request, err := c.newRequest(pbRequest)
if err != nil {
return err
}
@@ -232,19 +228,26 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
return nil
}
if resp.Header.Get("Content-Type") == "application/x-protobuf" {
var respProto coltracepb.ExportTraceServiceResponse
var respProto coltracepb.ExportTraceServiceResponse
switch resp.Header.Get("Content-Type") {
case contentTypeProto:
if err := proto.Unmarshal(respData.Bytes(), &respProto); err != nil {
return err
}
case contentTypeJSON:
if err := otlpjson.UnmarshalExportTraceServiceResponse(respData.Bytes(), &respProto); err != nil {
return err
}
default:
return nil
}
if respProto.PartialSuccess != nil {
msg := respProto.PartialSuccess.GetErrorMessage()
n := respProto.PartialSuccess.GetRejectedSpans()
if n != 0 || msg != "" {
err := internal.TracePartialSuccessError(n, msg)
uploadErr = errors.Join(uploadErr, err)
}
if respProto.PartialSuccess != nil {
msg := respProto.PartialSuccess.GetErrorMessage()
n := respProto.PartialSuccess.GetRejectedSpans()
if n != 0 || msg != "" {
err := internal.TracePartialSuccessError(n, msg)
uploadErr = errors.Join(uploadErr, err)
}
}
return nil
@@ -283,7 +286,24 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
}))
}
func (c *client) newRequest(body []byte) (request, error) {
func (c *client) marshalRequest(pbRequest *coltracepb.ExportTraceServiceRequest) ([]byte, error) {
if c.cfg.Protocol == otlpconfig.ProtocolHTTPJSON {
rawRequest, err := otlpjson.MarshalExportTraceServiceRequest(pbRequest)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body in json: %w", err)
}
return rawRequest, nil
}
rawRequest, err := proto.Marshal(pbRequest)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body in protobuf: %w", err)
}
return rawRequest, nil
}
func (c *client) newRequest(pbRequest *coltracepb.ExportTraceServiceRequest) (request, error) {
u := url.URL{Scheme: c.getScheme(), Host: c.cfg.Endpoint, Path: c.cfg.URLPath}
r, err := http.NewRequestWithContext(context.Background(), http.MethodPost, u.String(), http.NoBody)
if err != nil {
@@ -297,6 +317,18 @@ func (c *client) newRequest(body []byte) (request, error) {
r.Header.Set(k, v)
}
r.Header.Set("Content-Type", contentTypeProto)
if c.cfg.Protocol == otlpconfig.ProtocolHTTPJSON {
r.Header.Set("Content-Type", contentTypeJSON)
}
body, err := c.marshalRequest(pbRequest)
if err != nil {
return request{Request: r}, err
}
if maxSize := c.cfg.MaxRequestSize; maxSize > 0 && len(body) > maxSize {
return request{Request: r}, fmt.Errorf("request body too large: exceeded %d bytes", maxSize)
}
req := request{Request: r}
switch Compression(c.cfg.Compression) {
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Package otlptracehttp provides an OTLP span exporter using HTTP with protobuf payloads.
Package otlptracehttp provides an OTLP span exporter using HTTP with protobuf or JSON payloads.
By default the telemetry is sent to https://localhost:4318/v1/traces.
Exporter should be created using [New].
@@ -63,6 +63,12 @@ the filepath to the client's private key to use in mTLS communication in PEM for
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
The configuration can be overridden by [WithTLSClientConfig] option.
OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_TRACES_PROTOCOL (default: "http/protobuf") -
the transport protocol the exporter uses. For OTLP/HTTP exporters, it indicates the encoding format of the payloads sent to the collector.
Supported value: "http/protobuf", "http/json".
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL takes precedence over OTEL_EXPORTER_OTLP_PROTOCOL.
The configuration can be overridden by [WithEncoding] option.
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
*/
package otlptracehttp
@@ -100,6 +100,8 @@ func getOptionsFromEnv() []GenericOption {
envconfig.WithHeaders("TRACES_HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
WithEnvCompression("COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
WithEnvCompression("TRACES_COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
WithEnvProtocol("PROTOCOL", func(p Protocol) { opts = append(opts, WithProtocol(p)) }),
WithEnvProtocol("TRACES_PROTOCOL", func(p Protocol) { opts = append(opts, WithProtocol(p)) }),
envconfig.WithDuration("TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
envconfig.WithDuration("TRACES_TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
)
@@ -139,6 +141,25 @@ func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOpt
}
}
// WithEnvProtocol retrieves the specified config and passes it to ConfigFn as a Protocol.
func WithEnvProtocol(n string, fn func(Protocol)) func(e *envconfig.EnvOptionsReader) {
return func(e *envconfig.EnvOptionsReader) {
if v, ok := e.GetEnvValue(n); ok {
protocol := ProtocolHTTPProtobuf
switch v {
case "grpc":
protocol = ProtocolGRPC
case "http/protobuf":
protocol = ProtocolHTTPProtobuf
case "http/json":
protocol = ProtocolHTTPJSON
}
fn(protocol)
}
}
}
// revive:disable-next-line:flag-parameter
func withInsecure(b bool) GenericOption {
if b {
@@ -50,6 +50,7 @@ type (
TLSCfg *tls.Config
Headers map[string]string
Compression Compression
Protocol Protocol
MaxRequestSize int
Timeout time.Duration
URLPath string
@@ -84,6 +85,7 @@ func NewHTTPConfig(opts ...HTTPOption) Config {
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
URLPath: DefaultTracesPath,
Compression: NoCompression,
Protocol: ProtocolHTTPProtobuf,
MaxRequestSize: DefaultMaxRequestSize,
Timeout: DefaultTimeout,
},
@@ -119,6 +121,7 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
URLPath: DefaultTracesPath,
Compression: NoCompression,
Protocol: ProtocolGRPC,
MaxRequestSize: DefaultMaxRequestSize,
Timeout: DefaultTimeout,
},
@@ -376,3 +379,25 @@ func WithHTTPClient(c *http.Client) GenericOption {
return cfg
})
}
func WithProtocol(protocol Protocol) GenericOption {
return newSplitOption(
// For OTLP/HTTP endpoints, this is the encoding format of the payloads sent to the collector.
func(cfg Config) Config {
if protocol == ProtocolGRPC {
global.Warn("grpc is not a valid protocol for OTLP/HTTP, defaulting to http/protobuf")
protocol = ProtocolHTTPProtobuf
}
cfg.Traces.Protocol = protocol
return cfg
},
// For OTLP/gRPC endpoints, it's always "grpc".
func(cfg Config) Config {
if protocol != ProtocolGRPC {
global.Debug("protocol option is ignored for OTLP/gRPC and is set to grpc")
}
cfg.Traces.Protocol = ProtocolGRPC
return cfg
},
)
}
@@ -38,3 +38,15 @@ const (
// MarshalJSON tells the driver to send using json format.
MarshalJSON
)
// Protocol describes the transport protocol used to send data to the collector.
type Protocol int
const (
// ProtocolGRPC describes the "grpc" protocol.
ProtocolGRPC Protocol = iota
// ProtocolHTTPProtobuf describes the "http/protobuf" protocol.
ProtocolHTTPProtobuf
// ProtocolHTTPJSON describes the "http/json" protocol.
ProtocolHTTPJSON
)
@@ -0,0 +1,642 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package otlpjson implements OTLP JSON Protobuf encoding for trace data.
//
// The encoding conforms to the OTLP specs
// (https://opentelemetry.io/docs/specs/otlp/#json-protobuf-encoding):
// - trace ID and span ID byte arrays are encoded as case-insensitive hex-encoded strings
// - enum values encoded as integers
// - field names in lowerCamelCase
// - 64-bit integers encoded as quoted decimal strings (ProtoJSON specs)
package otlpjson
import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"strconv"
"strings"
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
resourcepb "go.opentelemetry.io/proto/otlp/resource/v1"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
// ExportTraceServiceRequest corresponds to coltracepb.ExportTraceServiceRequest.
type ExportTraceServiceRequest struct {
ResourceSpans []*ResourceSpans `json:"resourceSpans,omitempty"`
}
// ResourceSpans corresponds to tracepb.ResourceSpans.
type ResourceSpans struct {
Resource *Resource `json:"resource,omitempty"`
ScopeSpans []*ScopeSpans `json:"scopeSpans,omitempty"`
SchemaURL string `json:"schemaUrl,omitempty"`
}
// Resource corresponds to resourcepb.Resource.
type Resource struct {
Attributes []*KeyValue `json:"attributes,omitempty"`
DroppedAttributesCount uint32 `json:"droppedAttributesCount,omitempty"`
EntityRefs []*EntityRef `json:"entityRefs,omitempty"`
}
// ScopeSpans corresponds to tracepb.ScopeSpans.
type ScopeSpans struct {
Scope *InstrumentationScope `json:"scope,omitempty"`
Spans []*Span `json:"spans,omitempty"`
SchemaURL string `json:"schemaUrl,omitempty"`
}
// InstrumentationScope corresponds to tracepb.InstrumentationScope.
type InstrumentationScope struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
Attributes []*KeyValue `json:"attributes,omitempty"`
DroppedAttributesCount uint32 `json:"droppedAttributesCount,omitempty"`
}
// EntityRef corresponds to resourcepb.EntityRef.
type EntityRef struct {
SchemaURL string `json:"schemaUrl,omitempty"`
Type string `json:"type,omitempty"`
IdKeys []string `json:"idKeys,omitempty"`
DescriptionKeys []string `json:"descriptionKeys,omitempty"`
}
// KeyValue corresponds to commonpb.KeyValue.
type KeyValue struct {
Key string `json:"key"`
Value *AnyValue `json:"value,omitempty"`
}
// AnyValue corresponds to commonpb.AnyValue.
type AnyValue struct {
StringValue *string `json:"stringValue,omitempty"`
BoolValue *bool `json:"boolValue,omitempty"`
IntValue *Int64 `json:"intValue,omitempty"`
DoubleValue *Float64 `json:"doubleValue,omitempty"`
ArrayValue *ArrayValue `json:"arrayValue,omitempty"`
KvlistValue *KvlistValue `json:"kvlistValue,omitempty"`
BytesValue []byte `json:"bytesValue,omitempty"`
}
// ArrayValue corresponds to commonpb.ArrayValue.
type ArrayValue struct {
Values []*AnyValue `json:"values,omitempty"`
}
// KvlistValue corresponds to commonpb.KeyValueList.
type KvlistValue struct {
Values []*KeyValue `json:"values,omitempty"`
}
// Span corresponds to tracepb.Span.
type Span struct {
TraceID TraceID `json:"traceId"`
SpanID SpanID `json:"spanId"`
TraceState string `json:"traceState,omitempty"`
ParentSpanID *SpanID `json:"parentSpanId,omitempty"`
Flags uint32 `json:"flags,omitempty"`
Name string `json:"name,omitempty"`
Kind int32 `json:"kind,omitempty"`
StartTimeUnixNano Uint64 `json:"startTimeUnixNano,omitempty"`
EndTimeUnixNano Uint64 `json:"endTimeUnixNano,omitempty"`
Attributes []*KeyValue `json:"attributes,omitempty"`
DroppedAttributesCount uint32 `json:"droppedAttributesCount,omitempty"`
Events []*Event `json:"events,omitempty"`
DroppedEventsCount uint32 `json:"droppedEventsCount,omitempty"`
Links []*Link `json:"links,omitempty"`
DroppedLinksCount uint32 `json:"droppedLinksCount,omitempty"`
Status *Status `json:"status,omitempty"`
}
// Event corresponds to tracepb.Span_Event.
type Event struct {
TimeUnixNano Uint64 `json:"timeUnixNano,omitempty"`
Name string `json:"name,omitempty"`
Attributes []*KeyValue `json:"attributes,omitempty"`
DroppedAttributesCount uint32 `json:"droppedAttributesCount,omitempty"`
}
// Link corresponds to tracepb.Span_Link.
type Link struct {
TraceID TraceID `json:"traceId"`
SpanID SpanID `json:"spanId"`
TraceState string `json:"traceState,omitempty"`
Attributes []*KeyValue `json:"attributes,omitempty"`
DroppedAttributesCount uint32 `json:"droppedAttributesCount,omitempty"`
Flags uint32 `json:"flags,omitempty"`
}
// Status corresponds to tracepb.Status.
type Status struct {
Message string `json:"message,omitempty"`
Code int32 `json:"code,omitempty"`
}
// MarshalExportTraceServiceRequest encodes an ExportTraceServiceRequest as JSON Protobuf encoded bytes.
func MarshalExportTraceServiceRequest(req *coltracepb.ExportTraceServiceRequest) ([]byte, error) {
if req == nil {
return []byte("{}"), nil
}
r := &ExportTraceServiceRequest{}
for _, rs := range req.ResourceSpans {
r.ResourceSpans = append(r.ResourceSpans, encodeResourceSpans(rs))
}
return json.Marshal(r)
}
func encodeResourceSpans(rs *tracepb.ResourceSpans) *ResourceSpans {
if rs == nil {
return nil
}
out := &ResourceSpans{SchemaURL: rs.SchemaUrl}
if rs.Resource != nil {
out.Resource = &Resource{
Attributes: encodeKeyValues(rs.Resource.Attributes),
DroppedAttributesCount: rs.Resource.DroppedAttributesCount,
EntityRefs: encodeEntityRefs(rs.Resource.EntityRefs),
}
}
for _, ss := range rs.ScopeSpans {
out.ScopeSpans = append(out.ScopeSpans, encodeScopeSpans(ss))
}
return out
}
func encodeEntityRefs(ers []*commonpb.EntityRef) []*EntityRef {
if len(ers) == 0 {
return nil
}
out := make([]*EntityRef, len(ers))
for i, er := range ers {
if er == nil {
continue
}
out[i] = &EntityRef{
SchemaURL: er.SchemaUrl,
Type: er.Type,
IdKeys: er.IdKeys,
DescriptionKeys: er.DescriptionKeys,
}
}
return out
}
func encodeScopeSpans(ss *tracepb.ScopeSpans) *ScopeSpans {
if ss == nil {
return nil
}
out := &ScopeSpans{SchemaURL: ss.SchemaUrl}
if ss.Scope != nil {
out.Scope = &InstrumentationScope{
Name: ss.Scope.Name,
Version: ss.Scope.Version,
Attributes: encodeKeyValues(ss.Scope.Attributes),
DroppedAttributesCount: ss.Scope.DroppedAttributesCount,
}
}
for _, s := range ss.Spans {
out.Spans = append(out.Spans, encodeSpan(s))
}
return out
}
func encodeSpan(s *tracepb.Span) *Span {
if s == nil {
return nil
}
out := &Span{
TraceState: s.TraceState,
Flags: s.Flags,
Name: s.Name,
Kind: int32(s.Kind),
StartTimeUnixNano: Uint64(s.StartTimeUnixNano),
EndTimeUnixNano: Uint64(s.EndTimeUnixNano),
Attributes: encodeKeyValues(s.Attributes),
DroppedAttributesCount: s.DroppedAttributesCount,
DroppedEventsCount: s.DroppedEventsCount,
DroppedLinksCount: s.DroppedLinksCount,
}
copy(out.TraceID[:], s.TraceId)
copy(out.SpanID[:], s.SpanId)
if len(s.ParentSpanId) > 0 {
var psid SpanID
copy(psid[:], s.ParentSpanId)
if psid != (SpanID{}) {
out.ParentSpanID = &psid
}
}
for _, e := range s.Events {
if e == nil {
continue
}
out.Events = append(out.Events, &Event{
TimeUnixNano: Uint64(e.TimeUnixNano),
Name: e.Name,
Attributes: encodeKeyValues(e.Attributes),
DroppedAttributesCount: e.DroppedAttributesCount,
})
}
for _, l := range s.Links {
out.Links = append(out.Links, encodeLink(l))
}
if s.Status != nil {
out.Status = &Status{
Message: s.Status.Message,
Code: int32(s.Status.Code),
}
}
return out
}
func encodeLink(l *tracepb.Span_Link) *Link {
if l == nil {
return nil
}
out := &Link{
TraceState: l.TraceState,
Attributes: encodeKeyValues(l.Attributes),
DroppedAttributesCount: l.DroppedAttributesCount,
Flags: l.Flags,
}
copy(out.TraceID[:], l.TraceId)
copy(out.SpanID[:], l.SpanId)
return out
}
func encodeKeyValues(kvs []*commonpb.KeyValue) []*KeyValue {
if len(kvs) == 0 {
return nil
}
out := make([]*KeyValue, len(kvs))
for i, kv := range kvs {
if kv == nil {
continue
}
out[i] = &KeyValue{
Key: kv.Key,
Value: encodeAnyValue(kv.Value),
}
}
return out
}
func encodeAnyValue(av *commonpb.AnyValue) *AnyValue {
if av == nil {
return nil
}
out := &AnyValue{}
switch v := av.Value.(type) {
case *commonpb.AnyValue_StringValue:
out.StringValue = &v.StringValue
case *commonpb.AnyValue_BoolValue:
out.BoolValue = &v.BoolValue
case *commonpb.AnyValue_IntValue:
iv := Int64(v.IntValue)
out.IntValue = &iv
case *commonpb.AnyValue_DoubleValue:
dv := Float64(v.DoubleValue)
out.DoubleValue = &dv
case *commonpb.AnyValue_ArrayValue:
if v.ArrayValue != nil {
arr := &ArrayValue{}
for _, val := range v.ArrayValue.Values {
arr.Values = append(arr.Values, encodeAnyValue(val))
}
out.ArrayValue = arr
}
case *commonpb.AnyValue_KvlistValue:
if v.KvlistValue != nil {
out.KvlistValue = &KvlistValue{
Values: encodeKeyValues(v.KvlistValue.Values),
}
}
case *commonpb.AnyValue_BytesValue:
out.BytesValue = v.BytesValue
}
return out
}
// UnmarshalExportTraceServiceRequest decodes JSON Protobuf encoded payload into an ExportTraceServiceRequest.
func UnmarshalExportTraceServiceRequest(data []byte, req *coltracepb.ExportTraceServiceRequest) error {
var jr ExportTraceServiceRequest
if err := json.Unmarshal(data, &jr); err != nil {
return err
}
for _, rs := range jr.ResourceSpans {
req.ResourceSpans = append(req.ResourceSpans, decodeResourceSpans(rs))
}
return nil
}
func decodeResourceSpans(jrs *ResourceSpans) *tracepb.ResourceSpans {
rs := &tracepb.ResourceSpans{SchemaUrl: jrs.SchemaURL}
if jrs.Resource != nil {
rs.Resource = &resourcepb.Resource{
Attributes: decodeKeyValues(jrs.Resource.Attributes),
DroppedAttributesCount: jrs.Resource.DroppedAttributesCount,
EntityRefs: decodeEntityRefs(jrs.Resource.EntityRefs),
}
}
for _, ss := range jrs.ScopeSpans {
rs.ScopeSpans = append(rs.ScopeSpans, decodeScopeSpans(ss))
}
return rs
}
func decodeEntityRefs(jers []*EntityRef) []*commonpb.EntityRef {
if len(jers) == 0 {
return nil
}
ers := make([]*commonpb.EntityRef, len(jers))
for i, jer := range jers {
if jer == nil {
continue
}
ers[i] = &commonpb.EntityRef{
SchemaUrl: jer.SchemaURL,
Type: jer.Type,
IdKeys: jer.IdKeys,
DescriptionKeys: jer.DescriptionKeys,
}
}
return ers
}
func decodeScopeSpans(jss *ScopeSpans) *tracepb.ScopeSpans {
ss := &tracepb.ScopeSpans{SchemaUrl: jss.SchemaURL}
if jss.Scope != nil {
ss.Scope = &commonpb.InstrumentationScope{
Name: jss.Scope.Name,
Version: jss.Scope.Version,
Attributes: decodeKeyValues(jss.Scope.Attributes),
DroppedAttributesCount: jss.Scope.DroppedAttributesCount,
}
}
for _, s := range jss.Spans {
ss.Spans = append(ss.Spans, decodeSpan(s))
}
return ss
}
func decodeSpan(js *Span) *tracepb.Span {
s := &tracepb.Span{
TraceId: js.TraceID[:],
SpanId: js.SpanID[:],
TraceState: js.TraceState,
Flags: js.Flags,
Name: js.Name,
Kind: tracepb.Span_SpanKind(js.Kind),
StartTimeUnixNano: uint64(js.StartTimeUnixNano),
EndTimeUnixNano: uint64(js.EndTimeUnixNano),
Attributes: decodeKeyValues(js.Attributes),
DroppedAttributesCount: js.DroppedAttributesCount,
DroppedEventsCount: js.DroppedEventsCount,
DroppedLinksCount: js.DroppedLinksCount,
}
if js.ParentSpanID != nil {
s.ParentSpanId = js.ParentSpanID[:]
}
for _, e := range js.Events {
s.Events = append(s.Events, &tracepb.Span_Event{
TimeUnixNano: uint64(e.TimeUnixNano),
Name: e.Name,
Attributes: decodeKeyValues(e.Attributes),
DroppedAttributesCount: e.DroppedAttributesCount,
})
}
for _, l := range js.Links {
s.Links = append(s.Links, decodeLink(l))
}
if js.Status != nil {
s.Status = &tracepb.Status{
Message: js.Status.Message,
Code: tracepb.Status_StatusCode(js.Status.Code),
}
}
return s
}
func decodeLink(jl *Link) *tracepb.Span_Link {
return &tracepb.Span_Link{
TraceId: jl.TraceID[:],
SpanId: jl.SpanID[:],
TraceState: jl.TraceState,
Attributes: decodeKeyValues(jl.Attributes),
DroppedAttributesCount: jl.DroppedAttributesCount,
Flags: jl.Flags,
}
}
func decodeKeyValues(jkvs []*KeyValue) []*commonpb.KeyValue {
if len(jkvs) == 0 {
return nil
}
kvs := make([]*commonpb.KeyValue, len(jkvs))
for i, jkv := range jkvs {
kvs[i] = &commonpb.KeyValue{
Key: jkv.Key,
Value: decodeAnyValue(jkv.Value),
}
}
return kvs
}
func decodeAnyValue(jav *AnyValue) *commonpb.AnyValue {
if jav == nil {
return nil
}
av := &commonpb.AnyValue{}
switch {
case jav.StringValue != nil:
av.Value = &commonpb.AnyValue_StringValue{StringValue: *jav.StringValue}
case jav.BoolValue != nil:
av.Value = &commonpb.AnyValue_BoolValue{BoolValue: *jav.BoolValue}
case jav.IntValue != nil:
av.Value = &commonpb.AnyValue_IntValue{IntValue: int64(*jav.IntValue)}
case jav.DoubleValue != nil:
av.Value = &commonpb.AnyValue_DoubleValue{DoubleValue: float64(*jav.DoubleValue)}
case jav.ArrayValue != nil:
arr := &commonpb.ArrayValue{}
for _, v := range jav.ArrayValue.Values {
arr.Values = append(arr.Values, decodeAnyValue(v))
}
av.Value = &commonpb.AnyValue_ArrayValue{ArrayValue: arr}
case jav.KvlistValue != nil:
av.Value = &commonpb.AnyValue_KvlistValue{
KvlistValue: &commonpb.KeyValueList{
Values: decodeKeyValues(jav.KvlistValue.Values),
},
}
case jav.BytesValue != nil:
av.Value = &commonpb.AnyValue_BytesValue{BytesValue: jav.BytesValue}
}
return av
}
// Float64 encodes non-finite values as strings per ProtoJSON specs.
type Float64 float64
func (f Float64) MarshalJSON() ([]byte, error) {
switch value := float64(f); {
case math.IsNaN(value):
return []byte(`"NaN"`), nil
case math.IsInf(value, 1):
return []byte(`"Infinity"`), nil
case math.IsInf(value, -1):
return []byte(`"-Infinity"`), nil
default:
return strconv.AppendFloat(nil, value, 'g', -1, 64), nil
}
}
func (f *Float64) UnmarshalJSON(data []byte) error {
var str string
if err := json.Unmarshal(data, &str); err == nil {
switch str {
case "NaN":
*f = Float64(math.NaN())
case "Infinity":
*f = Float64(math.Inf(1))
case "-Infinity":
*f = Float64(math.Inf(-1))
default:
var value *float64
if str != strings.TrimSpace(str) || json.Unmarshal([]byte(str), &value) != nil || value == nil {
return fmt.Errorf("invalid float value %q", str)
}
*f = Float64(*value)
}
return nil
}
var value float64
if err := json.Unmarshal(data, &value); err != nil {
return err
}
*f = Float64(value)
return nil
}
// Int64 encodes int64 as a quoted decimal string per ProtoJSON specs.
type Int64 int64
func (i Int64) MarshalJSON() ([]byte, error) {
return []byte(`"` + strconv.FormatInt(int64(i), 10) + `"`), nil
}
func (i *Int64) UnmarshalJSON(data []byte) error {
// expects either a string representation or a number
var s string
if err := json.Unmarshal(data, &s); err == nil {
v, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return err
}
*i = Int64(v)
return nil
}
var v int64
if err := json.Unmarshal(data, &v); err != nil {
return err
}
*i = Int64(v)
return nil
}
// Uint64 encodes uint64 as a quoted decimal string per ProtoJSON specs.
type Uint64 uint64
func (i Uint64) MarshalJSON() ([]byte, error) {
return []byte(`"` + strconv.FormatUint(uint64(i), 10) + `"`), nil
}
func (i *Uint64) UnmarshalJSON(data []byte) error {
// expects either a string representation or a number
var s string
if err := json.Unmarshal(data, &s); err == nil {
v, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return err
}
*i = Uint64(v)
return nil
}
var v uint64
if err := json.Unmarshal(data, &v); err != nil {
return err
}
*i = Uint64(v)
return nil
}
const base16Alphabets = "0123456789ABCDEF"
// TraceID encodes a 16-byte trace ID as a case-insensitive hex-encoded string.
type TraceID [16]byte
func (t TraceID) MarshalJSON() ([]byte, error) {
var b [34]byte
b[0] = '"'
for i, v := range t {
b[1+i*2] = base16Alphabets[v>>4]
b[2+i*2] = base16Alphabets[v&0x0f]
}
b[33] = '"'
return b[:], nil
}
func (t *TraceID) UnmarshalJSON(data []byte) error {
var str string
if err := json.Unmarshal(data, &str); err != nil {
return err
}
b, err := hex.DecodeString(str)
if err != nil {
return err
}
if len(b) != len(t) {
return fmt.Errorf("invalid trace ID length: got %d, want %d", len(b), len(t))
}
copy(t[:], b)
return nil
}
// SpanID encodes an 8-byte span ID as a case-insensitive hex-encoded string.
type SpanID [8]byte
func (s SpanID) MarshalJSON() ([]byte, error) {
var b [18]byte
b[0] = '"'
for i, v := range s {
b[1+i*2] = base16Alphabets[v>>4]
b[2+i*2] = base16Alphabets[v&0x0f]
}
b[17] = '"'
return b[:], nil
}
func (s *SpanID) UnmarshalJSON(data []byte) error {
var str string
if err := json.Unmarshal(data, &str); err != nil {
return err
}
b, err := hex.DecodeString(str)
if err != nil {
return err
}
if len(b) != len(s) {
return fmt.Errorf("invalid span ID length: got %d, want %d", len(b), len(s))
}
copy(s[:], b)
return nil
}
@@ -0,0 +1,22 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package otlpjson
import (
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
"google.golang.org/protobuf/encoding/protojson"
)
// MarshalExportTraceServiceResponse encodes an ExportTraceServiceResponse as JSON Protobuf encoded bytes.
func MarshalExportTraceServiceResponse(resp *coltracepb.ExportTraceServiceResponse) ([]byte, error) {
return protojson.Marshal(resp)
}
// UnmarshalExportTraceServiceResponse decodes JSON Protobuf encoded payload into an ExportTraceServiceResponse.
func UnmarshalExportTraceServiceResponse(data []byte, resp *coltracepb.ExportTraceServiceResponse) error {
// ignore message fields with unknown names per OTLP specs.
var unmarshaler protojson.UnmarshalOptions
unmarshaler.DiscardUnknown = true
return unmarshaler.Unmarshal(data, resp)
}
@@ -17,7 +17,7 @@ import (
"github.com/cenkalti/backoff/v5"
)
// DefaultConfig are the recommended defaults to use.
// DefaultConfig is the recommended default configuration.
var DefaultConfig = Config{
Enabled: true,
InitialInterval: 5 * time.Second,
@@ -25,42 +25,40 @@ var DefaultConfig = Config{
MaxElapsedTime: time.Minute,
}
// Config defines configuration for retrying batches in case of export failure
// using an exponential backoff.
// Config defines configuration for retrying batches after an export failure
// using exponential backoff.
type Config struct {
// Enabled indicates whether to not retry sending batches in case of
// export failure.
// Enabled indicates whether to retry sending batches after an export
// failure.
Enabled bool
// InitialInterval the time to wait after the first failure before
// InitialInterval is the time to wait after the first failure before
// retrying.
InitialInterval time.Duration
// MaxInterval is the upper bound on backoff interval. Once this value is
// reached the delay between consecutive retries will always be
// `MaxInterval`.
// MaxInterval is the upper bound on the backoff interval before
// randomization. Once this value is reached, the base interval remains at
// MaxInterval.
MaxInterval time.Duration
// MaxElapsedTime is the maximum amount of time (including retries) spent
// trying to send a request/batch. Once this value is reached, the data
// is discarded.
// trying to send a request/batch. Once this value is reached, retrying stops.
MaxElapsedTime time.Duration
}
// RequestFunc wraps a request with retry logic.
type RequestFunc func(context.Context, func(context.Context) error) error
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
// duration should be honored that was included in the error.
// EvaluateFunc reports whether an error is retryable and returns any explicit
// throttle duration to honor.
//
// The function must return true if the error argument is retry-able,
// otherwise it must return false for the first return parameter.
// The function must return true as its first return value if the error
// argument is retryable; otherwise, it must return false.
//
// The function must return a non-zero time.Duration if the error contains
// explicit throttle duration that should be honored, otherwise it must return
// a zero valued time.Duration.
// The function must return a nonzero time.Duration if the error contains an
// explicit throttle duration that should be honored; otherwise, it must return
// the zero value for time.Duration.
type EvaluateFunc func(error) (bool, time.Duration)
// RequestFunc returns a RequestFunc using the evaluate function to determine
// if requests can be retried and based on the exponential backoff
// configuration of c.
// RequestFunc returns a RequestFunc that uses evaluate to determine whether
// requests can be retried and uses c to configure exponential backoff.
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
if !c.Enabled {
return func(ctx context.Context, fn func(context.Context) error) error {
@@ -96,7 +94,7 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
// Check if context is canceled before attempting to wait and retry.
if ctx.Err() != nil {
return fmt.Errorf("%w: %w", ctx.Err(), err)
return fmt.Errorf("%w: %w", context.Cause(ctx), err)
}
if maxElapsedTime != 0 && time.Since(startTime) > maxElapsedTime {
@@ -119,12 +117,12 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
}
}
// Allow override for testing.
// waitFunc can be overridden for testing.
var waitFunc = wait
// wait takes the caller's context, and the amount of time to wait. It will
// return nil if the timer fires before or at the same time as the context's
// deadline. This indicates that the call can be retried.
// wait blocks until delay elapses or ctx is done. If both happen
// simultaneously, wait favors the elapsed delay and returns nil to indicate
// that the call can be retried.
func wait(ctx context.Context, delay time.Duration) error {
timer := time.NewTimer(delay)
defer timer.Stop()
@@ -5,4 +5,4 @@ package internal
// Version is the current release version of the OpenTelemetry OTLP HTTP trace
// exporter in use.
const Version = "1.45.0"
const Version = "1.46.0"
@@ -34,9 +34,9 @@ func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T]
// feature.
func (f Feature[T]) Keys() []string { return f.keys }
// Lookup returns the user configured value for the feature and true if the
// Lookup returns the user-configured value for the feature and true if the
// user has enabled the feature. Otherwise, if the feature is not enabled, a
// zero-value and false are returned.
// zero value and false are returned.
func (f Feature[T]) Lookup() (v T, ok bool) {
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
//
@@ -17,6 +17,9 @@ import (
// collector.
type Compression otlpconfig.Compression
// Encoding describes the encoding used for payloads sent to the collector.
type Encoding int
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy for a given request.
// This type is compatible with http.Transport.Proxy and can be used to set a custom proxy function
// to the OTLP HTTP client.
@@ -31,6 +34,13 @@ const (
GzipCompression = Compression(otlpconfig.GzipCompression)
)
const (
// EncodingProtobuf tells the driver to send protobuf-encoded payloads.
EncodingProtobuf Encoding = iota
// EncodingJSON tells the driver to send JSON-encoded payloads.
EncodingJSON
)
// Option applies an option to the HTTP client.
type Option interface {
applyHTTPOption(otlpconfig.Config) otlpconfig.Config
@@ -111,6 +121,15 @@ func WithCompression(compression Compression) Option {
return wrappedOption{otlpconfig.WithCompression(otlpconfig.Compression(compression))}
}
// WithEncoding tells the driver to use a specific encoding for the request payload.
func WithEncoding(encoding Encoding) Option {
protocol := otlpconfig.ProtocolHTTPProtobuf
if encoding == EncodingJSON {
protocol = otlpconfig.ProtocolHTTPJSON
}
return wrappedOption{otlpconfig.WithProtocol(protocol)}
}
// WithURLPath allows one to override the default URL path used
// for sending traces. If unset, default ("/v1/traces") will be used.
func WithURLPath(urlPath string) Option {
+1 -1
View File
@@ -5,5 +5,5 @@ package otlptrace
// Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
func Version() string {
return "1.45.0"
return "1.46.0"
}
+7
View File
@@ -15,6 +15,13 @@
"matchDepTypes": ["indirect"],
"enabled": true
},
{
"description": "Disable Go module major updates to v2+",
"matchManagers": ["gomod"],
"matchUpdateTypes": ["major"],
"matchNewValue": "/^v?([2-9]|[1-9][0-9]+)\\./",
"enabled": false
},
{
"matchPackageNames": ["go.opentelemetry.io/build-tools/**"],
"groupName": "build-tools"
+11 -13
View File
@@ -16,10 +16,10 @@ import (
// Truncate returns a truncated version of attr. Only string, string slice,
// byte slice, slice, and map attribute values are truncated. String values are
// truncated to at most a length of limit. Each string slice value is truncated
// in this fashion (the slice length itself is unaffected), and byte slice
// values are truncated to at most limit bytes. For slice and map attribute
// values, the limit is applied recursively to contained values.
// truncated according to limit. Each string slice value is truncated in this
// fashion (the slice length itself is unaffected), and byte slice values are
// truncated to at most limit bytes. For slice and map attribute values, the
// limit is applied recursively to contained values.
//
// No truncation is performed for a negative limit.
func Truncate(limit int, attr attribute.KeyValue) attribute.KeyValue {
@@ -155,22 +155,20 @@ func needsTruncation(limit int, v attribute.Value) bool {
return false
}
// truncate returns a truncated version of s such that it contains less than
// the limit number of characters. Truncation is applied by returning the limit
// number of valid characters contained in s.
// truncate returns a version of s truncated according to limit.
//
// If limit is negative, it returns the original string.
//
// UTF-8 is supported. When truncating, all invalid characters are dropped
// UTF-8 is supported. When truncating, all invalid UTF-8 bytes are dropped
// before applying truncation.
//
// If s already contains less than the limit number of bytes, it is returned
// unchanged. No invalid characters are removed.
// If s already contains at most limit bytes, it is returned unchanged. No
// invalid characters are removed.
func truncate(limit int, s string) string {
// This prioritize performance in the following order based on the most
// common expected use-cases.
// This prioritizes performance in the following order based on the most
// common expected use cases.
//
// - Short values less than the default limit (128).
// - Values shorter than the default limit (128).
// - Strings with valid encodings that exceed the limit.
// - No limit.
// - Strings with invalid encodings that exceed the limit.
+2 -2
View File
@@ -34,9 +34,9 @@ func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T]
// feature.
func (f Feature[T]) Keys() []string { return f.keys }
// Lookup returns the user configured value for the feature and true if the
// Lookup returns the user-configured value for the feature and true if the
// user has enabled the feature. Otherwise, if the feature is not enabled, a
// zero-value and false are returned.
// zero value and false are returned.
func (f Feature[T]) Lookup() (v T, ok bool) {
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
//
+3
View File
@@ -120,6 +120,9 @@ func (r *Resource) String() string {
// MarshalLog is the marshaling function used by the logging system to represent this Resource.
func (r *Resource) MarshalLog() any {
if r == nil {
r = Empty()
}
return struct {
Attributes attribute.Set
SchemaURL string
@@ -6,7 +6,6 @@ package observ
import (
"context"
"fmt"
"sync"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
@@ -17,22 +16,10 @@ import (
"go.opentelemetry.io/otel/semconv/v1.43.0/otelconv"
)
var measureAttrsPool = sync.Pool{
New: func() any {
// "component.name" + "component.type" + "error.type"
const n = 1 + 1 + 1
s := make([]attribute.KeyValue, 0, n)
// Return a pointer to a slice instead of a slice itself
// to avoid allocations on every call.
return &s
},
}
// SSP is the instrumentation for an OTel SDK SimpleSpanProcessor.
type SSP struct {
spansProcessedCounter metric.Int64Counter
addOpts []metric.AddOption
attrs []attribute.KeyValue
}
// SSPComponentName returns the component name attribute for a
@@ -70,29 +57,12 @@ func NewSSP(id int64) (*SSP, error) {
return &SSP{
spansProcessedCounter: spansProcessedCounter.Inst(),
addOpts: addOpts,
attrs: attrs,
}, err
}
// SpanProcessed records that a span has been processed by the SimpleSpanProcessor.
// If err is non-nil, it records the processing error as an attribute.
func (ssp *SSP) SpanProcessed(ctx context.Context, err error) {
ssp.spansProcessedCounter.Add(ctx, 1, ssp.addOption(err)...)
}
func (ssp *SSP) addOption(err error) []metric.AddOption {
if err == nil {
return ssp.addOpts
}
attrs := measureAttrsPool.Get().(*[]attribute.KeyValue)
defer func() {
clear(*attrs)
*attrs = (*attrs)[:0] // reset the slice for reuse
measureAttrsPool.Put(attrs)
}()
*attrs = append(*attrs, ssp.attrs...)
*attrs = append(*attrs, semconv.ErrorType(err))
// Do not inefficiently make a copy of attrs by using
// WithAttributes instead of WithAttributeSet.
return []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(*attrs...))}
// SpanProcessed records that a span has been submitted to the exporter by the
// SimpleSpanProcessor. Per the semantic conventions, this count is recorded at
// submission time and MUST NOT be affected by the export outcome.
func (ssp *SSP) SpanProcessed(ctx context.Context) {
ssp.spansProcessedCounter.Add(ctx, 1, ssp.addOpts...)
}
+10 -10
View File
@@ -67,19 +67,19 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) {
ssp.exporterMu.Lock()
defer ssp.exporterMu.Unlock()
var err error
if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() {
err = ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s})
if err != nil {
otel.Handle(err)
}
}
if ssp.inst != nil {
// Add the span to the context to ensure the metric is recorded
// with the correct span context.
// with the correct span context. Record the span as processed before
// invoking the exporter so the count is unaffected by the export
// outcome.
ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext())
ssp.inst.SpanProcessed(ctx, err)
ssp.inst.SpanProcessed(ctx)
}
if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() {
if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil {
otel.Handle(err)
}
}
}
+31
View File
@@ -138,6 +138,11 @@ type recordingSpan struct {
attributes []attribute.KeyValue
droppedAttributes int
logDropAttrsOnce sync.Once
// attributesDirty indicates attributes may contain duplicate keys.
attributesDirty bool
// attributesShared indicates the attributes backing array has been exposed
// to a reader and must not be modified.
attributesShared bool
// events are stored in FIFO queue capped by configured limit.
events evictedQueue[Event]
@@ -251,6 +256,11 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
s.addDroppedAttr(len(attributes))
return
}
maxCap := len(s.attributes) + len(attributes)
if limit > 0 {
maxCap = min(maxCap, limit)
}
s.ensureAttrsWritable(maxCap)
// If adding these attributes could exceed the capacity of s perform a
// de-duplication and truncation while adding to avoid over allocation.
@@ -271,9 +281,23 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
a = dedupAttr(a)
a = attrnorm.Truncate(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a)
s.attributes = append(s.attributes, a)
s.attributesDirty = true
}
}
// ensureAttrsWritable copies shared attributes into a backing array with at
// least capacity. This method assumes s.mu.Lock is held by the caller.
func (s *recordingSpan) ensureAttrsWritable(capacity int) {
if !s.attributesShared {
return
}
capacity = max(capacity, len(s.attributes))
attrs := make([]attribute.KeyValue, len(s.attributes), capacity)
copy(attrs, s.attributes)
s.attributes = attrs
s.attributesShared = false
}
// Declared as a var so tests can override.
var logDropAttrs = func() {
global.Warn("limit reached: dropping trace Span attributes")
@@ -592,6 +616,7 @@ func (s *recordingSpan) Attributes() []attribute.KeyValue {
s.mu.Lock()
defer s.mu.Unlock()
s.dedupeAttrs()
s.attributesShared = cap(s.attributes) > 0
return s.attributes
}
@@ -599,6 +624,10 @@ func (s *recordingSpan) Attributes() []attribute.KeyValue {
//
// This method assumes s.mu.Lock is held by the caller.
func (s *recordingSpan) dedupeAttrs() {
if !s.attributesDirty {
return
}
s.ensureAttrsWritable(len(s.attributes))
// Do not set a capacity when creating this map. Benchmark testing has
// showed this to only add unused memory allocations in general use.
exists := make(map[attribute.Key]int, len(s.attributes))
@@ -622,6 +651,7 @@ func (s *recordingSpan) dedupeAttrsFromRecord(record map[attribute.Key]int) {
}
clear(s.attributes[len(unique):]) // Erase unneeded elements to let GC collect objects.
s.attributes = unique
s.attributesDirty = false
}
// Links returns the links of this span.
@@ -765,6 +795,7 @@ func (s *recordingSpan) snapshot() ReadOnlySpan {
if len(s.attributes) > 0 {
s.dedupeAttrs()
sd.attributes = s.attributes
s.attributesShared = true
}
sd.droppedAttributeCount = s.droppedAttributes
if len(s.events.queue) > 0 {
+1 -1
View File
@@ -6,5 +6,5 @@ package sdk
// Version is the current release version of the OpenTelemetry SDK in use.
func Version() string {
return "1.45.0"
return "1.46.0"
}
+1 -1
View File
@@ -5,5 +5,5 @@ package otel
// Version is the current release version of OpenTelemetry in use.
func Version() string {
return "1.45.0"
return "1.46.0"
}
+4 -4
View File
@@ -3,7 +3,7 @@
module-sets:
stable-v1:
version: v1.45.0
version: v1.46.0
modules:
- go.opentelemetry.io/otel
- go.opentelemetry.io/otel/bridge/opencensus
@@ -22,12 +22,12 @@ module-sets:
- go.opentelemetry.io/otel/sdk/metric
- go.opentelemetry.io/otel/trace
experimental-metrics:
version: v0.67.0
version: v0.68.0
modules:
- go.opentelemetry.io/otel/exporters/prometheus
- go.opentelemetry.io/otel/metric/x
experimental-logs:
version: v0.21.0
version: v0.22.0
modules:
- go.opentelemetry.io/otel/log
- go.opentelemetry.io/otel/log/logtest
@@ -37,7 +37,7 @@ module-sets:
- go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
- go.opentelemetry.io/otel/exporters/stdout/stdoutlog
experimental-schema:
version: v0.0.18
version: v0.0.19
modules:
- go.opentelemetry.io/otel/schema
excluded-modules:
+14 -13
View File
@@ -397,7 +397,7 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus
# github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
## explicit; go 1.19
github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors
# github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
# github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0
## explicit; go 1.25.0
github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule
github.com/grpc-ecosystem/grpc-gateway/v2/runtime
@@ -609,19 +609,19 @@ go.etcd.io/bbolt/internal/freelist
## explicit; go 1.24.0
go.opentelemetry.io/auto/sdk
go.opentelemetry.io/auto/sdk/internal/telemetry
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0
## explicit; go 1.25.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0
## explicit; go 1.25.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv
# go.opentelemetry.io/contrib/propagators/envcar v0.70.0
# go.opentelemetry.io/contrib/propagators/envcar v0.71.0
## explicit; go 1.25.0
go.opentelemetry.io/contrib/propagators/envcar
# go.opentelemetry.io/otel v1.45.0
# go.opentelemetry.io/otel v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel
go.opentelemetry.io/otel/attribute
@@ -641,11 +641,11 @@ go.opentelemetry.io/otel/semconv/v1.43.0
go.opentelemetry.io/otel/semconv/v1.43.0/httpconv
go.opentelemetry.io/otel/semconv/v1.43.0/otelconv
go.opentelemetry.io/otel/semconv/v1.43.0/rpcconv
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace
go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal
@@ -655,7 +655,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal
@@ -663,14 +663,15 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/counter
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/envconfig
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpjson
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/retry
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/x
# go.opentelemetry.io/otel/metric v1.45.0
# go.opentelemetry.io/otel/metric v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/metric
go.opentelemetry.io/otel/metric/embedded
go.opentelemetry.io/otel/metric/noop
# go.opentelemetry.io/otel/sdk v1.45.0
# go.opentelemetry.io/otel/sdk v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/sdk
go.opentelemetry.io/otel/sdk/instrumentation
@@ -681,7 +682,7 @@ go.opentelemetry.io/otel/sdk/trace
go.opentelemetry.io/otel/sdk/trace/internal/env
go.opentelemetry.io/otel/sdk/trace/internal/observ
go.opentelemetry.io/otel/sdk/trace/tracetest
# go.opentelemetry.io/otel/trace v1.45.0
# go.opentelemetry.io/otel/trace v1.46.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/trace
go.opentelemetry.io/otel/trace/embedded
@@ -755,10 +756,10 @@ golang.org/x/text/unicode/norm
# golang.org/x/time v0.15.0
## explicit; go 1.25.0
golang.org/x/time/rate
# google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d
# google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5
## explicit; go 1.25.0
google.golang.org/genproto/googleapis/api/httpbody
# google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d
# google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5
## explicit; go 1.25.0
google.golang.org/genproto/googleapis/rpc/code
google.golang.org/genproto/googleapis/rpc/errdetails