From 2f08c2c3bfe5364a7208fa6e5e0f0402587f6e6b Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Tue, 30 Sep 2025 14:12:14 +0200 Subject: [PATCH 01/35] added gRPC metrics --- go.mod | 29 +++++++++++--------- go.sum | 46 ++++++++++++++++++++++++++++++++ metrics/grpc/metrics.go | 44 +++++++++++++++++++++++++++++++ metrics/grpc/metrics_test.go | 51 ++++++++++++++++++++++++++++++++++++ metrics/grpc/options.go | 32 ++++++++++++++++++++++ 5 files changed, 189 insertions(+), 13 deletions(-) create mode 100644 metrics/grpc/metrics.go create mode 100644 metrics/grpc/metrics_test.go create mode 100644 metrics/grpc/options.go diff --git a/go.mod b/go.mod index 080630c1..854afa4e 100644 --- a/go.mod +++ b/go.mod @@ -18,16 +18,16 @@ require ( github.com/stretchr/testify v1.9.0 github.com/uber/jaeger-client-go v2.29.1+incompatible gitlab.com/gitlab-org/go/reopen v1.0.0 - go.opencensus.io v0.23.0 + go.opencensus.io v0.24.0 golang.org/x/crypto v0.41.0 - google.golang.org/api v0.54.0 - google.golang.org/grpc v1.40.0 + google.golang.org/api v0.160.0 + google.golang.org/grpc v1.63.2 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) require ( - cloud.google.com/go v0.92.2 // indirect - cloud.google.com/go/trace v0.1.0 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/trace v1.10.5 // indirect github.com/DataDog/datadog-go v4.4.0+incompatible // indirect github.com/DataDog/sketches-go v1.0.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.1 // indirect @@ -35,18 +35,20 @@ require ( github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/aws/aws-sdk-go v1.37.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210804190019-f964ff605595 // indirect - github.com/google/uuid v1.1.2 // indirect - github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -70,10 +72,11 @@ require ( golang.org/x/sync v0.16.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect - golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210813162853-db860fec028c // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8c933563..85b9bcfb 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWc cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.92.2 h1:podK44+0gcW5rWGMjJiPH0+rzkCTQx/zT0qF5CLqVkM= cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -45,8 +47,11 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= cloud.google.com/go/trace v0.1.0 h1:nUGUK79FOkN0UGUXhBmVBkbu1PYsHe0YyFSPLOD9Npg= cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= +cloud.google.com/go/trace v1.10.5 h1:0pr4lIKJ5XZFYD9GtxXEWr0KkVeigc3wlGpZco0X1oA= +cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= contrib.go.opencensus.io/exporter/stackdriver v0.13.8 h1:lIFYmQsqejvlq+GobFUbC5F0prD5gvhP6r0gWLZRDq4= contrib.go.opencensus.io/exporter/stackdriver v0.13.8/go.mod h1:huNtlWx75MwO7qMs0KrMxPZXzNNWebav1Sq/pm02JdQ= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -73,6 +78,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -123,6 +129,8 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -151,6 +159,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -195,11 +204,18 @@ github.com/google/pprof v0.0.0-20210804190019-f964ff605595/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -283,6 +299,8 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -291,6 +309,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= @@ -308,6 +329,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/gitlab-org/go/reopen v1.0.0 h1:6BujZ0lkkjGIejTUJdNO1w56mN1SI10qcVQyQlOPM+8= gitlab.com/gitlab-org/go/reopen v1.0.0/go.mod h1:D6OID8YJDzEVZNYW02R/Pkj0v8gYFSIhXFTArAsBQw8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -319,6 +341,7 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -329,6 +352,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -370,6 +394,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -407,6 +432,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -437,6 +463,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -484,13 +511,17 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -501,6 +532,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -508,6 +541,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -564,6 +599,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -601,6 +637,8 @@ google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNe google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0 h1:ECJUVngj71QI6XEm7b1sAf8BljU5inEhMbKPR8Lxhhk= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4= +google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -609,6 +647,8 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -664,6 +704,10 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c h1:iLQakcwWG3k/++1q/46apVb1sUQ3IqIdn9yUE6eh/xA= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= 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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -691,6 +735,8 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= 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/metrics/grpc/metrics.go b/metrics/grpc/metrics.go new file mode 100644 index 00000000..a9ea563e --- /dev/null +++ b/metrics/grpc/metrics.go @@ -0,0 +1,44 @@ +package metricsgrpc + +import ( + grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" + "github.com/prometheus/client_golang/prometheus" + "google.golang.org/grpc" +) + +// ServerMetrics represents a set of gRPC metrics for a server +type ServerMetrics struct { + serverMetrics *grpcprom.ServerMetrics +} + +// NewServerMetrics creates a new set of gRPC server metrics. +func NewServerMetrics(opts ...Option) *ServerMetrics { + cfg := newConfig(opts) + + serverMetrics := grpcprom.NewServerMetrics( + grpcprom.WithServerHandlingTimeHistogram( + grpcprom.WithHistogramBuckets(cfg.buckets), + ), + ) + + return &ServerMetrics{ + serverMetrics: serverMetrics, + } +} + +// Initialize initializes the gRPC metrics +func (m *ServerMetrics) Initialize(grpcServer *grpc.Server) { + // Initialization logic, if any, would go here. + // For now, we just ensure the metrics are registered. + prometheus.MustRegister(m.serverMetrics) +} + +// UnaryServerInterceptor returns a new unary server interceptor that collects metrics. +func (m *ServerMetrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor { + return m.serverMetrics.UnaryServerInterceptor() +} + +// StreamServerInterceptor returns a new stream server interceptor that collects metrics. +func (m *ServerMetrics) StreamServerInterceptor() grpc.StreamServerInterceptor { + return m.serverMetrics.StreamServerInterceptor() +} diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go new file mode 100644 index 00000000..6c438ec2 --- /dev/null +++ b/metrics/grpc/metrics_test.go @@ -0,0 +1,51 @@ +package metricsgrpc + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +// mockServerStream is a mock implementation of grpc.ServerStream for testing. +type mockServerStream struct { + grpc.ServerStream + ctx context.Context +} + +func (m *mockServerStream) Context() context.Context { + return m.ctx +} + +func TestNewServerMetrics(t *testing.T) { + serverMetrics := NewServerMetrics() + require.NotNil(t, serverMetrics) + + // Test Unary Interceptor + unaryInterceptor := serverMetrics.UnaryServerInterceptor() + require.NotNil(t, unaryInterceptor) + + _, err := unaryInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, func(ctx context.Context, req any) (any, error) { + return nil, nil + }) + require.NoError(t, err) + + // Test Stream Interceptor + streamInterceptor := serverMetrics.StreamServerInterceptor() + require.NotNil(t, streamInterceptor) + + mockStream := &mockServerStream{ctx: context.Background()} + err = streamInterceptor(nil, mockStream, &grpc.StreamServerInfo{}, func(srv any, stream grpc.ServerStream) error { + return nil + }) + require.NoError(t, err) +} + +func TestInitialize(t *testing.T) { + serverMetrics := NewServerMetrics() + require.NotNil(t, serverMetrics) + + grpcServer := grpc.NewServer() + serverMetrics.Initialize(grpcServer) +} diff --git a/metrics/grpc/options.go b/metrics/grpc/options.go new file mode 100644 index 00000000..a463c944 --- /dev/null +++ b/metrics/grpc/options.go @@ -0,0 +1,32 @@ +package metricsgrpc + +import "github.com/prometheus/client_golang/prometheus" + +// Default Buckets for histogram metrics +var ( + DefaultRequestDurationBuckets = prometheus.DefBuckets +) + +type config struct { + buckets []float64 +} + +// Option is a functional option for configuring gRPC server metrics. +type Option func(*config) + +func newConfig(opts []Option) *config { + cfg := &config{ + buckets: DefaultRequestDurationBuckets, + } + for _, opt := range opts { + opt(cfg) + } + return cfg +} + +// WithRequestDurationBuckets sets the buckets for the request duration histogram. +func WithRequestDurationBuckets(buckets []float64) Option { + return func(c *config) { + c.buckets = buckets + } +} -- GitLab From 5327e73d6368ea1b5168a6d4d599484418147cc6 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Tue, 30 Sep 2025 14:32:14 +0200 Subject: [PATCH 02/35] go mod tidy --- go.mod | 19 +++++++++++--- go.sum | 78 +++++++++++++++++++++++++++++++--------------------------- 2 files changed, 57 insertions(+), 40 deletions(-) diff --git a/go.mod b/go.mod index 854afa4e..8247a502 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/getsentry/raven-go v0.2.0 github.com/getsentry/sentry-go v0.13.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 github.com/lightstep/lightstep-tracer-go v0.25.0 github.com/oklog/ulid/v2 v2.0.2 github.com/opentracing/opentracing-go v1.2.0 @@ -20,13 +21,15 @@ require ( gitlab.com/gitlab-org/go/reopen v1.0.0 go.opencensus.io v0.24.0 golang.org/x/crypto v0.41.0 - google.golang.org/api v0.160.0 + google.golang.org/api v0.162.0 google.golang.org/grpc v1.63.2 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) require ( cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/monitoring v1.18.0 // indirect cloud.google.com/go/trace v1.10.5 // indirect github.com/DataDog/datadog-go v4.4.0+incompatible // indirect github.com/DataDog/sketches-go v1.0.0 // indirect @@ -39,15 +42,18 @@ require ( github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210804190019-f964ff605595 // indirect + github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -66,6 +72,11 @@ require ( github.com/tklauser/go-sysconf v0.3.4 // indirect github.com/tklauser/numcpus v0.2.1 // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect go.uber.org/atomic v1.4.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect @@ -74,8 +85,8 @@ require ( golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 85b9bcfb..b6640ad5 100644 --- a/go.sum +++ b/go.sum @@ -23,7 +23,6 @@ cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAV cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.92.2 h1:podK44+0gcW5rWGMjJiPH0+rzkCTQx/zT0qF5CLqVkM= cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= @@ -33,8 +32,14 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/monitoring v1.18.0 h1:NfkDLQDG2UR3WYZVQE8kwSbUIEyIqJUPl+aOQdFH1T4= +cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg= cloud.google.com/go/profiler v0.1.0 h1:MG/rxKC1MztRfEWMGYKFISxyZak5hNh29f0A/z2tvWk= cloud.google.com/go/profiler v0.1.0/go.mod h1:D7S7LV/zKbRWkOzYL1b5xytpqt8Ikd/v/yvf1/Tx2pQ= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -45,11 +50,9 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= -cloud.google.com/go/trace v0.1.0 h1:nUGUK79FOkN0UGUXhBmVBkbu1PYsHe0YyFSPLOD9Npg= -cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/trace v1.10.5 h1:0pr4lIKJ5XZFYD9GtxXEWr0KkVeigc3wlGpZco0X1oA= cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= contrib.go.opencensus.io/exporter/stackdriver v0.13.8 h1:lIFYmQsqejvlq+GobFUbC5F0prD5gvhP6r0gWLZRDq4= @@ -66,7 +69,6 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.1 h1:cJXY5VLMHgejurPjZH6Fo9rIwRGLef github.com/HdrHistogram/hdrhistogram-go v1.1.1/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -76,12 +78,11 @@ github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zK github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -92,6 +93,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -104,6 +107,10 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= @@ -118,6 +125,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +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/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -127,7 +139,6 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -157,8 +168,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -202,11 +213,14 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210804190019-f964ff605595 h1:uNrRgpnKjTfxu4qHaZAAs3eKTYV1EzGF3dAykpnxgDE= github.com/google/pprof v0.0.0-20210804190019-f964ff605595/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= @@ -296,7 +310,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -329,7 +342,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/gitlab-org/go/reopen v1.0.0 h1:6BujZ0lkkjGIejTUJdNO1w56mN1SI10qcVQyQlOPM+8= gitlab.com/gitlab-org/go/reopen v1.0.0/go.mod h1:D6OID8YJDzEVZNYW02R/Pkj0v8gYFSIhXFTArAsBQw8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -339,9 +351,21 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= 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/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -352,7 +376,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -394,7 +417,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -432,7 +454,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -463,7 +484,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -511,17 +531,13 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -532,15 +548,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -599,7 +611,6 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -635,20 +646,16 @@ google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59t google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0 h1:ECJUVngj71QI6XEm7b1sAf8BljU5inEhMbKPR8Lxhhk= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4= -google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= +google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= +google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -702,10 +709,11 @@ google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c h1:iLQakcwWG3k/++1q/46apVb1sUQ3IqIdn9yUE6eh/xA= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -733,8 +741,6 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -- GitLab From cfae803b8de8d97965b5b49f805d926d300bbb0f Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Tue, 30 Sep 2025 15:53:39 +0200 Subject: [PATCH 03/35] metrics example --- metrics/grpc/_example/main.go | 64 ++++++++ metrics/grpc/_example/ping.pb.go | 215 ++++++++++++++++++++++++++ metrics/grpc/_example/ping.proto | 18 +++ metrics/grpc/_example/ping_grpc.pb.go | 105 +++++++++++++ 4 files changed, 402 insertions(+) create mode 100644 metrics/grpc/_example/main.go create mode 100644 metrics/grpc/_example/ping.pb.go create mode 100644 metrics/grpc/_example/ping.proto create mode 100644 metrics/grpc/_example/ping_grpc.pb.go diff --git a/metrics/grpc/_example/main.go b/metrics/grpc/_example/main.go new file mode 100644 index 00000000..e67d707c --- /dev/null +++ b/metrics/grpc/_example/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "context" + "log" + "net" + "net/http" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + + grpcmetrics "gitlab.com/gitlab-org/labkit/metrics/grpc" + "gitlab.com/gitlab-org/labkit/metrics/grpc/_example/ping" +) + +// server is used to implement ping.PingServiceServer. +type server struct { + ping.UnimplementedPingServiceServer +} + +// Ping implements ping.PingServiceServer +func (s *server) Ping(ctx context.Context, in *ping.PingRequest) (*ping.PingResponse, error) { + log.Printf("Received: %v", in.GetMessage()) + return &ping.PingResponse{Message: "Pong"}, nil +} + +func main() { + // Create a new set of gRPC server metrics. + serverMetrics := grpcmetrics.NewServerMetrics() + + // Create a gRPC server with the metrics interceptors. + grpcServer := grpc.NewServer( + grpc.UnaryInterceptor(serverMetrics.UnaryServerInterceptor()), + grpc.StreamInterceptor(serverMetrics.StreamServerInterceptor()), + ) + + // Register the PingService server. + ping.RegisterPingServiceServer(grpcServer, &server{}) + + // Initialize the gRPC metrics. + serverMetrics.Initialize(grpcServer) + + // Create a listener on a specific port. + lis, err := net.Listen("tcp", "localhost:8080") + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + + // Start the gRPC server in a separate goroutine. + go func() { + log.Println("gRPC server listening on localhost:8080") + if err := grpcServer.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } + }() + + // Create an HTTP server to serve the /metrics endpoint. + http.Handle("/metrics", promhttp.Handler()) + log.Println("Metrics server listening on localhost:9090") + if err := http.ListenAndServe("localhost:9090", nil); err != nil { + log.Fatalf("failed to serve metrics: %v", err) + } +} + diff --git a/metrics/grpc/_example/ping.pb.go b/metrics/grpc/_example/ping.pb.go new file mode 100644 index 00000000..a4093729 --- /dev/null +++ b/metrics/grpc/_example/ping.pb.go @@ -0,0 +1,215 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v6.32.0 +// source: metrics/grpc/_example/ping.proto + +package ping + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PingRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *PingRequest) Reset() { + *x = PingRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_metrics_grpc__example_ping_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingRequest) ProtoMessage() {} + +func (x *PingRequest) ProtoReflect() protoreflect.Message { + mi := &file_metrics_grpc__example_ping_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead. +func (*PingRequest) Descriptor() ([]byte, []int) { + return file_metrics_grpc__example_ping_proto_rawDescGZIP(), []int{0} +} + +func (x *PingRequest) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type PingResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *PingResponse) Reset() { + *x = PingResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_metrics_grpc__example_ping_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingResponse) ProtoMessage() {} + +func (x *PingResponse) ProtoReflect() protoreflect.Message { + mi := &file_metrics_grpc__example_ping_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingResponse.ProtoReflect.Descriptor instead. +func (*PingResponse) Descriptor() ([]byte, []int) { + return file_metrics_grpc__example_ping_proto_rawDescGZIP(), []int{1} +} + +func (x *PingResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_metrics_grpc__example_ping_proto protoreflect.FileDescriptor + +var file_metrics_grpc__example_ping_proto_rawDesc = []byte{ + 0x0a, 0x20, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x5f, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x22, 0x27, 0x0a, 0x0b, 0x50, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x44, + 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x35, 0x0a, + 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x61, + 0x62, 0x6b, 0x69, 0x74, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x67, 0x72, 0x70, + 0x63, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_metrics_grpc__example_ping_proto_rawDescOnce sync.Once + file_metrics_grpc__example_ping_proto_rawDescData = file_metrics_grpc__example_ping_proto_rawDesc +) + +func file_metrics_grpc__example_ping_proto_rawDescGZIP() []byte { + file_metrics_grpc__example_ping_proto_rawDescOnce.Do(func() { + file_metrics_grpc__example_ping_proto_rawDescData = protoimpl.X.CompressGZIP(file_metrics_grpc__example_ping_proto_rawDescData) + }) + return file_metrics_grpc__example_ping_proto_rawDescData +} + +var file_metrics_grpc__example_ping_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_metrics_grpc__example_ping_proto_goTypes = []interface{}{ + (*PingRequest)(nil), // 0: example.PingRequest + (*PingResponse)(nil), // 1: example.PingResponse +} +var file_metrics_grpc__example_ping_proto_depIdxs = []int32{ + 0, // 0: example.PingService.Ping:input_type -> example.PingRequest + 1, // 1: example.PingService.Ping:output_type -> example.PingResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_metrics_grpc__example_ping_proto_init() } +func file_metrics_grpc__example_ping_proto_init() { + if File_metrics_grpc__example_ping_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_metrics_grpc__example_ping_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_metrics_grpc__example_ping_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_metrics_grpc__example_ping_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_metrics_grpc__example_ping_proto_goTypes, + DependencyIndexes: file_metrics_grpc__example_ping_proto_depIdxs, + MessageInfos: file_metrics_grpc__example_ping_proto_msgTypes, + }.Build() + File_metrics_grpc__example_ping_proto = out.File + file_metrics_grpc__example_ping_proto_rawDesc = nil + file_metrics_grpc__example_ping_proto_goTypes = nil + file_metrics_grpc__example_ping_proto_depIdxs = nil +} diff --git a/metrics/grpc/_example/ping.proto b/metrics/grpc/_example/ping.proto new file mode 100644 index 00000000..05425c2b --- /dev/null +++ b/metrics/grpc/_example/ping.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package example; + +option go_package = "gitlab.com/gitlab-org/labkit/metrics/grpc/_example/ping"; + +service PingService { + rpc Ping(PingRequest) returns (PingResponse) {} +} + +message PingRequest { + string message = 1; +} + +message PingResponse { + string message = 1; +} + diff --git a/metrics/grpc/_example/ping_grpc.pb.go b/metrics/grpc/_example/ping_grpc.pb.go new file mode 100644 index 00000000..05668cca --- /dev/null +++ b/metrics/grpc/_example/ping_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v6.32.0 +// source: metrics/grpc/_example/ping.proto + +package ping + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// PingServiceClient is the client API for PingService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PingServiceClient interface { + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) +} + +type pingServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPingServiceClient(cc grpc.ClientConnInterface) PingServiceClient { + return &pingServiceClient{cc} +} + +func (c *pingServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { + out := new(PingResponse) + err := c.cc.Invoke(ctx, "/example.PingService/Ping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PingServiceServer is the server API for PingService service. +// All implementations must embed UnimplementedPingServiceServer +// for forward compatibility +type PingServiceServer interface { + Ping(context.Context, *PingRequest) (*PingResponse, error) + mustEmbedUnimplementedPingServiceServer() +} + +// UnimplementedPingServiceServer must be embedded to have forward compatible implementations. +type UnimplementedPingServiceServer struct { +} + +func (UnimplementedPingServiceServer) Ping(context.Context, *PingRequest) (*PingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") +} +func (UnimplementedPingServiceServer) mustEmbedUnimplementedPingServiceServer() {} + +// UnsafePingServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PingServiceServer will +// result in compilation errors. +type UnsafePingServiceServer interface { + mustEmbedUnimplementedPingServiceServer() +} + +func RegisterPingServiceServer(s grpc.ServiceRegistrar, srv PingServiceServer) { + s.RegisterService(&PingService_ServiceDesc, srv) +} + +func _PingService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PingServiceServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/example.PingService/Ping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PingServiceServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PingService_ServiceDesc is the grpc.ServiceDesc for PingService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PingService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "example.PingService", + HandlerType: (*PingServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Ping", + Handler: _PingService_Ping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "metrics/grpc/_example/ping.proto", +} -- GitLab From 02a9ca3de35bcc6d50e74b587867b7cba7b95f5b Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Tue, 30 Sep 2025 17:06:36 +0200 Subject: [PATCH 04/35] request and response sizes --- metrics/grpc/_example/main.go | 64 -------- metrics/grpc/_example/ping.pb.go | 215 -------------------------- metrics/grpc/_example/ping.proto | 18 --- metrics/grpc/_example/ping_grpc.pb.go | 105 ------------- metrics/grpc/metrics.go | 143 +++++++++++++++-- metrics/grpc/options.go | 28 +++- 6 files changed, 155 insertions(+), 418 deletions(-) delete mode 100644 metrics/grpc/_example/main.go delete mode 100644 metrics/grpc/_example/ping.pb.go delete mode 100644 metrics/grpc/_example/ping.proto delete mode 100644 metrics/grpc/_example/ping_grpc.pb.go diff --git a/metrics/grpc/_example/main.go b/metrics/grpc/_example/main.go deleted file mode 100644 index e67d707c..00000000 --- a/metrics/grpc/_example/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "context" - "log" - "net" - "net/http" - - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - - grpcmetrics "gitlab.com/gitlab-org/labkit/metrics/grpc" - "gitlab.com/gitlab-org/labkit/metrics/grpc/_example/ping" -) - -// server is used to implement ping.PingServiceServer. -type server struct { - ping.UnimplementedPingServiceServer -} - -// Ping implements ping.PingServiceServer -func (s *server) Ping(ctx context.Context, in *ping.PingRequest) (*ping.PingResponse, error) { - log.Printf("Received: %v", in.GetMessage()) - return &ping.PingResponse{Message: "Pong"}, nil -} - -func main() { - // Create a new set of gRPC server metrics. - serverMetrics := grpcmetrics.NewServerMetrics() - - // Create a gRPC server with the metrics interceptors. - grpcServer := grpc.NewServer( - grpc.UnaryInterceptor(serverMetrics.UnaryServerInterceptor()), - grpc.StreamInterceptor(serverMetrics.StreamServerInterceptor()), - ) - - // Register the PingService server. - ping.RegisterPingServiceServer(grpcServer, &server{}) - - // Initialize the gRPC metrics. - serverMetrics.Initialize(grpcServer) - - // Create a listener on a specific port. - lis, err := net.Listen("tcp", "localhost:8080") - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - - // Start the gRPC server in a separate goroutine. - go func() { - log.Println("gRPC server listening on localhost:8080") - if err := grpcServer.Serve(lis); err != nil { - log.Fatalf("failed to serve: %v", err) - } - }() - - // Create an HTTP server to serve the /metrics endpoint. - http.Handle("/metrics", promhttp.Handler()) - log.Println("Metrics server listening on localhost:9090") - if err := http.ListenAndServe("localhost:9090", nil); err != nil { - log.Fatalf("failed to serve metrics: %v", err) - } -} - diff --git a/metrics/grpc/_example/ping.pb.go b/metrics/grpc/_example/ping.pb.go deleted file mode 100644 index a4093729..00000000 --- a/metrics/grpc/_example/ping.pb.go +++ /dev/null @@ -1,215 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v6.32.0 -// source: metrics/grpc/_example/ping.proto - -package ping - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type PingRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *PingRequest) Reset() { - *x = PingRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_metrics_grpc__example_ping_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PingRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PingRequest) ProtoMessage() {} - -func (x *PingRequest) ProtoReflect() protoreflect.Message { - mi := &file_metrics_grpc__example_ping_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead. -func (*PingRequest) Descriptor() ([]byte, []int) { - return file_metrics_grpc__example_ping_proto_rawDescGZIP(), []int{0} -} - -func (x *PingRequest) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type PingResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *PingResponse) Reset() { - *x = PingResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_metrics_grpc__example_ping_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PingResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PingResponse) ProtoMessage() {} - -func (x *PingResponse) ProtoReflect() protoreflect.Message { - mi := &file_metrics_grpc__example_ping_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PingResponse.ProtoReflect.Descriptor instead. -func (*PingResponse) Descriptor() ([]byte, []int) { - return file_metrics_grpc__example_ping_proto_rawDescGZIP(), []int{1} -} - -func (x *PingResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -var File_metrics_grpc__example_ping_proto protoreflect.FileDescriptor - -var file_metrics_grpc__example_ping_proto_rawDesc = []byte{ - 0x0a, 0x20, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x5f, - 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x22, 0x27, 0x0a, 0x0b, 0x50, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x44, - 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x35, 0x0a, - 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, - 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0x61, - 0x62, 0x6b, 0x69, 0x74, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x67, 0x72, 0x70, - 0x63, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_metrics_grpc__example_ping_proto_rawDescOnce sync.Once - file_metrics_grpc__example_ping_proto_rawDescData = file_metrics_grpc__example_ping_proto_rawDesc -) - -func file_metrics_grpc__example_ping_proto_rawDescGZIP() []byte { - file_metrics_grpc__example_ping_proto_rawDescOnce.Do(func() { - file_metrics_grpc__example_ping_proto_rawDescData = protoimpl.X.CompressGZIP(file_metrics_grpc__example_ping_proto_rawDescData) - }) - return file_metrics_grpc__example_ping_proto_rawDescData -} - -var file_metrics_grpc__example_ping_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_metrics_grpc__example_ping_proto_goTypes = []interface{}{ - (*PingRequest)(nil), // 0: example.PingRequest - (*PingResponse)(nil), // 1: example.PingResponse -} -var file_metrics_grpc__example_ping_proto_depIdxs = []int32{ - 0, // 0: example.PingService.Ping:input_type -> example.PingRequest - 1, // 1: example.PingService.Ping:output_type -> example.PingResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_metrics_grpc__example_ping_proto_init() } -func file_metrics_grpc__example_ping_proto_init() { - if File_metrics_grpc__example_ping_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_metrics_grpc__example_ping_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_metrics_grpc__example_ping_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_metrics_grpc__example_ping_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_metrics_grpc__example_ping_proto_goTypes, - DependencyIndexes: file_metrics_grpc__example_ping_proto_depIdxs, - MessageInfos: file_metrics_grpc__example_ping_proto_msgTypes, - }.Build() - File_metrics_grpc__example_ping_proto = out.File - file_metrics_grpc__example_ping_proto_rawDesc = nil - file_metrics_grpc__example_ping_proto_goTypes = nil - file_metrics_grpc__example_ping_proto_depIdxs = nil -} diff --git a/metrics/grpc/_example/ping.proto b/metrics/grpc/_example/ping.proto deleted file mode 100644 index 05425c2b..00000000 --- a/metrics/grpc/_example/ping.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -package example; - -option go_package = "gitlab.com/gitlab-org/labkit/metrics/grpc/_example/ping"; - -service PingService { - rpc Ping(PingRequest) returns (PingResponse) {} -} - -message PingRequest { - string message = 1; -} - -message PingResponse { - string message = 1; -} - diff --git a/metrics/grpc/_example/ping_grpc.pb.go b/metrics/grpc/_example/ping_grpc.pb.go deleted file mode 100644 index 05668cca..00000000 --- a/metrics/grpc/_example/ping_grpc.pb.go +++ /dev/null @@ -1,105 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v6.32.0 -// source: metrics/grpc/_example/ping.proto - -package ping - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// PingServiceClient is the client API for PingService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type PingServiceClient interface { - Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) -} - -type pingServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewPingServiceClient(cc grpc.ClientConnInterface) PingServiceClient { - return &pingServiceClient{cc} -} - -func (c *pingServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { - out := new(PingResponse) - err := c.cc.Invoke(ctx, "/example.PingService/Ping", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// PingServiceServer is the server API for PingService service. -// All implementations must embed UnimplementedPingServiceServer -// for forward compatibility -type PingServiceServer interface { - Ping(context.Context, *PingRequest) (*PingResponse, error) - mustEmbedUnimplementedPingServiceServer() -} - -// UnimplementedPingServiceServer must be embedded to have forward compatible implementations. -type UnimplementedPingServiceServer struct { -} - -func (UnimplementedPingServiceServer) Ping(context.Context, *PingRequest) (*PingResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") -} -func (UnimplementedPingServiceServer) mustEmbedUnimplementedPingServiceServer() {} - -// UnsafePingServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to PingServiceServer will -// result in compilation errors. -type UnsafePingServiceServer interface { - mustEmbedUnimplementedPingServiceServer() -} - -func RegisterPingServiceServer(s grpc.ServiceRegistrar, srv PingServiceServer) { - s.RegisterService(&PingService_ServiceDesc, srv) -} - -func _PingService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PingRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PingServiceServer).Ping(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/example.PingService/Ping", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PingServiceServer).Ping(ctx, req.(*PingRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// PingService_ServiceDesc is the grpc.ServiceDesc for PingService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var PingService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "example.PingService", - HandlerType: (*PingServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Ping", - Handler: _PingService_Ping_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "metrics/grpc/_example/ping.proto", -} diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index a9ea563e..533219b2 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -1,44 +1,161 @@ package metricsgrpc import ( + "context" + "strings" + grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "github.com/prometheus/client_golang/prometheus" "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) // ServerMetrics represents a set of gRPC metrics for a server type ServerMetrics struct { - serverMetrics *grpcprom.ServerMetrics + serverMetrics *grpcprom.ServerMetrics + reqSizeHistogram *prometheus.HistogramVec + respSizeHistogram *prometheus.HistogramVec + reqDurationHistogram *prometheus.HistogramVec + reqSizeBuckets []float64 + respSizeBuckets []float64 + reqDurationBuckets []float64 + enableReqSizeHistogram bool + enableRespSizeHistogram bool + enableReqDuration bool } // NewServerMetrics creates a new set of gRPC server metrics. func NewServerMetrics(opts ...Option) *ServerMetrics { cfg := newConfig(opts) - serverMetrics := grpcprom.NewServerMetrics( - grpcprom.WithServerHandlingTimeHistogram( - grpcprom.WithHistogramBuckets(cfg.buckets), - ), - ) + serverMetrics := &ServerMetrics{ + reqSizeBuckets: cfg.reqSizeBuckets, + respSizeBuckets: cfg.respSizeBuckets, + reqDurationBuckets: cfg.reqDurationBuckets, + enableReqSizeHistogram: len(cfg.reqSizeBuckets) > 0, + enableRespSizeHistogram: len(cfg.respSizeBuckets) > 0, + enableReqDuration: len(cfg.reqDurationBuckets) > 0, + } + + if serverMetrics.enableReqDuration { + serverMetrics.reqDurationHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "grpc_server_handling_seconds", + Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", + Buckets: serverMetrics.reqDurationBuckets, + }, + []string{"grpc_service", "grpc_method"}, + ) + } + + if serverMetrics.enableReqSizeHistogram { + serverMetrics.reqSizeHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "grpc_server_handled_req_size_bytes", + Help: "Histogram of request size in bytes of gRPC that had been application-level handled by the server.", + Buckets: serverMetrics.reqSizeBuckets, + }, + []string{"grpc_service", "grpc_method"}, + ) + } - return &ServerMetrics{ - serverMetrics: serverMetrics, + if serverMetrics.enableRespSizeHistogram { + serverMetrics.respSizeHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "grpc_server_handled_resp_size_bytes", + Help: "Histogram of response size in bytes of gRPC that had been application-level handled by the server.", + Buckets: serverMetrics.respSizeBuckets, + }, + []string{"grpc_service", "grpc_method"}, + ) } + + return serverMetrics } // Initialize initializes the gRPC metrics func (m *ServerMetrics) Initialize(grpcServer *grpc.Server) { - // Initialization logic, if any, would go here. - // For now, we just ensure the metrics are registered. - prometheus.MustRegister(m.serverMetrics) + if m.enableReqDuration { + prometheus.MustRegister(m.reqDurationHistogram) + } + if m.enableReqSizeHistogram { + prometheus.MustRegister(m.reqSizeHistogram) + } + if m.enableRespSizeHistogram { + prometheus.MustRegister(m.respSizeHistogram) + } } // UnaryServerInterceptor returns a new unary server interceptor that collects metrics. func (m *ServerMetrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor { - return m.serverMetrics.UnaryServerInterceptor() + return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + service, method := splitMethodName(info.FullMethod) + + if m.enableReqSizeHistogram { + if p, ok := req.(proto.Message); ok { + m.reqSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) + } + } + + timer := prometheus.NewTimer(m.reqDurationHistogram.WithLabelValues(service, method)) + resp, err := handler(ctx, req) + timer.ObserveDuration() + + if m.enableRespSizeHistogram { + if p, ok := resp.(proto.Message); ok { + m.respSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) + } + } + + return resp, err + } } // StreamServerInterceptor returns a new stream server interceptor that collects metrics. func (m *ServerMetrics) StreamServerInterceptor() grpc.StreamServerInterceptor { - return m.serverMetrics.StreamServerInterceptor() + return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + service, method := splitMethodName(info.FullMethod) + + wrappedStream := &serverStreamWrapper{ + ServerStream: ss, + reqSizeHistogram: m.reqSizeHistogram.WithLabelValues(service, method), + respSizeHistogram: m.respSizeHistogram.WithLabelValues(service, method), + } + + timer := prometheus.NewTimer(m.reqDurationHistogram.WithLabelValues(service, method)) + err := handler(srv, wrappedStream) + timer.ObserveDuration() + + return err + } +} + +type serverStreamWrapper struct { + grpc.ServerStream + reqSizeHistogram prometheus.Observer + respSizeHistogram prometheus.Observer +} + +func (w *serverStreamWrapper) SendMsg(m any) error { + if p, ok := m.(proto.Message); ok { + w.respSizeHistogram.Observe(float64(proto.Size(p))) + } + return w.ServerStream.SendMsg(m) +} + +func (w *serverStreamWrapper) RecvMsg(m any) error { + err := w.ServerStream.RecvMsg(m) + if err == nil { + if p, ok := m.(proto.Message); ok { + w.reqSizeHistogram.Observe(float64(proto.Size(p))) + } + } + return err +} + +func splitMethodName(fullMethodName string) (string, string) { + if i := strings.LastIndex(fullMethodName, "/"); i >= 0 { + return fullMethodName[1:i], fullMethodName[i+1:] + } + return "unknown", "unknown" } diff --git a/metrics/grpc/options.go b/metrics/grpc/options.go index a463c944..3eac7473 100644 --- a/metrics/grpc/options.go +++ b/metrics/grpc/options.go @@ -5,10 +5,16 @@ import "github.com/prometheus/client_golang/prometheus" // Default Buckets for histogram metrics var ( DefaultRequestDurationBuckets = prometheus.DefBuckets + // Default Buckets for histogram metrics + DefaultRequestSizeBuckets = prometheus.ExponentialBuckets(100, 10, 8) + // Default Buckets for histogram metrics + DefaultResponseSizeBuckets = prometheus.ExponentialBuckets(100, 10, 8) ) type config struct { - buckets []float64 + reqDurationBuckets []float64 + reqSizeBuckets []float64 + respSizeBuckets []float64 } // Option is a functional option for configuring gRPC server metrics. @@ -16,7 +22,9 @@ type Option func(*config) func newConfig(opts []Option) *config { cfg := &config{ - buckets: DefaultRequestDurationBuckets, + reqDurationBuckets: DefaultRequestDurationBuckets, + reqSizeBuckets: DefaultRequestSizeBuckets, + respSizeBuckets: DefaultResponseSizeBuckets, } for _, opt := range opts { opt(cfg) @@ -27,6 +35,20 @@ func newConfig(opts []Option) *config { // WithRequestDurationBuckets sets the buckets for the request duration histogram. func WithRequestDurationBuckets(buckets []float64) Option { return func(c *config) { - c.buckets = buckets + c.reqDurationBuckets = buckets + } +} + +// WithRequestSizeBuckets sets the buckets for the request size histogram. +func WithRequestSizeBuckets(buckets []float64) Option { + return func(c *config) { + c.reqSizeBuckets = buckets + } +} + +// WithResponseSizeBuckets sets the buckets for the response size histogram. +func WithResponseSizeBuckets(buckets []float64) Option { + return func(c *config) { + c.respSizeBuckets = buckets } } -- GitLab From a89ce3af043391e5ebea21c96a02e155d06eef23 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Tue, 30 Sep 2025 17:38:45 +0200 Subject: [PATCH 05/35] handler factory --- metrics/grpc/factory.go | 67 +++++++++++++++++++++++++ metrics/grpc/factory_options.go | 64 ++++++++++++++++++++++++ metrics/grpc/metrics.go | 88 +++------------------------------ metrics/grpc/options.go | 54 -------------------- 4 files changed, 138 insertions(+), 135 deletions(-) create mode 100644 metrics/grpc/factory.go create mode 100644 metrics/grpc/factory_options.go delete mode 100644 metrics/grpc/options.go diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go new file mode 100644 index 00000000..bae40e8d --- /dev/null +++ b/metrics/grpc/factory.go @@ -0,0 +1,67 @@ +package metricsgrpc + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// ServerMetricsFactory creates gRPC server metric interceptors. Created by NewServerMetricsFactory. +type ServerMetricsFactory func() (*ServerMetrics, error) + +// NewServerMetricsFactory creates a new factory for gRPC server metrics. +func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error) { + factoryConfig := applyFactoryOptions(opts) + + var ( + reqSizeHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: factoryConfig.namespace, + Subsystem: factoryConfig.subsystem, + Name: "grpc_server_handled_req_size_bytes", + Help: "Histogram of request size in bytes of gRPC that had been application-level handled by the server.", + Buckets: factoryConfig.reqSizeBuckets, + }, + []string{"grpc_service", "grpc_method"}, + ) + + respSizeHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: factoryConfig.namespace, + Subsystem: factoryConfig.subsystem, + Name: "grpc_server_handled_resp_size_bytes", + Help: "Histogram of response size in bytes of gRPC that had been application-level handled by the server.", + Buckets: factoryConfig.respSizeBuckets, + }, + []string{"grpc_service", "grpc_method"}, + ) + + reqDurationHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: factoryConfig.namespace, + Subsystem: factoryConfig.subsystem, + Name: "grpc_server_handling_seconds", + Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", + Buckets: factoryConfig.reqDurationBuckets, + }, + []string{"grpc_service", "grpc_method"}, + ) + ) + + if err := prometheus.Register(reqSizeHistogram); err != nil { + return nil, err + } + if err := prometheus.Register(respSizeHistogram); err != nil { + return nil, err + } + if err := prometheus.Register(reqDurationHistogram); err != nil { + return nil, err + } + + return func() (*ServerMetrics, error) { + return &ServerMetrics{ + reqSizeHistogram: reqSizeHistogram, + respSizeHistogram: respSizeHistogram, + reqDurationHistogram: reqDurationHistogram, + }, nil + }, nil +} + diff --git a/metrics/grpc/factory_options.go b/metrics/grpc/factory_options.go new file mode 100644 index 00000000..d3c38ded --- /dev/null +++ b/metrics/grpc/factory_options.go @@ -0,0 +1,64 @@ +package metricsgrpc + +import "github.com/prometheus/client_golang/prometheus" + +type factoryConfig struct { + namespace string + subsystem string + reqSizeBuckets []float64 + respSizeBuckets []float64 + reqDurationBuckets []float64 +} + +// FactoryOption is a functional option for configuring the gRPC metrics factory. +type FactoryOption func(*factoryConfig) + +func applyFactoryOptions(opts []FactoryOption) *factoryConfig { + cfg := &factoryConfig{ + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: prometheus.DefBuckets, + reqDurationBuckets: prometheus.DefBuckets, + } + + for _, opt := range opts { + opt(cfg) + } + + return cfg +} + +// WithNamespace sets the Prometheus namespace for the metrics. +func WithNamespace(namespace string) FactoryOption { + return func(c *factoryConfig) { + c.namespace = namespace + } +} + +// WithSubsystem sets the Prometheus subsystem for the metrics. +func WithSubsystem(subsystem string) FactoryOption { + return func(c *factoryConfig) { + c.subsystem = subsystem + } +} + +// WithReqSizeBuckets sets the buckets for the request size histogram. +func WithReqSizeBuckets(buckets []float64) FactoryOption { + return func(c *factoryConfig) { + c.reqSizeBuckets = buckets + } +} + +// WithRespSizeBuckets sets the buckets for the response size histogram. +func WithRespSizeBuckets(buckets []float64) FactoryOption { + return func(c *factoryConfig) { + c.respSizeBuckets = buckets + } +} + +// WithReqDurationBuckets sets the buckets for the request duration histogram. +func WithReqDurationBuckets(buckets []float64) FactoryOption { + return func(c *factoryConfig) { + c.reqDurationBuckets = buckets + } +} + diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index 533219b2..2d96b762 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -4,7 +4,6 @@ import ( "context" "strings" - grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "github.com/prometheus/client_golang/prometheus" "google.golang.org/grpc" "google.golang.org/protobuf/proto" @@ -12,78 +11,9 @@ import ( // ServerMetrics represents a set of gRPC metrics for a server type ServerMetrics struct { - serverMetrics *grpcprom.ServerMetrics - reqSizeHistogram *prometheus.HistogramVec - respSizeHistogram *prometheus.HistogramVec - reqDurationHistogram *prometheus.HistogramVec - reqSizeBuckets []float64 - respSizeBuckets []float64 - reqDurationBuckets []float64 - enableReqSizeHistogram bool - enableRespSizeHistogram bool - enableReqDuration bool -} - -// NewServerMetrics creates a new set of gRPC server metrics. -func NewServerMetrics(opts ...Option) *ServerMetrics { - cfg := newConfig(opts) - - serverMetrics := &ServerMetrics{ - reqSizeBuckets: cfg.reqSizeBuckets, - respSizeBuckets: cfg.respSizeBuckets, - reqDurationBuckets: cfg.reqDurationBuckets, - enableReqSizeHistogram: len(cfg.reqSizeBuckets) > 0, - enableRespSizeHistogram: len(cfg.respSizeBuckets) > 0, - enableReqDuration: len(cfg.reqDurationBuckets) > 0, - } - - if serverMetrics.enableReqDuration { - serverMetrics.reqDurationHistogram = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "grpc_server_handling_seconds", - Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", - Buckets: serverMetrics.reqDurationBuckets, - }, - []string{"grpc_service", "grpc_method"}, - ) - } - - if serverMetrics.enableReqSizeHistogram { - serverMetrics.reqSizeHistogram = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "grpc_server_handled_req_size_bytes", - Help: "Histogram of request size in bytes of gRPC that had been application-level handled by the server.", - Buckets: serverMetrics.reqSizeBuckets, - }, - []string{"grpc_service", "grpc_method"}, - ) - } - - if serverMetrics.enableRespSizeHistogram { - serverMetrics.respSizeHistogram = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "grpc_server_handled_resp_size_bytes", - Help: "Histogram of response size in bytes of gRPC that had been application-level handled by the server.", - Buckets: serverMetrics.respSizeBuckets, - }, - []string{"grpc_service", "grpc_method"}, - ) - } - - return serverMetrics -} - -// Initialize initializes the gRPC metrics -func (m *ServerMetrics) Initialize(grpcServer *grpc.Server) { - if m.enableReqDuration { - prometheus.MustRegister(m.reqDurationHistogram) - } - if m.enableReqSizeHistogram { - prometheus.MustRegister(m.reqSizeHistogram) - } - if m.enableRespSizeHistogram { - prometheus.MustRegister(m.respSizeHistogram) - } + reqSizeHistogram *prometheus.HistogramVec + respSizeHistogram *prometheus.HistogramVec + reqDurationHistogram *prometheus.HistogramVec } // UnaryServerInterceptor returns a new unary server interceptor that collects metrics. @@ -91,20 +21,16 @@ func (m *ServerMetrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { service, method := splitMethodName(info.FullMethod) - if m.enableReqSizeHistogram { - if p, ok := req.(proto.Message); ok { - m.reqSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) - } + if p, ok := req.(proto.Message); ok { + m.reqSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) } timer := prometheus.NewTimer(m.reqDurationHistogram.WithLabelValues(service, method)) resp, err := handler(ctx, req) timer.ObserveDuration() - if m.enableRespSizeHistogram { - if p, ok := resp.(proto.Message); ok { - m.respSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) - } + if p, ok := resp.(proto.Message); ok { + m.respSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) } return resp, err diff --git a/metrics/grpc/options.go b/metrics/grpc/options.go deleted file mode 100644 index 3eac7473..00000000 --- a/metrics/grpc/options.go +++ /dev/null @@ -1,54 +0,0 @@ -package metricsgrpc - -import "github.com/prometheus/client_golang/prometheus" - -// Default Buckets for histogram metrics -var ( - DefaultRequestDurationBuckets = prometheus.DefBuckets - // Default Buckets for histogram metrics - DefaultRequestSizeBuckets = prometheus.ExponentialBuckets(100, 10, 8) - // Default Buckets for histogram metrics - DefaultResponseSizeBuckets = prometheus.ExponentialBuckets(100, 10, 8) -) - -type config struct { - reqDurationBuckets []float64 - reqSizeBuckets []float64 - respSizeBuckets []float64 -} - -// Option is a functional option for configuring gRPC server metrics. -type Option func(*config) - -func newConfig(opts []Option) *config { - cfg := &config{ - reqDurationBuckets: DefaultRequestDurationBuckets, - reqSizeBuckets: DefaultRequestSizeBuckets, - respSizeBuckets: DefaultResponseSizeBuckets, - } - for _, opt := range opts { - opt(cfg) - } - return cfg -} - -// WithRequestDurationBuckets sets the buckets for the request duration histogram. -func WithRequestDurationBuckets(buckets []float64) Option { - return func(c *config) { - c.reqDurationBuckets = buckets - } -} - -// WithRequestSizeBuckets sets the buckets for the request size histogram. -func WithRequestSizeBuckets(buckets []float64) Option { - return func(c *config) { - c.reqSizeBuckets = buckets - } -} - -// WithResponseSizeBuckets sets the buckets for the response size histogram. -func WithResponseSizeBuckets(buckets []float64) Option { - return func(c *config) { - c.respSizeBuckets = buckets - } -} -- GitLab From a94a354a29c6b1e1454bb3a32f51053bbf470aad Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Wed, 1 Oct 2025 13:07:07 +0200 Subject: [PATCH 06/35] renamed histograms --- metrics/grpc/factory.go | 7 +++---- metrics/grpc/factory_options.go | 13 ++++++------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index bae40e8d..5e508e49 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -16,7 +16,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error prometheus.HistogramOpts{ Namespace: factoryConfig.namespace, Subsystem: factoryConfig.subsystem, - Name: "grpc_server_handled_req_size_bytes", + Name: "request_size_bytes", Help: "Histogram of request size in bytes of gRPC that had been application-level handled by the server.", Buckets: factoryConfig.reqSizeBuckets, }, @@ -27,7 +27,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error prometheus.HistogramOpts{ Namespace: factoryConfig.namespace, Subsystem: factoryConfig.subsystem, - Name: "grpc_server_handled_resp_size_bytes", + Name: "response_size_bytes", Help: "Histogram of response size in bytes of gRPC that had been application-level handled by the server.", Buckets: factoryConfig.respSizeBuckets, }, @@ -38,7 +38,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error prometheus.HistogramOpts{ Namespace: factoryConfig.namespace, Subsystem: factoryConfig.subsystem, - Name: "grpc_server_handling_seconds", + Name: "request_duration_seconds", Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", Buckets: factoryConfig.reqDurationBuckets, }, @@ -64,4 +64,3 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error }, nil }, nil } - diff --git a/metrics/grpc/factory_options.go b/metrics/grpc/factory_options.go index d3c38ded..17110328 100644 --- a/metrics/grpc/factory_options.go +++ b/metrics/grpc/factory_options.go @@ -3,10 +3,10 @@ package metricsgrpc import "github.com/prometheus/client_golang/prometheus" type factoryConfig struct { - namespace string - subsystem string - reqSizeBuckets []float64 - respSizeBuckets []float64 + namespace string + subsystem string + reqSizeBuckets []float64 + respSizeBuckets []float64 reqDurationBuckets []float64 } @@ -15,8 +15,8 @@ type FactoryOption func(*factoryConfig) func applyFactoryOptions(opts []FactoryOption) *factoryConfig { cfg := &factoryConfig{ - reqSizeBuckets: prometheus.DefBuckets, - respSizeBuckets: prometheus.DefBuckets, + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: prometheus.DefBuckets, reqDurationBuckets: prometheus.DefBuckets, } @@ -61,4 +61,3 @@ func WithReqDurationBuckets(buckets []float64) FactoryOption { c.reqDurationBuckets = buckets } } - -- GitLab From 1b7178287798a5cb66fac66f33083422d712bfec Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Wed, 1 Oct 2025 14:31:14 +0200 Subject: [PATCH 07/35] added tests --- metrics/grpc/factory.go | 12 ++- metrics/grpc/metrics_test.go | 143 ++++++++++++++++++++++++++++------- 2 files changed, 124 insertions(+), 31 deletions(-) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index 5e508e49..d7cfa415 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -47,13 +47,19 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error ) if err := prometheus.Register(reqSizeHistogram); err != nil { - return nil, err + if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { + return nil, err + } } if err := prometheus.Register(respSizeHistogram); err != nil { - return nil, err + if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { + return nil, err + } } if err := prometheus.Register(reqDurationHistogram); err != nil { - return nil, err + if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { + return nil, err + } } return func() (*ServerMetrics, error) { diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index 6c438ec2..bf7cc8b9 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -2,50 +2,137 @@ package metricsgrpc import ( "context" + "net" "testing" + "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/health/grpc_health_v1" ) -// mockServerStream is a mock implementation of grpc.ServerStream for testing. -type mockServerStream struct { - grpc.ServerStream - ctx context.Context -} +func TestNewServerMetricsFactory(t *testing.T) { + factory, err := NewServerMetricsFactory() + require.NoError(t, err) + require.NotNil(t, factory) -func (m *mockServerStream) Context() context.Context { - return m.ctx + _, err = factory() + require.NoError(t, err) } -func TestNewServerMetrics(t *testing.T) { - serverMetrics := NewServerMetrics() - require.NotNil(t, serverMetrics) +func TestInterceptors(t *testing.T) { + factory, err := NewServerMetricsFactory() + require.NoError(t, err) - // Test Unary Interceptor - unaryInterceptor := serverMetrics.UnaryServerInterceptor() - require.NotNil(t, unaryInterceptor) + metrics, err := factory() + require.NoError(t, err) - _, err := unaryInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, func(ctx context.Context, req any) (any, error) { - return nil, nil - }) + server := grpc.NewServer( + grpc.UnaryInterceptor(metrics.UnaryServerInterceptor()), + grpc.StreamInterceptor(metrics.StreamServerInterceptor()), + ) + grpc_health_v1.RegisterHealthServer(server, &healthService{}) + + lis, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) - // Test Stream Interceptor - streamInterceptor := serverMetrics.StreamServerInterceptor() - require.NotNil(t, streamInterceptor) + go server.Serve(lis) + defer server.Stop() - mockStream := &mockServerStream{ctx: context.Background()} - err = streamInterceptor(nil, mockStream, &grpc.StreamServerInfo{}, func(srv any, stream grpc.ServerStream) error { - return nil - }) + conn, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) + defer conn.Close() + + client := grpc_health_v1.NewHealthClient(conn) + + t.Run("Unary", func(t *testing.T) { + _, err := client.Check(context.Background(), &grpc_health_v1.HealthCheckRequest{}) + require.NoError(t, err) + + require.Equal(t, 1, testutil.CollectAndCount(metrics.reqDurationHistogram)) + require.Equal(t, 1, testutil.CollectAndCount(metrics.reqSizeHistogram)) + require.Equal(t, 1, testutil.CollectAndCount(metrics.respSizeHistogram)) + }) + + t.Run("Stream", func(t *testing.T) { + stream, err := client.Watch(context.Background(), &grpc_health_v1.HealthCheckRequest{}) + require.NoError(t, err) + + // The server sends a single response, then we close the stream. + _, err = stream.Recv() + require.NoError(t, err) + + require.NoError(t, stream.CloseSend()) + + // We need to drain the stream to ensure the interceptor has finished. + for { + if _, err := stream.Recv(); err != nil { + break + } + } + + require.Equal(t, 2, testutil.CollectAndCount(metrics.reqDurationHistogram)) + require.Equal(t, 2, testutil.CollectAndCount(metrics.reqSizeHistogram)) + require.Equal(t, 2, testutil.CollectAndCount(metrics.respSizeHistogram)) + }) } -func TestInitialize(t *testing.T) { - serverMetrics := NewServerMetrics() - require.NotNil(t, serverMetrics) +type healthService struct { + grpc_health_v1.UnimplementedHealthServer +} - grpcServer := grpc.NewServer() - serverMetrics.Initialize(grpcServer) +func (s *healthService) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { + return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil } + +func (s *healthService) Watch(req *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error { + stream.Send(&grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}) + + // Keep the stream open until the client closes it. + for { + // The client will close the stream, and RecvMsg will return an error. + if err := stream.RecvMsg(new(grpc_health_v1.HealthCheckRequest)); err != nil { + return nil + } + } +} + +func (s *healthService) mustEmbedUnimplementedHealthServer() {} + +func TestSplitMethodName(t *testing.T) { + testCases := []struct { + desc string + in string + service string + method string + }{ + { + desc: "valid full method", + in: "/grpc.health.v1.Health/Check", + service: "grpc.health.v1.Health", + method: "Check", + }, + { + desc: "no separator", + in: "foo", + service: "unknown", + method: "unknown", + }, + { + desc: "empty string", + in: "", + service: "unknown", + method: "unknown", + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + service, method := splitMethodName(tc.in) + require.Equal(t, tc.service, service) + require.Equal(t, tc.method, method) + }) + } +} + + -- GitLab From cb351a82841a0efa9b79ca56710283367ba875a8 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Wed, 1 Oct 2025 14:38:38 +0200 Subject: [PATCH 08/35] go mod tidy --- go.mod | 4 +--- go.sum | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 8247a502..9f0208c5 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/getsentry/raven-go v0.2.0 github.com/getsentry/sentry-go v0.13.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 - github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 github.com/lightstep/lightstep-tracer-go v0.25.0 github.com/oklog/ulid/v2 v2.0.2 github.com/opentracing/opentracing-go v1.2.0 @@ -23,6 +22,7 @@ require ( golang.org/x/crypto v0.41.0 google.golang.org/api v0.162.0 google.golang.org/grpc v1.63.2 + google.golang.org/protobuf v1.34.2 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) @@ -54,7 +54,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -88,6 +87,5 @@ require ( google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect - google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b6640ad5..1229688d 100644 --- a/go.sum +++ b/go.sum @@ -226,10 +226,6 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -- GitLab From d838ae6225b8318cdfd8e2c731f17501f3f08418 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Wed, 1 Oct 2025 14:51:29 +0200 Subject: [PATCH 09/35] lining errors --- metrics/grpc/metrics.go | 2 +- metrics/grpc/metrics_test.go | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index 2d96b762..2333a31f 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -9,7 +9,7 @@ import ( "google.golang.org/protobuf/proto" ) -// ServerMetrics represents a set of gRPC metrics for a server +// ServerMetrics represents a set of gRPC metrics for a server. type ServerMetrics struct { reqSizeHistogram *prometheus.HistogramVec respSizeHistogram *prometheus.HistogramVec diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index bf7cc8b9..51b63741 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -98,8 +98,6 @@ func (s *healthService) Watch(req *grpc_health_v1.HealthCheckRequest, stream grp } } -func (s *healthService) mustEmbedUnimplementedHealthServer() {} - func TestSplitMethodName(t *testing.T) { testCases := []struct { desc string @@ -134,5 +132,3 @@ func TestSplitMethodName(t *testing.T) { }) } } - - -- GitLab From 38e0a290faaa75a5f21c3391603acb32a57b31a1 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Wed, 1 Oct 2025 14:56:54 +0200 Subject: [PATCH 10/35] added lint exception --- metrics/grpc/metrics_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index 51b63741..ab60f5ff 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -92,6 +92,7 @@ func (s *healthService) Watch(req *grpc_health_v1.HealthCheckRequest, stream grp // Keep the stream open until the client closes it. for { // The client will close the stream, and RecvMsg will return an error. + //nolint:nilerr` if err := stream.RecvMsg(new(grpc_health_v1.HealthCheckRequest)); err != nil { return nil } -- GitLab From 647e71f6ea22d5d26073d386a8d7f370fe86dca1 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Wed, 1 Oct 2025 15:07:28 +0200 Subject: [PATCH 11/35] err --- metrics/grpc/metrics_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index ab60f5ff..32aaa568 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -92,9 +92,8 @@ func (s *healthService) Watch(req *grpc_health_v1.HealthCheckRequest, stream grp // Keep the stream open until the client closes it. for { // The client will close the stream, and RecvMsg will return an error. - //nolint:nilerr` if err := stream.RecvMsg(new(grpc_health_v1.HealthCheckRequest)); err != nil { - return nil + return err } } } -- GitLab From 725b7dba94351402f13c4b1e06db0aff6012d6ad Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 2 Oct 2025 18:24:05 +0200 Subject: [PATCH 12/35] added tests --- metrics/grpc/factory_options_test.go | 110 +++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 metrics/grpc/factory_options_test.go diff --git a/metrics/grpc/factory_options_test.go b/metrics/grpc/factory_options_test.go new file mode 100644 index 00000000..aca3a6b8 --- /dev/null +++ b/metrics/grpc/factory_options_test.go @@ -0,0 +1,110 @@ +package metricsgrpc + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" +) + +func TestApplyFactoryOptions(t *testing.T) { + t.Parallel() + + testBuckets := []float64{1, 2, 3} + + testCases := []struct { + desc string + opts []FactoryOption + expected *factoryConfig + }{ + { + desc: "with no options", + opts: nil, + expected: &factoryConfig{ + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: prometheus.DefBuckets, + reqDurationBuckets: prometheus.DefBuckets, + }, + }, + { + desc: "with all options", + opts: []FactoryOption{ + WithNamespace("test_namespace"), + WithSubsystem("test_subsystem"), + WithReqSizeBuckets(testBuckets), + WithRespSizeBuckets(testBuckets), + WithReqDurationBuckets(testBuckets), + }, + expected: &factoryConfig{ + namespace: "test_namespace", + subsystem: "test_subsystem", + reqSizeBuckets: testBuckets, + respSizeBuckets: testBuckets, + reqDurationBuckets: testBuckets, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + cfg := applyFactoryOptions(tc.opts) + require.Equal(t, tc.expected, cfg) + }) + } +} + +func TestFactoryOptions(t *testing.T) { + t.Parallel() + + testBuckets := []float64{1, 2, 3} + + testCases := []struct { + desc string + opt FactoryOption + assert func(t *testing.T, cfg *factoryConfig) + }{ + { + "WithNamespace", + WithNamespace("test_namespace"), + func(t *testing.T, cfg *factoryConfig) { + require.Equal(t, "test_namespace", cfg.namespace) + }, + }, + { + "WithSubsystem", + WithSubsystem("test_subsystem"), + func(t *testing.T, cfg *factoryConfig) { + require.Equal(t, "test_subsystem", cfg.subsystem) + }, + }, + { + "WithReqSizeBuckets", + WithReqSizeBuckets(testBuckets), + func(t *testing.T, cfg *factoryConfig) { + require.Equal(t, testBuckets, cfg.reqSizeBuckets) + }, + }, + { + "WithRespSizeBuckets", + WithRespSizeBuckets(testBuckets), + func(t *testing.T, cfg *factoryConfig) { + require.Equal(t, testBuckets, cfg.respSizeBuckets) + }, + }, + { + "WithReqDurationBuckets", + WithReqDurationBuckets(testBuckets), + func(t *testing.T, cfg *factoryConfig) { + require.Equal(t, testBuckets, cfg.reqDurationBuckets) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + cfg := applyFactoryOptions([]FactoryOption{tc.opt}) + tc.assert(t, cfg) + }) + } +} + -- GitLab From c975cfe4d8a242ec2dd056c75dff85d075fbcf17 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 2 Oct 2025 18:31:30 +0200 Subject: [PATCH 13/35] helpers --- metrics/grpc/factory_options_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/metrics/grpc/factory_options_test.go b/metrics/grpc/factory_options_test.go index aca3a6b8..aea01487 100644 --- a/metrics/grpc/factory_options_test.go +++ b/metrics/grpc/factory_options_test.go @@ -67,6 +67,7 @@ func TestFactoryOptions(t *testing.T) { "WithNamespace", WithNamespace("test_namespace"), func(t *testing.T, cfg *factoryConfig) { + t.Helper() require.Equal(t, "test_namespace", cfg.namespace) }, }, @@ -74,6 +75,7 @@ func TestFactoryOptions(t *testing.T) { "WithSubsystem", WithSubsystem("test_subsystem"), func(t *testing.T, cfg *factoryConfig) { + t.Helper() require.Equal(t, "test_subsystem", cfg.subsystem) }, }, @@ -81,6 +83,7 @@ func TestFactoryOptions(t *testing.T) { "WithReqSizeBuckets", WithReqSizeBuckets(testBuckets), func(t *testing.T, cfg *factoryConfig) { + t.Helper() require.Equal(t, testBuckets, cfg.reqSizeBuckets) }, }, @@ -88,6 +91,7 @@ func TestFactoryOptions(t *testing.T) { "WithRespSizeBuckets", WithRespSizeBuckets(testBuckets), func(t *testing.T, cfg *factoryConfig) { + t.Helper() require.Equal(t, testBuckets, cfg.respSizeBuckets) }, }, @@ -95,6 +99,7 @@ func TestFactoryOptions(t *testing.T) { "WithReqDurationBuckets", WithReqDurationBuckets(testBuckets), func(t *testing.T, cfg *factoryConfig) { + t.Helper() require.Equal(t, testBuckets, cfg.reqDurationBuckets) }, }, @@ -107,4 +112,3 @@ func TestFactoryOptions(t *testing.T) { }) } } - -- GitLab From 82a41f923e8f772bf3932c9229ddae89c2315f8b Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 3 Oct 2025 12:16:26 +0200 Subject: [PATCH 14/35] addded tests --- metrics/grpc/factory_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 metrics/grpc/factory_test.go diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go new file mode 100644 index 00000000..962f642b --- /dev/null +++ b/metrics/grpc/factory_test.go @@ -0,0 +1,29 @@ +package metricsgrpc_test + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + + metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" +) + +func TestNewServerMetricsFactory(t *testing.T) { + t.Parallel() + + // Unregister all collectors to ensure a clean state for the test + prometheus.DefaultRegisterer = prometheus.NewRegistry() + + // The first call should succeed + factory, err := metricsgrpc.NewServerMetricsFactory() + require.NoError(t, err) + require.NotNil(t, factory) + + // The second call should also succeed, as the factory should handle the + // prometheus.AlreadyRegisteredError gracefully + factory, err = metricsgrpc.NewServerMetricsFactory() + require.NoError(t, err) + require.NotNil(t, factory) +} + -- GitLab From 7bab36d9632fb85c529c82ff9f3aef1094d1f532 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 3 Oct 2025 13:53:17 +0200 Subject: [PATCH 15/35] format file --- metrics/grpc/factory_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index 962f642b..670615e2 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -5,7 +5,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" ) @@ -26,4 +25,3 @@ func TestNewServerMetricsFactory(t *testing.T) { require.NoError(t, err) require.NotNil(t, factory) } - -- GitLab From 2901a632a720624b2a63ee92170d02909772886e Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 13:40:06 +0200 Subject: [PATCH 16/35] use standard metrics --- go.mod | 4 +- go.sum | 4 ++ metrics/grpc/factory.go | 64 ++++++--------------------- metrics/grpc/factory_options.go | 21 +++++---- metrics/grpc/factory_test.go | 12 +++-- metrics/grpc/metrics.go | 78 +++------------------------------ metrics/grpc/metrics_test.go | 59 +++---------------------- 7 files changed, 54 insertions(+), 188 deletions(-) diff --git a/go.mod b/go.mod index 9f0208c5..8247a502 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/getsentry/raven-go v0.2.0 github.com/getsentry/sentry-go v0.13.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 github.com/lightstep/lightstep-tracer-go v0.25.0 github.com/oklog/ulid/v2 v2.0.2 github.com/opentracing/opentracing-go v1.2.0 @@ -22,7 +23,6 @@ require ( golang.org/x/crypto v0.41.0 google.golang.org/api v0.162.0 google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.34.2 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) @@ -54,6 +54,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -87,5 +88,6 @@ require ( google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1229688d..b6640ad5 100644 --- a/go.sum +++ b/go.sum @@ -226,6 +226,10 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index d7cfa415..6f5e6a3c 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -1,6 +1,7 @@ package metricsgrpc import ( + prom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "github.com/prometheus/client_golang/prometheus" ) @@ -11,62 +12,23 @@ type ServerMetricsFactory func() (*ServerMetrics, error) func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error) { factoryConfig := applyFactoryOptions(opts) - var ( - reqSizeHistogram = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: factoryConfig.namespace, - Subsystem: factoryConfig.subsystem, - Name: "request_size_bytes", - Help: "Histogram of request size in bytes of gRPC that had been application-level handled by the server.", - Buckets: factoryConfig.reqSizeBuckets, - }, - []string{"grpc_service", "grpc_method"}, - ) - - respSizeHistogram = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: factoryConfig.namespace, - Subsystem: factoryConfig.subsystem, - Name: "response_size_bytes", - Help: "Histogram of response size in bytes of gRPC that had been application-level handled by the server.", - Buckets: factoryConfig.respSizeBuckets, - }, - []string{"grpc_service", "grpc_method"}, - ) - - reqDurationHistogram = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: factoryConfig.namespace, - Subsystem: factoryConfig.subsystem, - Name: "request_duration_seconds", - Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", - Buckets: factoryConfig.reqDurationBuckets, - }, - []string{"grpc_service", "grpc_method"}, + return func() (*ServerMetrics, error) { + metrics := prom.NewServerMetrics( + prom.WithServerHandlingTimeHistogram( + prom.WithHistogramBuckets(factoryConfig.reqDurationBuckets), + prom.WithHistogramSubsystem(factoryConfig.subsystem), + prom.WithHistogramNamespace(factoryConfig.namespace), + ), ) - ) - if err := prometheus.Register(reqSizeHistogram); err != nil { - if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { - return nil, err - } - } - if err := prometheus.Register(respSizeHistogram); err != nil { - if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { - return nil, err + if err := prometheus.Register(metrics); err != nil { + if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { + return nil, err + } } - } - if err := prometheus.Register(reqDurationHistogram); err != nil { - if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { - return nil, err - } - } - return func() (*ServerMetrics, error) { return &ServerMetrics{ - reqSizeHistogram: reqSizeHistogram, - respSizeHistogram: respSizeHistogram, - reqDurationHistogram: reqDurationHistogram, + ServerMetrics: metrics, }, nil }, nil } diff --git a/metrics/grpc/factory_options.go b/metrics/grpc/factory_options.go index 17110328..ac33e5f7 100644 --- a/metrics/grpc/factory_options.go +++ b/metrics/grpc/factory_options.go @@ -2,6 +2,9 @@ package metricsgrpc import "github.com/prometheus/client_golang/prometheus" +// FactoryOption configures the gRPC server metrics factory. +type FactoryOption func(*factoryConfig) + type factoryConfig struct { namespace string subsystem string @@ -10,21 +13,22 @@ type factoryConfig struct { reqDurationBuckets []float64 } -// FactoryOption is a functional option for configuring the gRPC metrics factory. -type FactoryOption func(*factoryConfig) - -func applyFactoryOptions(opts []FactoryOption) *factoryConfig { - cfg := &factoryConfig{ +func defaultFactoryConfig() *factoryConfig { + return &factoryConfig{ + namespace: "gitlab", + subsystem: "grpc", reqSizeBuckets: prometheus.DefBuckets, respSizeBuckets: prometheus.DefBuckets, reqDurationBuckets: prometheus.DefBuckets, } +} +func applyFactoryOptions(opts []FactoryOption) *factoryConfig { + config := defaultFactoryConfig() for _, opt := range opts { - opt(cfg) + opt(config) } - - return cfg + return config } // WithNamespace sets the Prometheus namespace for the metrics. @@ -61,3 +65,4 @@ func WithReqDurationBuckets(buckets []float64) FactoryOption { c.reqDurationBuckets = buckets } } + diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index 670615e2..da721bc4 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -5,7 +5,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" + "gitlab.com/gitlab-org/labkit/metrics/grpc" ) func TestNewServerMetricsFactory(t *testing.T) { @@ -14,14 +14,18 @@ func TestNewServerMetricsFactory(t *testing.T) { // Unregister all collectors to ensure a clean state for the test prometheus.DefaultRegisterer = prometheus.NewRegistry() - // The first call should succeed factory, err := metricsgrpc.NewServerMetricsFactory() require.NoError(t, err) require.NotNil(t, factory) + // The first call should succeed + metrics, err := factory() + require.NoError(t, err) + require.NotNil(t, metrics) + // The second call should also succeed, as the factory should handle the // prometheus.AlreadyRegisteredError gracefully - factory, err = metricsgrpc.NewServerMetricsFactory() + metrics, err = factory() require.NoError(t, err) - require.NotNil(t, factory) + require.NotNil(t, metrics) } diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index 2333a31f..f5ea3be8 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -1,87 +1,21 @@ package metricsgrpc import ( - "context" - "strings" - - "github.com/prometheus/client_golang/prometheus" + prom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "google.golang.org/grpc" - "google.golang.org/protobuf/proto" ) // ServerMetrics represents a set of gRPC metrics for a server. type ServerMetrics struct { - reqSizeHistogram *prometheus.HistogramVec - respSizeHistogram *prometheus.HistogramVec - reqDurationHistogram *prometheus.HistogramVec + *prom.ServerMetrics } // UnaryServerInterceptor returns a new unary server interceptor that collects metrics. -func (m *ServerMetrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor { - return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { - service, method := splitMethodName(info.FullMethod) - - if p, ok := req.(proto.Message); ok { - m.reqSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) - } - - timer := prometheus.NewTimer(m.reqDurationHistogram.WithLabelValues(service, method)) - resp, err := handler(ctx, req) - timer.ObserveDuration() - - if p, ok := resp.(proto.Message); ok { - m.respSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) - } - - return resp, err - } +func (m *ServerMetrics) UnaryServerInterceptor(opts ...prom.Option) grpc.UnaryServerInterceptor { + return m.ServerMetrics.UnaryServerInterceptor(opts...) } // StreamServerInterceptor returns a new stream server interceptor that collects metrics. -func (m *ServerMetrics) StreamServerInterceptor() grpc.StreamServerInterceptor { - return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { - service, method := splitMethodName(info.FullMethod) - - wrappedStream := &serverStreamWrapper{ - ServerStream: ss, - reqSizeHistogram: m.reqSizeHistogram.WithLabelValues(service, method), - respSizeHistogram: m.respSizeHistogram.WithLabelValues(service, method), - } - - timer := prometheus.NewTimer(m.reqDurationHistogram.WithLabelValues(service, method)) - err := handler(srv, wrappedStream) - timer.ObserveDuration() - - return err - } -} - -type serverStreamWrapper struct { - grpc.ServerStream - reqSizeHistogram prometheus.Observer - respSizeHistogram prometheus.Observer -} - -func (w *serverStreamWrapper) SendMsg(m any) error { - if p, ok := m.(proto.Message); ok { - w.respSizeHistogram.Observe(float64(proto.Size(p))) - } - return w.ServerStream.SendMsg(m) -} - -func (w *serverStreamWrapper) RecvMsg(m any) error { - err := w.ServerStream.RecvMsg(m) - if err == nil { - if p, ok := m.(proto.Message); ok { - w.reqSizeHistogram.Observe(float64(proto.Size(p))) - } - } - return err -} - -func splitMethodName(fullMethodName string) (string, string) { - if i := strings.LastIndex(fullMethodName, "/"); i >= 0 { - return fullMethodName[1:i], fullMethodName[i+1:] - } - return "unknown", "unknown" +func (m *ServerMetrics) StreamServerInterceptor(opts ...prom.Option) grpc.StreamServerInterceptor { + return m.ServerMetrics.StreamServerInterceptor(opts...) } diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index 32aaa568..ca355ca1 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -1,4 +1,4 @@ -package metricsgrpc +package metricsgrpc_test import ( "context" @@ -7,22 +7,14 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/labkit/metrics/grpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/health/grpc_health_v1" ) -func TestNewServerMetricsFactory(t *testing.T) { - factory, err := NewServerMetricsFactory() - require.NoError(t, err) - require.NotNil(t, factory) - - _, err = factory() - require.NoError(t, err) -} - func TestInterceptors(t *testing.T) { - factory, err := NewServerMetricsFactory() + factory, err := metricsgrpc.NewServerMetricsFactory() require.NoError(t, err) metrics, err := factory() @@ -50,9 +42,8 @@ func TestInterceptors(t *testing.T) { _, err := client.Check(context.Background(), &grpc_health_v1.HealthCheckRequest{}) require.NoError(t, err) - require.Equal(t, 1, testutil.CollectAndCount(metrics.reqDurationHistogram)) - require.Equal(t, 1, testutil.CollectAndCount(metrics.reqSizeHistogram)) - require.Equal(t, 1, testutil.CollectAndCount(metrics.respSizeHistogram)) + require.Equal(t, 1, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handled_total")) + require.Equal(t, 1, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handling_seconds")) }) t.Run("Stream", func(t *testing.T) { @@ -72,9 +63,8 @@ func TestInterceptors(t *testing.T) { } } - require.Equal(t, 2, testutil.CollectAndCount(metrics.reqDurationHistogram)) - require.Equal(t, 2, testutil.CollectAndCount(metrics.reqSizeHistogram)) - require.Equal(t, 2, testutil.CollectAndCount(metrics.respSizeHistogram)) + require.Equal(t, 2, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handled_total")) + require.Equal(t, 2, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handling_seconds")) }) } @@ -97,38 +87,3 @@ func (s *healthService) Watch(req *grpc_health_v1.HealthCheckRequest, stream grp } } } - -func TestSplitMethodName(t *testing.T) { - testCases := []struct { - desc string - in string - service string - method string - }{ - { - desc: "valid full method", - in: "/grpc.health.v1.Health/Check", - service: "grpc.health.v1.Health", - method: "Check", - }, - { - desc: "no separator", - in: "foo", - service: "unknown", - method: "unknown", - }, - { - desc: "empty string", - in: "", - service: "unknown", - method: "unknown", - }, - } - for _, tc := range testCases { - t.Run(tc.desc, func(t *testing.T) { - service, method := splitMethodName(tc.in) - require.Equal(t, tc.service, service) - require.Equal(t, tc.method, method) - }) - } -} -- GitLab From 54cf2fea41d52680b0b167bf821f014faebef19a Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 13:50:49 +0200 Subject: [PATCH 17/35] go mod tidy --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6b781055..11fc2e05 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/getsentry/sentry-go v0.13.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/lightstep/lightstep-tracer-go v0.25.0 github.com/oklog/ulid/v2 v2.0.2 github.com/opentracing/opentracing-go v1.2.0 @@ -23,6 +24,7 @@ require ( golang.org/x/crypto v0.41.0 google.golang.org/api v0.162.0 google.golang.org/grpc v1.63.2 + google.golang.org/protobuf v1.34.2 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) @@ -54,7 +56,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -88,6 +89,5 @@ require ( google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect - google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f1792839..49724f07 100644 --- a/go.sum +++ b/go.sum @@ -325,8 +325,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M= @@ -757,8 +757,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 h1:DkD0plWEVUB8v/Ru6kRBW30Hy/fRNBC8hPdcExuBZMc= gopkg.in/DataDog/dd-trace-go.v1 v1.32.0/go.mod h1:wRKMf/tRASHwH/UOfPQ3IQmVFhTz2/1a1/mpXoIjF54= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -- GitLab From 08ae820efea4cd376303fee5d647744a9c293c0a Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:17:47 +0200 Subject: [PATCH 18/35] added request/response sizes --- metrics/grpc/metrics.go | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index f5ea3be8..30a989fc 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -1,21 +1,59 @@ package metricsgrpc import ( + "context" + "strings" + + middleware "github.com/grpc-ecosystem/go-grpc-middleware" prom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" + "github.com/prometheus/client_golang/prometheus" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) // ServerMetrics represents a set of gRPC metrics for a server. type ServerMetrics struct { *prom.ServerMetrics + reqSizeHistogram *prometheus.HistogramVec + respSizeHistogram *prometheus.HistogramVec +} + +// UnaryServerSizeInterceptor returns a new unary server interceptor that collects metrics. +func (m *ServerMetrics) UnaryServerSizeInterceptor() grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + service, method := splitMethodName(info.FullMethod) + + if p, ok := req.(proto.Message); ok { + m.reqSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) + } + + resp, err := handler(ctx, req) + + if p, ok := resp.(proto.Message); ok { + m.respSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) + } + + return resp, err + } } // UnaryServerInterceptor returns a new unary server interceptor that collects metrics. func (m *ServerMetrics) UnaryServerInterceptor(opts ...prom.Option) grpc.UnaryServerInterceptor { - return m.ServerMetrics.UnaryServerInterceptor(opts...) + return middleware.ChainUnaryServer( + m.ServerMetrics.UnaryServerInterceptor(opts...), + m.UnaryServerSizeInterceptor(), + ) } // StreamServerInterceptor returns a new stream server interceptor that collects metrics. func (m *ServerMetrics) StreamServerInterceptor(opts ...prom.Option) grpc.StreamServerInterceptor { return m.ServerMetrics.StreamServerInterceptor(opts...) } + +func splitMethodName(fullMethodName string) (string, string) { + if i := strings.LastIndex(fullMethodName, "/"); i >= 0 { + return fullMethodName[1:i], fullMethodName[i+1:] + } + return "unknown", "unknown" +} -- GitLab From a4d0650380e8b38fa24283f3cf1011eb9a1c770b Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:20:51 +0200 Subject: [PATCH 19/35] initialize histograms --- metrics/grpc/factory.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index 6f5e6a3c..ac82ea16 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -27,8 +27,42 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error } } + reqSizeHistogram := prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "grpc_server_request_size_bytes", + Help: "RPC request size in bytes.", + Buckets: prometheus.ExponentialBuckets(100, 10, 8), + Subsystem: factoryConfig.subsystem, + Namespace: factoryConfig.namespace, + }, + []string{"grpc_service", "grpc_method"}, + ) + respSizeHistogram := prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "grpc_server_response_size_bytes", + Help: "RPC response size in bytes.", + Buckets: prometheus.ExponentialBuckets(100, 10, 8), + Subsystem: factoryConfig.subsystem, + Namespace: factoryConfig.namespace, + }, + []string{"grpc_service", "grpc_method"}, + ) + + if err := prometheus.Register(reqSizeHistogram); err != nil { + if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { + return nil, err + } + } + if err := prometheus.Register(respSizeHistogram); err != nil { + if _, ok := err.(prometheus.AlreadyRegisteredError); !ok { + return nil, err + } + } + return &ServerMetrics{ - ServerMetrics: metrics, + ServerMetrics: metrics, + reqSizeHistogram: reqSizeHistogram, + respSizeHistogram: respSizeHistogram, }, nil }, nil } -- GitLab From 949037ded51c2c96ba820288e27216ad8cefbd44 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:27:22 +0200 Subject: [PATCH 20/35] naming --- metrics/grpc/factory.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index ac82ea16..33d2aa05 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -29,7 +29,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error reqSizeHistogram := prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Name: "grpc_server_request_size_bytes", + Name: "request_size_bytes", Help: "RPC request size in bytes.", Buckets: prometheus.ExponentialBuckets(100, 10, 8), Subsystem: factoryConfig.subsystem, @@ -39,7 +39,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error ) respSizeHistogram := prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Name: "grpc_server_response_size_bytes", + Name: "response_size_bytes", Help: "RPC response size in bytes.", Buckets: prometheus.ExponentialBuckets(100, 10, 8), Subsystem: factoryConfig.subsystem, -- GitLab From ce637ceee61b470802f6ed70f9c643e5d39d6a4e Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:33:19 +0200 Subject: [PATCH 21/35] unified labels --- metrics/grpc/factory.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index 33d2aa05..b252672b 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -16,7 +16,6 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error metrics := prom.NewServerMetrics( prom.WithServerHandlingTimeHistogram( prom.WithHistogramBuckets(factoryConfig.reqDurationBuckets), - prom.WithHistogramSubsystem(factoryConfig.subsystem), prom.WithHistogramNamespace(factoryConfig.namespace), ), ) @@ -35,7 +34,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error Subsystem: factoryConfig.subsystem, Namespace: factoryConfig.namespace, }, - []string{"grpc_service", "grpc_method"}, + []string{"grpc_service", "grpc_method", "grpc_type"}, ) respSizeHistogram := prometheus.NewHistogramVec( prometheus.HistogramOpts{ @@ -45,7 +44,7 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error Subsystem: factoryConfig.subsystem, Namespace: factoryConfig.namespace, }, - []string{"grpc_service", "grpc_method"}, + []string{"grpc_service", "grpc_method", "grpc_type"}, ) if err := prometheus.Register(reqSizeHistogram); err != nil { -- GitLab From bc1728b3e44f90b83ce04e44ee50f1133b1aa364 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:38:19 +0200 Subject: [PATCH 22/35] labels --- metrics/grpc/metrics.go | 66 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index 30a989fc..393f3f1e 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -25,13 +25,13 @@ func (m *ServerMetrics) UnaryServerSizeInterceptor() grpc.UnaryServerInterceptor service, method := splitMethodName(info.FullMethod) if p, ok := req.(proto.Message); ok { - m.reqSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) + m.reqSizeHistogram.WithLabelValues(service, method, "unary").Observe(float64(proto.Size(p))) } resp, err := handler(ctx, req) if p, ok := resp.(proto.Message); ok { - m.respSizeHistogram.WithLabelValues(service, method).Observe(float64(proto.Size(p))) + m.respSizeHistogram.WithLabelValues(service, method, "unary").Observe(float64(proto.Size(p))) } return resp, err @@ -48,7 +48,67 @@ func (m *ServerMetrics) UnaryServerInterceptor(opts ...prom.Option) grpc.UnarySe // StreamServerInterceptor returns a new stream server interceptor that collects metrics. func (m *ServerMetrics) StreamServerInterceptor(opts ...prom.Option) grpc.StreamServerInterceptor { - return m.ServerMetrics.StreamServerInterceptor(opts...) + return middleware.ChainStreamServer( + m.ServerMetrics.StreamServerInterceptor(opts...), + m.StreamServerSizeInterceptor(), + ) +} + +// StreamServerSizeInterceptor returns a new stream server interceptor that collects metrics. +func (m *ServerMetrics) StreamServerSizeInterceptor() grpc.StreamServerInterceptor { + return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + service, method := splitMethodName(info.FullMethod) + streamType := streamType(info) + + wrapped := middleware.WrapServerStream(ss) + wrapped.WrappedContext = ss.Context() + + ws := &wrappedServerStream{ + ServerStream: wrapped, + reqSizeHistogram: m.reqSizeHistogram, + respSizeHistogram: m.respSizeHistogram, + service: service, + method: method, + streamType: streamType, + } + + return handler(srv, ws) + } +} + +type wrappedServerStream struct { + grpc.ServerStream + reqSizeHistogram *prometheus.HistogramVec + respSizeHistogram *prometheus.HistogramVec + service string + method string + streamType string +} + +func (w *wrappedServerStream) SendMsg(m any) error { + if p, ok := m.(proto.Message); ok { + w.respSizeHistogram.WithLabelValues(w.service, w.method, w.streamType).Observe(float64(proto.Size(p))) + } + return w.ServerStream.SendMsg(m) +} + +func (w *wrappedServerStream) RecvMsg(m any) error { + err := w.ServerStream.RecvMsg(m) + if err == nil { + if p, ok := m.(proto.Message); ok { + w.reqSizeHistogram.WithLabelValues(w.service, w.method, w.streamType).Observe(float64(proto.Size(p))) + } + } + return err +} + +func streamType(info *grpc.StreamServerInfo) string { + if info.IsClientStream && !info.IsServerStream { + return "client_stream" + } else if !info.IsClientStream && info.IsServerStream { + return "server_stream" + } + return "bidi_stream" } func splitMethodName(fullMethodName string) (string, string) { -- GitLab From 891674b10dd67ea9447f266fc0aa69fd44b6575d Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:53:42 +0200 Subject: [PATCH 23/35] added namespace --- metrics/grpc/factory.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index b252672b..62b51ede 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -14,9 +14,11 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error return func() (*ServerMetrics, error) { metrics := prom.NewServerMetrics( + prom.WithServerCounterOptions(prom.WithNamespace(factoryConfig.namespace), prom.WithSubsystem(factoryConfig.subsystem)), prom.WithServerHandlingTimeHistogram( prom.WithHistogramBuckets(factoryConfig.reqDurationBuckets), prom.WithHistogramNamespace(factoryConfig.namespace), + prom.WithHistogramSubsystem(factoryConfig.subsystem), ), ) -- GitLab From b49cfdbd6ca8e2243666ac42839400ad2bab611b Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Thu, 16 Oct 2025 17:55:50 +0200 Subject: [PATCH 24/35] redundant subsystem --- metrics/grpc/factory.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metrics/grpc/factory.go b/metrics/grpc/factory.go index 62b51ede..9400465e 100644 --- a/metrics/grpc/factory.go +++ b/metrics/grpc/factory.go @@ -14,11 +14,10 @@ func NewServerMetricsFactory(opts ...FactoryOption) (ServerMetricsFactory, error return func() (*ServerMetrics, error) { metrics := prom.NewServerMetrics( - prom.WithServerCounterOptions(prom.WithNamespace(factoryConfig.namespace), prom.WithSubsystem(factoryConfig.subsystem)), + prom.WithServerCounterOptions(prom.WithNamespace(factoryConfig.namespace)), prom.WithServerHandlingTimeHistogram( prom.WithHistogramBuckets(factoryConfig.reqDurationBuckets), prom.WithHistogramNamespace(factoryConfig.namespace), - prom.WithHistogramSubsystem(factoryConfig.subsystem), ), ) -- GitLab From 842828ea244afcd8c3291a5f399eb452465ae67e Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 11:06:50 +0200 Subject: [PATCH 25/35] format file --- metrics/grpc/factory_options.go | 1 - metrics/grpc/factory_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/metrics/grpc/factory_options.go b/metrics/grpc/factory_options.go index ac33e5f7..3d470212 100644 --- a/metrics/grpc/factory_options.go +++ b/metrics/grpc/factory_options.go @@ -65,4 +65,3 @@ func WithReqDurationBuckets(buckets []float64) FactoryOption { c.reqDurationBuckets = buckets } } - diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index da721bc4..6078aff3 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -5,7 +5,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/labkit/metrics/grpc" + metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" ) func TestNewServerMetricsFactory(t *testing.T) { -- GitLab From 6178e58736fba5df3d9c03c912552637487f2144 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 11:23:19 +0200 Subject: [PATCH 26/35] fixing tests --- metrics/grpc/factory_options_test.go | 2 ++ metrics/grpc/metrics_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/metrics/grpc/factory_options_test.go b/metrics/grpc/factory_options_test.go index aea01487..eae75cd9 100644 --- a/metrics/grpc/factory_options_test.go +++ b/metrics/grpc/factory_options_test.go @@ -24,6 +24,8 @@ func TestApplyFactoryOptions(t *testing.T) { reqSizeBuckets: prometheus.DefBuckets, respSizeBuckets: prometheus.DefBuckets, reqDurationBuckets: prometheus.DefBuckets, + namespace: "gitlab", // default namespace + subsystem: "grpc", // default subsystem }, }, { diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index ca355ca1..f72c2804 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -42,8 +42,8 @@ func TestInterceptors(t *testing.T) { _, err := client.Check(context.Background(), &grpc_health_v1.HealthCheckRequest{}) require.NoError(t, err) - require.Equal(t, 1, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handled_total")) - require.Equal(t, 1, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handling_seconds")) + require.Equal(t, 1, testutil.CollectAndCount(metrics.ServerMetrics, "gitlab_grpc_server_handled_total")) + require.Equal(t, 1, testutil.CollectAndCount(metrics.ServerMetrics, "gitlab_grpc_server_handling_seconds")) }) t.Run("Stream", func(t *testing.T) { @@ -63,8 +63,8 @@ func TestInterceptors(t *testing.T) { } } - require.Equal(t, 2, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handled_total")) - require.Equal(t, 2, testutil.CollectAndCount(metrics.ServerMetrics, "grpc_server_handling_seconds")) + require.Equal(t, 2, testutil.CollectAndCount(metrics.ServerMetrics, "gitlab_grpc_server_handled_total")) + require.Equal(t, 2, testutil.CollectAndCount(metrics.ServerMetrics, "gitlab_grpc_server_handling_seconds")) }) } -- GitLab From 9a364de38431b9c039cd800c5bd63137928dbd12 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 11:31:41 +0200 Subject: [PATCH 27/35] lint --- metrics/grpc/metrics.go | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics/grpc/metrics.go b/metrics/grpc/metrics.go index 393f3f1e..bf724431 100644 --- a/metrics/grpc/metrics.go +++ b/metrics/grpc/metrics.go @@ -7,7 +7,6 @@ import ( middleware "github.com/grpc-ecosystem/go-grpc-middleware" prom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "github.com/prometheus/client_golang/prometheus" - "google.golang.org/grpc" "google.golang.org/protobuf/proto" ) -- GitLab From fc36c1eeaf8e9b646c5dba3329e2c3594dc9bf16 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 11:38:04 +0200 Subject: [PATCH 28/35] goimports --- metrics/grpc/metrics_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/grpc/metrics_test.go b/metrics/grpc/metrics_test.go index f72c2804..b2654240 100644 --- a/metrics/grpc/metrics_test.go +++ b/metrics/grpc/metrics_test.go @@ -7,7 +7,7 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/labkit/metrics/grpc" + metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/health/grpc_health_v1" -- GitLab From b7ac873bc11c43b530714efc889830cbcc0f8a48 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 12:49:43 +0200 Subject: [PATCH 29/35] added test --- metrics/grpc/factory_test.go | 46 ++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index 6078aff3..89dccbe5 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -1,16 +1,40 @@ package metricsgrpc_test import ( + "errors" "testing" "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" ) -func TestNewServerMetricsFactory(t *testing.T) { - t.Parallel() +// fakeRegistry is a mock prometheus.Registerer for testing error handling +type fakeRegistry struct { + errOnRegister error +} +func (r *fakeRegistry) Register(c prometheus.Collector) error { + if r.errOnRegister != nil { + return r.errOnRegister + } + return nil +} + +func (r *fakeRegistry) MustRegister(c ...prometheus.Collector) { + // Do nothing for the test +} + +func (r *fakeRegistry) Unregister(c prometheus.Collector) bool { + return true +} + +func (r *fakeRegistry) Gather() ([]*dto.MetricFamily, error) { + return nil, nil +} + +func TestNewServerMetricsFactory(t *testing.T) { // Unregister all collectors to ensure a clean state for the test prometheus.DefaultRegisterer = prometheus.NewRegistry() @@ -29,3 +53,21 @@ func TestNewServerMetricsFactory(t *testing.T) { require.NoError(t, err) require.NotNil(t, metrics) } + +func TestNewServerMetricsFactory_RegistrationError(t *testing.T) { + // We create a fake registry that will return a specific error + expectedErr := errors.New("a generic registration error") + prometheus.DefaultRegisterer = &fakeRegistry{errOnRegister: expectedErr} + defer func() { + // Restore the default registerer + prometheus.DefaultRegisterer = prometheus.NewRegistry() + }() + + factory, err := metricsgrpc.NewServerMetricsFactory() + require.NoError(t, err) + + // The factory call should now fail with our specific error + _, err = factory() + require.Error(t, err) + require.Equal(t, expectedErr, err) +} -- GitLab From 7d6126b63857011a96b78078833015035641220e Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 12:55:10 +0200 Subject: [PATCH 30/35] added more tests --- metrics/grpc/factory_test.go | 59 ++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index 89dccbe5..26c57a8c 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -2,6 +2,7 @@ package metricsgrpc_test import ( "errors" + "strings" "testing" "github.com/prometheus/client_golang/prometheus" @@ -12,12 +13,24 @@ import ( // fakeRegistry is a mock prometheus.Registerer for testing error handling type fakeRegistry struct { - errOnRegister error + errOnRegister error + errOnRegisterFQN string } func (r *fakeRegistry) Register(c prometheus.Collector) error { if r.errOnRegister != nil { - return r.errOnRegister + // To get the name of the collector, we need to describe it and check the FQName + ch := make(chan *prometheus.Desc) + go func() { + c.Describe(ch) + close(ch) + }() + + for desc := range ch { + if r.errOnRegisterFQN == "" || strings.Contains(desc.String(), r.errOnRegisterFQN) { + return r.errOnRegister + } + } } return nil } @@ -71,3 +84,45 @@ func TestNewServerMetricsFactory_RegistrationError(t *testing.T) { require.Error(t, err) require.Equal(t, expectedErr, err) } + +func TestNewServerMetricsFactory_RequestSizeHistogramError(t *testing.T) { + // We create a fake registry that will return a specific error + expectedErr := errors.New("a generic registration error") + prometheus.DefaultRegisterer = &fakeRegistry{ + errOnRegister: expectedErr, + errOnRegisterFQN: "request_size_bytes", + } + defer func() { + // Restore the default registerer + prometheus.DefaultRegisterer = prometheus.NewRegistry() + }() + + factory, err := metricsgrpc.NewServerMetricsFactory() + require.NoError(t, err) + + // The factory call should now fail with our specific error + _, err = factory() + require.Error(t, err) + require.Equal(t, expectedErr, err) +} + +func TestNewServerMetricsFactory_ResponseSizeHistogramError(t *testing.T) { + // We create a fake registry that will return a specific error + expectedErr := errors.New("a generic registration error") + prometheus.DefaultRegisterer = &fakeRegistry{ + errOnRegister: expectedErr, + errOnRegisterFQN: "response_size_bytes", + } + defer func() { + // Restore the default registerer + prometheus.DefaultRegisterer = prometheus.NewRegistry() + }() + + factory, err := metricsgrpc.NewServerMetricsFactory() + require.NoError(t, err) + + // The factory call should now fail with our specific error + _, err = factory() + require.Error(t, err) + require.Equal(t, expectedErr, err) +} -- GitLab From 09061055a87afd3da4a4b34a0803b49f9281d2f4 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 12:59:34 +0200 Subject: [PATCH 31/35] lint --- metrics/grpc/factory_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index 26c57a8c..b66b33ba 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -11,7 +11,7 @@ import ( metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" ) -// fakeRegistry is a mock prometheus.Registerer for testing error handling +// fakeRegistry is a mock prometheus.Registerer for testing error handling. type fakeRegistry struct { errOnRegister error errOnRegisterFQN string -- GitLab From 6d0b53ec5ea56e92e111626f4b433ff0a8777f9e Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Fri, 17 Oct 2025 13:00:44 +0200 Subject: [PATCH 32/35] removed comment --- metrics/grpc/factory_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index b66b33ba..bf40f754 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -11,7 +11,6 @@ import ( metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" ) -// fakeRegistry is a mock prometheus.Registerer for testing error handling. type fakeRegistry struct { errOnRegister error errOnRegisterFQN string -- GitLab From 8c4eef83996dd3269a54ab1f7b88b4ef653b21a1 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Mon, 20 Oct 2025 11:38:23 +0200 Subject: [PATCH 33/35] default namespace and subsystem --- metrics/grpc/factory_options.go | 7 +++++-- metrics/grpc/factory_options_test.go | 4 ++-- metrics/grpc/factory_test.go | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/metrics/grpc/factory_options.go b/metrics/grpc/factory_options.go index 3d470212..41fdaf01 100644 --- a/metrics/grpc/factory_options.go +++ b/metrics/grpc/factory_options.go @@ -13,10 +13,13 @@ type factoryConfig struct { reqDurationBuckets []float64 } +const defaultNamespace = "gitlab" +const defaultSubsystem = "grpc" + func defaultFactoryConfig() *factoryConfig { return &factoryConfig{ - namespace: "gitlab", - subsystem: "grpc", + namespace: defaultNamespace, + subsystem: defaultSubsystem, reqSizeBuckets: prometheus.DefBuckets, respSizeBuckets: prometheus.DefBuckets, reqDurationBuckets: prometheus.DefBuckets, diff --git a/metrics/grpc/factory_options_test.go b/metrics/grpc/factory_options_test.go index eae75cd9..2e93c3c9 100644 --- a/metrics/grpc/factory_options_test.go +++ b/metrics/grpc/factory_options_test.go @@ -24,8 +24,8 @@ func TestApplyFactoryOptions(t *testing.T) { reqSizeBuckets: prometheus.DefBuckets, respSizeBuckets: prometheus.DefBuckets, reqDurationBuckets: prometheus.DefBuckets, - namespace: "gitlab", // default namespace - subsystem: "grpc", // default subsystem + namespace: defaultNamespace, + subsystem: defaultSubsystem, // default subsystem }, }, { diff --git a/metrics/grpc/factory_test.go b/metrics/grpc/factory_test.go index bf40f754..b66b33ba 100644 --- a/metrics/grpc/factory_test.go +++ b/metrics/grpc/factory_test.go @@ -11,6 +11,7 @@ import ( metricsgrpc "gitlab.com/gitlab-org/labkit/metrics/grpc" ) +// fakeRegistry is a mock prometheus.Registerer for testing error handling. type fakeRegistry struct { errOnRegister error errOnRegisterFQN string -- GitLab From e1121541152025bf46835852f8b095725cb5204f Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Mon, 20 Oct 2025 12:23:21 +0200 Subject: [PATCH 34/35] combined test cases --- metrics/grpc/factory_options_test.go | 93 +++++++++++++--------------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/metrics/grpc/factory_options_test.go b/metrics/grpc/factory_options_test.go index 2e93c3c9..c210d030 100644 --- a/metrics/grpc/factory_options_test.go +++ b/metrics/grpc/factory_options_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestApplyFactoryOptions(t *testing.T) { +func TestFactoryOptions(t *testing.T) { t.Parallel() testBuckets := []float64{1, 2, 3} @@ -25,7 +25,7 @@ func TestApplyFactoryOptions(t *testing.T) { respSizeBuckets: prometheus.DefBuckets, reqDurationBuckets: prometheus.DefBuckets, namespace: defaultNamespace, - subsystem: defaultSubsystem, // default subsystem + subsystem: defaultSubsystem, }, }, { @@ -45,72 +45,67 @@ func TestApplyFactoryOptions(t *testing.T) { reqDurationBuckets: testBuckets, }, }, - } - - for _, tc := range testCases { - t.Run(tc.desc, func(t *testing.T) { - cfg := applyFactoryOptions(tc.opts) - require.Equal(t, tc.expected, cfg) - }) - } -} - -func TestFactoryOptions(t *testing.T) { - t.Parallel() - - testBuckets := []float64{1, 2, 3} - - testCases := []struct { - desc string - opt FactoryOption - assert func(t *testing.T, cfg *factoryConfig) - }{ { - "WithNamespace", - WithNamespace("test_namespace"), - func(t *testing.T, cfg *factoryConfig) { - t.Helper() - require.Equal(t, "test_namespace", cfg.namespace) + desc: "with namespace", + opts: []FactoryOption{WithNamespace("test_namespace")}, + expected: &factoryConfig{ + namespace: "test_namespace", + subsystem: defaultSubsystem, + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: prometheus.DefBuckets, + reqDurationBuckets: prometheus.DefBuckets, }, }, { - "WithSubsystem", - WithSubsystem("test_subsystem"), - func(t *testing.T, cfg *factoryConfig) { - t.Helper() - require.Equal(t, "test_subsystem", cfg.subsystem) + desc: "with subsystem", + opts: []FactoryOption{WithSubsystem("test_subsystem")}, + expected: &factoryConfig{ + namespace: defaultNamespace, + subsystem: "test_subsystem", + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: prometheus.DefBuckets, + reqDurationBuckets: prometheus.DefBuckets, }, }, { - "WithReqSizeBuckets", - WithReqSizeBuckets(testBuckets), - func(t *testing.T, cfg *factoryConfig) { - t.Helper() - require.Equal(t, testBuckets, cfg.reqSizeBuckets) + desc: "with request size buckets", + opts: []FactoryOption{WithReqSizeBuckets(testBuckets)}, + expected: &factoryConfig{ + namespace: defaultNamespace, + subsystem: defaultSubsystem, + reqSizeBuckets: testBuckets, + respSizeBuckets: prometheus.DefBuckets, + reqDurationBuckets: prometheus.DefBuckets, }, }, { - "WithRespSizeBuckets", - WithRespSizeBuckets(testBuckets), - func(t *testing.T, cfg *factoryConfig) { - t.Helper() - require.Equal(t, testBuckets, cfg.respSizeBuckets) + desc: "with response size buckets", + opts: []FactoryOption{WithRespSizeBuckets(testBuckets)}, + expected: &factoryConfig{ + namespace: defaultNamespace, + subsystem: defaultSubsystem, + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: testBuckets, + reqDurationBuckets: prometheus.DefBuckets, }, }, { - "WithReqDurationBuckets", - WithReqDurationBuckets(testBuckets), - func(t *testing.T, cfg *factoryConfig) { - t.Helper() - require.Equal(t, testBuckets, cfg.reqDurationBuckets) + desc: "with request duration buckets", + opts: []FactoryOption{WithReqDurationBuckets(testBuckets)}, + expected: &factoryConfig{ + namespace: defaultNamespace, + subsystem: defaultSubsystem, + reqSizeBuckets: prometheus.DefBuckets, + respSizeBuckets: prometheus.DefBuckets, + reqDurationBuckets: testBuckets, }, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - cfg := applyFactoryOptions([]FactoryOption{tc.opt}) - tc.assert(t, cfg) + cfg := applyFactoryOptions(tc.opts) + require.Equal(t, tc.expected, cfg) }) } } -- GitLab From eb0aa35547e612b15c560437c269b9a9357de2b3 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Mon, 20 Oct 2025 12:25:28 +0200 Subject: [PATCH 35/35] upgrade go middleware --- go.mod | 12 ++++++------ go.sum | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 11fc2e05..1cd8f4cf 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/getsentry/sentry-go v0.13.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 github.com/lightstep/lightstep-tracer-go v0.25.0 github.com/oklog/ulid/v2 v2.0.2 github.com/opentracing/opentracing-go v1.2.0 @@ -23,14 +23,14 @@ require ( go.opencensus.io v0.24.0 golang.org/x/crypto v0.41.0 google.golang.org/api v0.162.0 - google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.34.2 + google.golang.org/grpc v1.67.1 + google.golang.org/protobuf v1.36.6 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) require ( cloud.google.com/go v0.112.0 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/monitoring v1.18.0 // indirect cloud.google.com/go/trace v1.10.5 // indirect github.com/DataDog/datadog-go v4.4.0+incompatible // indirect @@ -87,7 +87,7 @@ require ( golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 49724f07..be5f4ec5 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= @@ -93,8 +93,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg= +github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -107,8 +107,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -228,8 +228,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaW github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -712,10 +712,10 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= 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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -741,8 +741,8 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= 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= @@ -757,8 +757,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 h1:DkD0plWEVUB8v/Ru6kRBW30Hy/fRNBC8hPdcExuBZMc= gopkg.in/DataDog/dd-trace-go.v1 v1.32.0/go.mod h1:wRKMf/tRASHwH/UOfPQ3IQmVFhTz2/1a1/mpXoIjF54= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -- GitLab