Skip to content

Commit abf7abb

Browse files
committed
add metrics middleware
Signed-off-by: Augustin Husson <husson.augustin@gmail.com>
1 parent ee7b1fa commit abf7abb

2 files changed

Lines changed: 76 additions & 1 deletion

File tree

rest/api.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol"
2323
"github.com/prometheus-community/promql-langserver/langserver"
2424
promClient "github.com/prometheus-community/promql-langserver/prometheus"
25+
"github.com/prometheus/client_golang/prometheus"
26+
"github.com/prometheus/client_golang/prometheus/promhttp"
2527
"github.com/prometheus/common/route"
2628
)
2729

@@ -73,6 +75,7 @@ func (d *lspData) returnPosition() (protocol.Position, error) {
7375

7476
type API struct {
7577
langServer langserver.HeadlessServer
78+
mdws []middlewareFunc
7679
enableMetrics bool
7780
}
7881

@@ -81,8 +84,15 @@ func NewLangServerAPI(ctx context.Context, metadataService promClient.MetadataSe
8184
if err != nil {
8285
return nil, err
8386
}
87+
mdws := []middlewareFunc{manageDocumentMiddleware(lgs)}
88+
if enableMetrics {
89+
apiMetric := newAPIMetrics()
90+
prometheus.MustRegister(apiMetric)
91+
mdws = append(mdws, apiMetric.instrumentHTTPRequest)
92+
}
8493
return &API{
8594
langServer: lgs,
95+
mdws: mdws,
8696
enableMetrics: enableMetrics,
8797
}, nil
8898
}
@@ -93,10 +103,16 @@ func (a *API) Register(r *route.Router, prefix string) {
93103
r.Post(prefix+"/completion", a.handle(a.completion))
94104
r.Post(prefix+"/hover", a.handle(a.hover))
95105
r.Post(prefix+"/signatureHelp", a.handle(a.signature))
106+
if a.enableMetrics {
107+
r.Get("/metrics", promhttp.Handler().ServeHTTP)
108+
}
96109
}
97110

98111
func (a *API) handle(h http.HandlerFunc) http.HandlerFunc {
99-
endpoint := manageDocumentMiddleware(a.langServer)(h)
112+
endpoint := h
113+
for _, mdw := range a.mdws {
114+
endpoint = mdw(endpoint)
115+
}
100116
return endpoint
101117
}
102118

rest/middleware.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ import (
1717
"fmt"
1818
"io"
1919
"net/http"
20+
"time"
2021

2122
"github.com/google/uuid"
2223
"github.com/pkg/errors"
2324
"github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol"
2425
"github.com/prometheus-community/promql-langserver/langserver"
26+
"github.com/prometheus/client_golang/prometheus"
2527
)
2628

2729
type key int
@@ -129,3 +131,60 @@ func manageDocumentMiddleware(langServer langserver.HeadlessServer) middlewareFu
129131
}
130132
}
131133
}
134+
135+
const (
136+
defaultMetricNamespace = "langserver"
137+
labelHandler = "handler"
138+
)
139+
140+
type apiMetrics struct {
141+
totalHTTPRequest *prometheus.CounterVec
142+
durationHTTPRequest *prometheus.SummaryVec
143+
}
144+
145+
func newAPIMetrics() *apiMetrics {
146+
return &apiMetrics{
147+
totalHTTPRequest: prometheus.NewCounterVec(prometheus.CounterOpts{
148+
Namespace: defaultMetricNamespace,
149+
Name: "http_request_total",
150+
Help: "Total of HTTP requests received by the API",
151+
}, []string{labelHandler}),
152+
durationHTTPRequest: prometheus.NewSummaryVec(prometheus.SummaryOpts{
153+
Namespace: defaultMetricNamespace,
154+
Name: "http_request_duration_second",
155+
Help: "HTTP request latencies in second",
156+
}, []string{labelHandler}),
157+
}
158+
}
159+
160+
func (m *apiMetrics) Collect(ch chan<- prometheus.Metric) {
161+
m.totalHTTPRequest.Collect(ch)
162+
m.durationHTTPRequest.Collect(ch)
163+
}
164+
165+
func (m *apiMetrics) Describe(ch chan<- *prometheus.Desc) {
166+
m.totalHTTPRequest.Describe(ch)
167+
m.durationHTTPRequest.Describe(ch)
168+
}
169+
170+
func (m *apiMetrics) instrumentHTTPRequest(next http.HandlerFunc) http.HandlerFunc {
171+
return func(w http.ResponseWriter, r *http.Request) {
172+
start := time.Now()
173+
// Call the next middleware.
174+
// This middleware should be executed in the first in the chain of the middleware in order to have an accurate execution time.
175+
next(w, r)
176+
177+
elaspsed := time.Since(start).Seconds()
178+
179+
counter, err := m.totalHTTPRequest.GetMetricWith(prometheus.Labels{labelHandler: r.URL.Path})
180+
if err != nil {
181+
return
182+
}
183+
counter.Inc()
184+
sum, err := m.durationHTTPRequest.GetMetricWith(prometheus.Labels{labelHandler: r.URL.Path})
185+
if err != nil {
186+
return
187+
}
188+
sum.Observe(elaspsed)
189+
}
190+
}

0 commit comments

Comments
 (0)