From 2b1b804b235e74194066b3838e621887a35937e1 Mon Sep 17 00:00:00 2001 From: Raphael Simon Date: Mon, 1 Jan 2024 17:39:56 -0800 Subject: [PATCH] Use GRPC stat.handler for tracing in gRPC This replaces the deprecated use of interceptors. See https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3002. Remove use of Goa request ID middleware. This is superseded by logging span IDs. --- go.mod | 12 +++--- go.sum | 12 ++++++ go.work.sum | 17 ++++++--- trace/context.go | 10 ++--- trace/context_test.go | 2 +- trace/grpc.go | 48 ++++++++++++++++++++++++ trace/grpc_test.go | 65 +++++++++++++++++++------------- trace/http.go | 86 +++++++++++++++++-------------------------- trace/http_test.go | 54 ++++----------------------- trace/options.go | 3 +- trace/span_test.go | 2 +- 11 files changed, 168 insertions(+), 143 deletions(-) diff --git a/go.mod b/go.mod index a7ba84dd..de7c432e 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_model v0.5.0 github.com/stretchr/testify v1.8.4 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 @@ -32,11 +32,11 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-chi/chi/v5 v5.0.10 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-chi/chi/v5 v5.0.11 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect @@ -52,7 +52,7 @@ require ( golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2672248d..3f87b9ae 100644 --- a/go.sum +++ b/go.sum @@ -25,11 +25,15 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= +github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -40,6 +44,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U= @@ -77,6 +83,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= @@ -122,8 +130,12 @@ google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= +google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE= google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/go.work.sum b/go.work.sum index f27a733c..0465dc37 100644 --- a/go.work.sum +++ b/go.work.sum @@ -559,7 +559,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -580,6 +579,7 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksP github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= @@ -605,6 +605,7 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -657,7 +658,6 @@ go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -719,6 +719,7 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -728,7 +729,6 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -739,7 +739,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -914,22 +913,29 @@ google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -955,6 +961,7 @@ google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/trace/context.go b/trace/context.go index aabfde26..018d2313 100644 --- a/trace/context.go +++ b/trace/context.go @@ -51,11 +51,11 @@ func Context(ctx context.Context, svc string, opts ...TraceOption) (context.Cont } if options.disabled { - return withProvider(ctx, noop.NewTracerProvider(), options.propagator, svc), nil + return withConfig(ctx, noop.NewTracerProvider(), options.propagator, svc), nil } if options.exporter == nil { - return nil, errors.New("missing exporter") + return nil, errors.New("missing exporter, set one with the option 'trace.WithExporter'") } res := options.resource @@ -69,7 +69,7 @@ func Context(ctx context.Context, svc string, opts ...TraceOption) (context.Cont sdktrace.WithResource(res), sdktrace.WithBatcher(options.exporter), ) - return withProvider(ctx, provider, options.propagator, svc), nil + return withConfig(ctx, provider, options.propagator, svc), nil } // IsTraced returns true if the current request is traced. @@ -84,8 +84,8 @@ func TraceProvider(ctx context.Context) trace.TracerProvider { return sb.provider } -// withProvider stores the tracer provider in the context. -func withProvider(ctx context.Context, provider trace.TracerProvider, propagator propagation.TextMapPropagator, svc string) context.Context { +// withConfig stores the clue tracing config in the context. +func withConfig(ctx context.Context, provider trace.TracerProvider, propagator propagation.TextMapPropagator, svc string) context.Context { return context.WithValue(ctx, stateKey, &stateBag{provider: provider, propagator: propagator, svc: svc}) } diff --git a/trace/context_test.go b/trace/context_test.go index 3b7c49a1..0044799a 100644 --- a/trace/context_test.go +++ b/trace/context_test.go @@ -12,7 +12,7 @@ import ( ) func testContext(provider trace.TracerProvider) context.Context { - return withProvider(context.Background(), provider, propagation.TraceContext{}, "test") + return withConfig(context.Background(), provider, propagation.TraceContext{}, "test") } func TestContext(t *testing.T) { diff --git a/trace/grpc.go b/trace/grpc.go index bd271493..4717c7bd 100644 --- a/trace/grpc.go +++ b/trace/grpc.go @@ -8,10 +8,55 @@ import ( "go.opentelemetry.io/otel/trace" "goa.design/goa/v3/middleware" "google.golang.org/grpc" + "google.golang.org/grpc/stats" ) +// NewServerHandler creates a stats.Handler for a gRPC server that uses an +// adaptive sampler for limiting the number of traced requests while +// guraranteeing that a certain number of requests are traced. +// It panics if the context has not been initialized with Context. +// +// Example: +// +// // Connect to remote trace collector. +// conn, err := grpc.DialContext(ctx, collectorAddr) +// if err != nil { +// log.Error(ctx, err) +// os.Exit(1) +// } +// // Initialize context for tracing +// ctx := trace.Context(ctx, svcgen.ServiceName, trace.WithGRPCExporter(conn)) +// // Create stats handler +// handler := trace.NewServerHandler(ctx) +// // Create gRPC server +// grpcServer := grpc.NewServer(grpc.StatsHandler(handler)) +func NewServerHandler(traceCtx context.Context) stats.Handler { + state := traceCtx.Value(stateKey) + if state == nil { + panic(errContextMissing) + } + return otelgrpc.NewServerHandler( + otelgrpc.WithTracerProvider(state.(*stateBag).provider), + otelgrpc.WithPropagators(state.(*stateBag).propagator), + ) +} + +// NewClientHandler creates a stats.Handler for a gRPC client. +// It panics if the context has not been initialized with Context. +func NewClientHandler(traceCtx context.Context) stats.Handler { + state := traceCtx.Value(stateKey) + if state == nil { + panic(errContextMissing) + } + return otelgrpc.NewClientHandler( + otelgrpc.WithTracerProvider(state.(*stateBag).provider), + otelgrpc.WithPropagators(state.(*stateBag).propagator), + ) +} + // UnaryServerInterceptor returns an OpenTelemetry UnaryServerInterceptor. It // panics if the context has not been initialized with Context. +// Deprecated: Use NewServerHandler instead. func UnaryServerInterceptor(traceCtx context.Context) grpc.UnaryServerInterceptor { state := traceCtx.Value(stateKey) if state == nil { @@ -34,6 +79,7 @@ func UnaryServerInterceptor(traceCtx context.Context) grpc.UnaryServerIntercepto // StreamServerInterceptor returns an OpenTelemetry StreamServerInterceptor. It // panics if the context has not been initialized with Context. +// Deprecated: Use NewServerHandler instead. func StreamServerInterceptor(traceCtx context.Context) grpc.StreamServerInterceptor { state := traceCtx.Value(stateKey) if state == nil { @@ -57,6 +103,7 @@ func StreamServerInterceptor(traceCtx context.Context) grpc.StreamServerIntercep // UnaryClientInterceptor returns an OpenTelemetry UnaryClientInterceptor. It // panics if the context has not been initialized with Context. +// Deprecated: Use NewClientHandler instead. func UnaryClientInterceptor(traceCtx context.Context) grpc.UnaryClientInterceptor { state := traceCtx.Value(stateKey) if state == nil { @@ -69,6 +116,7 @@ func UnaryClientInterceptor(traceCtx context.Context) grpc.UnaryClientIntercepto // StreamClientInterceptor returns an OpenTelemetry StreamClientInterceptor. It // panics if the context has not been initialized with Context. +// Deprecated: Use NewClientHandler instead. func StreamClientInterceptor(traceCtx context.Context) grpc.StreamClientInterceptor { state := traceCtx.Value(stateKey) if state == nil { diff --git a/trace/grpc_test.go b/trace/grpc_test.go index 83f531de..c1028d99 100644 --- a/trace/grpc_test.go +++ b/trace/grpc_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "goa.design/clue/internal/testsvc" @@ -11,8 +13,44 @@ import ( "google.golang.org/grpc" ) -// NOTE: We are not testing otel here, just make sure a span exists and that the -// request ID is in the attributes on the server. +func TestNewServerHandler(t *testing.T) { + exporter := tracetest.NewInMemoryExporter() + provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter)) + ctx := testContext(provider) + handler := NewServerHandler(ctx) + require.NotNil(t, handler) + cli, stop := testsvc.SetupGRPC(t, + testsvc.WithServerOptions(grpc.StatsHandler(handler)), + testsvc.WithUnaryFunc(addEventUnaryMethod)) + _, err := cli.GRPCMethod(context.Background(), &testsvc.Fields{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + stop() + spans := exporter.GetSpans() + require.Len(t, spans, 1) + assert.Equal(t, "test.Test/GrpcMethod", spans[0].Name) +} + +func TestNewClientHandler(t *testing.T) { + exporter := tracetest.NewInMemoryExporter() + provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter)) + ctx := testContext(provider) + handler := NewClientHandler(ctx) + require.NotNil(t, handler) + cli, stop := testsvc.SetupGRPC(t, + testsvc.WithDialOptions(grpc.WithStatsHandler(handler)), + testsvc.WithUnaryFunc(addEventUnaryMethod), + ) + _, err := cli.GRPCMethod(context.Background(), &testsvc.Fields{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + stop() + spans := exporter.GetSpans() + require.Len(t, spans, 1) + assert.Equal(t, "test.Test/GrpcMethod", spans[0].Name) +} func TestUnaryServerTrace(t *testing.T) { exporter := tracetest.NewInMemoryExporter() @@ -140,29 +178,6 @@ func TestUnaryClientTrace(t *testing.T) { } } -func TestStreamClientTrace(t *testing.T) { - exporter := tracetest.NewInMemoryExporter() - provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter)) - cli, stop := testsvc.SetupGRPC(t, - testsvc.WithDialOptions(grpc.WithStreamInterceptor(StreamClientInterceptor(testContext(provider)))), - testsvc.WithStreamFunc(echoMethod)) - ctx, cancel := context.WithCancel(context.Background()) - stream, err := cli.GRPCStream(ctx) - if err != nil { - t.Errorf("unexpected stream error: %v", err) - } - if err := stream.Send(&testsvc.Fields{}); err != nil { - t.Errorf("unexpected send error: %v", err) - } - stream.Recv() // nolint: errcheck - cancel() - stop() - spans := exporter.GetSpans() - if len(spans) != 1 { - t.Fatalf("got %d spans, want 1", len(spans)) - } -} - func addEventUnaryMethod(ctx context.Context, _ *testsvc.Fields) (*testsvc.Fields, error) { AddEvent(ctx, "unary method") return &testsvc.Fields{}, nil diff --git a/trace/http.go b/trace/http.go index 65db959f..3cace953 100644 --- a/trace/http.go +++ b/trace/http.go @@ -5,52 +5,59 @@ import ( "net/http" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - "goa.design/goa/v3/middleware" ) // Message printed by panic when using a method with a non-initialized context. const errContextMissing = "context not initialized for tracing, use trace.Context to set it up" -// HTTP returns a tracing middleware that uses a parent based sampler (i.e. -// traces if the parent request traces) and an adaptive root sampler (i.e. when -// there is no parent uses a target number of requests per second to trace). -// The implementation leverages the OpenTelemetry SDK and can thus be configured -// to send traces to an OpenTelemetry remote collector. It is aware of the Goa -// RequestID middleware and will use it to propagate the request ID to the -// trace. HTTP panics if the context hasn't been initialized with Context. +// HTTP returns a tracing middleware that uses an adaptive sampler for limiting +// the number of traced requests while guraranteeing that a certain number of +// requests are traced. +// HTTP panics if the context hasn't been initialized with Context. // // Example: // -// // Connect to remote trace collector. -// conn, err := grpc.DialContext(ctx, collectorAddr, -// grpc.WithTransportCrendentials(insecure.Credentials())) -// if err != nil { -// log.Error(ctx, err) -// os.Exit(1) -// } -// // Initialize context for tracing -// ctx := trace.Context(ctx, svcgen.ServiceName, trace.WithGRPCExporter(conn)) -// // Mount middleware -// handler := trace.HTTP(ctx)(mux) -// +// // Connect to remote trace collector. +// conn, err := grpc.DialContext(ctx, collectorAddr) +// if err != nil { +// log.Error(ctx, err) +// os.Exit(1) +// } +// // Initialize context for tracing +// ctx := trace.Context(ctx, svcgen.ServiceName, trace.WithGRPCExporter(conn)) +// // Mount middleware +// handler := trace.HTTP(ctx)(mux) func HTTP(ctx context.Context) func(http.Handler) http.Handler { s := ctx.Value(stateKey) if s == nil { panic(errContextMissing) } return func(h http.Handler) http.Handler { - h = initTracingContext(ctx, h) - h = addRequestIDHTTP(h) return otelhttp.NewHandler(h, s.(*stateBag).svc, otelhttp.WithTracerProvider(s.(*stateBag).provider), otelhttp.WithPropagators(s.(*stateBag).propagator)) } } -// Client returns a roundtripper that wraps t and creates spans for each request. -// It panics if the context hasn't been initialized with Context. +// Client returns a http.RoundTripper that wraps t and creates spans for each +// request. It panics if the context hasn't been initialized with Context. +// +// Example: +// +// // Connect to remote trace collector. +// conn, err := grpc.DialContext(ctx, collectorAddr) +// if err != nil { +// log.Error(ctx, err) +// os.Exit(1) +// } +// // Initialize context for tracing +// ctx := trace.Context(ctx, svcgen.ServiceName, trace.WithGRPCExporter(conn)) +// // Create client +// cli := &http.Client{ +// Transport: trace.Client(ctx, http.DefaultTransport), +// } +// // Use client +// resp, err := cli.Get("http://example.com") func Client(ctx context.Context, t http.RoundTripper, opts ...otelhttp.Option) http.RoundTripper { s := ctx.Value(stateKey) if s == nil { @@ -61,30 +68,3 @@ func Client(ctx context.Context, t http.RoundTripper, opts ...otelhttp.Option) h otelhttp.WithPropagators(s.(*stateBag).propagator)) return otelhttp.NewTransport(t, opts...) } - -// addRequestIDHTTP is a middleware that adds the request ID to the current span -// attributes. -func addRequestIDHTTP(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - requestID := req.Context().Value(middleware.RequestIDKey) - if requestID == nil { - h.ServeHTTP(w, req) - return - } - span := trace.SpanFromContext(req.Context()) - span.SetAttributes(attribute.String(AttributeRequestID, requestID.(string))) - h.ServeHTTP(w, req) - }) -} - -// initTracingContext is a middleware that adds the tracing state to the request -// context. -func initTracingContext(traceCtx context.Context, h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if IsTraced(req.Context()) { - ctx := withTracing(traceCtx, req.Context()) - req = req.WithContext(ctx) - } - h.ServeHTTP(w, req) - }) -} diff --git a/trace/http_test.go b/trace/http_test.go index 08bd56ab..d59e7694 100644 --- a/trace/http_test.go +++ b/trace/http_test.go @@ -5,64 +5,28 @@ import ( "net/http" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" + "goa.design/clue/internal/testsvc" - "goa.design/goa/v3/http/middleware" ) func TestHTTP(t *testing.T) { - exporter := tracetest.NewInMemoryExporter() - provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter)) - ctx := testContext(provider) - cli, stop := testsvc.SetupHTTP(t, - testsvc.WithHTTPMiddleware( - middleware.RequestID(), - HTTP(ctx)), - testsvc.WithHTTPFunc(addEventUnaryMethod)) - if _, err := cli.HTTPMethod(context.Background(), &testsvc.Fields{}); err != nil { - t.Errorf("unexpected error: %v", err) - } - stop() - spans := exporter.GetSpans() - if len(spans) != 1 { - t.Fatalf("got %d spans, want 1", len(spans)) - } - found := false - for _, att := range spans[0].Attributes { - if att.Key == AttributeRequestID { - found = true - break - } - } - if !found { - t.Errorf("request ID not in span attributes") - } - events := spans[0].Events - if len(events) != 1 { - t.Fatalf("got %d events, want 1", len(events)) - } - if events[0].Name != "unary method" { - t.Errorf("unexpected event name: %s", events[0].Name) - } -} - -func TestHTTPRequestID(t *testing.T) { exporter := tracetest.NewInMemoryExporter() provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter)) ctx := testContext(provider) cli, stop := testsvc.SetupHTTP(t, testsvc.WithHTTPMiddleware(HTTP(ctx)), testsvc.WithHTTPFunc(addEventUnaryMethod)) - if _, err := cli.HTTPMethod(context.Background(), &testsvc.Fields{}); err != nil { - t.Errorf("unexpected error: %v", err) - } + _, err := cli.HTTPMethod(context.Background(), &testsvc.Fields{}) + assert.NoError(t, err) stop() spans := exporter.GetSpans() - if len(spans) != 1 { - t.Fatalf("got %d spans, want 1", len(spans)) - } + require.Len(t, spans, 1) + assert.Equal(t, "test", spans[0].Name) } func TestClient(t *testing.T) { @@ -71,7 +35,5 @@ func TestClient(t *testing.T) { ctx := testContext(provider) c := http.Client{Transport: Client(ctx, http.DefaultTransport)} otelt, ok := c.Transport.(*otelhttp.Transport) - if !ok { - t.Errorf("got %T, want %T", c.Transport, otelt) - } + assert.True(t, ok, "got %T, want %T", c.Transport, otelt) } diff --git a/trace/options.go b/trace/options.go index e6327e4e..9678da9f 100644 --- a/trace/options.go +++ b/trace/options.go @@ -83,7 +83,7 @@ func WithResource(res *resource.Resource) TraceOption { } } -// WithPropagator sets the otel propagators +// WithPropagator sets the trace propagator. func WithPropagator(propagator propagation.TextMapPropagator) TraceOption { return func(_ context.Context, opts *options) error { opts.propagator = propagator @@ -91,6 +91,7 @@ func WithPropagator(propagator propagation.TextMapPropagator) TraceOption { } } +// WithGRPCExporter sets the connection to the span exporter. func WithGRPCExporter(conn *grpc.ClientConn) TraceOption { return func(ctx context.Context, opts *options) error { exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) diff --git a/trace/span_test.go b/trace/span_test.go index ee06fdf8..3b0528dc 100644 --- a/trace/span_test.go +++ b/trace/span_test.go @@ -16,7 +16,7 @@ import ( func newTestTracingContext() (context.Context, *tracetest.InMemoryExporter) { exporter := tracetest.NewInMemoryExporter() provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter)) - ctx := withProvider(context.Background(), provider, propagation.TraceContext{}, "test") + ctx := withConfig(context.Background(), provider, propagation.TraceContext{}, "test") return ctx, exporter }