diff --git a/go.mod b/go.mod index 0109e06..3af5639 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,19 @@ go 1.24.2 require ( github.com/DavidMovas/gopherbox v0.0.0-20250329141646-145b4e0827ef github.com/Masterminds/squirrel v1.5.4 - github.com/QuizWars-Ecosystem/go-common v0.0.0-20250421115754-c81ffe56c7c5 + github.com/QuizWars-Ecosystem/go-common v0.0.0-20250430145400-a93f9561350d github.com/google/uuid v1.6.0 + github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 github.com/jackc/pgx/v5 v5.7.4 - github.com/redis/go-redis/v9 v9.7.3 + github.com/prometheus/client_golang v1.21.1 + github.com/redis/go-redis/v9 v9.8.0 github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.36.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 go.uber.org/zap v1.27.0 + golang.org/x/sync v0.13.0 google.golang.org/grpc v1.71.1 google.golang.org/protobuf v1.36.6 ) @@ -29,6 +34,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/bufbuild/protovalidate-go v0.9.2 // indirect github.com/caarlos0/env/v11 v11.3.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -43,6 +49,7 @@ require ( github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.8.2 // indirect + github.com/exaring/otelpgx v0.9.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect @@ -96,12 +103,18 @@ require ( github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/redis/go-redis/extra/rediscmd/v9 v9.8.0 // indirect + github.com/redis/go-redis/extra/redisotel/v9 v9.8.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/shirou/gopsutil/v4 v4.25.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -122,14 +135,17 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/net v0.39.0 // indirect - golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect diff --git a/go.sum b/go.sum index 5b70654..7c51ae3 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/QuizWars-Ecosystem/go-common v0.0.0-20250421115754-c81ffe56c7c5 h1:1iZSebr3LayoMz8DZzJ9J9m3Jw2E3xMfxXI0rTfRmNY= -github.com/QuizWars-Ecosystem/go-common v0.0.0-20250421115754-c81ffe56c7c5/go.mod h1:pt+KxBK12KmuYMvtHWvRjwxzVDVWmbf49hNL8DeTCSM= +github.com/QuizWars-Ecosystem/go-common v0.0.0-20250430145400-a93f9561350d h1:162fSXBXsig4wHC09N8wFttNlb1kWWKswR8PovKeD18= +github.com/QuizWars-Ecosystem/go-common v0.0.0-20250430145400-a93f9561350d/go.mod h1:c/YHg5Zncbz1yhWPvtPqahsDI5gMSwu9/45czIZMaNA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -45,6 +45,7 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= @@ -107,6 +108,8 @@ github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/exaring/otelpgx v0.9.0 h1:Bo0RIhBNrzLlVzih46qBy/KQRvRs9vwRbgT/fE363NM= +github.com/exaring/otelpgx v0.9.0/go.mod h1:ANkRZDfgfmN6yJS1xKMkshbnsHO8at5sYwtVEYOX8hc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= @@ -183,7 +186,9 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= @@ -347,6 +352,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -410,6 +417,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -439,26 +448,38 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/extra/rediscmd/v9 v9.8.0 h1:/A+PnpT6ufTUt/6YPXiZlCRoyyfEnDag5WGrEK8Gq0I= +github.com/redis/go-redis/extra/rediscmd/v9 v9.8.0/go.mod h1:FGO4BNjl5TfH9U771826GIW2Ul4pOEqHAN+0xjfw+dU= +github.com/redis/go-redis/extra/redisotel/v9 v9.8.0 h1:mnKrl8WqyGJK4pletf2itS+Te/ng3Qm4YjtveY406J8= +github.com/redis/go-redis/extra/redisotel/v9 v9.8.0/go.mod h1:iObamxrrXt4hGWiCWv5BAs68xPYc/MfrLd34H9TaKyk= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= @@ -555,12 +576,16 @@ github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 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/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= @@ -748,8 +773,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/apis/handler/admin.go b/internal/apis/handler/admin.go index 40c848f..5213900 100644 --- a/internal/apis/handler/admin.go +++ b/internal/apis/handler/admin.go @@ -3,6 +3,8 @@ package handler import ( "context" + "github.com/QuizWars-Ecosystem/questions-service/internal/metrics" + "github.com/QuizWars-Ecosystem/go-common/pkg/abstractions" apperrors "github.com/QuizWars-Ecosystem/go-common/pkg/error" "github.com/QuizWars-Ecosystem/go-common/pkg/jwt" @@ -16,6 +18,7 @@ import ( func (h *Handler) GetFilteredQuestions(ctx context.Context, request *questionsv1.GetFilteredQuestionsRequest) (*questionsv1.GetFilteredQuestionsResponse, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("GetFilteredQuestions", err.Error()).Inc() return nil, err } @@ -40,6 +43,8 @@ func (h *Handler) GetFilteredQuestions(ctx context.Context, request *questionsv1 questionsList[i] = question } + metrics.AdminActionsTotalCounter.WithLabelValues("GetFilteredQuestions").Inc() + return &questionsv1.GetFilteredQuestionsResponse{ Questions: questionsList, Page: req.Offset, @@ -53,6 +58,7 @@ func (h *Handler) GetFilteredQuestions(ctx context.Context, request *questionsv1 func (h *Handler) CreateCategory(ctx context.Context, request *questionsv1.CreateCategoryRequest) (*questionsv1.CreateCategoryResponse, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("CreateCategory", err.Error()).Inc() return nil, err } @@ -61,6 +67,9 @@ func (h *Handler) CreateCategory(ctx context.Context, request *questionsv1.Creat return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("CreateCategory").Inc() + metrics.CategoriesTotalCounter.Inc() + return &questionsv1.CreateCategoryResponse{ Id: id, }, nil @@ -69,6 +78,7 @@ func (h *Handler) CreateCategory(ctx context.Context, request *questionsv1.Creat func (h *Handler) CreateQuestion(ctx context.Context, request *questionsv1.CreateQuestionRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("CreateQuestion", err.Error()).Inc() return nil, err } @@ -82,12 +92,16 @@ func (h *Handler) CreateQuestion(ctx context.Context, request *questionsv1.Creat return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("CreateQuestion").Inc() + metrics.QuestionsTotalGauge.Inc() + return Empty, nil } func (h *Handler) CreateQuestionOption(ctx context.Context, request *questionsv1.CreateQuestionOptionRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("CreateQuestionOption", err.Error()).Inc() return nil, err } @@ -106,12 +120,16 @@ func (h *Handler) CreateQuestionOption(ctx context.Context, request *questionsv1 return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("CreateQuestionOption").Inc() + metrics.QuestionsOptionsTotalGauge.Inc() + return Empty, nil } func (h *Handler) UpdateCategory(ctx context.Context, request *questionsv1.UpdateCategoryRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("UpdateCategory", err.Error()).Inc() return nil, err } @@ -120,12 +138,15 @@ func (h *Handler) UpdateCategory(ctx context.Context, request *questionsv1.Updat return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("UpdateCategory").Inc() + return Empty, nil } func (h *Handler) UpdateQuestion(ctx context.Context, request *questionsv1.UpdateQuestionRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("UpdateQuestion", err.Error()).Inc() return nil, err } @@ -144,12 +165,15 @@ func (h *Handler) UpdateQuestion(ctx context.Context, request *questionsv1.Updat return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("UpdateQuestion").Inc() + return Empty, nil } func (h *Handler) UpdateQuestionOption(ctx context.Context, request *questionsv1.UpdateQuestionOptionRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("UpdateQuestionOption", err.Error()).Inc() return nil, err } @@ -168,12 +192,15 @@ func (h *Handler) UpdateQuestionOption(ctx context.Context, request *questionsv1 return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("UpdateQuestionOption").Inc() + return Empty, nil } func (h *Handler) DeleteQuestion(ctx context.Context, request *questionsv1.DeleteQuestionRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("DeleteQuestion", err.Error()).Inc() return nil, err } @@ -187,12 +214,16 @@ func (h *Handler) DeleteQuestion(ctx context.Context, request *questionsv1.Delet return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("DeleteQuestion").Inc() + metrics.QuestionsTotalGauge.Dec() + return Empty, nil } func (h *Handler) DeleteQuestionOption(ctx context.Context, request *questionsv1.DeleteQuestionOptionRequest) (*emptypb.Empty, error) { err := h.auth.ValidateRoleWithContext(ctx, string(jwt.Admin)) if err != nil { + metrics.AdminForbittenActionsTotalCounter.WithLabelValues("DeleteQuestionOption", err.Error()).Inc() return nil, err } @@ -206,5 +237,8 @@ func (h *Handler) DeleteQuestionOption(ctx context.Context, request *questionsv1 return nil, err } + metrics.AdminActionsTotalCounter.WithLabelValues("DeleteQuestionOption").Inc() + metrics.QuestionsOptionsTotalGauge.Dec() + return Empty, nil } diff --git a/internal/apis/handler/questions.go b/internal/apis/handler/questions.go index 77c339f..b673888 100644 --- a/internal/apis/handler/questions.go +++ b/internal/apis/handler/questions.go @@ -3,6 +3,8 @@ package handler import ( "context" + "github.com/QuizWars-Ecosystem/questions-service/internal/metrics" + "github.com/QuizWars-Ecosystem/go-common/pkg/abstractions" questionsv1 "github.com/QuizWars-Ecosystem/questions-service/gen/external/questions/v1" "github.com/QuizWars-Ecosystem/questions-service/internal/models/filter" @@ -31,6 +33,8 @@ func (h *Handler) GetQuestions(ctx context.Context, request *questionsv1.GetQues questionsList[i] = q } + metrics.QuestionsRequestsGauge.Set(float64(len(questionsList))) + return &questionsv1.QuestionsResponse{ Questions: questionsList, }, nil @@ -57,6 +61,8 @@ func (h *Handler) GetQuestionBatch(ctx context.Context, request *questionsv1.Get questionsList[i] = q } + metrics.QuestionsRequestsGauge.Set(float64(len(questionsList))) + return &questionsv1.QuestionsResponse{ Questions: questionsList, }, nil diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go new file mode 100644 index 0000000..7ad0917 --- /dev/null +++ b/internal/metrics/metrics.go @@ -0,0 +1,62 @@ +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +var ( + AdminActionsTotalCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "admin_actions_total", + Help: "Number of admin actions", + }, + []string{"method"}, + ) + + AdminForbittenActionsTotalCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "admin_forbitten_actions_total", + Help: "Number of forbitten admin actions", + }, + []string{"method", "reason"}, + ) +) + +var CategoriesTotalCounter = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "categories_total", + Help: "Number of categories", + }, +) + +var ( + QuestionsRequestsGauge = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "questions_requests", + Help: "Number of question requests", + }, + ) + + QuestionsTotalGauge = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "questions_total", + Help: "Number of questions", + }, + ) + + QuestionsOptionsTotalGauge = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "questions_options_total", + Help: "Number of question's options", + }, + ) +) + +func Initialize() { + prometheus.MustRegister(AdminActionsTotalCounter) + prometheus.MustRegister(AdminForbittenActionsTotalCounter) + + prometheus.MustRegister(CategoriesTotalCounter) + + prometheus.MustRegister(QuestionsRequestsGauge) + prometheus.MustRegister(QuestionsTotalGauge) + prometheus.MustRegister(QuestionsOptionsTotalGauge) +} diff --git a/internal/server/server.go b/internal/server/server.go index a8a9e94..f25a1b5 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -5,7 +5,18 @@ import ( "errors" "fmt" "net" - + "net/http" + "time" + + "github.com/QuizWars-Ecosystem/go-common/pkg/grpcx/telemetry" + "github.com/QuizWars-Ecosystem/questions-service/internal/metrics" + grpcrecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" + grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "golang.org/x/sync/errgroup" + + grpccommon "github.com/QuizWars-Ecosystem/go-common/pkg/grpcx/metrics" "google.golang.org/grpc/reflection" "github.com/DavidMovas/gopherbox/pkg/closer" @@ -29,12 +40,14 @@ import ( var _ abstractions.Server = (*Server)(nil) type Server struct { - grpcServer *grpc.Server - listener net.Listener - consul *consul.Consul - logger *log.Logger - manager *manager.Manager[config.Config] - closer *closer.Closer + grpcServer *grpc.Server + httpServer *http.Server + grpcListener net.Listener + httpListener net.Listener + consul *consul.Consul + logger *log.Logger + manager *manager.Manager[config.Config] + closer *closer.Closer } func NewServer(ctx context.Context, manager *manager.Manager[config.Config]) (*Server, error) { @@ -55,18 +68,34 @@ func NewServer(ctx context.Context, manager *manager.Manager[config.Config]) (*S cl.Push(consulManager.Stop) - postgresClient, err := clients.NewPostgresClient(ctx, cfg.Postgres.URL, nil) + provider, err := telemetry.NewTracerProvider(ctx, cfg.Name, cfg.Telemetry.URL) + if err != nil { + logger.Zap().Error("error initializing telemetry tracer", zap.Error(err)) + } + + cl.PushCtx(provider.Shutdown) + + postgresClient, err := clients.NewPostgresClient(ctx, cfg.Postgres.URL, + clients.NewPostgresOptions(cfg.Postgres.URL). + WithConnectTimeout(time.Second*10). + WithTracerProvider(provider), + ) if err != nil { logger.Zap().Error("error initializing postgres client", zap.Error(err)) return nil, fmt.Errorf("error initializing postgres client: %w", err) } - redisClient, err := clients.NewRedisClient(cfg.Redis.URL, nil) + redisClient, err := clients.NewRedisClient(cfg.Redis.URL, + clients.NewRedisOptions(cfg.Redis.URL). + WithDialTimeout(time.Second*10). + WithTraceProvider(provider)) if err != nil { logger.Zap().Error("error initializing redis client", zap.Error(err)) return nil, fmt.Errorf("error initializing redis client: %w", err) } + grpcprometheus.EnableHandlingTimeHistogram() + jwtService := jwt.NewService(cfg.JWT) manager.Subscribe(jwtService.SectionKey(), func(cfg *config.Config) error { return jwtService.UpdateConfig(cfg.JWT) }) @@ -74,7 +103,18 @@ func NewServer(ctx context.Context, manager *manager.Manager[config.Config]) (*S srv := service.NewService(storage, logger.Zap()) hand := handler.NewHandler(srv, jwtService, logger.Zap()) - grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor()) + grpcServer := grpc.NewServer( + grpc.ChainUnaryInterceptor( + grpcrecovery.UnaryServerInterceptor(), + grpccommon.ServerMetricsInterceptor(), + grpcprometheus.UnaryServerInterceptor, + ), + grpc.StatsHandler( + otelgrpc.NewServerHandler( + otelgrpc.WithTracerProvider(provider), + ), + ), + ) healthServer := health.NewServer() healthServer.SetServingStatus(fmt.Sprintf("%s-%d", cfg.Name, cfg.GRPCPort), grpc_health_v1.HealthCheckResponse_SERVING) @@ -86,12 +126,22 @@ func NewServer(ctx context.Context, manager *manager.Manager[config.Config]) (*S questionsv1.RegisterQuestionsClientServiceServer(grpcServer, hand) questionsv1.RegisterQuestionsAdminServiceServer(grpcServer, hand) + metrics.Initialize() + + metricsMux := http.NewServeMux() + metricsMux.Handle("/metrics", promhttp.Handler()) + metricsServer := &http.Server{ + Addr: fmt.Sprintf(":%d", cfg.Metrics.Port), + Handler: metricsMux, + } + if cfg.Local { reflection.Register(grpcServer) } return &Server{ grpcServer: grpcServer, + httpServer: metricsServer, consul: consulManager, logger: logger, manager: manager, @@ -105,24 +155,45 @@ func (s *Server) Start() error { cfg := s.manager.Config() s.manager.Watch(z) - z.Info("Starting server", zap.String("name", cfg.Name), zap.Int("port", cfg.GRPCPort)) + group := errgroup.Group{} - var err error - s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", cfg.GRPCPort)) - if err != nil { - z.Error("Failed to start listener", zap.String("name", cfg.Name), zap.Int("port", cfg.GRPCPort), zap.Error(err)) - return err - } + group.Go(func() error { + z.Info("Starting metrics server", zap.Int("port", cfg.Metrics.Port)) + + var err error + s.httpListener, err = net.Listen("tcp", fmt.Sprintf(":%d", cfg.Metrics.Port)) + if err != nil { + z.Error("Error starting metrics server", zap.Error(err)) + return err + } + + s.closer.PushIO(s.httpListener) + + return s.httpServer.Serve(s.httpListener) + }) + + group.Go(func() error { + z.Info("Starting server", zap.String("name", cfg.Name), zap.Int("port", cfg.GRPCPort)) - s.closer.PushIO(s.listener) + var err error + s.grpcListener, err = net.Listen("tcp", fmt.Sprintf(":%d", cfg.GRPCPort)) + if err != nil { + z.Error("Failed to start grpcListener", zap.String("name", cfg.Name), zap.Int("port", cfg.GRPCPort), zap.Error(err)) + return err + } - err = s.consul.RegisterService() + s.closer.PushIO(s.grpcListener) + + return s.grpcServer.Serve(s.grpcListener) + }) + + err := s.consul.RegisterService() if err != nil { z.Error("Failed to register service in consul registry", zap.String("name", cfg.Name), zap.Error(err)) return err } - return s.grpcServer.Serve(s.listener) + return group.Wait() } func (s *Server) Shutdown(ctx context.Context) error { @@ -133,6 +204,10 @@ func (s *Server) Shutdown(ctx context.Context) error { stopChan := make(chan struct{}) go func() { + if err := s.httpServer.Shutdown(ctx); err != nil { + z.Error("Error shutting down metrics server", zap.Error(err)) + } + s.grpcServer.GracefulStop() close(stopChan) }() @@ -144,8 +219,12 @@ func (s *Server) Shutdown(ctx context.Context) error { s.grpcServer.Stop() } - if err := s.listener.Close(); err != nil && !errors.Is(err, net.ErrClosed) { - return fmt.Errorf("shutting down listener: %w", err) + if err := s.grpcListener.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + return fmt.Errorf("shutting down grpc listener: %w", err) + } + + if err := s.httpListener.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + return fmt.Errorf("shutting down http listener: %w", err) } if err := s.logger.Close(); err != nil {