mirror of
https://github.com/distribution/distribution.git
synced 2026-10-28 10:15:27 +08:00
chore(deps): bump oltp version (#4968)
This commit is contained in:
324 files changed
+20943
-2957
No files matched your search
@@ -32,10 +32,10 @@ require (
|
||||
github.com/stretchr/testify v1.12.1
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.67.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
go.opentelemetry.io/otel v1.45.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0
|
||||
go.opentelemetry.io/otel/sdk v1.44.0
|
||||
go.opentelemetry.io/otel/trace v1.44.0
|
||||
go.opentelemetry.io/otel/sdk v1.45.0
|
||||
go.opentelemetry.io/otel/trace v1.45.0
|
||||
go.yaml.in/yaml/v2 v2.4.4
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/net v0.59.0
|
||||
@@ -66,13 +66,13 @@ require (
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@@ -93,28 +93,28 @@ require (
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.18.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.64.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.18.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.21.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect
|
||||
google.golang.org/grpc v1.83.2 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
@@ -101,8 +101,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -126,8 +126,8 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE
|
||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
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/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/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw=
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
|
||||
@@ -210,12 +210,12 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
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.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.18.0 h1:deI9UQMoGFgrg5iLPgzueqFPHevDl+28YKfSpPTI6rY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.18.0/go.mod h1:PFx9NgpNUKXdf7J4Q3agRxMs3Y07QhTCVipKmLsMKnU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro=
|
||||
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/otlplog/otlploggrpc v0.21.0 h1:WseeVYf5dJZTsyPiyW5L14k5qsSibqXAMTSiFEDiWr0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0/go.mod h1:SiLZnQS6Qk2eCpvr2CH/XMAOa64TWGXxEZJZCpD2Lmc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 h1:fvNHGyo3CdRv/DQveXqhqBxnKTDyRaC5sMSQxilX/A0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0/go.mod h1:zyGrjRKL2B/6+Jc/m4/otPoZqV2MY9ZjC/aBraRO7zc=
|
||||
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.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY=
|
||||
@@ -228,30 +228,30 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJK
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.64.0 h1:g0LRDXMX/G1SEZtK8zl8Chm4K6GBwRkjPKE36LxiTYs=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.64.0/go.mod h1:UrgcjnarfdlBDP3GjDIJWe6HTprwSazNjwsI+Ru6hro=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.18.0 h1:KJVjPD3rcPb98rIs3HznyJlrfx9ge5oJvxxlGR+P/7s=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.18.0/go.mod h1:K3kRa2ckmHWQaTWQdPRHc7qGXASuVuoEQXzrvlA98Ws=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0 h1:2lpf4hnrasYIsUyEXwnTZq5lsxrMm4T2Bwb06IctAZQ=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0/go.mod h1:YWOW6h7jwApz9Pl76ie/izUsSPj0s2MdIlpqbPqaf3U=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.42.0 h1:lSZHgNHfbmQTPfuTmWVkEu8J8qXaQwuV30pjCcAUvP8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.42.0/go.mod h1:so9ounLcuoRDu033MW/E0AD4hhUjVqswrMF5FoZlBcw=
|
||||
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/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4=
|
||||
go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko=
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.opentelemetry.io/otel/log v0.21.0 h1:SLsVDGmtyBrdw8/a2Z0bOIxou/+bN4z56GebH7T0LvA=
|
||||
go.opentelemetry.io/otel/log v0.21.0/go.mod h1:iReetQrZL9Wyg84cCkOoCmqDHS5RCFfyxC7J+r8fn8g=
|
||||
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/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns=
|
||||
go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI=
|
||||
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/log v0.21.0 h1:QsE7XSR0ktQdKmRKGnR+f1ObGF32WG+7MER/P9KgmYc=
|
||||
go.opentelemetry.io/otel/sdk/log v0.21.0/go.mod h1:m9mApjCoD2/1QuKCAptjv+BrG9WKOvQLVdNx+iBldTo=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.21.0 h1:X+JBBgKlswCGYsmgL0CnoUUtlE//VB345c84jYAYkdQ=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.21.0/go.mod h1:HD1575K8e6sIFBBDd5tZB3t9DlMytWXq9FuR+Y4rfjE=
|
||||
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/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/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
@@ -282,10 +282,10 @@ google.golang.org/api v0.264.0 h1:+Fo3DQXBK8gLdf8rFZ3uLu39JpOnhvzJrLMQSoSYZJM=
|
||||
google.golang.org/api v0.264.0/go.mod h1:fAU1xtNNisHgOF5JooAs8rRaTkl2rT3uaoNGo9NS3R8=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
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/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.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build !go1.21
|
||||
// +build !go1.21
|
||||
|
||||
/*
|
||||
Copyright 2019 The logr Authors.
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2019 The logr Authors.
|
||||
|
||||
+36
-11
@@ -426,7 +426,7 @@ func (f Formatter) colon() byte {
|
||||
}
|
||||
|
||||
func (f Formatter) pretty(value any) string {
|
||||
return f.prettyWithFlags(value, 0, 0)
|
||||
return f.prettyWithFlags(value, 0, 0, 0, nil)
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -434,7 +434,13 @@ const (
|
||||
)
|
||||
|
||||
// TODO: This is not fast. Most of the overhead goes here.
|
||||
func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
// value: The value to render
|
||||
// flags: Bitmask of flags (see above)
|
||||
// depth: The current depth of nested structs, slices, arrays, and maps
|
||||
// ptrDepth: The current depth of including pointer dereferences
|
||||
// ptrMap: A map of pointers already seen, to avoid infinite recursion (usually
|
||||
// nil unless ptrDepth is large)
|
||||
func (f Formatter) prettyWithFlags(value any, flags uint32, depth int, ptrDepth int, ptrMap map[uintptr]bool) string {
|
||||
if depth > f.opts.MaxLogDepth {
|
||||
return `"<max-log-depth-exceeded>"`
|
||||
}
|
||||
@@ -504,7 +510,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
// arbitrary keys might need escaping
|
||||
buf.WriteString(prettyString(k))
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1))
|
||||
buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1, ptrDepth+1, ptrMap))
|
||||
}
|
||||
if flags&flagRawStruct == 0 {
|
||||
buf.WriteByte('}')
|
||||
@@ -576,7 +582,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
}
|
||||
printComma = true // if we got here, we are rendering a field
|
||||
if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
|
||||
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1))
|
||||
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1, ptrDepth+1, ptrMap))
|
||||
continue
|
||||
}
|
||||
if name == "" {
|
||||
@@ -585,7 +591,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
// field names can't contain characters which need escaping
|
||||
buf.WriteString(f.quoted(name, false))
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1))
|
||||
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1, ptrDepth+1, ptrMap))
|
||||
}
|
||||
if flags&flagRawStruct == 0 {
|
||||
buf.WriteByte('}')
|
||||
@@ -612,7 +618,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
e := v.Index(i)
|
||||
buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1))
|
||||
buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1, ptrDepth+1, ptrMap))
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
@@ -637,7 +643,8 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
keystr = prettyString(keystr)
|
||||
} else {
|
||||
// prettyWithFlags will produce already-escaped values
|
||||
keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1)
|
||||
// key depth is unrelated to overall depth
|
||||
keystr = f.prettyWithFlags(it.Key().Interface(), 0, 0, ptrDepth, ptrMap)
|
||||
if t.Key().Kind() != reflect.String {
|
||||
// JSON only does string keys. Unlike Go's standard JSON, we'll
|
||||
// convert just about anything to a string.
|
||||
@@ -646,16 +653,34 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
||||
}
|
||||
buf.WriteString(keystr)
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1))
|
||||
buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1, ptrDepth+1, ptrMap))
|
||||
i++
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return "null"
|
||||
}
|
||||
return f.prettyWithFlags(v.Elem().Interface(), 0, depth)
|
||||
// Special case: recursive pointers. For normal use we do not want to
|
||||
// count pointer dereferences as depth, but if we see the same pointer
|
||||
// again we have a recursion and need to stop. After a large number of
|
||||
// pointer dereferences we will start tracking pointers to avoid the
|
||||
// perf hit of doing it in the normal path.
|
||||
//
|
||||
// This should not happen accidentally (e.g. json decoding should never
|
||||
// do this) but we can handle it gracefully.
|
||||
if ptrMap != nil && ptrMap[uintptr(v.Pointer())] {
|
||||
depth = f.opts.MaxLogDepth + 1 // force a depth error
|
||||
}
|
||||
const maxDepthFactor = 4 // arbitrary, but we want it large enough to not false-alert
|
||||
if ptrDepth > f.opts.MaxLogDepth*maxDepthFactor && ptrMap == nil {
|
||||
ptrMap = map[uintptr]bool{}
|
||||
}
|
||||
if ptrMap != nil {
|
||||
ptrMap[(uintptr)(v.Pointer())] = true
|
||||
}
|
||||
return f.prettyWithFlags(v.Elem().Interface(), 0, depth, ptrDepth+1, ptrMap)
|
||||
}
|
||||
return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
|
||||
}
|
||||
@@ -697,7 +722,7 @@ func isEmpty(v reflect.Value) bool {
|
||||
return v.Float() == 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return v.Complex() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
case reflect.Interface, reflect.Pointer:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
|
||||
+16
-6
@@ -1,5 +1,4 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
@@ -33,7 +32,7 @@ const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink
|
||||
func (l fnlogger) Handle(_ context.Context, record slog.Record) error {
|
||||
kvList := make([]any, 0, 2*record.NumAttrs())
|
||||
record.Attrs(func(attr slog.Attr) bool {
|
||||
kvList = attrToKVs(attr, kvList)
|
||||
kvList = attrToKVs(attr, kvList, l.opts.MaxLogDepth)
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -49,7 +48,7 @@ func (l fnlogger) Handle(_ context.Context, record slog.Record) error {
|
||||
func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
|
||||
kvList := make([]any, 0, 2*len(attrs))
|
||||
for _, attr := range attrs {
|
||||
kvList = attrToKVs(attr, kvList)
|
||||
kvList = attrToKVs(attr, kvList, l.opts.MaxLogDepth)
|
||||
}
|
||||
l.AddValues(kvList)
|
||||
return &l
|
||||
@@ -61,14 +60,25 @@ func (l fnlogger) WithGroup(name string) logr.SlogSink {
|
||||
}
|
||||
|
||||
// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups
|
||||
// and other details of slog.
|
||||
func attrToKVs(attr slog.Attr, kvList []any) []any {
|
||||
// and other details of slog. maxDepth bounds recursion into nested groups so a
|
||||
// deeply-nested slog.Group cannot exhaust the stack; it is decremented per group
|
||||
// level and starts at the Formatter's MaxLogDepth (past which the formatter would
|
||||
// truncate the rendering anyway).
|
||||
func attrToKVs(attr slog.Attr, kvList []any, maxDepth int) []any {
|
||||
attrVal := attr.Value.Resolve()
|
||||
if attrVal.Kind() == slog.KindGroup {
|
||||
if maxDepth <= 0 {
|
||||
// Nesting is too deep to build without risking a stack overflow.
|
||||
// Stop here; the formatter truncates below MaxLogDepth regardless.
|
||||
if attr.Key != "" {
|
||||
kvList = append(kvList, attr.Key, "<max-log-depth-exceeded>")
|
||||
}
|
||||
return kvList
|
||||
}
|
||||
groupVal := attrVal.Group()
|
||||
grpKVs := make([]any, 0, 2*len(groupVal))
|
||||
for _, attr := range groupVal {
|
||||
grpKVs = attrToKVs(attr, grpKVs)
|
||||
grpKVs = attrToKVs(attr, grpKVs, maxDepth-1)
|
||||
}
|
||||
if attr.Key == "" {
|
||||
// slog says we have to inline these
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
+17
-1
@@ -71,6 +71,7 @@ type ServeMux struct {
|
||||
streamErrorHandler StreamErrorHandlerFunc
|
||||
routingErrorHandler RoutingErrorHandlerFunc
|
||||
disablePathLengthFallback bool
|
||||
disableHTTPMethodOverride bool
|
||||
unescapingMode UnescapingMode
|
||||
writeContentLength bool
|
||||
disableChunkedEncoding bool
|
||||
@@ -271,6 +272,19 @@ func WithDisablePathLengthFallback() ServeMuxOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisableHTTPMethodOverride returns a ServeMuxOption that disables the
|
||||
// X-HTTP-Method-Override header handling.
|
||||
//
|
||||
// When this option is used, the mux will no longer allow POST requests with
|
||||
// the X-HTTP-Method-Override header to override the HTTP method. The path
|
||||
// length fallback (POST with application/x-www-form-urlencoded falling back
|
||||
// to a matching GET handler) is not affected by this option.
|
||||
func WithDisableHTTPMethodOverride() ServeMuxOption {
|
||||
return func(serveMux *ServeMux) {
|
||||
serveMux.disableHTTPMethodOverride = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithWriteContentLength returns a ServeMuxOption to enable writing content length on non-streaming responses
|
||||
func WithWriteContentLength() ServeMuxOption {
|
||||
return func(serveMux *ServeMux) {
|
||||
@@ -405,7 +419,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
path = r.URL.RawPath
|
||||
}
|
||||
|
||||
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) {
|
||||
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && !s.disableHTTPMethodOverride && s.isPathLengthFallback(r) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||
sterr := status.Error(codes.InvalidArgument, err.Error())
|
||||
@@ -467,6 +481,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: mse,
|
||||
})
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -509,6 +524,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: mse,
|
||||
})
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
+2
-2
@@ -96,9 +96,9 @@ linters:
|
||||
- "!**/exporters/zipkin/**"
|
||||
deny:
|
||||
- pkg: go.opentelemetry.io/otel/semconv
|
||||
desc: "Use go.opentelemetry.io/otel/semconv/v1.41.0 instead. If a newer semconv version has been released, update the depguard rule."
|
||||
desc: "Use go.opentelemetry.io/otel/semconv/v1.43.0 instead. If a newer semconv version has been released, update the depguard rule."
|
||||
allow:
|
||||
- go.opentelemetry.io/otel/semconv/v1.41.0
|
||||
- go.opentelemetry.io/otel/semconv/v1.43.0
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- appendAssign
|
||||
|
||||
+14
@@ -11,3 +11,17 @@ http://4.3.2.1:78/user/123
|
||||
file:///home/runner/work/opentelemetry-go/opentelemetry-go/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/dns:/:4317
|
||||
# URL works, but it has blocked link checkers.
|
||||
https://dl.acm.org/doi/10.1145/198429.198435
|
||||
https://pkg.go.dev/google.golang.org/grpc.*internal/resolver
|
||||
https://golang.org/src/crypto/tls/generate_cert.go
|
||||
https://github.com/grpc/grpc/blob/.*/doc/naming.md
|
||||
https://github.com/open-telemetry/opentelemetry-specification/blob/.*
|
||||
# Fake domains used in otlp options_test.go tests
|
||||
https?://env\.endpoint.*
|
||||
https?://env\.metrics\.endpoint.*
|
||||
https?://env_metrics_endpoint.*
|
||||
https?://env\.traces\.endpoint.*
|
||||
https?://env_traces_endpoint.*
|
||||
https?://someendpoint.*
|
||||
https?://overrode\.by\.signal\.specific.*
|
||||
https?://overrode_by_signal_specific.*
|
||||
https?://proxy\.com.*
|
||||
+3
@@ -47,6 +47,9 @@ For docs-only, test-only, or review-only tasks, still start with the required re
|
||||
- Prefer examples over long code snippets in GoDoc when practical.
|
||||
- Keep docs aligned with actual behavior. Do not leave stale comments, stale examples, or stale package documentation behind.
|
||||
- For user-visible changes, update `CHANGELOG.md` under the appropriate `Added`, `Changed`, `Deprecated`, `Fixed`, or `Removed` section within `## [Unreleased]`.
|
||||
- Always put the PR number at the end of the line (e.g., `(#1234)`), NOT the issue number.
|
||||
- If the PR number is not yet known, omit it until the PR is created, then update the changelog entry before merging.
|
||||
- Always use references to the go module that is updated (e.g., `go.opentelemetry.io/otel/sdk/metric`), instead of just the path (e.g., `sdk/metric`).
|
||||
|
||||
## Repository habits
|
||||
|
||||
|
||||
+74
-4
@@ -11,6 +11,75 @@ 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.45.0/0.67.0/0.21.0/0.0.18] - 2026-08-03
|
||||
|
||||
### Added
|
||||
|
||||
- Add experimental observability metrics to `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log`. (#7124)
|
||||
- Add the experimental `WithUnsafeAttributes` no-copy attribute option to `go.opentelemetry.io/otel/metric/x` for future performance improvements. This API is a work in progress. (#8251)
|
||||
- Add `Map` and `MapValue` functions for the new `MAP` attribute type in `go.opentelemetry.io/otel/attribute`. (#8445)
|
||||
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlptrace`. (#8453)
|
||||
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlplog`. (#8453)
|
||||
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8453)
|
||||
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/zipkin`. (#8453)
|
||||
- Apply `AttributeValueLengthLimit` recursively to values contained in `attribute.MAP` attributes in `go.opentelemetry.io/otel/sdk/trace`. (#8454)
|
||||
- Remove duplicate keys from `attribute.MAP` values in `go.opentelemetry.io/otel/sdk/resource` using last-value-wins semantics. (#8471)
|
||||
- Remove duplicate keys by default from `attribute.MAP` values in instrumentation scope attributes in `go.opentelemetry.io/otel/sdk/log` using last-value-wins semantics. (#8471)
|
||||
- Remove duplicate keys by default from `attribute.MAP` values in span, event, link, and instrumentation scope attributes in `go.opentelemetry.io/otel/sdk/trace` using last-value-wins semantics. (#8471)
|
||||
- Remove duplicate keys by default from `attribute.MAP` values in measurement and instrumentation scope attributes in `go.opentelemetry.io/otel/sdk/metric` using last-value-wins semantics. (#8471)
|
||||
- Extend `WithAllowKeyDuplication` in `go.opentelemetry.io/otel/sdk/log` to disable duplicate-key removal in `attribute.MAP` values for instrumentation scope attributes. (#8471)
|
||||
- Add the `go.opentelemetry.io/otel/semconv/v1.42.0` package.
|
||||
The package contains semantic conventions from the `v1.42.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.42.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.41.0`. (#8484)
|
||||
- Add `WithoutPanicRecording` as a `TracerProviderOption` in `go.opentelemetry.io/otel/sdk/trace` to disable exception event recording for panics. (#8532)
|
||||
- Add the `go.opentelemetry.io/otel/semconv/v1.43.0` package.
|
||||
The package contains semantic conventions from the `v1.43.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.43.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.42.0`. (#8628)
|
||||
|
||||
### Changed
|
||||
|
||||
- `HistogramReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` now uses a time-unbiased sampling algorithm for exemplars. (#8306)
|
||||
- ⚠️ **Breaking Change:** Use `go.opentelemetry.io/otel/attribute.Value` and `go.opentelemetry.io/otel/attribute.KeyValue` for log bodies and attributes in `go.opentelemetry.io/otel/log`, `go.opentelemetry.io/otel/log/logtest`, `go.opentelemetry.io/otel/sdk/log`, and `go.opentelemetry.io/otel/sdk/log/logtest`. (#8490)
|
||||
- Encode log bodies and attributes as `go.opentelemetry.io/otel/attribute.Value` JSON in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#8490)
|
||||
- Improve the performance of hashing `BOOLSLICE`, `INT64SLICE`, `FLOAT64SLICE`, and `STRINGSLICE` attribute values by avoiding reflection for short slices in `go.opentelemetry.io/otel/attribute`. (#8511)
|
||||
- ⚠️ **Breaking Change:** `WithEndpointURL` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` no longer appends the default signal path when an endpoint URL has no path, making the behavior consistent with `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` and with setting the endpoint through `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`. If the URL has no path component, the root path (`/`) is used. Use `WithEndpointURL(url.JoinPath(endpoint, "/v1/metrics"))` to preserve the previous behavior. (#8538)
|
||||
- ⚠️ **Breaking Change:** `WithEndpointURL` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` no longer appends the default signal path when an endpoint URL has no path, making the behavior consistent with `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` and with setting the endpoint through `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`. If the URL has no path component, the root path (`/`) is used. Use `WithEndpointURL(url.JoinPath(endpoint, "/v1/traces"))` to preserve the previous behavior. (#8538)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate `WithExportBufferSize` in `go.opentelemetry.io/otel/sdk/log`. The option remains available for source compatibility but no longer affects behavior; `BatchProcessor` no longer maintains a separate export-request buffer. (#8620)
|
||||
|
||||
### Removed
|
||||
|
||||
- ⚠️ **Breaking Change:** Remove `Kind`, `Value`, `KeyValue`, their constructors, and attribute conversion helpers from `go.opentelemetry.io/otel/log`. (#8490)
|
||||
- ⚠️ **Breaking Change:** Remove the `AttributeValueLengthLimit` and `AttributeCountLimit` fields from `RecordFactory` in `go.opentelemetry.io/otel/sdk/log/logtest`; records produced by the factory now keep attribute limits disabled so test code can append exact attributes. (#8556)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Apply TLS certificates configured through environment variables to gRPC connections in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`.
|
||||
- Prevent panics in `go.opentelemetry.io/otel/bridge/opentracing` when OpenTracing baggage is propagated concurrently with `Span.SetBaggageItem`.
|
||||
- Fix an off-by-one error in `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` that prevented the first exemplar from being sampled after the reservoir was filled. (#8309)
|
||||
- Interpret HTTP `Retry-After` header values as seconds instead of nanoseconds when retrying OTLP HTTP exports in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`, and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8383)
|
||||
- Fix a memory leak in the `Reservoir` implementation in `go.opentelemetry.io/otel/sdk/metric/exemplar`, where storing the full `context.Context` pinned large objects such as gRPC transport buffers. (#8389)
|
||||
- Prevent a non-empty attribute set whose computed hash is zero from collapsing to an empty set in `go.opentelemetry.io/otel/attribute`. (#8402)
|
||||
- Fix histogram data point reuse in `go.opentelemetry.io/otel/sdk/metric` aggregation to avoid leaking stale sum, minimum, and maximum values when they are disabled in subsequent collections. (#8403)
|
||||
- Avoid preallocating scope attributes when they are disabled in `go.opentelemetry.io/otel/exporters/prometheus`. (#8404)
|
||||
- Support HTTP-date values in the HTTP `Retry-After` header when retrying OTLP HTTP exports in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`, and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8417)
|
||||
- Reduce histogram heap allocations by reusing `BucketCounts` and `Exemplars` slices across `Collect` cycles in the cumulative histogram aggregation in `go.opentelemetry.io/otel/sdk/metric`. (#8428)
|
||||
- Fix `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` self-observability to record `error.type` on the operation-duration histogram when the `exportedSpans` metric is disabled. (#8432)
|
||||
- Stop including trace exporter endpoint configuration in internal logs from `go.opentelemetry.io/otel/sdk/trace`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, and `go.opentelemetry.io/otel/exporters/zipkin`. (#8438)
|
||||
- Fix invalid error formatting for out-of-range JSON code values in `go.opentelemetry.io/otel/codes`. (#8497)
|
||||
- Clarify in `go.opentelemetry.io/otel/log` that `Logger.Enabled` should be checked for every log emission because its result may change over time. (#8565)
|
||||
- Preserve user-provided exception attributes while independently deriving missing exception message and type attributes in `go.opentelemetry.io/otel/sdk/log`. (#8566)
|
||||
- Make `WithAttributeCountLimit(0)` and `OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT=0` discard all log record attributes in `go.opentelemetry.io/otel/sdk/log`. (#8570)
|
||||
- Clarify that the `Record` methods of `Float64Histogram` and `Int64Histogram` in `go.opentelemetry.io/otel/metric` expect non-negative values. (#8574)
|
||||
- Clarify in `go.opentelemetry.io/otel/log` that `LoggerProvider` implementations should retain an empty `Logger` name instead of replacing it with a default. (#8587)
|
||||
- Ensure that the built-in processors in `go.opentelemetry.io/otel/sdk/log` call exporter `ForceFlush` during `Shutdown`. (#8599)
|
||||
- Prevent processor operations in `go.opentelemetry.io/otel/sdk/log` from overlapping with processor shutdown or running after `LoggerProvider` shutdown. (#8608)
|
||||
- Prevent `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` from busy-spinning under exporter backpressure and serialize dequeue, export, force-flush, and shutdown work in one worker. (#8620)
|
||||
- Make `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` return errors encountered while draining records during `ForceFlush` and `Shutdown`, while continuing to attempt later batches as long as the request context remains valid. (#8620)
|
||||
- Keep the default `BatchProcessor` maximum export batch size in `go.opentelemetry.io/otel/sdk/log` at or below the configured maximum queue size. (#8620)
|
||||
|
||||
## [1.44.0/0.66.0/0.20.0/0.0.17] 2026-05-27
|
||||
|
||||
### Added
|
||||
@@ -42,13 +111,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
Set `OTEL_GO_X_METRIC_EXPORT_BATCH_SIZE=<max_size>` to enable for all periodic readers.
|
||||
See `go.opentelemetry.io/otel/sdk/metric/internal/x` for feature documentation. (#8071)
|
||||
- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`.
|
||||
Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable.
|
||||
Enable with `OTEL_GO_X_OBSERVABILITY=true` environment variable.
|
||||
See `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/x` for feature documentation. (#8192)
|
||||
- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`.
|
||||
Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable.
|
||||
Enable with `OTEL_GO_X_OBSERVABILITY=true` environment variable.
|
||||
See `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/x` for feature documentation. (#8194)
|
||||
- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`.
|
||||
Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable.
|
||||
Enable with `OTEL_GO_X_OBSERVABILITY=true` environment variable.
|
||||
See `go.opentelemetry.io/otel/stdout/stdoutlog/internal/x` for feature documentation. (#8263)
|
||||
- Add `WithDefaultAttributes` to `go.opentelemetry.io/otel/metric/x` to support setting default attributes on instruments. (#8135)
|
||||
- Add `go.opentelemetry.io/otel/semconv/v1.41.0` package.
|
||||
@@ -3713,7 +3782,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.44.0...HEAD
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...HEAD
|
||||
[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
|
||||
[1.42.0/0.64.0/0.18.0/0.0.16]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.42.0
|
||||
|
||||
+7
-5
@@ -8,8 +8,10 @@ repo for information on this and other language SIGs.
|
||||
See the [public meeting
|
||||
notes](https://docs.google.com/document/d/1E5e7Ld0NuU1iVvf-42tOBpu2VBBLYnh73GJuITGJTTU/edit)
|
||||
for a summary description of past meetings. To request edit access,
|
||||
join the meeting or get in touch on
|
||||
[Slack](https://cloud-native.slack.com/archives/C01NPAXACKT).
|
||||
join the meeting or get in touch on the
|
||||
[#otel-go](https://cloud-native.slack.com/archives/C01NPAXACKT)
|
||||
channel on CNCF Slack. If you are new to the CNCF Slack community,
|
||||
you can [create an account](https://slack.cncf.io/).
|
||||
|
||||
The meeting is open for all to join. We invite everyone to join our
|
||||
meeting, regardless of your experience level. Whether you're a
|
||||
@@ -752,8 +754,8 @@ Encapsulate setup in constructor functions, ensuring clear ownership and scope:
|
||||
import (
|
||||
"errors"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.41.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.43.0/otelconv"
|
||||
)
|
||||
|
||||
type SDKComponent struct {
|
||||
@@ -1054,7 +1056,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.41.0/otelconv/metric.go).
|
||||
Use the metric semantic conventions convenience package [otelconv](./semconv/v1.43.0/otelconv/metric.go).
|
||||
|
||||
##### Component Identification
|
||||
|
||||
|
||||
+5
-15
@@ -10,6 +10,7 @@ ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {}
|
||||
|
||||
GO = go
|
||||
TIMEOUT = 60
|
||||
GO_VERSION := $(shell awk '/^go [0-9]/ {print $$2; exit}' go.mod)
|
||||
|
||||
# User to run as in docker images.
|
||||
DOCKER_USER=$(shell id -u):$(shell id -g)
|
||||
@@ -19,7 +20,7 @@ DEPENDENCIES_DOCKERFILE=./dependencies.Dockerfile
|
||||
|
||||
.PHONY: precommit ci
|
||||
precommit: generate toolchain-check license-check misspell go-mod-tidy golangci-lint-fix verify-readmes verify-mods test-default
|
||||
ci: generate toolchain-check license-check lint vanity-import-check verify-readmes verify-mods build test-default check-clean-work-tree test-coverage
|
||||
ci: generate toolchain-check license-check lint verify-readmes verify-mods build test-default check-clean-work-tree test-coverage
|
||||
|
||||
# Tools
|
||||
|
||||
@@ -59,9 +60,6 @@ $(TOOLS)/gocovmerge: PACKAGE=github.com/wadey/gocovmerge
|
||||
STRINGER = $(TOOLS)/stringer
|
||||
$(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer
|
||||
|
||||
PORTO = $(TOOLS)/porto
|
||||
$(TOOLS)/porto: PACKAGE=github.com/jcchavezs/porto/cmd/porto
|
||||
|
||||
GOTMPL = $(TOOLS)/gotmpl
|
||||
$(GOTMPL): PACKAGE=go.opentelemetry.io/build-tools/gotmpl
|
||||
|
||||
@@ -72,7 +70,7 @@ GOVULNCHECK = $(TOOLS)/govulncheck
|
||||
$(TOOLS)/govulncheck: PACKAGE=golang.org/x/vuln/cmd/govulncheck
|
||||
|
||||
.PHONY: tools
|
||||
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(VERIFYREADMES) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
|
||||
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(VERIFYREADMES) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
|
||||
|
||||
# Virtualized python tools via docker
|
||||
|
||||
@@ -110,7 +108,7 @@ $(CODESPELL): PACKAGE=codespell
|
||||
# Generate
|
||||
|
||||
.PHONY: generate
|
||||
generate: go-generate vanity-import-fix
|
||||
generate: go-generate
|
||||
|
||||
.PHONY: go-generate
|
||||
go-generate: $(OTEL_GO_MOD_DIRS:%=go-generate/%)
|
||||
@@ -120,14 +118,10 @@ go-generate/%: $(STRINGER) $(GOTMPL)
|
||||
&& cd $(DIR) \
|
||||
&& PATH="$(TOOLS):$${PATH}" $(GO) generate ./...
|
||||
|
||||
.PHONY: vanity-import-fix
|
||||
vanity-import-fix: $(PORTO)
|
||||
@$(PORTO) --include-internal -w .
|
||||
|
||||
# Generate go.work file for local development.
|
||||
.PHONY: go-work
|
||||
go-work: $(CROSSLINK)
|
||||
$(CROSSLINK) work --root=$(shell pwd) --go=1.22.7
|
||||
$(CROSSLINK) work --root=$(shell pwd) --go=$(GO_VERSION)
|
||||
|
||||
# Build
|
||||
|
||||
@@ -228,10 +222,6 @@ go-mod-tidy/%: crosslink
|
||||
.PHONY: lint
|
||||
lint: misspell go-mod-tidy golangci-lint
|
||||
|
||||
.PHONY: vanity-import-check
|
||||
vanity-import-check: $(PORTO)
|
||||
@$(PORTO) --include-internal -l . || ( echo "(run: make vanity-import-fix)"; exit 1 )
|
||||
|
||||
.PHONY: misspell
|
||||
misspell: $(MISSPELL)
|
||||
@$(MISSPELL) -w $(ALL_DOCS)
|
||||
|
||||
+7
@@ -113,3 +113,10 @@ All officially supported exporters for the OpenTelemetry project are contained i
|
||||
## Contributing
|
||||
|
||||
See the [contributing documentation](CONTRIBUTING.md).
|
||||
|
||||
### Emeritus
|
||||
|
||||
- [Alex Kats](https://github.com/akats7), Triager
|
||||
|
||||
For more information about the emeritus role, see the
|
||||
[community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package attribute provides key and value attributes.
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
// Filter supports removing certain attributes from attribute sets. When
|
||||
// the filter returns true, the attribute will be kept in the filtered
|
||||
|
||||
+136
-26
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian)
|
||||
byteSliceID uint64 = 6874028470941080415 // "_[]byte_" (little endian)
|
||||
sliceID uint64 = 7883494272577650031 // "__slice_" (little endian)
|
||||
mapID uint64 = 6872316492666199903 // "__map___" (little endian)
|
||||
emptyID uint64 = 7305809155345288421 // "__empty_" (little endian)
|
||||
)
|
||||
|
||||
@@ -38,7 +39,13 @@ func hashKVs(kvs []KeyValue) uint64 {
|
||||
for _, kv := range kvs {
|
||||
h = hashKV(h, kv)
|
||||
}
|
||||
return h.Sum64()
|
||||
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
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// hashKV returns the xxHash64 hash of kv with h as the base.
|
||||
@@ -64,27 +71,79 @@ func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
h = h.String(v.stringly)
|
||||
case BOOLSLICE:
|
||||
h = h.Uint64(boolSliceID)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Bool(rv.Index(i).Bool())
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]bool:
|
||||
case [1]bool:
|
||||
h = h.Bool(vals[0])
|
||||
case [2]bool:
|
||||
h = h.Bool(vals[0])
|
||||
h = h.Bool(vals[1])
|
||||
case [3]bool:
|
||||
h = h.Bool(vals[0])
|
||||
h = h.Bool(vals[1])
|
||||
h = h.Bool(vals[2])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Bool(rv.Index(i).Bool())
|
||||
}
|
||||
}
|
||||
case INT64SLICE:
|
||||
h = h.Uint64(int64SliceID)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Int64(rv.Index(i).Int())
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]int64:
|
||||
case [1]int64:
|
||||
h = h.Int64(vals[0])
|
||||
case [2]int64:
|
||||
h = h.Int64(vals[0])
|
||||
h = h.Int64(vals[1])
|
||||
case [3]int64:
|
||||
h = h.Int64(vals[0])
|
||||
h = h.Int64(vals[1])
|
||||
h = h.Int64(vals[2])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Int64(rv.Index(i).Int())
|
||||
}
|
||||
}
|
||||
case FLOAT64SLICE:
|
||||
h = h.Uint64(float64SliceID)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Float64(rv.Index(i).Float())
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]float64:
|
||||
case [1]float64:
|
||||
h = h.Float64(vals[0])
|
||||
case [2]float64:
|
||||
h = h.Float64(vals[0])
|
||||
h = h.Float64(vals[1])
|
||||
case [3]float64:
|
||||
h = h.Float64(vals[0])
|
||||
h = h.Float64(vals[1])
|
||||
h = h.Float64(vals[2])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Float64(rv.Index(i).Float())
|
||||
}
|
||||
}
|
||||
case STRINGSLICE:
|
||||
h = h.Uint64(stringSliceID)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.String(rv.Index(i).String())
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]string:
|
||||
case [1]string:
|
||||
h = h.String(vals[0])
|
||||
case [2]string:
|
||||
h = h.String(vals[0])
|
||||
h = h.String(vals[1])
|
||||
case [3]string:
|
||||
h = h.String(vals[0])
|
||||
h = h.String(vals[1])
|
||||
h = h.String(vals[2])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.String(rv.Index(i).String())
|
||||
}
|
||||
}
|
||||
case BYTESLICE:
|
||||
h = h.Uint64(byteSliceID)
|
||||
@@ -95,21 +154,79 @@ func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
case [0]Value:
|
||||
// No values to hash, but the type identifier is still hashed above.
|
||||
case [1]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
case [2]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
case [3]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
h = hashValue(h, vals[2])
|
||||
case [4]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
h = hashValue(h, vals[2])
|
||||
h = hashValue(h, vals[3])
|
||||
case [5]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
h = hashValue(h, vals[2])
|
||||
h = hashValue(h, vals[3])
|
||||
h = hashValue(h, vals[4])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = hashValue(h, rv.Index(i).Interface().(Value))
|
||||
}
|
||||
}
|
||||
case MAP:
|
||||
h = h.Uint64(mapID)
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]KeyValue:
|
||||
// No values to hash, but the type identifier is still hashed above.
|
||||
case [1]KeyValue:
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
case [2]KeyValue:
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
case [3]KeyValue:
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
h = h.String(string(vals[2].Key))
|
||||
h = hashValue(h, vals[2].Value)
|
||||
case [4]KeyValue:
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
h = h.String(string(vals[2].Key))
|
||||
h = hashValue(h, vals[2].Value)
|
||||
h = h.String(string(vals[3].Key))
|
||||
h = hashValue(h, vals[3].Value)
|
||||
case [5]KeyValue:
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
h = h.String(string(vals[2].Key))
|
||||
h = hashValue(h, vals[2].Value)
|
||||
h = h.String(string(vals[3].Key))
|
||||
h = hashValue(h, vals[3].Value)
|
||||
h = h.String(string(vals[4].Key))
|
||||
h = hashValue(h, vals[4].Value)
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
kv := rv.Index(i).Interface().(KeyValue)
|
||||
h = h.String(string(kv.Key))
|
||||
h = hashValue(h, kv.Value)
|
||||
}
|
||||
}
|
||||
case EMPTY:
|
||||
h = h.Uint64(emptyID)
|
||||
default:
|
||||
@@ -121,10 +238,3 @@ func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func hashValueSlice(h xxhash.Hash, vals []Value) xxhash.Hash {
|
||||
for _, v := range vals {
|
||||
h = hashValue(h, v)
|
||||
}
|
||||
return h
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
Package attribute provide several helper functions for some commonly used
|
||||
logic of processing attributes.
|
||||
*/
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute/internal"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package xxhash provides a wrapper around the xxhash library for attribute hashing.
|
||||
package xxhash // import "go.opentelemetry.io/otel/attribute/internal/xxhash"
|
||||
package xxhash
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
// Iterator allows iterating over the set of attributes in order, sorted by
|
||||
// key.
|
||||
|
||||
+97
-37
@@ -1,16 +1,16 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
// Key represents the key part in key-value pairs. It's a string. The
|
||||
// allowed character set in the key depends on the use of the key.
|
||||
type Key string
|
||||
|
||||
// Bool creates a KeyValue instance with a BOOL Value.
|
||||
// Bool returns a [KeyValue] for a bool value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Bool(name, value).
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [Bool] function.
|
||||
func (k Key) Bool(v bool) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -18,10 +18,15 @@ func (k Key) Bool(v bool) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// BoolSlice creates a KeyValue instance with a BOOLSLICE Value.
|
||||
// BoolSlice returns a [KeyValue] for a []bool value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- BoolSlice(name, value).
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [BoolSlice] function.
|
||||
func (k Key) BoolSlice(v []bool) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -29,10 +34,12 @@ func (k Key) BoolSlice(v []bool) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Int creates a KeyValue instance with an INT64 Value.
|
||||
// Int returns a [KeyValue] for an int value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Int(name, value).
|
||||
// It is provided as a convenience for [Key.Int64].
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level [Int]
|
||||
// function.
|
||||
func (k Key) Int(v int) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -40,10 +47,17 @@ func (k Key) Int(v int) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// IntSlice creates a KeyValue instance with an INT64SLICE Value.
|
||||
// IntSlice returns a [KeyValue] for a []int value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- IntSlice(name, value).
|
||||
// It is provided as a convenience for [Key.Int64Slice].
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [IntSlice] function.
|
||||
func (k Key) IntSlice(v []int) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -51,10 +65,10 @@ func (k Key) IntSlice(v []int) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Int64 creates a KeyValue instance with an INT64 Value.
|
||||
// Int64 returns a [KeyValue] for an int64 value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Int64(name, value).
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [Int64] function.
|
||||
func (k Key) Int64(v int64) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -62,10 +76,15 @@ func (k Key) Int64(v int64) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Int64Slice creates a KeyValue instance with an INT64SLICE Value.
|
||||
// Int64Slice returns a [KeyValue] for a []int64 value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Int64Slice(name, value).
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [Int64Slice] function.
|
||||
func (k Key) Int64Slice(v []int64) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -73,10 +92,10 @@ func (k Key) Int64Slice(v []int64) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 creates a KeyValue instance with a FLOAT64 Value.
|
||||
// Float64 returns a [KeyValue] for a float64 value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Float64(name, value).
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [Float64] function.
|
||||
func (k Key) Float64(v float64) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -84,10 +103,15 @@ func (k Key) Float64(v float64) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value.
|
||||
// Float64Slice returns a [KeyValue] for a []float64 value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Float64(name, value).
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [Float64Slice] function.
|
||||
func (k Key) Float64Slice(v []float64) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -95,10 +119,10 @@ func (k Key) Float64Slice(v []float64) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// String creates a KeyValue instance with a STRING Value.
|
||||
// String returns a [KeyValue] for a string value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- String(name, value).
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [String] function.
|
||||
func (k Key) String(v string) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -106,10 +130,15 @@ func (k Key) String(v string) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// StringSlice creates a KeyValue instance with a STRINGSLICE Value.
|
||||
// StringSlice returns a [KeyValue] for a []string value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- StringSlice(name, value).
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [StringSlice] function.
|
||||
func (k Key) StringSlice(v []string) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -117,10 +146,15 @@ func (k Key) StringSlice(v []string) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// ByteSlice creates a KeyValue instance with a BYTESLICE Value.
|
||||
// ByteSlice returns a [KeyValue] for a []byte value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- ByteSlice(name, value).
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [ByteSlice] function.
|
||||
func (k Key) ByteSlice(v []byte) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -128,10 +162,15 @@ func (k Key) ByteSlice(v []byte) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Slice creates a KeyValue instance with a SLICE Value.
|
||||
// Slice returns a [KeyValue] for a []Value value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Slice(name, values...).
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level
|
||||
// [Slice] function.
|
||||
func (k Key) Slice(v ...Value) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
@@ -139,6 +178,27 @@ func (k Key) Slice(v ...Value) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// Map returns a [KeyValue] for a []KeyValue value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// Users should avoid providing duplicate keys; many receivers handle maps
|
||||
// containing duplicate keys unpredictably.
|
||||
//
|
||||
// The order of v is not preserved.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the package-level [Map]
|
||||
// function.
|
||||
func (k Key) Map(v ...KeyValue) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
Value: MapValue(v...),
|
||||
}
|
||||
}
|
||||
|
||||
// Defined reports whether the key is not empty.
|
||||
func (k Key) Defined() bool {
|
||||
return len(k) != 0
|
||||
|
||||
+67
-13
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -18,66 +18,120 @@ func (kv KeyValue) Valid() bool {
|
||||
return kv.Key.Defined()
|
||||
}
|
||||
|
||||
// Bool creates a KeyValue with a BOOL Value type.
|
||||
// Bool returns a [KeyValue] for a bool value.
|
||||
func Bool(k string, v bool) KeyValue {
|
||||
return Key(k).Bool(v)
|
||||
}
|
||||
|
||||
// BoolSlice creates a KeyValue with a BOOLSLICE Value type.
|
||||
// BoolSlice returns a [KeyValue] for a []bool value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func BoolSlice(k string, v []bool) KeyValue {
|
||||
return Key(k).BoolSlice(v)
|
||||
}
|
||||
|
||||
// Int creates a KeyValue with an INT64 Value type.
|
||||
// Int returns a [KeyValue] for an int value.
|
||||
//
|
||||
// It is provided as a convenience for [Int64].
|
||||
func Int(k string, v int) KeyValue {
|
||||
return Key(k).Int(v)
|
||||
}
|
||||
|
||||
// IntSlice creates a KeyValue with an INT64SLICE Value type.
|
||||
// IntSlice returns a [KeyValue] for a []int value.
|
||||
//
|
||||
// It is provided as a convenience for [Int64Slice].
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func IntSlice(k string, v []int) KeyValue {
|
||||
return Key(k).IntSlice(v)
|
||||
}
|
||||
|
||||
// Int64 creates a KeyValue with an INT64 Value type.
|
||||
// Int64 returns a [KeyValue] for an int64 value.
|
||||
func Int64(k string, v int64) KeyValue {
|
||||
return Key(k).Int64(v)
|
||||
}
|
||||
|
||||
// Int64Slice creates a KeyValue with an INT64SLICE Value type.
|
||||
// Int64Slice returns a [KeyValue] for a []int64 value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func Int64Slice(k string, v []int64) KeyValue {
|
||||
return Key(k).Int64Slice(v)
|
||||
}
|
||||
|
||||
// Float64 creates a KeyValue with a FLOAT64 Value type.
|
||||
// Float64 returns a [KeyValue] for a float64 value.
|
||||
func Float64(k string, v float64) KeyValue {
|
||||
return Key(k).Float64(v)
|
||||
}
|
||||
|
||||
// Float64Slice creates a KeyValue with a FLOAT64SLICE Value type.
|
||||
// Float64Slice returns a [KeyValue] for a []float64 value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func Float64Slice(k string, v []float64) KeyValue {
|
||||
return Key(k).Float64Slice(v)
|
||||
}
|
||||
|
||||
// String creates a KeyValue with a STRING Value type.
|
||||
// String returns a [KeyValue] for a string value.
|
||||
func String(k, v string) KeyValue {
|
||||
return Key(k).String(v)
|
||||
}
|
||||
|
||||
// StringSlice creates a KeyValue with a STRINGSLICE Value type.
|
||||
// StringSlice returns a [KeyValue] for a []string value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func StringSlice(k string, v []string) KeyValue {
|
||||
return Key(k).StringSlice(v)
|
||||
}
|
||||
|
||||
// ByteSlice creates a KeyValue with a BYTESLICE Value type.
|
||||
// ByteSlice returns a [KeyValue] for a []byte value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func ByteSlice(k string, v []byte) KeyValue {
|
||||
return Key(k).ByteSlice(v)
|
||||
}
|
||||
|
||||
// Slice creates a KeyValue with a SLICE Value type.
|
||||
// Slice returns a [KeyValue] for a []Value value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func Slice(k string, v ...Value) KeyValue {
|
||||
return Key(k).Slice(v...)
|
||||
}
|
||||
|
||||
// Map returns a [KeyValue] for a []KeyValue value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// Users should avoid providing duplicate keys; many receivers handle maps
|
||||
// containing duplicate keys unpredictably.
|
||||
//
|
||||
// The order of v is not preserved.
|
||||
func Map(k string, v ...KeyValue) KeyValue {
|
||||
return Key(k).Map(v...)
|
||||
}
|
||||
|
||||
// Stringer creates a new key-value pair with a passed name and a string
|
||||
// value generated by the passed Stringer interface.
|
||||
func Stringer(k string, v fmt.Stringer) KeyValue {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
|
||||
+3
-2
@@ -19,11 +19,12 @@ func _() {
|
||||
_ = x[STRINGSLICE-8]
|
||||
_ = x[BYTESLICE-9]
|
||||
_ = x[SLICE-10]
|
||||
_ = x[MAP-11]
|
||||
}
|
||||
|
||||
const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICEBYTESLICESLICE"
|
||||
const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICEBYTESLICESLICEMAP"
|
||||
|
||||
var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69, 78, 83}
|
||||
var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69, 78, 83, 86}
|
||||
|
||||
func (i Type) String() string {
|
||||
idx := int(i) - 0
|
||||
|
||||
+281
-31
@@ -1,14 +1,16 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
@@ -18,10 +20,10 @@ import (
|
||||
|
||||
//go:generate stringer -type=Type
|
||||
|
||||
// Type describes the type of the data Value holds.
|
||||
// Type describes the kind of data a [Value] holds.
|
||||
type Type int // nolint: revive // redefines builtin Type.
|
||||
|
||||
// Value represents the value part in key-value pairs.
|
||||
// Value represents the value in key-value pairs.
|
||||
//
|
||||
// Note that the zero value is a valid empty value.
|
||||
type Value struct {
|
||||
@@ -32,35 +34,40 @@ type Value struct {
|
||||
}
|
||||
|
||||
const (
|
||||
// EMPTY is used for a Value with no value set.
|
||||
// EMPTY identifies a Value with no data.
|
||||
EMPTY Type = iota
|
||||
// BOOL is a boolean Type Value.
|
||||
// BOOL identifies a Value containing a bool.
|
||||
BOOL
|
||||
// INT64 is a 64-bit signed integral Type Value.
|
||||
// INT64 identifies a Value containing an int64.
|
||||
INT64
|
||||
// FLOAT64 is a 64-bit floating point Type Value.
|
||||
// FLOAT64 identifies a Value containing a float64.
|
||||
FLOAT64
|
||||
// STRING is a string Type Value.
|
||||
// STRING identifies a Value containing a string.
|
||||
STRING
|
||||
// BOOLSLICE is a slice of booleans Type Value.
|
||||
// BOOLSLICE identifies a Value containing a []bool.
|
||||
BOOLSLICE
|
||||
// INT64SLICE is a slice of 64-bit signed integral numbers Type Value.
|
||||
// INT64SLICE identifies a Value containing a []int64.
|
||||
INT64SLICE
|
||||
// FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value.
|
||||
// FLOAT64SLICE identifies a Value containing a []float64.
|
||||
FLOAT64SLICE
|
||||
// STRINGSLICE is a slice of strings Type Value.
|
||||
// STRINGSLICE identifies a Value containing a []string.
|
||||
STRINGSLICE
|
||||
// BYTESLICE is a slice of bytes Type Value.
|
||||
// BYTESLICE identifies a Value containing a []byte.
|
||||
BYTESLICE
|
||||
// SLICE is a slice of Value Type values.
|
||||
// SLICE identifies a Value containing a []Value.
|
||||
SLICE
|
||||
// MAP identifies a Value containing a []KeyValue representation of a map.
|
||||
//
|
||||
// Note that MAP values may contain duplicate keys if duplicate keys are
|
||||
// provided when creating the value.
|
||||
MAP
|
||||
// INVALID is used for a Value with no value set.
|
||||
//
|
||||
// Deprecated: Use EMPTY instead as an empty value is a valid value.
|
||||
INVALID = EMPTY
|
||||
)
|
||||
|
||||
// BoolValue creates a BOOL Value.
|
||||
// BoolValue returns a [Value] for a bool value.
|
||||
func BoolValue(v bool) Value {
|
||||
return Value{
|
||||
vtype: BOOL,
|
||||
@@ -68,17 +75,31 @@ func BoolValue(v bool) Value {
|
||||
}
|
||||
}
|
||||
|
||||
// BoolSliceValue creates a BOOLSLICE Value.
|
||||
// BoolSliceValue returns a [Value] for a []bool value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func BoolSliceValue(v []bool) Value {
|
||||
return Value{vtype: BOOLSLICE, slice: attribute.SliceValue(v)}
|
||||
}
|
||||
|
||||
// IntValue creates an INT64 Value.
|
||||
// IntValue returns a [Value] for an int value.
|
||||
//
|
||||
// It is provided as a convenience for [Int64Value].
|
||||
func IntValue(v int) Value {
|
||||
return Int64Value(int64(v))
|
||||
}
|
||||
|
||||
// IntSliceValue creates an INT64SLICE Value.
|
||||
// IntSliceValue returns a [Value] for a []int value.
|
||||
//
|
||||
// It is provided as a convenience for [Int64SliceValue].
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func IntSliceValue(v []int) Value {
|
||||
val := Value{vtype: INT64SLICE}
|
||||
|
||||
@@ -104,7 +125,7 @@ func IntSliceValue(v []int) Value {
|
||||
return val
|
||||
}
|
||||
|
||||
// Int64Value creates an INT64 Value.
|
||||
// Int64Value returns a [Value] for an int64 value.
|
||||
func Int64Value(v int64) Value {
|
||||
return Value{
|
||||
vtype: INT64,
|
||||
@@ -112,12 +133,17 @@ func Int64Value(v int64) Value {
|
||||
}
|
||||
}
|
||||
|
||||
// Int64SliceValue creates an INT64SLICE Value.
|
||||
// Int64SliceValue returns a [Value] for a []int64 value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func Int64SliceValue(v []int64) Value {
|
||||
return Value{vtype: INT64SLICE, slice: attribute.SliceValue(v)}
|
||||
}
|
||||
|
||||
// Float64Value creates a FLOAT64 Value.
|
||||
// Float64Value returns a [Value] for a float64 value.
|
||||
func Float64Value(v float64) Value {
|
||||
return Value{
|
||||
vtype: FLOAT64,
|
||||
@@ -125,12 +151,17 @@ func Float64Value(v float64) Value {
|
||||
}
|
||||
}
|
||||
|
||||
// Float64SliceValue creates a FLOAT64SLICE Value.
|
||||
// Float64SliceValue returns a [Value] for a []float64 value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func Float64SliceValue(v []float64) Value {
|
||||
return Value{vtype: FLOAT64SLICE, slice: attribute.SliceValue(v)}
|
||||
}
|
||||
|
||||
// StringValue creates a STRING Value.
|
||||
// StringValue returns a [Value] for a string value.
|
||||
func StringValue(v string) Value {
|
||||
return Value{
|
||||
vtype: STRING,
|
||||
@@ -138,12 +169,22 @@ func StringValue(v string) Value {
|
||||
}
|
||||
}
|
||||
|
||||
// StringSliceValue creates a STRINGSLICE Value.
|
||||
// StringSliceValue returns a [Value] for a []string value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func StringSliceValue(v []string) Value {
|
||||
return Value{vtype: STRINGSLICE, slice: attribute.SliceValue(v)}
|
||||
}
|
||||
|
||||
// ByteSliceValue creates a BYTESLICE Value.
|
||||
// ByteSliceValue returns a [Value] for a []byte value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func ByteSliceValue(v []byte) Value {
|
||||
return Value{
|
||||
vtype: BYTESLICE,
|
||||
@@ -151,12 +192,32 @@ func ByteSliceValue(v []byte) Value {
|
||||
}
|
||||
}
|
||||
|
||||
// SliceValue creates a SLICE Value.
|
||||
// SliceValue returns a [Value] for a []Value value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
func SliceValue(v ...Value) Value {
|
||||
return Value{vtype: SLICE, slice: sliceValue(v)}
|
||||
}
|
||||
|
||||
// Type returns a type of the Value.
|
||||
// MapValue returns a [Value] for a []KeyValue value.
|
||||
//
|
||||
// Note that many observability backends are not optimized to query, index, or
|
||||
// aggregate complex attribute values. Complex values may also carry
|
||||
// additional performance overhead. Prefer primitive values when
|
||||
// possible.
|
||||
//
|
||||
// Users should avoid providing duplicate keys; many receivers handle maps
|
||||
// containing duplicate keys unpredictably.
|
||||
//
|
||||
// The order of v is not preserved.
|
||||
func MapValue(v ...KeyValue) Value {
|
||||
return Value{vtype: MAP, slice: mapValue(v)}
|
||||
}
|
||||
|
||||
// Type returns v's type.
|
||||
func (v Value) Type() Type {
|
||||
return v.vtype
|
||||
}
|
||||
@@ -277,6 +338,53 @@ func asValueSliceReflect(v any) []Value {
|
||||
return cpy
|
||||
}
|
||||
|
||||
// AsMap returns the []KeyValue value. Make sure that the Value's type is
|
||||
// MAP.
|
||||
//
|
||||
// The returned slice is sorted by key and may differ from the order
|
||||
// provided when creating the map value.
|
||||
//
|
||||
// The returned slice may contain duplicate keys if duplicate keys were
|
||||
// provided when creating the map value. Callers should not assume the returned
|
||||
// keys are unique.
|
||||
func (v Value) AsMap() []KeyValue {
|
||||
if v.vtype != MAP {
|
||||
return nil
|
||||
}
|
||||
return v.asMap()
|
||||
}
|
||||
|
||||
func (v Value) asMap() []KeyValue {
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]KeyValue:
|
||||
return []KeyValue{}
|
||||
case [1]KeyValue:
|
||||
return []KeyValue{vals[0]}
|
||||
case [2]KeyValue:
|
||||
return []KeyValue{vals[0], vals[1]}
|
||||
case [3]KeyValue:
|
||||
return []KeyValue{vals[0], vals[1], vals[2]}
|
||||
case [4]KeyValue:
|
||||
return []KeyValue{vals[0], vals[1], vals[2], vals[3]}
|
||||
case [5]KeyValue:
|
||||
return []KeyValue{vals[0], vals[1], vals[2], vals[3], vals[4]}
|
||||
default:
|
||||
return asKeyValueSliceReflect(v.slice)
|
||||
}
|
||||
}
|
||||
|
||||
func asKeyValueSliceReflect(v any) []KeyValue {
|
||||
rv := reflect.ValueOf(v)
|
||||
if !rv.IsValid() || rv.Kind() != reflect.Array || rv.Type().Elem() != reflect.TypeFor[KeyValue]() {
|
||||
return nil
|
||||
}
|
||||
cpy := make([]KeyValue, rv.Len())
|
||||
if len(cpy) > 0 {
|
||||
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// AsByteSlice returns the bytes value. Make sure that the Value's type
|
||||
// is BYTESLICE.
|
||||
func (v Value) AsByteSlice() []byte {
|
||||
@@ -315,6 +423,8 @@ func (v Value) AsInterface() any {
|
||||
return v.asByteSlice()
|
||||
case SLICE:
|
||||
return v.asSlice()
|
||||
case MAP:
|
||||
return v.asMap()
|
||||
case EMPTY:
|
||||
return nil
|
||||
}
|
||||
@@ -327,10 +437,10 @@ func (v Value) AsInterface() any {
|
||||
// Strings are returned as-is without JSON quoting, booleans and integers use
|
||||
// JSON literals, floating-point values use JSON numbers except that NaN and
|
||||
// ±Inf are rendered as NaN, Infinity, and -Infinity, byte slices are
|
||||
// base64-encoded, empty values are the empty string, and slices are encoded as
|
||||
// JSON arrays. String, byte, and special floating-point values inside arrays
|
||||
// are encoded as JSON strings, and empty values inside arrays are encoded as
|
||||
// null.
|
||||
// base64-encoded, empty values are the empty string, slices are encoded as JSON
|
||||
// arrays, and maps are encoded as JSON objects. String, byte, and special
|
||||
// floating-point values inside arrays and maps are encoded as JSON strings, and
|
||||
// empty values inside arrays and maps are encoded as null.
|
||||
//
|
||||
// [OpenTelemetry AnyValue representation for non-OTLP protocols]: https://opentelemetry.io/docs/specs/otel/common/#anyvalue-representation-for-non-otlp-protocols
|
||||
func (v Value) String() string {
|
||||
@@ -355,6 +465,8 @@ func (v Value) String() string {
|
||||
return formatByteSlice(v.stringly)
|
||||
case SLICE:
|
||||
return formatValueSliceValue(v.slice)
|
||||
case MAP:
|
||||
return formatMapValue(v.slice)
|
||||
case EMPTY:
|
||||
return ""
|
||||
default:
|
||||
@@ -399,6 +511,8 @@ func (v Value) Emit() string {
|
||||
return formatByteSlice(v.stringly)
|
||||
case SLICE:
|
||||
return formatValueSliceValue(v.slice)
|
||||
case MAP:
|
||||
return formatMapValue(v.slice)
|
||||
case EMPTY:
|
||||
return ""
|
||||
default:
|
||||
@@ -439,6 +553,47 @@ func sliceValueReflect(v []Value) any {
|
||||
return cp.Interface()
|
||||
}
|
||||
|
||||
func mapValue(v []KeyValue) any {
|
||||
switch len(v) {
|
||||
case 0:
|
||||
return [0]KeyValue{}
|
||||
case 1:
|
||||
return [1]KeyValue{v[0]}
|
||||
case 2:
|
||||
vals := [2]KeyValue{v[0], v[1]}
|
||||
sortKeyValues(vals[:])
|
||||
return vals
|
||||
case 3:
|
||||
vals := [3]KeyValue{v[0], v[1], v[2]}
|
||||
sortKeyValues(vals[:])
|
||||
return vals
|
||||
case 4:
|
||||
vals := [4]KeyValue{v[0], v[1], v[2], v[3]}
|
||||
sortKeyValues(vals[:])
|
||||
return vals
|
||||
case 5:
|
||||
vals := [5]KeyValue{v[0], v[1], v[2], v[3], v[4]}
|
||||
sortKeyValues(vals[:])
|
||||
return vals
|
||||
default:
|
||||
return mapValueReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func mapValueReflect(v []KeyValue) any {
|
||||
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[KeyValue]())).Elem()
|
||||
reflect.Copy(cp, reflect.ValueOf(v))
|
||||
vals := cp.Slice(0, len(v)).Interface().([]KeyValue)
|
||||
sortKeyValues(vals)
|
||||
return cp.Interface()
|
||||
}
|
||||
|
||||
func sortKeyValues(vals []KeyValue) {
|
||||
slices.SortStableFunc(vals, func(a, b KeyValue) int {
|
||||
return cmp.Compare(a.Key, b.Key)
|
||||
})
|
||||
}
|
||||
|
||||
func formatBoolSliceValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]bool:
|
||||
@@ -807,6 +962,37 @@ func formatValueSliceReflect(v any) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatMapValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]KeyValue:
|
||||
return "{}"
|
||||
case [1]KeyValue:
|
||||
return formatMap(vals[:])
|
||||
case [2]KeyValue:
|
||||
return formatMap(vals[:])
|
||||
case [3]KeyValue:
|
||||
return formatMap(vals[:])
|
||||
case [4]KeyValue:
|
||||
return formatMap(vals[:])
|
||||
case [5]KeyValue:
|
||||
return formatMap(vals[:])
|
||||
default:
|
||||
return formatMapReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func formatMap(vals []KeyValue) string {
|
||||
var b strings.Builder
|
||||
appendMap(&b, vals)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatMapReflect(v any) string {
|
||||
var b strings.Builder
|
||||
appendMapReflect(&b, reflect.ValueOf(v))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func appendValueSliceValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]Value:
|
||||
@@ -852,6 +1038,68 @@ func appendValueSliceReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendMapValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]KeyValue:
|
||||
_, _ = dst.WriteString("{}")
|
||||
case [1]KeyValue:
|
||||
appendMap(dst, vals[:])
|
||||
case [2]KeyValue:
|
||||
appendMap(dst, vals[:])
|
||||
case [3]KeyValue:
|
||||
appendMap(dst, vals[:])
|
||||
case [4]KeyValue:
|
||||
appendMap(dst, vals[:])
|
||||
case [5]KeyValue:
|
||||
appendMap(dst, vals[:])
|
||||
default:
|
||||
appendMapReflect(dst, reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func appendMap(dst *strings.Builder, vals []KeyValue) {
|
||||
// Estimate 32 bytes per value for small values, plus key quotes, colon,
|
||||
// and commas. Escaped keys and larger values grow the builder as needed.
|
||||
size := len("{}") + len(vals)*commaLen + len(vals)*32
|
||||
for _, val := range vals {
|
||||
size += len(val.Key) + len(`"":`)
|
||||
}
|
||||
|
||||
dst.Grow(size)
|
||||
_ = dst.WriteByte('{')
|
||||
for i, val := range vals {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
appendJSONString(dst, string(val.Key))
|
||||
_ = dst.WriteByte(':')
|
||||
appendJSONValue(dst, val.Value)
|
||||
}
|
||||
_ = dst.WriteByte('}')
|
||||
}
|
||||
|
||||
func appendMapReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
// Estimate 32 bytes per value for small values, plus key quotes, colon,
|
||||
// and commas. Escaped keys and larger values grow the builder as needed.
|
||||
size := len("{}") + rv.Len()*commaLen + rv.Len()*32
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
size += len(rv.Index(i).Field(0).String()) + len(`"":`)
|
||||
}
|
||||
|
||||
dst.Grow(size)
|
||||
_ = dst.WriteByte('{')
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
val := rv.Index(i).Interface().(KeyValue)
|
||||
appendJSONString(dst, string(val.Key))
|
||||
_ = dst.WriteByte(':')
|
||||
appendJSONValue(dst, val.Value)
|
||||
}
|
||||
_ = dst.WriteByte('}')
|
||||
}
|
||||
|
||||
func appendJSONValue(dst *strings.Builder, v Value) {
|
||||
switch v.Type() {
|
||||
case BOOL:
|
||||
@@ -894,6 +1142,8 @@ func appendJSONValue(dst *strings.Builder, v Value) {
|
||||
_ = dst.WriteByte('"')
|
||||
case SLICE:
|
||||
appendValueSliceValue(dst, v.slice)
|
||||
case MAP:
|
||||
appendMapValue(dst, v.slice)
|
||||
case EMPTY:
|
||||
_, _ = dst.WriteString("null")
|
||||
default:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package baggage // import "go.opentelemetry.io/otel/baggage"
|
||||
package baggage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package baggage // import "go.opentelemetry.io/otel/baggage"
|
||||
package baggage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ Package baggage provides functionality for storing and retrieving
|
||||
baggage items in Go context. For propagating the baggage, see the
|
||||
go.opentelemetry.io/otel/propagation package.
|
||||
*/
|
||||
package baggage // import "go.opentelemetry.io/otel/baggage"
|
||||
package baggage
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package codes // import "go.opentelemetry.io/otel/codes"
|
||||
package codes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -81,7 +81,7 @@ func (c *Code) UnmarshalJSON(b []byte) error {
|
||||
case float64:
|
||||
if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil {
|
||||
if ci >= maxCode {
|
||||
return fmt.Errorf("invalid code: %q", ci)
|
||||
return fmt.Errorf("invalid code: %d", ci)
|
||||
}
|
||||
|
||||
*c = Code(ci) // nolint: gosec // Bit size of 32 check above.
|
||||
|
||||
+1
-1
@@ -7,4 +7,4 @@ Package codes defines the canonical error codes used by OpenTelemetry.
|
||||
It conforms to [the OpenTelemetry
|
||||
specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/api.md#set-status).
|
||||
*/
|
||||
package codes // import "go.opentelemetry.io/otel/codes"
|
||||
package codes
|
||||
+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.23.0@sha256:7984ecb55b859eb3034ae9d836c4eeda137e2bdd0873b7ba2bb6c3d24d6ff457 AS weaver
|
||||
FROM otel/weaver:v0.25.1@sha256:9ad46ca9cd4fa5974b121f886aa3e9946a8ef8ea905001a96c018d21f9db87ca AS weaver
|
||||
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
|
||||
+1
-1
@@ -22,4 +22,4 @@ To read more about logs, see go.opentelemetry.io/otel/log.
|
||||
To read more about propagation, see go.opentelemetry.io/otel/propagation and
|
||||
go.opentelemetry.io/otel/baggage.
|
||||
*/
|
||||
package otel // import "go.opentelemetry.io/otel"
|
||||
package otel
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otel // import "go.opentelemetry.io/otel"
|
||||
package otel
|
||||
|
||||
// ErrorHandler handles irremediable events.
|
||||
type ErrorHandler interface {
|
||||
|
||||
+26
-12
@@ -1,11 +1,12 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
package otlploggrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/observ"
|
||||
@@ -28,9 +30,10 @@ import (
|
||||
|
||||
// The methods of this type are not expected to be called concurrently.
|
||||
type client struct {
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
requestFunc retry.RequestFunc
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
maxRequestSize int
|
||||
requestFunc retry.RequestFunc
|
||||
|
||||
// ourConn keeps track of where conn was created: true if created here in
|
||||
// NewClient, or false if passed with an option. This is important on
|
||||
@@ -49,9 +52,10 @@ var newGRPCClientFn = grpc.NewClient
|
||||
// newClient creates a new gRPC log client.
|
||||
func newClient(cfg config) (*client, error) {
|
||||
c := &client{
|
||||
exportTimeout: cfg.timeout.Value,
|
||||
requestFunc: cfg.retryCfg.Value.RequestFunc(retryable),
|
||||
conn: cfg.gRPCConn.Value,
|
||||
exportTimeout: cfg.timeout.Value,
|
||||
maxRequestSize: cfg.maxRequestSize.Value,
|
||||
requestFunc: cfg.retryCfg.Value.RequestFunc(retryable),
|
||||
conn: cfg.gRPCConn.Value,
|
||||
}
|
||||
|
||||
if len(cfg.headers.Value) > 0 {
|
||||
@@ -99,10 +103,12 @@ func newGRPCDialOptions(cfg config) []grpc.DialOption {
|
||||
if cfg.serviceConfig.Value != "" {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(cfg.serviceConfig.Value))
|
||||
}
|
||||
// Prioritize GRPCCredentials over Insecure (passing both is an error).
|
||||
// Prioritize configured credentials over Insecure (passing both is an error).
|
||||
switch {
|
||||
case cfg.gRPCCredentials.Value != nil:
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(cfg.gRPCCredentials.Value))
|
||||
case cfg.tlsCfg.Value != nil:
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(cfg.tlsCfg.Value)))
|
||||
case cfg.insecure.Value:
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
default:
|
||||
@@ -146,18 +152,26 @@ func (c *client) UploadLogs(ctx context.Context, rl []*logpb.ResourceLogs) (uplo
|
||||
ctx, cancel := c.exportContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
count := int64(len(rl))
|
||||
pbRequest := &collogpb.ExportLogsServiceRequest{ResourceLogs: rl}
|
||||
if c.instrumentation != nil {
|
||||
var count int64
|
||||
for _, resLogs := range rl {
|
||||
for _, scopeLogs := range resLogs.ScopeLogs {
|
||||
count += int64(len(scopeLogs.LogRecords))
|
||||
}
|
||||
}
|
||||
eo := c.instrumentation.ExportLogs(ctx, count)
|
||||
defer func() {
|
||||
eo.End(uploadErr)
|
||||
}()
|
||||
}
|
||||
|
||||
if maxSize := c.maxRequestSize; maxSize > 0 && proto.Size(pbRequest) > maxSize {
|
||||
return fmt.Errorf("request message too large: exceeded %d bytes", maxSize)
|
||||
}
|
||||
|
||||
return errors.Join(uploadErr, c.requestFunc(ctx, func(ctx context.Context) error {
|
||||
resp, err := c.lsc.Export(ctx, &collogpb.ExportLogsServiceRequest{
|
||||
ResourceLogs: rl,
|
||||
})
|
||||
resp, err := c.lsc.Export(ctx, pbRequest)
|
||||
if resp != nil && resp.PartialSuccess != nil {
|
||||
msg := resp.PartialSuccess.GetErrorMessage()
|
||||
n := resp.PartialSuccess.GetRejectedLogRecords()
|
||||
|
||||
+29
-11
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
package otlploggrpc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@@ -25,9 +25,10 @@ import (
|
||||
|
||||
// Default values.
|
||||
var (
|
||||
defaultEndpoint = "localhost:4317"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultRetryCfg = retry.DefaultConfig
|
||||
defaultEndpoint = "localhost:4317"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultMaxRequestSize = 64 * 1024 * 1024
|
||||
defaultRetryCfg = retry.DefaultConfig
|
||||
)
|
||||
|
||||
// Environment variable keys.
|
||||
@@ -85,13 +86,14 @@ type Option interface {
|
||||
}
|
||||
|
||||
type config struct {
|
||||
endpoint setting[string]
|
||||
insecure setting[bool]
|
||||
tlsCfg setting[*tls.Config]
|
||||
headers setting[map[string]string]
|
||||
compression setting[Compression]
|
||||
timeout setting[time.Duration]
|
||||
retryCfg setting[retry.Config]
|
||||
endpoint setting[string]
|
||||
insecure setting[bool]
|
||||
tlsCfg setting[*tls.Config]
|
||||
headers setting[map[string]string]
|
||||
compression setting[Compression]
|
||||
maxRequestSize setting[int]
|
||||
timeout setting[time.Duration]
|
||||
retryCfg setting[retry.Config]
|
||||
|
||||
// gRPC configurations
|
||||
gRPCCredentials setting[credentials.TransportCredentials]
|
||||
@@ -129,6 +131,9 @@ func newConfig(options []Option) config {
|
||||
getEnv[time.Duration](envTimeout, convDuration),
|
||||
fallback[time.Duration](defaultTimeout),
|
||||
)
|
||||
c.maxRequestSize = c.maxRequestSize.Resolve(
|
||||
fallback[int](defaultMaxRequestSize),
|
||||
)
|
||||
c.retryCfg = c.retryCfg.Resolve(
|
||||
fallback[retry.Config](defaultRetryCfg),
|
||||
)
|
||||
@@ -353,6 +358,19 @@ func WithTimeout(duration time.Duration) Option {
|
||||
})
|
||||
}
|
||||
|
||||
// WithMaxRequestSize sets the maximum size, in bytes, of a serialized export
|
||||
// request, before compression, that the exporter will send.
|
||||
//
|
||||
// If size is less than or equal to zero, no request-size limit is applied.
|
||||
// Disabling the limit is not recommended because it can lead to excessive
|
||||
// resource consumption or abuse.
|
||||
func WithMaxRequestSize(size int) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.maxRequestSize = newSetting(size)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithRetry sets the retry policy for transient retryable errors that are
|
||||
// returned by the target endpoint.
|
||||
//
|
||||
|
||||
+1
-1
@@ -60,4 +60,4 @@ The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] opti
|
||||
|
||||
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
|
||||
*/
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
package otlploggrpc
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
package otlploggrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -3,9 +3,9 @@
|
||||
|
||||
// Package internal provides internal functionality for the otlploggrpc
|
||||
// package.
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal"
|
||||
package internal
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target.go.tmpl "--data={ \"pkg\": \"observ\", \"pkg_path\": \"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/observ\" }" --out=observ/target.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target.go.tmpl "--data={ \"pkg\": \"observ\" }" --out=observ/target.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target_test.go.tmpl "--data={ \"pkg\": \"observ\" }" --out=observ/target_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc\" }" --out=x/x.go
|
||||
|
||||
Generated
Vendored
+8
-4
@@ -3,7 +3,7 @@
|
||||
|
||||
// Package observ provides observability metrics for OTLP log exporters.
|
||||
// This is an experimental feature controlled by the x.Observability feature flag.
|
||||
package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/observ"
|
||||
package observ
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/x"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.43.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -68,6 +68,7 @@ var (
|
||||
|
||||
func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) }
|
||||
func put[T any](p *sync.Pool, s *[]T) {
|
||||
clear(*s) // erase elements to allow GC to collect what they refer to.
|
||||
*s = (*s)[:0]
|
||||
p.Put(s)
|
||||
}
|
||||
@@ -274,7 +275,10 @@ var errPool = sync.Pool{
|
||||
// the provided non-nil err.
|
||||
func rejectedCount(n int64, err error) int64 {
|
||||
ps := errPool.Get().(*internal.PartialSuccess)
|
||||
defer errPool.Put(ps)
|
||||
defer func() {
|
||||
*ps = internal.PartialSuccess{} // erase fields to allow GC to collect them.
|
||||
errPool.Put(ps)
|
||||
}()
|
||||
|
||||
// check for partial success
|
||||
if errors.As(err, ps) {
|
||||
|
||||
Generated
Vendored
+6
-7
@@ -1,10 +1,10 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/observ/target.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/observ"
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/otlp/observ/target.go.tmpl
|
||||
|
||||
package observ
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -41,11 +41,10 @@ func ParseCanonicalTarget(target string) (string, int, error) {
|
||||
const sep = "://"
|
||||
|
||||
// Find scheme. Do not allocate the string by using url.Parse.
|
||||
idx := strings.Index(target, sep)
|
||||
if idx == -1 {
|
||||
scheme, endpoint, found := strings.Cut(target, sep)
|
||||
if !found {
|
||||
return "", -1, fmt.Errorf("invalid target %q: missing scheme", target)
|
||||
}
|
||||
scheme, endpoint := target[:idx], target[idx+len(sep):]
|
||||
|
||||
// Check for unix schemes.
|
||||
if scheme == schemeUnix || scheme == schemeUnixAbstract {
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal"
|
||||
package internal
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
||||
Generated
Vendored
+4
-4
@@ -1,13 +1,13 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/retry"
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
Generated
Vendored
+29
-82
@@ -1,12 +1,12 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
||||
|
||||
// Package transform provides transformation functionality from the
|
||||
// sdk/log data-types into OTLP data-types.
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform"
|
||||
package transform
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -94,13 +94,13 @@ func LogRecord(record log.Record) *lpb.LogRecord {
|
||||
EventName: record.EventName(),
|
||||
SeverityNumber: SeverityNumber(record.Severity()),
|
||||
SeverityText: record.SeverityText(),
|
||||
Body: LogAttrValue(record.Body()),
|
||||
Body: AttrValue(record.Body()),
|
||||
Attributes: make([]*cpb.KeyValue, 0, record.AttributesLen()),
|
||||
Flags: uint32(record.TraceFlags()),
|
||||
// TODO: DroppedAttributesCount: /* ... */,
|
||||
}
|
||||
record.WalkAttributes(func(kv api.KeyValue) bool {
|
||||
r.Attributes = append(r.Attributes, LogAttr(kv))
|
||||
record.WalkAttributes(func(kv attribute.KeyValue) bool {
|
||||
r.Attributes = append(r.Attributes, Attr(kv))
|
||||
return true
|
||||
})
|
||||
if tID := record.TraceID(); tID.IsValid() {
|
||||
@@ -195,12 +195,29 @@ func AttrValue(v attribute.Value) *cpb.AnyValue {
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.BYTESLICE:
|
||||
av.Value = &cpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsByteSlice(),
|
||||
}
|
||||
case attribute.SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: attrValues(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.MAP:
|
||||
av.Value = &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: Attrs(v.AsMap()),
|
||||
},
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: stringSliceValues(v.AsStringSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.EMPTY:
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
@@ -257,82 +274,12 @@ func stringSliceValues(vals []string) []*cpb.AnyValue {
|
||||
return converted
|
||||
}
|
||||
|
||||
// LogAttrs transforms a slice of [api.KeyValue] into OTLP key-values.
|
||||
func LogAttrs(attrs []api.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
func attrValues(vals []attribute.Value) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = AttrValue(v)
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, LogAttr(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttr transforms an [api.KeyValue] into an OTLP key-value.
|
||||
func LogAttr(attr api.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{
|
||||
Key: attr.Key,
|
||||
Value: LogAttrValue(attr.Value),
|
||||
}
|
||||
}
|
||||
|
||||
// LogAttrValues transforms a slice of [api.Value] into an OTLP []AnyValue.
|
||||
func LogAttrValues(vals []api.Value) []*cpb.AnyValue {
|
||||
if len(vals) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.AnyValue, 0, len(vals))
|
||||
for _, v := range vals {
|
||||
out = append(out, LogAttrValue(v))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttrValue transforms an [api.Value] into an OTLP AnyValue.
|
||||
func LogAttrValue(v api.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Kind() {
|
||||
case api.KindBool:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case api.KindInt64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case api.KindFloat64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case api.KindString:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case api.KindBytes:
|
||||
av.Value = &cpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsBytes(),
|
||||
}
|
||||
case api.KindSlice:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: LogAttrValues(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case api.KindMap:
|
||||
av.Value = &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: LogAttrs(v.AsMap()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
return converted
|
||||
}
|
||||
|
||||
// SeverityNumber transforms a [log.Severity] into an OTLP SeverityNumber.
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal"
|
||||
package internal
|
||||
|
||||
// Version is the current release version of the OpenTelemetry otlploggrpc
|
||||
// exporter in use.
|
||||
const Version = "0.18.0"
|
||||
const Version = "0.21.0"
|
||||
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc].
|
||||
package x // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/x"
|
||||
package x
|
||||
|
||||
import "strings"
|
||||
|
||||
|
||||
Generated
Vendored
+4
-4
@@ -1,11 +1,11 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc].
|
||||
package x // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/x"
|
||||
package x
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
package otlploggrpc
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over gRPC logs exporter in use.
|
||||
func Version() string {
|
||||
|
||||
+51
-21
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
package otlploghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -111,10 +111,11 @@ func newHTTPClient(ctx context.Context, cfg config) (*client, error) {
|
||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||
|
||||
c := &httpClient{
|
||||
compression: cfg.compression.Value,
|
||||
req: req,
|
||||
requestFunc: cfg.retryCfg.Value.RequestFunc(evaluate),
|
||||
client: hc,
|
||||
compression: cfg.compression.Value,
|
||||
maxRequestSize: cfg.maxRequestSize.Value,
|
||||
req: req,
|
||||
requestFunc: cfg.retryCfg.Value.RequestFunc(evaluate),
|
||||
client: hc,
|
||||
}
|
||||
|
||||
id := nextExporterID()
|
||||
@@ -125,10 +126,11 @@ func newHTTPClient(ctx context.Context, cfg config) (*client, error) {
|
||||
|
||||
type httpClient struct {
|
||||
// req is cloned for every upload the client makes.
|
||||
req *http.Request
|
||||
compression Compression
|
||||
requestFunc retry.RequestFunc
|
||||
client *http.Client
|
||||
req *http.Request
|
||||
compression Compression
|
||||
maxRequestSize int
|
||||
requestFunc retry.RequestFunc
|
||||
client *http.Client
|
||||
|
||||
inst *observ.Instrumentation
|
||||
}
|
||||
@@ -159,17 +161,27 @@ func (c *httpClient) uploadLogs(ctx context.Context, data []*logpb.ResourceLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request, err := c.newRequest(ctx, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var statusCode int
|
||||
if c.inst != nil {
|
||||
op := c.inst.ExportLogs(ctx, int64(len(data)))
|
||||
var count int64
|
||||
for _, resLogs := range data {
|
||||
for _, scopeLogs := range resLogs.ScopeLogs {
|
||||
count += int64(len(scopeLogs.LogRecords))
|
||||
}
|
||||
}
|
||||
op := c.inst.ExportLogs(ctx, count)
|
||||
defer func() { op.End(uploadErr, statusCode) }()
|
||||
}
|
||||
|
||||
if maxSize := c.maxRequestSize; maxSize > 0 && len(body) > maxSize {
|
||||
return fmt.Errorf("request body too large: exceeded %d bytes", maxSize)
|
||||
}
|
||||
request, err := c.newRequest(ctx, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.Join(uploadErr, c.requestFunc(ctx, func(iCtx context.Context) error {
|
||||
select {
|
||||
case <-iCtx.Done():
|
||||
@@ -177,6 +189,7 @@ func (c *httpClient) uploadLogs(ctx context.Context, data []*logpb.ResourceLogs)
|
||||
default:
|
||||
}
|
||||
|
||||
statusCode = 0
|
||||
request.reset(iCtx)
|
||||
// nolint:gosec // URL is constructed from validated OTLP endpoint configuration
|
||||
resp, err := c.client.Do(request.Request)
|
||||
@@ -285,7 +298,10 @@ func (c *httpClient) newRequest(ctx context.Context, body []byte) (request, erro
|
||||
r.Header.Set("Content-Encoding", "gzip")
|
||||
|
||||
gz := gzPool.Get().(*gzip.Writer)
|
||||
defer gzPool.Put(gz)
|
||||
defer func() {
|
||||
gz.Reset(io.Discard)
|
||||
gzPool.Put(gz)
|
||||
}()
|
||||
|
||||
var b bytes.Buffer
|
||||
gz.Reset(&b)
|
||||
@@ -299,7 +315,7 @@ func (c *httpClient) newRequest(ctx context.Context, body []byte) (request, erro
|
||||
}
|
||||
|
||||
req.bodyReader = bodyReader(b.Bytes())
|
||||
req.GetBody = bodyReaderErr(body)
|
||||
req.GetBody = bodyReaderErr(b.Bytes())
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -335,7 +351,7 @@ func (r *request) reset(ctx context.Context) {
|
||||
|
||||
// retryableError represents a request failure that can be retried.
|
||||
type retryableError struct {
|
||||
throttle int64
|
||||
throttle time.Duration
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -345,15 +361,29 @@ type retryableError struct {
|
||||
func newResponseError(header http.Header, wrapped error) error {
|
||||
var rErr retryableError
|
||||
if v := header.Get("Retry-After"); v != "" {
|
||||
if t, err := strconv.ParseInt(v, 10, 64); err == nil {
|
||||
rErr.throttle = t
|
||||
}
|
||||
rErr.throttle = retryAfterDuration(v)
|
||||
}
|
||||
|
||||
rErr.err = wrapped
|
||||
return rErr
|
||||
}
|
||||
|
||||
func retryAfterDuration(v string) time.Duration {
|
||||
if t, err := strconv.ParseInt(v, 10, 64); err == nil && t >= 0 {
|
||||
const maxRetryAfterSeconds = int64(1<<63-1) / int64(time.Second)
|
||||
if t > maxRetryAfterSeconds {
|
||||
return time.Duration(1<<63 - 1)
|
||||
}
|
||||
return time.Duration(t) * time.Second
|
||||
}
|
||||
|
||||
if date, err := http.ParseTime(v); err == nil {
|
||||
return max(time.Until(date), 0)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e retryableError) Error() string {
|
||||
if e.err != nil {
|
||||
return fmt.Sprintf("retry-able request failure: %v", e.err.Error())
|
||||
@@ -395,5 +425,5 @@ func evaluate(err error) (bool, time.Duration) {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
return true, time.Duration(rErr.throttle)
|
||||
return true, rErr.throttle
|
||||
}
|
||||
+34
-16
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
package otlploghttp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@@ -23,11 +23,12 @@ import (
|
||||
|
||||
// Default values.
|
||||
var (
|
||||
defaultEndpoint = "localhost:4318"
|
||||
defaultPath = "/v1/logs"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultProxy HTTPTransportProxyFunc = http.ProxyFromEnvironment
|
||||
defaultRetryCfg = retry.DefaultConfig
|
||||
defaultEndpoint = "localhost:4318"
|
||||
defaultPath = "/v1/logs"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultMaxRequestSize = 64 * 1024 * 1024
|
||||
defaultProxy HTTPTransportProxyFunc = http.ProxyFromEnvironment
|
||||
defaultRetryCfg = retry.DefaultConfig
|
||||
)
|
||||
|
||||
// Environment variable keys.
|
||||
@@ -89,16 +90,17 @@ type fnOpt func(config) config
|
||||
func (f fnOpt) applyHTTPOption(c config) config { return f(c) }
|
||||
|
||||
type config struct {
|
||||
endpoint setting[string]
|
||||
path setting[string]
|
||||
insecure setting[bool]
|
||||
tlsCfg setting[*tls.Config]
|
||||
headers setting[map[string]string]
|
||||
compression setting[Compression]
|
||||
timeout setting[time.Duration]
|
||||
proxy setting[HTTPTransportProxyFunc]
|
||||
retryCfg setting[retry.Config]
|
||||
httpClient *http.Client
|
||||
endpoint setting[string]
|
||||
path setting[string]
|
||||
insecure setting[bool]
|
||||
tlsCfg setting[*tls.Config]
|
||||
headers setting[map[string]string]
|
||||
compression setting[Compression]
|
||||
maxRequestSize setting[int]
|
||||
timeout setting[time.Duration]
|
||||
proxy setting[HTTPTransportProxyFunc]
|
||||
retryCfg setting[retry.Config]
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func newConfig(options []Option) config {
|
||||
@@ -133,6 +135,9 @@ func newConfig(options []Option) config {
|
||||
getenv[time.Duration](envTimeout, convDuration),
|
||||
fallback[time.Duration](defaultTimeout),
|
||||
)
|
||||
c.maxRequestSize = c.maxRequestSize.Resolve(
|
||||
fallback[int](defaultMaxRequestSize),
|
||||
)
|
||||
c.proxy = c.proxy.Resolve(
|
||||
fallback[HTTPTransportProxyFunc](defaultProxy),
|
||||
)
|
||||
@@ -313,6 +318,19 @@ func WithTimeout(duration time.Duration) Option {
|
||||
})
|
||||
}
|
||||
|
||||
// WithMaxRequestSize sets the maximum size, in bytes, of a serialized export
|
||||
// request, before compression, that the exporter will send.
|
||||
//
|
||||
// If size is less than or equal to zero, no request-size limit is applied.
|
||||
// Disabling the limit is not recommended because it can lead to excessive
|
||||
// resource consumption or abuse.
|
||||
func WithMaxRequestSize(size int) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.maxRequestSize = newSetting(size)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying the export of log data that
|
||||
// failed.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
+1
-1
@@ -65,4 +65,4 @@ The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
|
||||
*/
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
package otlploghttp
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
package otlploghttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
// Package internal provides internal functionality for the otlploghttp
|
||||
// package.
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal"
|
||||
package internal
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp\" }" --out=x/x.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -3,4 +3,4 @@
|
||||
|
||||
// Package observ provides experimental observability instrumentation for the
|
||||
// otlploghttp exporter.
|
||||
package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/observ"
|
||||
package observ
|
||||
Generated
Vendored
+13
-10
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/observ"
|
||||
package observ
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/x"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.43.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -69,6 +69,7 @@ func get[T any](pool *sync.Pool) *[]T {
|
||||
}
|
||||
|
||||
func put[T any](pool *sync.Pool, value *[]T) {
|
||||
clear(*value) // erase elements to allow GC to collect what they refer to.
|
||||
*value = (*value)[:0]
|
||||
pool.Put(value)
|
||||
}
|
||||
@@ -268,11 +269,10 @@ func (e ExportOp) recordOption(err error, code int) metric.RecordOption {
|
||||
defer put(attrsPool, attrs)
|
||||
|
||||
*attrs = append(*attrs, e.inst.presetAttrs...)
|
||||
*attrs = append(
|
||||
*attrs,
|
||||
semconv.HTTPResponseStatusCode(code),
|
||||
semconv.ErrorType(err),
|
||||
)
|
||||
if code != 0 {
|
||||
*attrs = append(*attrs, semconv.HTTPResponseStatusCode(code))
|
||||
}
|
||||
*attrs = append(*attrs, semconv.ErrorType(err))
|
||||
return metric.WithAttributeSet(attribute.NewSet(*attrs...))
|
||||
}
|
||||
|
||||
@@ -305,7 +305,10 @@ var errPool = sync.Pool{
|
||||
// the provided non-nil err.
|
||||
func rejected(n int64, err error) int64 {
|
||||
ps := errPool.Get().(*internal.PartialSuccess)
|
||||
defer errPool.Put(ps)
|
||||
defer func() {
|
||||
*ps = internal.PartialSuccess{} // erase fields to allow GC to collect them.
|
||||
errPool.Put(ps)
|
||||
}()
|
||||
|
||||
if errors.As(err, ps) {
|
||||
// Bound RejectedItems to [0, n]. This should not be needed,
|
||||
@@ -316,7 +319,7 @@ func rejected(n int64, err error) int64 {
|
||||
return n
|
||||
}
|
||||
|
||||
// parseEndpoint parses the host and port from target that has the form
|
||||
// parseTarget parses the host and port from target that has the form
|
||||
// "host[:port]", or it returns an error if the target is not parsable.
|
||||
//
|
||||
// If no port is specified, -1 is returned.
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal"
|
||||
package internal
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
||||
Generated
Vendored
+4
-4
@@ -1,13 +1,13 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry"
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
Generated
Vendored
+28
-83
@@ -1,12 +1,12 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
||||
|
||||
// Package transform provides transformation functionality from the
|
||||
// sdk/log data-types into OTLP data-types.
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
|
||||
package transform
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -94,13 +94,13 @@ func LogRecord(record log.Record) *lpb.LogRecord {
|
||||
EventName: record.EventName(),
|
||||
SeverityNumber: SeverityNumber(record.Severity()),
|
||||
SeverityText: record.SeverityText(),
|
||||
Body: LogAttrValue(record.Body()),
|
||||
Body: AttrValue(record.Body()),
|
||||
Attributes: make([]*cpb.KeyValue, 0, record.AttributesLen()),
|
||||
Flags: uint32(record.TraceFlags()),
|
||||
// TODO: DroppedAttributesCount: /* ... */,
|
||||
}
|
||||
record.WalkAttributes(func(kv api.KeyValue) bool {
|
||||
r.Attributes = append(r.Attributes, LogAttr(kv))
|
||||
record.WalkAttributes(func(kv attribute.KeyValue) bool {
|
||||
r.Attributes = append(r.Attributes, Attr(kv))
|
||||
return true
|
||||
})
|
||||
if tID := record.TraceID(); tID.IsValid() {
|
||||
@@ -195,6 +195,22 @@ func AttrValue(v attribute.Value) *cpb.AnyValue {
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.BYTESLICE:
|
||||
av.Value = &cpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsByteSlice(),
|
||||
}
|
||||
case attribute.SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: attrValues(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.MAP:
|
||||
av.Value = &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: Attrs(v.AsMap()),
|
||||
},
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
@@ -258,83 +274,12 @@ func stringSliceValues(vals []string) []*cpb.AnyValue {
|
||||
return converted
|
||||
}
|
||||
|
||||
// LogAttrs transforms a slice of [api.KeyValue] into OTLP key-values.
|
||||
func LogAttrs(attrs []api.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
func attrValues(vals []attribute.Value) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = AttrValue(v)
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, LogAttr(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttr transforms an [api.KeyValue] into an OTLP key-value.
|
||||
func LogAttr(attr api.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{
|
||||
Key: attr.Key,
|
||||
Value: LogAttrValue(attr.Value),
|
||||
}
|
||||
}
|
||||
|
||||
// LogAttrValues transforms a slice of [api.Value] into an OTLP []AnyValue.
|
||||
func LogAttrValues(vals []api.Value) []*cpb.AnyValue {
|
||||
if len(vals) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.AnyValue, 0, len(vals))
|
||||
for _, v := range vals {
|
||||
out = append(out, LogAttrValue(v))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttrValue transforms an [api.Value] into an OTLP AnyValue.
|
||||
func LogAttrValue(v api.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Kind() {
|
||||
case api.KindBool:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case api.KindInt64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case api.KindFloat64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case api.KindString:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case api.KindBytes:
|
||||
av.Value = &cpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsBytes(),
|
||||
}
|
||||
case api.KindSlice:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: LogAttrValues(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case api.KindMap:
|
||||
av.Value = &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: LogAttrs(v.AsMap()),
|
||||
},
|
||||
}
|
||||
case api.KindEmpty:
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
return converted
|
||||
}
|
||||
|
||||
// SeverityNumber transforms a [log.Severity] into an OTLP SeverityNumber.
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal"
|
||||
package internal
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf logs exporter in use.
|
||||
const Version = "0.19.0"
|
||||
const Version = "0.21.0"
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package x // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/x"
|
||||
package x
|
||||
|
||||
import "strings"
|
||||
|
||||
|
||||
Generated
Vendored
+4
-4
@@ -1,11 +1,11 @@
|
||||
// Code generated by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp].
|
||||
package x // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/x"
|
||||
package x
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
package otlploghttp
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf logs exporter in use.
|
||||
func Version() string {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
package stdoutlog
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@
|
||||
// format for OpenTelemetry that is supported with any stability or
|
||||
// compatibility guarantees. If these are needed features, please use the OTLP
|
||||
// exporter instead.
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
package stdoutlog
|
||||
+19
-4
@@ -1,13 +1,16 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
package stdoutlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/counter"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/observ"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
)
|
||||
|
||||
@@ -18,6 +21,7 @@ var _ log.Exporter = &Exporter{}
|
||||
type Exporter struct {
|
||||
encoder atomic.Pointer[json.Encoder]
|
||||
timestamps bool
|
||||
inst *observ.Instrumentation
|
||||
}
|
||||
|
||||
// New creates an [Exporter].
|
||||
@@ -29,21 +33,31 @@ func New(options ...Option) (*Exporter, error) {
|
||||
enc.SetIndent("", "\t")
|
||||
}
|
||||
|
||||
e := Exporter{
|
||||
e := &Exporter{
|
||||
timestamps: cfg.Timestamps,
|
||||
}
|
||||
e.encoder.Store(enc)
|
||||
|
||||
return &e, nil
|
||||
var err error
|
||||
e.inst, err = observ.NewInstrumentation(counter.NextExporterID())
|
||||
return e, err
|
||||
}
|
||||
|
||||
// Export exports log records to writer.
|
||||
func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
|
||||
func (e *Exporter) Export(ctx context.Context, records []log.Record) (err error) {
|
||||
enc := e.encoder.Load()
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var success int64
|
||||
if e.inst != nil {
|
||||
op := e.inst.ExportLogs(ctx, int64(len(records)))
|
||||
defer func() {
|
||||
op.End(success, err)
|
||||
}()
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
// Honor context cancellation.
|
||||
if err := ctx.Err(); err != nil {
|
||||
@@ -55,6 +69,7 @@ func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
|
||||
if err := enc.Encode(recordJSON); err != nil {
|
||||
return err
|
||||
}
|
||||
success++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/counter/counter.go.tmpl
|
||||
|
||||
// Package counter provides a simple counter for generating unique IDs.
|
||||
//
|
||||
// This package is used to generate unique IDs while allowing testing packages
|
||||
// to reset the counter.
|
||||
package counter
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// exporterN is a global 0-based count of the number of exporters created.
|
||||
var exporterN atomic.Int64
|
||||
|
||||
// NextExporterID returns the next unique ID for an exporter.
|
||||
func NextExporterID() int64 {
|
||||
const inc = 1
|
||||
return exporterN.Add(inc) - inc
|
||||
}
|
||||
|
||||
// SetExporterID sets the exporter ID counter to v and returns the previous
|
||||
// value.
|
||||
//
|
||||
// This function is useful for testing purposes, allowing you to reset the
|
||||
// counter. It should not be used in production code.
|
||||
func SetExporterID(v int64) int64 {
|
||||
return exporterN.Swap(v)
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package internal provides internal functionality for the stdoutlog
|
||||
// package.
|
||||
package internal
|
||||
|
||||
//go:generate gotmpl --body=../../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/stdout/stdoutlog\" }" --out=x/x.go
|
||||
//go:generate gotmpl --body=../../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go
|
||||
//go:generate gotmpl --body=../../../../internal/shared/counter/counter.go.tmpl "--data={}" --out=counter/counter.go
|
||||
//go:generate gotmpl --body=../../../../internal/shared/counter/counter_test.go.tmpl "--data={}" --out=counter/counter_test.go
|
||||
Generated
Vendored
+230
@@ -0,0 +1,230 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package observ provides observability metrics for OTLP log exporters.
|
||||
// This is an experimental feature controlled by the x.Observability feature flag.
|
||||
package observ
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/x"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.43.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// ScopeName is the unique name of the meter used for instrumentation.
|
||||
ScopeName = "go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/observ"
|
||||
|
||||
// ComponentType uniquely identifies the OpenTelemetry Exporter component
|
||||
// being instrumented.
|
||||
//
|
||||
// The STDOUT log 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/stdoutlog.Exporter"
|
||||
|
||||
// Version is the current version of this instrumentation.
|
||||
//
|
||||
// This matches the version of the exporter.
|
||||
Version = internal.Version
|
||||
)
|
||||
|
||||
var (
|
||||
addOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1
|
||||
s := make([]metric.AddOption, 0, n)
|
||||
return &s
|
||||
},
|
||||
}
|
||||
attrsPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1 + // component.name
|
||||
1 + // component.type
|
||||
1 // error.type
|
||||
s := make([]attribute.KeyValue, 0, n)
|
||||
return &s
|
||||
},
|
||||
}
|
||||
recordOptPool = &sync.Pool{
|
||||
New: func() any {
|
||||
const n = 1
|
||||
s := make([]metric.RecordOption, 0, n)
|
||||
return &s
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func get[T any](pool *sync.Pool) *[]T {
|
||||
return pool.Get().(*[]T)
|
||||
}
|
||||
|
||||
func put[T any](pool *sync.Pool, value *[]T) {
|
||||
clear(*value) // erase elements to allow GC to collect what they refer to.
|
||||
*value = (*value)[:0]
|
||||
pool.Put(value)
|
||||
}
|
||||
|
||||
// Instrumentation is experimental instrumentation for the exporter.
|
||||
type Instrumentation struct {
|
||||
inflight metric.Int64UpDownCounter
|
||||
exported metric.Int64Counter
|
||||
duration metric.Float64Histogram
|
||||
|
||||
attrs []attribute.KeyValue
|
||||
setOpt metric.MeasurementOption
|
||||
}
|
||||
|
||||
// GetComponentName returns the constant name for the exporter with the
|
||||
// provided id.
|
||||
func GetComponentName(id int64) string {
|
||||
return fmt.Sprintf("%s/%d", ComponentType, id)
|
||||
}
|
||||
|
||||
func getAttrs(id int64) []attribute.KeyValue {
|
||||
attrs := make([]attribute.KeyValue, 0, 2)
|
||||
attrs = append(attrs,
|
||||
semconv.OTelComponentName(GetComponentName(id)),
|
||||
semconv.OTelComponentTypeKey.String(ComponentType))
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// NewInstrumentation returns instrumentation for stdlog exporter.
|
||||
func NewInstrumentation(id int64) (*Instrumentation, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
inst := &Instrumentation{}
|
||||
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter(
|
||||
ScopeName,
|
||||
metric.WithInstrumentationVersion(Version),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
|
||||
inflight, e := otelconv.NewSDKExporterLogInflight(m)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create the inflight metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
inst.inflight = inflight.Inst()
|
||||
|
||||
exported, e := otelconv.NewSDKExporterLogExported(m)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create the exported metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
inst.exported = exported.Inst()
|
||||
|
||||
duration, e := otelconv.NewSDKExporterOperationDuration(m)
|
||||
if e != nil {
|
||||
e = fmt.Errorf("failed to create the duration metric: %w", e)
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
inst.duration = duration.Inst()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inst.attrs = getAttrs(id)
|
||||
inst.setOpt = metric.WithAttributeSet(attribute.NewSet(inst.attrs...))
|
||||
return inst, nil
|
||||
}
|
||||
|
||||
// ExportLogs instruments the ExportLogs method of the exporter. It returns
|
||||
// an [ExportOp] that must have its [ExportOp.End] method called when the
|
||||
// ExportLogs method returns.
|
||||
func (i *Instrumentation) ExportLogs(ctx context.Context, count int64) ExportOp {
|
||||
start := time.Now()
|
||||
|
||||
if i.inflight.Enabled(ctx) {
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, i.setOpt)
|
||||
i.inflight.Add(ctx, count, *addOpt...)
|
||||
}
|
||||
|
||||
return ExportOp{
|
||||
count: count,
|
||||
ctx: ctx,
|
||||
inst: i,
|
||||
start: start,
|
||||
}
|
||||
}
|
||||
|
||||
// ExportOp tracks the operation being observed by [Instrumentation.ExportLogs].
|
||||
type ExportOp struct {
|
||||
count int64
|
||||
ctx context.Context
|
||||
inst *Instrumentation
|
||||
start time.Time
|
||||
}
|
||||
|
||||
// End completes the observation of the operation being observed by a call to
|
||||
// [Instrumentation.ExportLogs].
|
||||
// The success parameter is the number of logs exported successfully.
|
||||
// Any error encountered during export is provided as err.
|
||||
//
|
||||
// If err is not nil, End records failed log exports as count-success with the
|
||||
// error.type attribute set from err.
|
||||
func (e ExportOp) End(success int64, err error) {
|
||||
inflightLogsEnable := e.inst.inflight.Enabled(e.ctx)
|
||||
exportedLogsEnable := e.inst.exported.Enabled(e.ctx)
|
||||
opDurationEnable := e.inst.duration.Enabled(e.ctx)
|
||||
|
||||
if !inflightLogsEnable && !exportedLogsEnable && !opDurationEnable {
|
||||
return
|
||||
}
|
||||
|
||||
addOpt := get[metric.AddOption](addOptPool)
|
||||
defer put(addOptPool, addOpt)
|
||||
*addOpt = append(*addOpt, e.inst.setOpt)
|
||||
|
||||
if inflightLogsEnable {
|
||||
e.inst.inflight.Add(e.ctx, -e.count, *addOpt...)
|
||||
}
|
||||
|
||||
if exportedLogsEnable {
|
||||
e.inst.exported.Add(e.ctx, success, *addOpt...)
|
||||
}
|
||||
|
||||
mOpt := e.inst.setOpt
|
||||
if err != nil && (exportedLogsEnable || opDurationEnable) {
|
||||
// Add the error.type attribute to the attribute set.
|
||||
attrs := get[attribute.KeyValue](attrsPool)
|
||||
defer put(attrsPool, attrs)
|
||||
*attrs = append(*attrs, e.inst.attrs...)
|
||||
*attrs = append(*attrs, semconv.ErrorType(err))
|
||||
|
||||
mOpt = metric.WithAttributeSet(attribute.NewSet(*attrs...))
|
||||
|
||||
if exportedLogsEnable {
|
||||
*addOpt = append((*addOpt)[:0], mOpt)
|
||||
e.inst.exported.Add(e.ctx, e.count-success, *addOpt...)
|
||||
}
|
||||
}
|
||||
|
||||
if opDurationEnable {
|
||||
recordOpt := get[metric.RecordOption](recordOptPool)
|
||||
defer put(recordOptPool, recordOpt)
|
||||
|
||||
*recordOpt = append(*recordOpt, mOpt)
|
||||
e.inst.duration.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
|
||||
|
||||
// Version is the current release version of the OpenTelemetry stdoutlog
|
||||
// exporter in use.
|
||||
const Version = "v0.21.0"
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Experimental Features
|
||||
|
||||
The `stdoutlog` exporter contains features that have not yet stabilized in the OpenTelemetry specification.
|
||||
These features are added to the `stdoutlog` exporter prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
|
||||
|
||||
These features may change in backwards incompatible ways as feedback is applied.
|
||||
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
|
||||
|
||||
## Features
|
||||
|
||||
- [Observability](#observability)
|
||||
|
||||
### Observability
|
||||
|
||||
The `stdoutlog` exporter can be configured to provide observability about itself using OpenTelemetry metrics.
|
||||
|
||||
To opt-in, set the environment variable `OTEL_GO_X_OBSERVABILITY` to `true`.
|
||||
|
||||
When enabled, the exporter will create the following metrics using the global `MeterProvider`:
|
||||
|
||||
- `otel.sdk.exporter.log.inflight`
|
||||
- `otel.sdk.exporter.log.exported`
|
||||
- `otel.sdk.exporter.operation.duration`
|
||||
|
||||
Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics.
|
||||
|
||||
[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md
|
||||
|
||||
## Compatibility and Stability
|
||||
|
||||
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../../VERSIONING.md).
|
||||
These features may be removed or modified in successive version releases, including patch versions.
|
||||
|
||||
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
|
||||
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
|
||||
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.
|
||||
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/stdoutlog].
|
||||
package 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"},
|
||||
func(v string) (string, bool) {
|
||||
if strings.EqualFold(v, "true") {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
},
|
||||
)
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DO NOT MODIFY. Generated by gotmpl.
|
||||
// source: internal/shared/x/x.go.tmpl
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/stdout/stdoutlog].
|
||||
package x
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
keys []string
|
||||
parse func(v string) (T, bool)
|
||||
}
|
||||
|
||||
func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] {
|
||||
const envKeyRoot = "OTEL_GO_X_"
|
||||
keys := make([]string, 0, len(suffix))
|
||||
for _, s := range suffix {
|
||||
keys = append(keys, envKeyRoot+s)
|
||||
}
|
||||
return Feature[T]{
|
||||
keys: keys,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns the environment variable keys that can be set to enable the
|
||||
// feature.
|
||||
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
|
||||
// zero-value and false are returned.
|
||||
func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
|
||||
//
|
||||
// > The SDK MUST interpret an empty value of an environment variable the
|
||||
// > same way as when the variable is unset.
|
||||
for _, key := range f.keys {
|
||||
vRaw := os.Getenv(key)
|
||||
if vRaw != "" {
|
||||
return f.parse(vRaw)
|
||||
}
|
||||
}
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Enabled reports whether the feature is enabled.
|
||||
func (f Feature[T]) Enabled() bool {
|
||||
_, ok := f.Lookup()
|
||||
return ok
|
||||
}
|
||||
+8
-72
@@ -1,13 +1,12 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
package stdoutlog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
sdklog "go.opentelemetry.io/otel/sdk/log"
|
||||
@@ -15,66 +14,6 @@ import (
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func newValue(v log.Value) value {
|
||||
return value{Value: v}
|
||||
}
|
||||
|
||||
type value struct {
|
||||
log.Value
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshal function to encode log.Value.
|
||||
func (v value) MarshalJSON() ([]byte, error) {
|
||||
var jsonVal struct {
|
||||
Type string
|
||||
Value any
|
||||
}
|
||||
jsonVal.Type = v.Kind().String()
|
||||
|
||||
switch v.Kind() {
|
||||
case log.KindString:
|
||||
jsonVal.Value = v.AsString()
|
||||
case log.KindInt64:
|
||||
jsonVal.Value = v.AsInt64()
|
||||
case log.KindFloat64:
|
||||
jsonVal.Value = v.AsFloat64()
|
||||
case log.KindBool:
|
||||
jsonVal.Value = v.AsBool()
|
||||
case log.KindBytes:
|
||||
jsonVal.Value = v.AsBytes()
|
||||
case log.KindMap:
|
||||
m := v.AsMap()
|
||||
values := make([]keyValue, 0, len(m))
|
||||
for _, kv := range m {
|
||||
values = append(values, keyValue{
|
||||
Key: kv.Key,
|
||||
Value: newValue(kv.Value),
|
||||
})
|
||||
}
|
||||
|
||||
jsonVal.Value = values
|
||||
case log.KindSlice:
|
||||
s := v.AsSlice()
|
||||
values := make([]value, 0, len(s))
|
||||
for _, e := range s {
|
||||
values = append(values, newValue(e))
|
||||
}
|
||||
|
||||
jsonVal.Value = values
|
||||
case log.KindEmpty:
|
||||
jsonVal.Value = nil
|
||||
default:
|
||||
return nil, errors.New("invalid Kind")
|
||||
}
|
||||
|
||||
return json.Marshal(jsonVal)
|
||||
}
|
||||
|
||||
type keyValue struct {
|
||||
Key string
|
||||
Value value
|
||||
}
|
||||
|
||||
// recordJSON is a JSON-serializable representation of a Record.
|
||||
type recordJSON struct {
|
||||
Timestamp *time.Time `json:",omitempty"`
|
||||
@@ -82,8 +21,8 @@ type recordJSON struct {
|
||||
EventName string `json:",omitempty"`
|
||||
Severity log.Severity
|
||||
SeverityText string
|
||||
Body value
|
||||
Attributes []keyValue
|
||||
Body attribute.Value
|
||||
Attributes []attribute.KeyValue
|
||||
TraceID trace.TraceID
|
||||
SpanID trace.SpanID
|
||||
TraceFlags trace.TraceFlags
|
||||
@@ -98,13 +37,13 @@ func (e *Exporter) newRecordJSON(r sdklog.Record) recordJSON {
|
||||
EventName: r.EventName(),
|
||||
Severity: r.Severity(),
|
||||
SeverityText: r.SeverityText(),
|
||||
Body: newValue(r.Body()),
|
||||
Body: r.Body(),
|
||||
|
||||
TraceID: r.TraceID(),
|
||||
SpanID: r.SpanID(),
|
||||
TraceFlags: r.TraceFlags(),
|
||||
|
||||
Attributes: make([]keyValue, 0, r.AttributesLen()),
|
||||
Attributes: make([]attribute.KeyValue, 0, r.AttributesLen()),
|
||||
|
||||
Resource: res,
|
||||
Scope: r.InstrumentationScope(),
|
||||
@@ -112,11 +51,8 @@ func (e *Exporter) newRecordJSON(r sdklog.Record) recordJSON {
|
||||
DroppedAttributes: r.DroppedAttributes(),
|
||||
}
|
||||
|
||||
r.WalkAttributes(func(kv log.KeyValue) bool {
|
||||
newRecord.Attributes = append(newRecord.Attributes, keyValue{
|
||||
Key: kv.Key,
|
||||
Value: newValue(kv.Value),
|
||||
})
|
||||
r.WalkAttributes(func(kv attribute.KeyValue) bool {
|
||||
newRecord.Attributes = append(newRecord.Attributes, kv)
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otel // import "go.opentelemetry.io/otel"
|
||||
package otel
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ this need this package would not need to exist and the
|
||||
`go.opentelemetry.io/otel/baggage` package would be the singular place where
|
||||
W3C baggage is handled.
|
||||
*/
|
||||
package baggage // import "go.opentelemetry.io/otel/internal/baggage"
|
||||
package baggage
|
||||
|
||||
// List is the collection of baggage members. The W3C allows for duplicates,
|
||||
// but OpenTelemetry does not, therefore, this is represented as a map.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package baggage // import "go.opentelemetry.io/otel/internal/baggage"
|
||||
package baggage
|
||||
|
||||
import "context"
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// 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"
|
||||
package errorhandler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package global provides the OpenTelemetry global API.
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/internal/errorhandler"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package global // import "go.opentelemetry.io/otel/internal/global"
|
||||
package global
|
||||
|
||||
/*
|
||||
This file contains the forwarding implementation of the TracerProvider used as
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otel // import "go.opentelemetry.io/otel"
|
||||
package otel
|
||||
|
||||
import (
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
+41
-109
@@ -62,10 +62,10 @@ Implementation requirements:
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#concurrency-requirements)
|
||||
the method to be safe to be called concurrently.
|
||||
|
||||
- The method should use some default name if the passed name is empty
|
||||
in order to meet the [specification's SDK requirement](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logger-creation)
|
||||
to return a working logger when an invalid name is passed
|
||||
as well as to resemble the behavior of getting tracers and meters.
|
||||
- If the passed name is empty, this should be reported as invalid as specified
|
||||
by the [Logs SDK specification](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logger-creation).
|
||||
The method should still use it as the instrumentation scope name and return
|
||||
a working logger.
|
||||
|
||||
`Logger` can be extended by adding new `LoggerOption` options
|
||||
and adding new exported fields to the `LoggerConfig` struct.
|
||||
@@ -148,16 +148,16 @@ func (r *Record) SetSeverityText(s string)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Body() Value
|
||||
func (r *Record) SetBody(v Value)
|
||||
func (r *Record) Body() attribute.Value
|
||||
func (r *Record) SetBody(v attribute.Value)
|
||||
```
|
||||
|
||||
[Log record attributes](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes)
|
||||
are accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) WalkAttributes(f func(KeyValue) bool)
|
||||
func (r *Record) AddAttributes(attrs ...KeyValue)
|
||||
func (r *Record) WalkAttributes(f func(attribute.KeyValue) bool)
|
||||
func (r *Record) AddAttributes(attrs ...attribute.KeyValue)
|
||||
```
|
||||
|
||||
`Record` has a `AttributesLen` method that returns
|
||||
@@ -175,49 +175,20 @@ while keeping the API user friendly.
|
||||
It relieves the user from making his own improvements
|
||||
for reducing the number of allocations when passing attributes.
|
||||
|
||||
The abstractions described in
|
||||
[the specification](https://opentelemetry.io/docs/specs/otel/logs/#new-first-party-application-logs)
|
||||
are defined in [keyvalue.go](keyvalue.go).
|
||||
Log body and attributes use the common
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value)
|
||||
and
|
||||
[`attribute.KeyValue`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#KeyValue)
|
||||
types.
|
||||
These types cover the Logs Data Model `any` value shape, including empty
|
||||
values, byte slices, generic slices, and maps.
|
||||
Reusing the attribute package keeps the API consistent across signals and
|
||||
avoids maintaining a second value model with conversion helpers.
|
||||
|
||||
`Value` is representing `any`.
|
||||
`KeyValue` is representing a key(string)-value(`any`) pair.
|
||||
|
||||
`Kind` is an enumeration used for specifying the underlying value type.
|
||||
`KindEmpty` is used for an empty (zero) value.
|
||||
`KindBool` is used for boolean value.
|
||||
`KindFloat64` is used for a double precision floating point (IEEE 754-1985) value.
|
||||
`KindInt64` is used for a signed integer value.
|
||||
`KindString` is used for a string value.
|
||||
`KindBytes` is used for a slice of bytes (in spec: A byte array).
|
||||
`KindSlice` is used for a slice of values (in spec: an array (a list) of any values).
|
||||
`KindMap` is used for a slice of key-value pairs (in spec: `map<string, any>`).
|
||||
|
||||
These types are defined in `go.opentelemetry.io/otel/log` package
|
||||
as they are tightly coupled with the API and different from common attributes.
|
||||
|
||||
The internal implementation of `Value` is based on
|
||||
[`slog.Value`](https://pkg.go.dev/log/slog#Value)
|
||||
and the API is mostly inspired by
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value).
|
||||
The benchmarks[^1] show that the implementation is more performant than
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value).
|
||||
|
||||
The value accessors (`func (v Value) As[Kind]` methods) must not panic,
|
||||
as it would violate the [specification](https://opentelemetry.io/docs/specs/otel/error-handling/):
|
||||
|
||||
> API methods MUST NOT throw unhandled exceptions when used incorrectly by end
|
||||
> users. The API and SDK SHOULD provide safe defaults for missing or invalid
|
||||
> arguments. [...] Whenever the library suppresses an error that would otherwise
|
||||
> have been exposed to the user, the library SHOULD log the error using
|
||||
> language-specific conventions.
|
||||
|
||||
Therefore, the value accessors should return a zero value
|
||||
and log an error when a bad accessor is called.
|
||||
|
||||
The `Severity`, `Kind`, `Value`, `KeyValue` may implement
|
||||
the [`fmt.Stringer`](https://pkg.go.dev/fmt#Stringer) interface.
|
||||
However, it is not needed for the first stable release
|
||||
and the `String` methods can be added later.
|
||||
The zero value of `attribute.Value` represents an empty body.
|
||||
Log maps use `attribute.MAP`, which may contain duplicate keys when callers
|
||||
construct one that way. Duplicate-key normalization is an SDK policy, not an
|
||||
API behavior.
|
||||
|
||||
The caller must not subsequently mutate the record passed to `Emit`.
|
||||
This would allow the implementation to not clone the record,
|
||||
@@ -252,12 +223,11 @@ Rejected alternatives:
|
||||
- [Passing record as pointer to Logger.Emit](#passing-record-as-pointer-to-loggeremit)
|
||||
- [Logger.WithAttributes](#loggerwithattributes)
|
||||
- [Record attributes as slice](#record-attributes-as-slice)
|
||||
- [Use any instead of defining Value](#use-any-instead-of-defining-value)
|
||||
- [Use any instead of attribute.Value](#use-any-instead-of-attributevalue)
|
||||
- [Severity type encapsulating number and text](#severity-type-encapsulating-number-and-text)
|
||||
- [Reuse attribute package](#reuse-attribute-package)
|
||||
- [Define log-specific value types](#define-log-specific-value-types)
|
||||
- [Mix receiver types for Record](#mix-receiver-types-for-record)
|
||||
- [Add XYZ method to Logger](#add-xyz-method-to-logger)
|
||||
- [Rename KeyValue to Attr](#rename-keyvalue-to-attr)
|
||||
|
||||
### Logger.Enabled
|
||||
|
||||
@@ -476,30 +446,21 @@ less user friendly (users and bridges would use e.g. a `sync.Pool` to reduce
|
||||
the number of heap allocation), less safe (more prone to use after free bugs
|
||||
and race conditions), and the benchmark differences were not significant.
|
||||
|
||||
### Use any instead of defining Value
|
||||
### Use any instead of attribute.Value
|
||||
|
||||
[Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
defines Body to be `any`.
|
||||
One could propose to define `Body` (and attribute values) as `any`
|
||||
instead of a defining a new type (`Value`).
|
||||
instead of using `attribute.Value`.
|
||||
|
||||
First of all, [`any` type defined in the specification](https://opentelemetry.io/docs/specs/otel/logs/data-model/#type-any)
|
||||
is not the same as `any` (`interface{}`) in Go.
|
||||
|
||||
Moreover, using `any` as a field would decrease the performance.[^7]
|
||||
|
||||
Notice it will be still possible to add following kind and factories
|
||||
in a backwards compatible way:
|
||||
|
||||
```go
|
||||
const KindMap Kind
|
||||
|
||||
func AnyValue(value any) KeyValue
|
||||
|
||||
func Any(key string, value any) KeyValue
|
||||
```
|
||||
|
||||
However, currently, it would not be specification compliant.
|
||||
Using `attribute.Value` preserves a typed, allocation-conscious representation
|
||||
of the Logs Data Model `any` values while avoiding unconstrained `interface{}`
|
||||
handling in bridge implementations.
|
||||
|
||||
### Severity type encapsulating number and text
|
||||
|
||||
@@ -518,20 +479,22 @@ It should be more user friendly to have them separated.
|
||||
Especially when having getter and setter methods, setting one value
|
||||
when the other is already set would be unpleasant.
|
||||
|
||||
### Reuse attribute package
|
||||
### Define log-specific value types
|
||||
|
||||
It was tempting to reuse the existing
|
||||
[https://pkg.go.dev/go.opentelemetry.io/otel/attribute] package
|
||||
for defining log attributes and body.
|
||||
The original design defined `Kind`, `Value`, and `KeyValue` in
|
||||
`go.opentelemetry.io/otel/log`.
|
||||
That avoided coupling log bodies to the common attribute package while logs were
|
||||
still exploring structured value support.
|
||||
|
||||
However, this would be wrong because [the log attribute definition](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes)
|
||||
is different from [the common attribute definition](https://opentelemetry.io/docs/specs/otel/common/#attribute).
|
||||
The Logs Data Model and common attribute value model now share the structured
|
||||
`any` shapes needed by Go: empty, bool, int64, float64, string, byte slice,
|
||||
homogeneous slices, generic slices, and maps.
|
||||
The specification direction is to reuse these value shapes across signals.
|
||||
Keeping log-specific types would duplicate API surface, require conversion
|
||||
helpers, and make bridge code choose between two equivalent value models.
|
||||
|
||||
Moreover, it there is nothing telling that [the body definition](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
has anything in common with a common attribute value.
|
||||
|
||||
Therefore, we define new types representing the abstract types defined
|
||||
in the [Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#definitions-used-in-this-document).
|
||||
Therefore log records now use `attribute.Value` for body values and
|
||||
`attribute.KeyValue` for attributes and map entries.
|
||||
|
||||
### Mix receiver types for Record
|
||||
|
||||
@@ -591,36 +554,6 @@ The `Logger` does not have methods like `SetSeverity`, etc.
|
||||
as the Logs API needs to follow (be compliant with)
|
||||
the [specification](https://opentelemetry.io/docs/specs/otel/logs/api/)
|
||||
|
||||
### Rename KeyValue to Attr
|
||||
|
||||
There was a proposal to rename `KeyValue` to `Attr` (or `Attribute`).[^11]
|
||||
New developers may not intuitively know that `log.KeyValue` is an attribute in
|
||||
the OpenTelemetry parlance.
|
||||
|
||||
During the discussion we agreed to keep the `KeyValue` name.
|
||||
|
||||
The type is used in multiple semantics:
|
||||
|
||||
- as a log attribute,
|
||||
- as a map item,
|
||||
- as a log record Body.
|
||||
|
||||
As for map item semantics, this type is a key-value pair, not an attribute.
|
||||
Naming the type as `Attr` would convey semantical meaning
|
||||
that would not be correct for a map.
|
||||
|
||||
We expect that most of the Logs API users will be OpenTelemetry contributors.
|
||||
We plan to implement bridges for the most popular logging libraries ourselves.
|
||||
Given we will all have the context needed to disambiguate these overlapping
|
||||
names, developers' confusion should not be an issue.
|
||||
|
||||
For bridges not developed by us,
|
||||
developers will likely look at our existing bridges for inspiration.
|
||||
Our correct use of these types will be a reference to them.
|
||||
|
||||
At last, we provide `ValueFromAttribute` and `KeyValueFromAttribute`
|
||||
to offer reuse of `attribute.Value` and `attribute.KeyValue`.
|
||||
|
||||
[^1]: [Handle structured body and attributes](https://github.com/pellared/opentelemetry-go/pull/7)
|
||||
[^2]: Jonathan Amsterdam, [The Go Blog: Structured Logging with slog](https://go.dev/blog/slog)
|
||||
[^3]: Jonathan Amsterdam, [GopherCon Europe 2023: A Fast Structured Logging Package](https://www.youtube.com/watch?v=tC4Jt3i62ns)
|
||||
@@ -631,4 +564,3 @@ to offer reuse of `attribute.Value` and `attribute.KeyValue`.
|
||||
[^8]: [log/slog: structured, leveled logging](https://github.com/golang/go/issues/56345#issuecomment-1302563756)
|
||||
[^9]: [Record with pointer receivers only](https://github.com/pellared/opentelemetry-go/pull/8)
|
||||
[^10]: [Go FAQ: Stack or heap](https://go.dev/doc/faq#stack_or_heap)
|
||||
[^11]: [Rename KeyValue to Attr discussion](https://github.com/open-telemetry/opentelemetry-go/pull/4809#discussion_r1476080093)
|
||||
+1
-1
@@ -82,4 +82,4 @@ implement all the API interfaces when a user updates their API.
|
||||
|
||||
[registry]: https://opentelemetry.io/ecosystem/registry/?language=go&component=log-bridge
|
||||
*/
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
package log
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
// bump of the API package).
|
||||
//
|
||||
// [OpenTelemetry Logs API]: https://pkg.go.dev/go.opentelemetry.io/otel/log
|
||||
package embedded // import "go.opentelemetry.io/otel/log/embedded"
|
||||
package embedded
|
||||
|
||||
// LoggerProvider is embedded in the [Logs API LoggerProvider].
|
||||
//
|
||||
|
||||
-443
@@ -1,443 +0,0 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:generate stringer -type=Kind -trimprefix=Kind
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// errKind is logged when a Value is decoded to an incompatible type.
|
||||
var errKind = errors.New("invalid Kind")
|
||||
|
||||
// Kind is the kind of a [Value].
|
||||
type Kind int
|
||||
|
||||
// Kind values.
|
||||
const (
|
||||
KindEmpty Kind = iota
|
||||
KindBool
|
||||
KindFloat64
|
||||
KindInt64
|
||||
KindString
|
||||
KindBytes
|
||||
KindSlice
|
||||
KindMap
|
||||
)
|
||||
|
||||
// A Value represents a structured log value.
|
||||
// A zero value is valid and represents an empty value.
|
||||
type Value struct {
|
||||
// Ensure forward compatibility by explicitly making this not comparable.
|
||||
noCmp [0]func() //nolint: unused // This is indeed used.
|
||||
|
||||
// num holds the value for Int64, Float64, and Bool. It holds the length
|
||||
// for String, Bytes, Slice, Map.
|
||||
num uint64
|
||||
// any holds either the KindBool, KindInt64, KindFloat64, stringptr,
|
||||
// bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64
|
||||
// then the value of Value is in num as described above. Otherwise, it
|
||||
// contains the value wrapped in the appropriate type.
|
||||
any any
|
||||
}
|
||||
|
||||
type (
|
||||
// sliceptr represents a value in Value.any for KindString Values.
|
||||
stringptr *byte
|
||||
// bytesptr represents a value in Value.any for KindBytes Values.
|
||||
bytesptr *byte
|
||||
// sliceptr represents a value in Value.any for KindSlice Values.
|
||||
sliceptr *Value
|
||||
// mapptr represents a value in Value.any for KindMap Values.
|
||||
mapptr *KeyValue
|
||||
)
|
||||
|
||||
// StringValue returns a new [Value] for a string.
|
||||
func StringValue(v string) Value {
|
||||
return Value{
|
||||
num: uint64(len(v)),
|
||||
any: stringptr(unsafe.StringData(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// IntValue returns a [Value] for an int.
|
||||
func IntValue(v int) Value { return Int64Value(int64(v)) }
|
||||
|
||||
// Int64Value returns a [Value] for an int64.
|
||||
func Int64Value(v int64) Value {
|
||||
// This can be later converted back to int64 (overflow not checked).
|
||||
return Value{num: uint64(v), any: KindInt64} // nolint:gosec
|
||||
}
|
||||
|
||||
// Float64Value returns a [Value] for a float64.
|
||||
func Float64Value(v float64) Value {
|
||||
return Value{num: math.Float64bits(v), any: KindFloat64}
|
||||
}
|
||||
|
||||
// BoolValue returns a [Value] for a bool.
|
||||
func BoolValue(v bool) Value { //nolint:revive // Not a control flag.
|
||||
var n uint64
|
||||
if v {
|
||||
n = 1
|
||||
}
|
||||
return Value{num: n, any: KindBool}
|
||||
}
|
||||
|
||||
// BytesValue returns a [Value] for a byte slice. The passed slice must not be
|
||||
// changed after it is passed.
|
||||
func BytesValue(v []byte) Value {
|
||||
return Value{
|
||||
num: uint64(len(v)),
|
||||
any: bytesptr(unsafe.SliceData(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// SliceValue returns a [Value] for a slice of [Value]. The passed slice must
|
||||
// not be changed after it is passed.
|
||||
func SliceValue(vs ...Value) Value {
|
||||
return Value{
|
||||
num: uint64(len(vs)),
|
||||
any: sliceptr(unsafe.SliceData(vs)),
|
||||
}
|
||||
}
|
||||
|
||||
// MapValue returns a new [Value] for a slice of key-value pairs. The passed
|
||||
// slice must not be changed after it is passed.
|
||||
func MapValue(kvs ...KeyValue) Value {
|
||||
return Value{
|
||||
num: uint64(len(kvs)),
|
||||
any: mapptr(unsafe.SliceData(kvs)),
|
||||
}
|
||||
}
|
||||
|
||||
// AsString returns the value held by v as a string.
|
||||
func (v Value) AsString() string {
|
||||
if sp, ok := v.any.(stringptr); ok {
|
||||
return unsafe.String(sp, v.num)
|
||||
}
|
||||
global.Error(errKind, "AsString", "Kind", v.Kind())
|
||||
return ""
|
||||
}
|
||||
|
||||
// asString returns the value held by v as a string. It will panic if the Value
|
||||
// is not KindString.
|
||||
func (v Value) asString() string {
|
||||
return unsafe.String(v.any.(stringptr), v.num)
|
||||
}
|
||||
|
||||
// AsInt64 returns the value held by v as an int64.
|
||||
func (v Value) AsInt64() int64 {
|
||||
if v.Kind() != KindInt64 {
|
||||
global.Error(errKind, "AsInt64", "Kind", v.Kind())
|
||||
return 0
|
||||
}
|
||||
return v.asInt64()
|
||||
}
|
||||
|
||||
// asInt64 returns the value held by v as an int64. If v is not of KindInt64,
|
||||
// this will return garbage.
|
||||
func (v Value) asInt64() int64 {
|
||||
// Assumes v.num was a valid int64 (overflow not checked).
|
||||
return int64(v.num) // nolint: gosec
|
||||
}
|
||||
|
||||
// AsBool returns the value held by v as a bool.
|
||||
func (v Value) AsBool() bool {
|
||||
if v.Kind() != KindBool {
|
||||
global.Error(errKind, "AsBool", "Kind", v.Kind())
|
||||
return false
|
||||
}
|
||||
return v.asBool()
|
||||
}
|
||||
|
||||
// asBool returns the value held by v as a bool. If v is not of KindBool, this
|
||||
// will return garbage.
|
||||
func (v Value) asBool() bool { return v.num == 1 }
|
||||
|
||||
// AsFloat64 returns the value held by v as a float64.
|
||||
func (v Value) AsFloat64() float64 {
|
||||
if v.Kind() != KindFloat64 {
|
||||
global.Error(errKind, "AsFloat64", "Kind", v.Kind())
|
||||
return 0
|
||||
}
|
||||
return v.asFloat64()
|
||||
}
|
||||
|
||||
// asFloat64 returns the value held by v as a float64. If v is not of
|
||||
// KindFloat64, this will return garbage.
|
||||
func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) }
|
||||
|
||||
// AsBytes returns the value held by v as a []byte.
|
||||
func (v Value) AsBytes() []byte {
|
||||
if sp, ok := v.any.(bytesptr); ok {
|
||||
return unsafe.Slice((*byte)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsBytes", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asBytes returns the value held by v as a []byte. It will panic if the Value
|
||||
// is not KindBytes.
|
||||
func (v Value) asBytes() []byte {
|
||||
return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num)
|
||||
}
|
||||
|
||||
// AsSlice returns the value held by v as a []Value.
|
||||
func (v Value) AsSlice() []Value {
|
||||
if sp, ok := v.any.(sliceptr); ok {
|
||||
return unsafe.Slice((*Value)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsSlice", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asSlice returns the value held by v as a []Value. It will panic if the Value
|
||||
// is not KindSlice.
|
||||
func (v Value) asSlice() []Value {
|
||||
return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num)
|
||||
}
|
||||
|
||||
// AsMap returns the value held by v as a []KeyValue.
|
||||
func (v Value) AsMap() []KeyValue {
|
||||
if sp, ok := v.any.(mapptr); ok {
|
||||
return unsafe.Slice((*KeyValue)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsMap", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asMap returns the value held by v as a []KeyValue. It will panic if the
|
||||
// Value is not KindMap.
|
||||
func (v Value) asMap() []KeyValue {
|
||||
return unsafe.Slice((*KeyValue)(v.any.(mapptr)), v.num)
|
||||
}
|
||||
|
||||
// Kind returns the Kind of v.
|
||||
func (v Value) Kind() Kind {
|
||||
switch x := v.any.(type) {
|
||||
case Kind:
|
||||
return x
|
||||
case stringptr:
|
||||
return KindString
|
||||
case bytesptr:
|
||||
return KindBytes
|
||||
case sliceptr:
|
||||
return KindSlice
|
||||
case mapptr:
|
||||
return KindMap
|
||||
default:
|
||||
return KindEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// Empty reports whether v does not hold any value.
|
||||
func (v Value) Empty() bool { return v.Kind() == KindEmpty }
|
||||
|
||||
// Equal reports whether v is equal to w.
|
||||
func (v Value) Equal(w Value) bool {
|
||||
k1 := v.Kind()
|
||||
k2 := w.Kind()
|
||||
if k1 != k2 {
|
||||
return false
|
||||
}
|
||||
switch k1 {
|
||||
case KindInt64, KindBool:
|
||||
return v.num == w.num
|
||||
case KindString:
|
||||
return v.asString() == w.asString()
|
||||
case KindFloat64:
|
||||
return v.asFloat64() == w.asFloat64()
|
||||
case KindSlice:
|
||||
return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal)
|
||||
case KindMap:
|
||||
sv := sortMap(v.asMap())
|
||||
sw := sortMap(w.asMap())
|
||||
return slices.EqualFunc(sv, sw, KeyValue.Equal)
|
||||
case KindBytes:
|
||||
return bytes.Equal(v.asBytes(), w.asBytes())
|
||||
case KindEmpty:
|
||||
return true
|
||||
default:
|
||||
global.Error(errKind, "Equal", "Kind", k1)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func sortMap(m []KeyValue) []KeyValue {
|
||||
sm := make([]KeyValue, len(m))
|
||||
copy(sm, m)
|
||||
slices.SortFunc(sm, func(a, b KeyValue) int {
|
||||
return cmp.Compare(a.Key, b.Key)
|
||||
})
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
// String returns Value's value as a string, formatted like [fmt.Sprint].
|
||||
//
|
||||
// The returned string is meant for debugging;
|
||||
// the string representation is not stable.
|
||||
func (v Value) String() string {
|
||||
switch v.Kind() {
|
||||
case KindString:
|
||||
return v.asString()
|
||||
case KindInt64:
|
||||
// Assumes v.num was a valid int64 (overflow not checked).
|
||||
return strconv.FormatInt(int64(v.num), 10) // nolint: gosec
|
||||
case KindFloat64:
|
||||
return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64)
|
||||
case KindBool:
|
||||
return strconv.FormatBool(v.asBool())
|
||||
case KindBytes:
|
||||
return fmt.Sprint(v.asBytes()) // nolint:staticcheck // Use fmt.Sprint to encode as slice.
|
||||
case KindMap:
|
||||
return fmt.Sprint(v.asMap())
|
||||
case KindSlice:
|
||||
return fmt.Sprint(v.asSlice())
|
||||
case KindEmpty:
|
||||
return "<nil>"
|
||||
default:
|
||||
// Try to handle this as gracefully as possible.
|
||||
//
|
||||
// Don't panic here. The goal here is to have developers find this
|
||||
// first if a slog.Kind is is not handled. It is
|
||||
// preferable to have user's open issue asking why their attributes
|
||||
// have a "unhandled: " prefix than say that their code is panicking.
|
||||
return fmt.Sprintf("<unhandled log.Kind: %s>", v.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
// A KeyValue is a key-value pair used to represent a log attribute (a
|
||||
// superset of [go.opentelemetry.io/otel/attribute.KeyValue]) and map item.
|
||||
type KeyValue struct {
|
||||
Key string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// Equal reports whether a is equal to b.
|
||||
func (a KeyValue) Equal(b KeyValue) bool {
|
||||
return a.Key == b.Key && a.Value.Equal(b.Value)
|
||||
}
|
||||
|
||||
// String returns a KeyValue for a string value.
|
||||
func String(key, value string) KeyValue {
|
||||
return KeyValue{key, StringValue(value)}
|
||||
}
|
||||
|
||||
// Int64 returns a KeyValue for an int64 value.
|
||||
func Int64(key string, value int64) KeyValue {
|
||||
return KeyValue{key, Int64Value(value)}
|
||||
}
|
||||
|
||||
// Int returns a KeyValue for an int value.
|
||||
func Int(key string, value int) KeyValue {
|
||||
return KeyValue{key, IntValue(value)}
|
||||
}
|
||||
|
||||
// Float64 returns a KeyValue for a float64 value.
|
||||
func Float64(key string, value float64) KeyValue {
|
||||
return KeyValue{key, Float64Value(value)}
|
||||
}
|
||||
|
||||
// Bool returns a KeyValue for a bool value.
|
||||
func Bool(key string, value bool) KeyValue {
|
||||
return KeyValue{key, BoolValue(value)}
|
||||
}
|
||||
|
||||
// Bytes returns a KeyValue for a []byte value.
|
||||
// The passed slice must not be changed after it is passed.
|
||||
func Bytes(key string, value []byte) KeyValue {
|
||||
return KeyValue{key, BytesValue(value)}
|
||||
}
|
||||
|
||||
// Slice returns a KeyValue for a []Value value.
|
||||
// The passed slice must not be changed after it is passed.
|
||||
func Slice(key string, value ...Value) KeyValue {
|
||||
return KeyValue{key, SliceValue(value...)}
|
||||
}
|
||||
|
||||
// Map returns a KeyValue for a map value.
|
||||
// The passed slice must not be changed after it is passed.
|
||||
func Map(key string, value ...KeyValue) KeyValue {
|
||||
return KeyValue{key, MapValue(value...)}
|
||||
}
|
||||
|
||||
// Empty returns a KeyValue with an empty value.
|
||||
func Empty(key string) KeyValue {
|
||||
return KeyValue{key, Value{}}
|
||||
}
|
||||
|
||||
// String returns key-value pair as a string, formatted like "key:value".
|
||||
//
|
||||
// The returned string is meant for debugging;
|
||||
// the string representation is not stable.
|
||||
func (a KeyValue) String() string {
|
||||
return fmt.Sprintf("%s:%s", a.Key, a.Value)
|
||||
}
|
||||
|
||||
// ValueFromAttribute converts [attribute.Value] to [Value].
|
||||
func ValueFromAttribute(value attribute.Value) Value {
|
||||
switch value.Type() {
|
||||
case attribute.EMPTY:
|
||||
return Value{}
|
||||
case attribute.BOOL:
|
||||
return BoolValue(value.AsBool())
|
||||
case attribute.BOOLSLICE:
|
||||
val := value.AsBoolSlice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, BoolValue(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
case attribute.INT64:
|
||||
return Int64Value(value.AsInt64())
|
||||
case attribute.INT64SLICE:
|
||||
val := value.AsInt64Slice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, Int64Value(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
case attribute.FLOAT64:
|
||||
return Float64Value(value.AsFloat64())
|
||||
case attribute.FLOAT64SLICE:
|
||||
val := value.AsFloat64Slice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, Float64Value(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
case attribute.STRING:
|
||||
return StringValue(value.AsString())
|
||||
case attribute.STRINGSLICE:
|
||||
val := value.AsStringSlice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, StringValue(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
}
|
||||
// This code should never be reached
|
||||
// as log attributes are a superset of standard attributes.
|
||||
panic("unknown attribute type")
|
||||
}
|
||||
|
||||
// KeyValueFromAttribute converts [attribute.KeyValue] to [KeyValue].
|
||||
func KeyValueFromAttribute(kv attribute.KeyValue) KeyValue {
|
||||
return KeyValue{
|
||||
Key: string(kv.Key),
|
||||
Value: ValueFromAttribute(kv.Value),
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
// Code generated by "stringer -type=Kind -trimprefix=Kind"; DO NOT EDIT.
|
||||
|
||||
package log
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[KindEmpty-0]
|
||||
_ = x[KindBool-1]
|
||||
_ = x[KindFloat64-2]
|
||||
_ = x[KindInt64-3]
|
||||
_ = x[KindString-4]
|
||||
_ = x[KindBytes-5]
|
||||
_ = x[KindSlice-6]
|
||||
_ = x[KindMap-7]
|
||||
}
|
||||
|
||||
const _Kind_name = "EmptyBoolFloat64Int64StringBytesSliceMap"
|
||||
|
||||
var _Kind_index = [...]uint8{0, 5, 9, 16, 21, 27, 32, 37, 40}
|
||||
|
||||
func (i Kind) String() string {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_Kind_index)-1 {
|
||||
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Kind_name[_Kind_index[idx]:_Kind_index[idx+1]]
|
||||
}
|
||||
+10
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -36,7 +36,8 @@ type Logger interface {
|
||||
//
|
||||
// This is useful for users that want to know if a [Record]
|
||||
// will be processed or dropped before they perform complex operations to
|
||||
// construct the [Record].
|
||||
// construct the [Record]. Callers should invoke Enabled before each call
|
||||
// to [Logger.Emit] because the enabled state may change over time.
|
||||
//
|
||||
// The passed param is likely to be a partial record information being
|
||||
// provided (e.g a param with only the Severity set).
|
||||
@@ -74,10 +75,17 @@ type LoggerConfig struct {
|
||||
attrs attribute.Set
|
||||
}
|
||||
|
||||
type experimentalOption interface {
|
||||
Experimental()
|
||||
}
|
||||
|
||||
// NewLoggerConfig returns a new [LoggerConfig] with all the options applied.
|
||||
func NewLoggerConfig(options ...LoggerOption) LoggerConfig {
|
||||
var c LoggerConfig
|
||||
for _, opt := range options {
|
||||
if _, ok := opt.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
c = opt.applyLogger(c)
|
||||
}
|
||||
return c
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
// defaults to no operation for methods it does not implement.
|
||||
//
|
||||
// [OpenTelemetry Logs API]: https://pkg.go.dev/go.opentelemetry.io/otel/log
|
||||
package noop // import "go.opentelemetry.io/otel/log/noop"
|
||||
package noop
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
package log
|
||||
|
||||
import "go.opentelemetry.io/otel/log/embedded"
|
||||
|
||||
@@ -24,7 +24,8 @@ type LoggerProvider interface {
|
||||
// commonly, this means a bridge will need to accept this value from its
|
||||
// users.
|
||||
//
|
||||
// If name is empty, implementations need to provide a default name.
|
||||
// An empty name is invalid. Implementations should retain the empty value as the
|
||||
// instrumentation scope name, return a working Logger, and report the invalid value.
|
||||
//
|
||||
// The version of the packages using a bridge can be critical information
|
||||
// to include when logging. The bridge should accept this version
|
||||
|
||||
+12
-9
@@ -1,11 +1,13 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
package log
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// attributesInlineCount is the number of attributes that are efficiently
|
||||
@@ -25,7 +27,7 @@ type Record struct {
|
||||
observedTimestamp time.Time
|
||||
severity Severity
|
||||
severityText string
|
||||
body Value
|
||||
body attribute.Value
|
||||
err error
|
||||
|
||||
// The fields below are for optimizing the implementation of Attributes and
|
||||
@@ -35,7 +37,7 @@ type Record struct {
|
||||
// Allocation optimization: an inline array sized to hold
|
||||
// the majority of log calls (based on examination of open-source
|
||||
// code). It holds the start of the list of attributes.
|
||||
front [attributesInlineCount]KeyValue
|
||||
front [attributesInlineCount]attribute.KeyValue
|
||||
|
||||
// The number of attributes in front.
|
||||
nFront int
|
||||
@@ -44,7 +46,7 @@ type Record struct {
|
||||
// Invariants:
|
||||
// - len(back) > 0 if nFront == len(front)
|
||||
// - Unused array elements are zero-ed. Used to detect mistakes.
|
||||
back []KeyValue
|
||||
back []attribute.KeyValue
|
||||
}
|
||||
|
||||
// EventName returns the event name.
|
||||
@@ -55,6 +57,7 @@ func (r *Record) EventName() string {
|
||||
|
||||
// SetEventName sets the event name.
|
||||
// A log record with non-empty event name is interpreted as an event record.
|
||||
// Event names should uniquely identify the event's attribute and body structure.
|
||||
func (r *Record) SetEventName(s string) {
|
||||
r.eventName = s
|
||||
}
|
||||
@@ -102,12 +105,12 @@ func (r *Record) SetSeverityText(text string) {
|
||||
}
|
||||
|
||||
// Body returns the body of the log record.
|
||||
func (r *Record) Body() Value {
|
||||
func (r *Record) Body() attribute.Value {
|
||||
return r.body
|
||||
}
|
||||
|
||||
// SetBody sets the body of the log record.
|
||||
func (r *Record) SetBody(v Value) {
|
||||
func (r *Record) SetBody(v attribute.Value) {
|
||||
r.body = v
|
||||
}
|
||||
|
||||
@@ -122,8 +125,8 @@ func (r *Record) SetErr(err error) {
|
||||
}
|
||||
|
||||
// WalkAttributes walks all attributes the log record holds by calling f for
|
||||
// each on each [KeyValue] in the [Record]. Iteration stops if f returns false.
|
||||
func (r *Record) WalkAttributes(f func(KeyValue) bool) {
|
||||
// each on each [attribute.KeyValue] in the [Record]. Iteration stops if f returns false.
|
||||
func (r *Record) WalkAttributes(f func(attribute.KeyValue) bool) {
|
||||
for i := 0; i < r.nFront; i++ {
|
||||
if !f(r.front[i]) {
|
||||
return
|
||||
@@ -137,7 +140,7 @@ func (r *Record) WalkAttributes(f func(KeyValue) bool) {
|
||||
}
|
||||
|
||||
// AddAttributes adds attributes to the log record.
|
||||
func (r *Record) AddAttributes(attrs ...KeyValue) {
|
||||
func (r *Record) AddAttributes(attrs ...attribute.KeyValue) {
|
||||
var i int
|
||||
for i = 0; i < len(attrs) && r.nFront < len(r.front); i++ {
|
||||
a := attrs[i]
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
//go:generate stringer -type=Severity -linecomment
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
package log
|
||||
|
||||
// Severity represents a log record severity (also known as log level). Smaller
|
||||
// numerical values correspond to less severe log records (such as debug
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otel // import "go.opentelemetry.io/otel"
|
||||
package otel
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
|
||||
Loaded 100 of 324 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user