chore(deps): bump oltp version (#4968)

This commit is contained in:
Milos Gajdos authored and GitHub committed 2026-09-22 07:18:23 -07:00
commit 8e7f698a2a
324 files changed
+20943 -2957

No files matched your search

+15 -15
View File
@@ -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
)
+34 -34
View File
@@ -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
View File
@@ -1,5 +1,4 @@
//go:build !go1.21
// +build !go1.21
/*
Copyright 2019 The logr Authors.
-1
View File
@@ -1,5 +1,4 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2019 The logr Authors.
+36 -11
View File
@@ -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
View File
@@ -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
View File
@@ -1,5 +1,4 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
-1
View File
@@ -1,5 +1,4 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
-1
View File
@@ -1,5 +1,4 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
+17 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,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"
@@ -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
@@ -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) {
@@ -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 {
@@ -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"
@@ -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"
@@ -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.
@@ -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"
@@ -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"
@@ -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,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
View File
@@ -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
View File
@@ -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
View File
@@ -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,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"
@@ -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
@@ -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
@@ -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.
@@ -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"
@@ -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"
@@ -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.
@@ -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"
@@ -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"
@@ -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,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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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)
}
@@ -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
@@ -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...)
}
}
@@ -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"
@@ -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.
@@ -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
},
)
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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