Skip to content

Commit 4e2b3e0

Browse files
export metrics in semcache service
1 parent 43b6367 commit 4e2b3e0

File tree

4 files changed

+138
-4
lines changed

4 files changed

+138
-4
lines changed

apps/semcache-service/cmd/server/main.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/labstack/echo/v4/middleware"
1515
"go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho"
1616
"github.com/nextinterfaces/semcache-service/internal/config"
17+
smmetrics "github.com/nextinterfaces/semcache-service/internal/metrics"
1718
"github.com/nextinterfaces/semcache-service/internal/database"
1819
"github.com/nextinterfaces/semcache-service/internal/handlers"
1920
"github.com/nextinterfaces/semcache-service/internal/logger"
@@ -59,6 +60,12 @@ func run() error {
5960
}
6061
}
6162

63+
// Initialize Prometheus metrics and middleware
64+
metricsHandler, err := smmetrics.Init(cfg.OTEL.ServiceName, cfg.Server.CommitSHA)
65+
if err != nil {
66+
logger.Logger.Warn(fmt.Sprintf("Failed to initialize Prometheus metrics: %v", err))
67+
}
68+
6269
// Connect to database
6370
db, err := database.New(&cfg.Database)
6471
if err != nil {
@@ -89,9 +96,16 @@ func run() error {
8996
e.Use(middleware.Logger())
9097
e.Use(middleware.Recover())
9198
e.Use(middleware.CORS())
99+
// record http_server_* metrics compatible with dashboards
100+
e.Use(smmetrics.Middleware())
92101

93102
e.GET("/v1/health", h.Health)
94103

104+
// Prometheus metrics endpoint
105+
if metricsHandler != nil {
106+
e.GET("/metrics", echo.WrapHandler(metricsHandler))
107+
}
108+
95109
e.GET("/docs", h.ServeSwaggerUI)
96110
e.GET("/api/openapi.yaml", h.ServeOpenAPISpec)
97111

apps/semcache-service/go.mod

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,38 @@ go 1.25
55
require (
66
github.com/labstack/echo/v4 v4.13.4
77
github.com/lib/pq v1.10.9
8+
github.com/prometheus/client_golang v1.23.0
89
go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.63.0
910
go.opentelemetry.io/otel v1.38.0
1011
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
12+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0
13+
go.opentelemetry.io/otel/metric v1.38.0
1114
go.opentelemetry.io/otel/sdk v1.38.0
15+
go.opentelemetry.io/otel/sdk/metric v1.38.0
1216
go.uber.org/zap v1.27.0
1317
)
1418

1519
require (
20+
github.com/beorn7/perks v1.0.1 // indirect
1621
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
22+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
1723
github.com/go-logr/logr v1.4.3 // indirect
1824
github.com/go-logr/stdr v1.2.2 // indirect
1925
github.com/google/uuid v1.6.0 // indirect
26+
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
2027
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
2128
github.com/labstack/gommon v0.4.2 // indirect
2229
github.com/mattn/go-colorable v0.1.14 // indirect
2330
github.com/mattn/go-isatty v0.0.20 // indirect
31+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
32+
github.com/prometheus/client_model v0.6.2 // indirect
33+
github.com/prometheus/common v0.65.0 // indirect
34+
github.com/prometheus/otlptranslator v0.0.2 // indirect
35+
github.com/prometheus/procfs v0.17.0 // indirect
2436
github.com/valyala/bytebufferpool v1.0.0 // indirect
2537
github.com/valyala/fasttemplate v1.2.2 // indirect
2638
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
2739
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
28-
go.opentelemetry.io/otel/metric v1.38.0 // indirect
2940
go.opentelemetry.io/otel/trace v1.38.0 // indirect
3041
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
3142
go.uber.org/multierr v1.10.0 // indirect
@@ -37,5 +48,5 @@ require (
3748
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
3849
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
3950
google.golang.org/grpc v1.63.2 // indirect
40-
google.golang.org/protobuf v1.33.0 // indirect
51+
google.golang.org/protobuf v1.36.8 // indirect
4152
)

apps/semcache-service/go.sum

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
13
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
24
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
5+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
6+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
37
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
48
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
59
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -11,8 +15,14 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
1115
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
1216
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
1317
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
18+
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
19+
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
1420
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
1521
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
22+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
23+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
24+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
25+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
1626
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
1727
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
1828
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
@@ -23,8 +33,20 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
2333
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
2434
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
2535
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
36+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
37+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
2638
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2739
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
40+
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
41+
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
42+
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
43+
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
44+
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
45+
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
46+
github.com/prometheus/otlptranslator v0.0.2 h1:+1CdeLVrRQ6Psmhnobldo0kTp96Rj80DRXRd5OSnMEQ=
47+
github.com/prometheus/otlptranslator v0.0.2/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
48+
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
49+
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
2850
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
2951
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
3052
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -43,6 +65,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm
4365
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
4466
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
4567
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
68+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 h1:cGtQxGvZbnrWdC2GyjZi0PDKVSLWP/Jocix3QWfXtbo=
69+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0/go.mod h1:hkd1EekxNo69PTV4OWFGZcKQiIqg0RfuWExcPKFvepk=
4670
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
4771
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
4872
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
@@ -78,7 +102,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:
78102
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
79103
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
80104
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
81-
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
82-
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
105+
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
106+
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
83107
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
84108
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package metrics
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"time"
7+
"strconv"
8+
9+
"github.com/labstack/echo/v4"
10+
promclient "github.com/prometheus/client_golang/prometheus"
11+
"github.com/prometheus/client_golang/prometheus/promhttp"
12+
"go.opentelemetry.io/otel"
13+
"go.opentelemetry.io/otel/attribute"
14+
"go.opentelemetry.io/otel/metric"
15+
"go.opentelemetry.io/otel/exporters/prometheus"
16+
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
17+
"go.opentelemetry.io/otel/sdk/resource"
18+
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
19+
)
20+
21+
var (
22+
exporter *prometheus.Exporter
23+
registry *promclient.Registry
24+
)
25+
26+
// Init sets up the Prometheus exporter and HTTP instruments, and returns a net/http handler for /metrics
27+
func Init(serviceName, serviceVersion string) (http.Handler, error) {
28+
var err error
29+
30+
// Create a dedicated Prometheus registry and register the OTel exporter with it
31+
registry = promclient.NewRegistry()
32+
exporter, err = prometheus.New(prometheus.WithRegisterer(registry))
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
res, _ := resource.New(
38+
context.Background(),
39+
resource.WithAttributes(
40+
semconv.ServiceNameKey.String(serviceName),
41+
semconv.ServiceVersionKey.String(serviceVersion),
42+
),
43+
)
44+
45+
mp := sdkmetric.NewMeterProvider(
46+
sdkmetric.WithReader(exporter),
47+
sdkmetric.WithResource(res),
48+
)
49+
otel.SetMeterProvider(mp)
50+
51+
m := otel.Meter(serviceName)
52+
// Ensure instruments are created upfront
53+
_, _ = m.Float64Histogram("http_server_duration")
54+
_, _ = m.Int64Counter("http_server_requests_total")
55+
56+
// Expose the registry via promhttp handler
57+
return promhttp.HandlerFor(registry, promhttp.HandlerOpts{}), nil
58+
}
59+
60+
// Middleware records HTTP metrics compatible with existing dashboards
61+
func Middleware() echo.MiddlewareFunc {
62+
return func(next echo.HandlerFunc) echo.HandlerFunc {
63+
return func(c echo.Context) error {
64+
start := time.Now()
65+
err := next(c)
66+
durMs := float64(time.Since(start).Microseconds()) / 1000.0
67+
68+
m := otel.Meter("semcache-service")
69+
// Lazily (re)acquire instruments from the global provider to avoid nils
70+
hist, _ := m.Float64Histogram("http_server_duration")
71+
ctr, _ := m.Int64Counter("http_server_requests_total")
72+
73+
attrs := []attribute.KeyValue{
74+
attribute.String("method", c.Request().Method),
75+
attribute.String("route", c.Path()),
76+
attribute.String("status_code", strconv.Itoa(c.Response().Status)),
77+
}
78+
79+
hist.Record(c.Request().Context(), durMs, metric.WithAttributes(attrs...))
80+
ctr.Add(c.Request().Context(), 1, metric.WithAttributes(attrs...))
81+
82+
return err
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)