Skip to content
This repository was archived by the owner on Nov 7, 2025. It is now read-only.

Commit 2bb2542

Browse files
authored
Implement Promethous metric endpoint (#1283)
Add a few basic Prometheus-style metrics. Easy to add more when you need them.
1 parent c18f2ab commit 2bb2542

File tree

5 files changed

+125
-4
lines changed

5 files changed

+125
-4
lines changed

quesma/go.mod

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ require (
4141
require (
4242
filippo.io/edwards25519 v1.1.0 // indirect
4343
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
44+
github.com/beorn7/perks v1.0.1 // indirect
45+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
4446
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
4547
github.com/golang/glog v1.2.4 // indirect
4648
github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -51,8 +53,13 @@ require (
5153
github.com/knadh/koanf/maps v0.1.1 // indirect
5254
github.com/mitchellh/copystructure v1.2.0 // indirect
5355
github.com/mitchellh/reflectwalk v1.0.2 // indirect
56+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
5457
github.com/pires/go-proxyproto v0.7.0 // indirect
5558
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
59+
github.com/prometheus/client_golang v1.21.0 // indirect
60+
github.com/prometheus/client_model v0.6.1 // indirect
61+
github.com/prometheus/common v0.62.0 // indirect
62+
github.com/prometheus/procfs v0.15.1 // indirect
5663
github.com/spf13/pflag v1.0.5 // indirect
5764
github.com/tidwall/gjson v1.18.0 // indirect
5865
github.com/tidwall/match v1.1.1 // indirect
@@ -62,7 +69,7 @@ require (
6269
golang.org/x/text v0.22.0 // indirect
6370
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
6471
google.golang.org/grpc v1.66.2 // indirect
65-
google.golang.org/protobuf v1.34.2 // indirect
72+
google.golang.org/protobuf v1.36.1 // indirect
6673
)
6774

6875
require (

quesma/go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4t
1818
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
1919
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0=
2020
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM=
21+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
22+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
23+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
24+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
2125
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
2226
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
2327
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -118,6 +122,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
118122
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
119123
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
120124
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
125+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
126+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
121127
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
122128
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
123129
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
@@ -134,6 +140,14 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
134140
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
135141
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
136142
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
143+
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
144+
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
145+
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
146+
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
147+
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
148+
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
149+
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
150+
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
137151
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
138152
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
139153
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
@@ -255,6 +269,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
255269
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
256270
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
257271
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
272+
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
273+
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
258274
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
259275
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
260276
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

quesma/quesma/ui/console_routes.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/gorilla/sessions"
1515
"github.com/markbates/goth"
1616
"github.com/markbates/goth/gothic"
17+
"github.com/prometheus/client_golang/prometheus/promhttp"
1718
"net/http"
1819
"net/http/pprof"
1920
"runtime"
@@ -23,6 +24,7 @@ const (
2324
uiTcpPort = "9999"
2425
managementInternalPath = "/_quesma"
2526
healthPath = managementInternalPath + "/health"
27+
metricsPath = "/metrics"
2628
loginWithElasticSearch = "/login-with-elasticsearch"
2729
)
2830

@@ -63,6 +65,7 @@ func (qmc *QuesmaManagementConsole) createRouting() *mux.Router {
6365
router.Use(panicRecovery)
6466

6567
router.HandleFunc(healthPath, qmc.checkHealth)
68+
router.Handle(metricsPath, promhttp.Handler())
6669

6770
qmc.initPprof(router)
6871

quesma/telemetry/phone_home.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ func NewPhoneHomeAgent(configuration *config.QuesmaConfiguration, clickHouseDb q
125125
clickHouseDb: clickHouseDb,
126126
config: configuration,
127127
clientId: clientId,
128-
clickHouseQueryTimes: newDurationMeasurement(ctx),
129-
clickHouseInsertsTimes: newDurationMeasurement(ctx),
128+
clickHouseQueryTimes: newQueryDurationWrapper(clickHouseRequestQueryDuration, newDurationMeasurement(ctx)),
129+
clickHouseInsertsTimes: newQueryDurationWrapper(clickHouseRequestIngestDuration, newDurationMeasurement(ctx)),
130130
elasticReadTimes: newDurationMeasurement(ctx),
131131
elasticWriteTimes: newDurationMeasurement(ctx),
132132
elasticBypassedReadTimes: newDurationMeasurement(ctx),
133133
elasticBypassedWriteTimes: newDurationMeasurement(ctx),
134134

135-
ingestCounters: NewMultiCounter(ctx, nil),
135+
ingestCounters: newPrometheusIngestionWrapper(NewMultiCounter(ctx, nil)),
136136
userAgentCounters: NewMultiCounter(ctx, processUserAgent),
137137
telemetryEndpoint: configuration.QuesmaInternalTelemetryUrl,
138138
httpClient: &http.Client{
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright Quesma, licensed under the Elastic License 2.0.
2+
// SPDX-License-Identifier: Elastic-2.0
3+
package telemetry
4+
5+
import (
6+
"github.com/QuesmaOrg/quesma/quesma/v2/core/diag"
7+
"github.com/prometheus/client_golang/prometheus"
8+
"time"
9+
)
10+
11+
// Define metrics
12+
var (
13+
ingestionTotalCount = prometheus.NewCounter(
14+
prometheus.CounterOpts{
15+
Name: "quesma_ingestion_total_entries",
16+
Help: "Total number of ingestion documents/logs transpiled by Quesma ",
17+
},
18+
)
19+
20+
clickHouseRequestQueryDuration = prometheus.NewHistogram(
21+
prometheus.HistogramOpts{
22+
Name: "quesma_clickhouse_query_duration_seconds",
23+
Help: "Histogram of ClickHouse query duration times",
24+
Buckets: prometheus.DefBuckets,
25+
},
26+
)
27+
28+
clickHouseRequestIngestDuration = prometheus.NewHistogram(
29+
prometheus.HistogramOpts{
30+
Name: "quesma_clickhouse_ingest_duration_seconds",
31+
Help: "Histogram of ClickHouse ingest duration times",
32+
Buckets: prometheus.DefBuckets,
33+
},
34+
)
35+
)
36+
37+
type ingestionCounterWrapper struct {
38+
wrapped diag.MultiCounter
39+
}
40+
41+
func newPrometheusIngestionWrapper(wrapped diag.MultiCounter) diag.MultiCounter {
42+
return &ingestionCounterWrapper{wrapped: wrapped}
43+
}
44+
45+
func (w *ingestionCounterWrapper) Add(key string, value int64) {
46+
w.wrapped.Add(key, value)
47+
ingestionTotalCount.Add(float64(value))
48+
}
49+
50+
func (w *ingestionCounterWrapper) AggregateAndReset() diag.MultiCounterStats {
51+
return w.wrapped.AggregateAndReset()
52+
}
53+
54+
func (w *ingestionCounterWrapper) AggregateTopValuesAndReset() diag.MultiCounterTopValuesStats {
55+
return w.wrapped.AggregateTopValuesAndReset()
56+
}
57+
58+
type queryDurationWrapper struct {
59+
histogram prometheus.Histogram
60+
wrapped diag.DurationMeasurement
61+
}
62+
63+
func newQueryDurationWrapper(histogram prometheus.Histogram, wrapped diag.DurationMeasurement) diag.DurationMeasurement {
64+
return &queryDurationWrapper{histogram: histogram, wrapped: wrapped}
65+
}
66+
67+
func (w *queryDurationWrapper) Begin() diag.Span {
68+
return newSpanDurationWrapper(w.histogram, w.wrapped.Begin())
69+
}
70+
71+
func (w *queryDurationWrapper) AggregateAndReset() diag.DurationStats {
72+
return w.wrapped.AggregateAndReset()
73+
}
74+
75+
type spanDurationWrapper struct {
76+
histogram prometheus.Histogram
77+
wrapped diag.Span
78+
}
79+
80+
func newSpanDurationWrapper(histogram prometheus.Histogram, wrapped diag.Span) diag.Span {
81+
return &spanDurationWrapper{histogram: histogram, wrapped: wrapped}
82+
}
83+
84+
func (w *spanDurationWrapper) End(err error) time.Duration {
85+
duration := w.wrapped.End(err)
86+
w.histogram.Observe(duration.Seconds())
87+
return duration
88+
}
89+
90+
func init() {
91+
// Register metrics with Prometheus
92+
prometheus.MustRegister(ingestionTotalCount)
93+
prometheus.MustRegister(clickHouseRequestQueryDuration)
94+
prometheus.MustRegister(clickHouseRequestIngestDuration)
95+
}

0 commit comments

Comments
 (0)