diff --git a/go.mod b/go.mod index dda720d3fd8bcb0198881de20a4568223d7c7a0a..a01a27947e8c69d8caa1bc601ddc8af92bb7dccc 100644 --- a/go.mod +++ b/go.mod @@ -16,13 +16,16 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 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.opentelemetry.io/otel/metric v1.38.0 + go.opentelemetry.io/otel/sdk/metric v1.38.0 golang.org/x/crypto v0.41.0 google.golang.org/api v0.54.0 - google.golang.org/grpc v1.67.1 + google.golang.org/grpc v1.68.0 + google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 google.golang.org/protobuf v1.36.6 gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 ) @@ -41,10 +44,12 @@ 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/go-logr/logr v1.4.3 // 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-20200121045136-8c9f03a8e57e // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/pprof v0.0.0-20210804190019-f964ff605595 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect @@ -65,6 +70,10 @@ 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/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.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 diff --git a/go.sum b/go.sum index 7653da72ae084751ff43791c522d6a3cc5561135..808a20e6f282023392eea98dcd579b6c7834496f 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg= +cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -85,6 +87,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-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/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= @@ -96,7 +100,11 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 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/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +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/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= @@ -111,6 +119,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.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/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= @@ -148,8 +161,9 @@ 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= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -165,8 +179,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -259,6 +273,8 @@ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= @@ -272,8 +288,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a h1:iLcLb5Fwwz7g/DLK89F+uQBDeAhHhwdzB5fSlVdhGcM= github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0= github.com/shirou/gopsutil/v3 v3.21.2 h1:fIOk3hyqV1oGKogfGNjUZa0lUbtlkx3+ZT0IoJth2uM= @@ -292,8 +308,8 @@ 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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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= @@ -320,6 +336,18 @@ 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.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= 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= @@ -691,9 +719,11 @@ 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.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 h1:hUfOButuEtpc0UvYiaYRbNwxVYr0mQQOWq6X8beJ9Gc= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg= 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= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/metrics/doc.go b/metrics/doc.go index ebfa6e782c5cf69e0d0490159d1d779ad6a8b321..cbd0a7c9ded7fe14e188ac55aca87ab95b5b785d 100644 --- a/metrics/doc.go +++ b/metrics/doc.go @@ -5,5 +5,14 @@ Package metrics is the primary entrypoint into LabKit's metrics utilities. - Provides http middlewares for recording metrics for an http server - Provides a collector for sql.DBStats metrics +- Provides OpenTelemetry-based gRPC metrics for both client and server instrumentation + +# gRPC Metrics + +The grpc subpackage provides OpenTelemetry instrumentation for gRPC applications +without requiring a global metrics registry. Clients must explicitly provide a +MeterProvider when creating metrics. + +See the grpc subpackage documentation for detailed usage examples. */ package metrics diff --git a/metrics/grpc/client.go b/metrics/grpc/client.go new file mode 100644 index 0000000000000000000000000000000000000000..97c6b381ebbe16a9150d32592eb630cddef79db2 --- /dev/null +++ b/metrics/grpc/client.go @@ -0,0 +1,31 @@ +package grpc + +import ( + "google.golang.org/grpc" + "google.golang.org/grpc/stats/opentelemetry" +) + +// NewClientMetrics creates a gRPC dial option for client-side metrics collection. +// The returned DialOption enables OpenTelemetry instrumentation for a gRPC client connection. +// +// The MeterProvider must be provided via WithMeterProvider option, otherwise +// no metrics will be recorded. +// +// Example: +// +// dialOpt := NewClientMetrics( +// WithMeterProvider(meterProvider), +// ) +// conn, err := grpc.NewClient("target", dialOpt) +func NewClientMetrics(opts ...MetricsOption) grpc.DialOption { + config := applyMetricsOptions(opts) + + // Create the OpenTelemetry options + otelOpts := opentelemetry.Options{ + MetricsOptions: opentelemetry.MetricsOptions{ + MeterProvider: config.meterProvider, + }, + } + + return opentelemetry.DialOption(otelOpts) +} diff --git a/metrics/grpc/client_test.go b/metrics/grpc/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c23a299a81cb3601eecd26d6773a178bb60a2ba7 --- /dev/null +++ b/metrics/grpc/client_test.go @@ -0,0 +1,56 @@ +package grpc + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/sdk/metric" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func TestNewClientMetrics(t *testing.T) { + tests := []struct { + name string + opts []MetricsOption + wantErr bool + }{ + { + name: "metrics without meter provider", + opts: []MetricsOption{}, + }, + { + name: "metrics with meter provider", + opts: []MetricsOption{ + WithMeterProvider(metric.NewMeterProvider()), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dialOpt := NewClientMetrics(tt.opts...) + assert.NotNil(t, dialOpt) + }) + } +} + +func TestClientMetrics_CanCreateConnection(t *testing.T) { + mp := metric.NewMeterProvider() + dialOpt := NewClientMetrics(WithMeterProvider(mp)) + assert.NotNil(t, dialOpt) + + // Create a gRPC client connection with metrics enabled + // Note: We're not actually connecting to a real server, just verifying the dial option is valid + cc, err := grpc.NewClient( + "localhost:9999", + dialOpt, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + + // The connection may fail due to connection issues, but that's expected + // We're just verifying that the dial option is valid and can be used + if err == nil { + defer cc.Close() + } +} diff --git a/metrics/grpc/doc.go b/metrics/grpc/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..9b57c5e4e30ac998edc54430bd9ec37d593b5db3 --- /dev/null +++ b/metrics/grpc/doc.go @@ -0,0 +1,38 @@ +/* +Package grpc provides OpenTelemetry instrumentation for gRPC clients and servers. + +This package uses the official OpenTelemetry gRPC metrics instrumentation from +google.golang.org/grpc/stats/opentelemetry, allowing applications to collect +metrics without relying on a global metrics registry. + +# Client-Side Metrics + +To instrument a gRPC client, create a dial option using NewClientMetrics: + + dialOption := grpc.NewClientMetrics( + grpc.WithMeterProvider(meterProvider), + ) + conn, err := grpc.NewClient("target", dialOption) + +# Server-Side Metrics + +To instrument a gRPC server, create a server option using NewServerMetrics: + + serverOption := grpc.NewServerMetrics( + grpc.WithMeterProvider(meterProvider), + ) + server := grpc.NewServer(serverOption) + +# Metrics Registration + +Unlike traditional metrics packages that use a global registry, this package +requires clients to explicitly provide a MeterProvider. This allows for: + +- Better control over metric collection +- Support for multiple isolated metric collectors +- Cleaner testing without global state +- More flexible deployment scenarios + +The MeterProvider should be configured with appropriate views and bounds as needed. +*/ +package grpc diff --git a/metrics/grpc/examples_test.go b/metrics/grpc/examples_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9c4a99f3d90de67ad6e79238ccfeb404f8c21d8f --- /dev/null +++ b/metrics/grpc/examples_test.go @@ -0,0 +1,210 @@ +package grpc + +import ( + "fmt" + "log" + "net" + "testing" + + "go.opentelemetry.io/otel/sdk/metric" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func ExampleNewClientMetrics() { + // Create a meter provider - in production, you would configure this + // with appropriate exporters (e.g., Prometheus, OTLP) + meterProvider := metric.NewMeterProvider() + + // Create a client metrics dial option with the meter provider + dialOption := NewClientMetrics( + WithMeterProvider(meterProvider), + ) + + // Create a gRPC client connection with metrics enabled + conn, err := grpc.NewClient( + "localhost:50051", + dialOption, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + log.Printf("failed to create client: %v", err) + return + } + defer conn.Close() + + fmt.Println("Client created with metrics collection enabled") +} + +func ExampleNewServerMetrics() { + // Create a meter provider - in production, you would configure this + // with appropriate exporters (e.g., Prometheus, OTLP) + meterProvider := metric.NewMeterProvider() + + // Create a server metrics option with the meter provider + serverOption := NewServerMetrics( + WithMeterProvider(meterProvider), + ) + + // Create a gRPC server with metrics enabled + server := grpc.NewServer(serverOption) + defer server.Stop() + + fmt.Println("Server created with metrics collection enabled") +} + +func ExampleWithMeterProvider() { + // Create a custom meter provider with specific configuration + meterProvider := metric.NewMeterProvider() + + // Create a client dial option with the meter provider + dialOption := NewClientMetrics( + WithMeterProvider(meterProvider), + ) + _ = dialOption + + fmt.Println("Client dial option created with custom meter provider") +} + +func TestExampleClientServerIntegration(t *testing.T) { + // This test demonstrates client and server metrics working together + meterProvider := metric.NewMeterProvider() + + // Create server with metrics + serverOption := NewServerMetrics( + WithMeterProvider(meterProvider), + ) + + server := grpc.NewServer(serverOption) + defer server.Stop() + + // Listen on a random port + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("failed to listen: %v", err) + } + + // Start server in a goroutine + go func() { + if err := server.Serve(listener); err != nil { + log.Printf("server error: %v", err) + } + }() + + // Create client with metrics + dialOption := NewClientMetrics( + WithMeterProvider(meterProvider), + ) + + conn, err := grpc.NewClient( + listener.Addr().String(), + dialOption, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("failed to create client: %v", err) + } + defer conn.Close() + + // Verify connection is established + if conn == nil { + t.Fatal("connection is nil") + } +} + +func TestExampleMultipleMetricsOptions(t *testing.T) { + // Demonstrate creating multiple metrics options with different configurations + mp1 := metric.NewMeterProvider() + mp2 := metric.NewMeterProvider() + + // Create first client metrics option + dialOpt1 := NewClientMetrics( + WithMeterProvider(mp1), + ) + + // Create second client metrics option with different configuration + dialOpt2 := NewClientMetrics( + WithMeterProvider(mp2), + ) + + if dialOpt1 == nil || dialOpt2 == nil { + t.Fatal("dial options are nil") + } +} + +func TestExampleClientMetricsReusability(t *testing.T) { + // Demonstrate that the same dial option can be reused for multiple connections + meterProvider := metric.NewMeterProvider() + dialOption := NewClientMetrics( + WithMeterProvider(meterProvider), + ) + + // Create multiple connections with the same dial option + conn1, err := grpc.NewClient( + "localhost:9999", + dialOption, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err == nil { + defer conn1.Close() + } + + conn2, err := grpc.NewClient( + "localhost:9998", + dialOption, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err == nil { + defer conn2.Close() + } + + // The same dial option was used for both connections + if dialOption == nil { + t.Fatal("dial option is nil") + } +} + +func TestExampleServerMetricsReusability(t *testing.T) { + // Demonstrate that the same server option can be reused for multiple servers + meterProvider := metric.NewMeterProvider() + serverOption := NewServerMetrics( + WithMeterProvider(meterProvider), + ) + + // Create multiple servers with the same server option + server1 := grpc.NewServer(serverOption) + defer server1.Stop() + + server2 := grpc.NewServer(serverOption) + defer server2.Stop() + + // Both servers are using the same meter provider and metrics + if server1 == nil || server2 == nil { + t.Fatal("servers are nil") + } +} + +func TestExampleNoGlobalRegistry(t *testing.T) { + // This test demonstrates that metrics are NOT registered in a global registry + // Instead, they are collected via the MeterProvider passed to the functions + + meterProvider := metric.NewMeterProvider() + + // Create server option - this does NOT register metrics globally + serverOption := NewServerMetrics( + WithMeterProvider(meterProvider), + ) + + // Create multiple servers with the same server option + server1 := grpc.NewServer(serverOption) + defer server1.Stop() + + server2 := grpc.NewServer(serverOption) + defer server2.Stop() + + // Both servers are using the same meter provider, not a global registry + // This allows for better isolation and testing + if server1 == nil || server2 == nil { + t.Fatal("servers are nil") + } +} diff --git a/metrics/grpc/options.go b/metrics/grpc/options.go new file mode 100644 index 0000000000000000000000000000000000000000..fc2d8d20432ada6dc3b1df5f8fbb386cfdb6d644 --- /dev/null +++ b/metrics/grpc/options.go @@ -0,0 +1,44 @@ +package grpc + +import ( + "go.opentelemetry.io/otel/metric" +) + +// MetricsOption is used to configure a metrics factory. +type MetricsOption interface { + apply(c *metricsConfig) +} + +type metricsConfig struct { + meterProvider metric.MeterProvider +} + +type funcMetricsOption struct { + f func(*metricsConfig) +} + +func (fdo funcMetricsOption) apply(do *metricsConfig) { + fdo.f(do) +} + +func newFuncMetricsOption(f func(*metricsConfig)) funcMetricsOption { + return funcMetricsOption{ + f: f, + } +} + +// WithMeterProvider sets the MeterProvider to use for creating instruments. +// This is required for metrics collection. If not set, no metrics will be recorded. +func WithMeterProvider(mp metric.MeterProvider) MetricsOption { + return newFuncMetricsOption(func(c *metricsConfig) { + c.meterProvider = mp + }) +} + +func applyMetricsOptions(opts []MetricsOption) *metricsConfig { + config := &metricsConfig{} + for _, opt := range opts { + opt.apply(config) + } + return config +} diff --git a/metrics/grpc/server.go b/metrics/grpc/server.go new file mode 100644 index 0000000000000000000000000000000000000000..ea5d5dbe2c86fbbe1ced9446e59260902d77303c --- /dev/null +++ b/metrics/grpc/server.go @@ -0,0 +1,31 @@ +package grpc + +import ( + "google.golang.org/grpc" + "google.golang.org/grpc/stats/opentelemetry" +) + +// NewServerMetrics creates a gRPC server option for server-side metrics collection. +// The returned ServerOption enables OpenTelemetry instrumentation for a gRPC server. +// +// The MeterProvider must be provided via WithMeterProvider option, otherwise +// no metrics will be recorded. +// +// Example: +// +// serverOpt := NewServerMetrics( +// WithMeterProvider(meterProvider), +// ) +// server := grpc.NewServer(serverOpt) +func NewServerMetrics(opts ...MetricsOption) grpc.ServerOption { + config := applyMetricsOptions(opts) + + // Create the OpenTelemetry options + otelOpts := opentelemetry.Options{ + MetricsOptions: opentelemetry.MetricsOptions{ + MeterProvider: config.meterProvider, + }, + } + + return opentelemetry.ServerOption(otelOpts) +} diff --git a/metrics/grpc/server_test.go b/metrics/grpc/server_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6bc3dc0b5e63a5d8f4eb1fe669f82304c86f2c15 --- /dev/null +++ b/metrics/grpc/server_test.go @@ -0,0 +1,81 @@ +package grpc + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/sdk/metric" + "google.golang.org/grpc" +) + +func TestNewServerMetrics(t *testing.T) { + tests := []struct { + name string + opts []MetricsOption + wantErr bool + }{ + { + name: "metrics without meter provider", + opts: []MetricsOption{}, + }, + { + name: "metrics with meter provider", + opts: []MetricsOption{ + WithMeterProvider(metric.NewMeterProvider()), + }, + }, + { + name: "metrics with method attribute filter", + opts: []MetricsOption{ + WithMeterProvider(metric.NewMeterProvider()), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + serverOpt := NewServerMetrics(tt.opts...) + assert.NotNil(t, serverOpt) + }) + } +} + +func TestServerMetrics_CanCreateServer(t *testing.T) { + mp := metric.NewMeterProvider() + serverOpt := NewServerMetrics(WithMeterProvider(mp)) + assert.NotNil(t, serverOpt) + + // Verify it's a valid grpc.ServerOption by attempting to use it + server := grpc.NewServer(serverOpt) + assert.NotNil(t, server) + server.Stop() +} + +func TestServerMetrics_WithAllOptions(t *testing.T) { + mp := metric.NewMeterProvider() + + serverOpt := NewServerMetrics( + WithMeterProvider(mp), + ) + + assert.NotNil(t, serverOpt) + + server := grpc.NewServer(serverOpt) + assert.NotNil(t, server) + server.Stop() +} + +func TestServerMetrics_MultipleServers(t *testing.T) { + mp := metric.NewMeterProvider() + serverOpt := NewServerMetrics(WithMeterProvider(mp)) + + // Create multiple servers with the same option + server1 := grpc.NewServer(serverOpt) + defer server1.Stop() + + server2 := grpc.NewServer(serverOpt) + defer server2.Stop() + + assert.NotNil(t, server1) + assert.NotNil(t, server2) +}