From 3253ee5a89a144a3c11b8457bfddfbc2c10de99b Mon Sep 17 00:00:00 2001 From: Kwon Date: Mon, 4 May 2026 19:48:14 +0900 Subject: [PATCH 1/3] Add custom metrics --- api/metric/metric.go | 11 ++----- build/autoconfig.sh | 3 ++ go.mod | 8 +++-- go.sum | 31 ++++++++++++++----- internal/metrics/Readme.md | 46 ++++++++++++++++++++++++++++ internal/metrics/init.go | 30 +++++++++++++++++++ internal/metrics/ping/ping.go | 56 +++++++++++++++++++++++++++++++++++ 7 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 internal/metrics/Readme.md create mode 100644 internal/metrics/init.go create mode 100644 internal/metrics/ping/ping.go diff --git a/api/metric/metric.go b/api/metric/metric.go index 51b8c99..6b5df05 100644 --- a/api/metric/metric.go +++ b/api/metric/metric.go @@ -3,8 +3,7 @@ package metric import ( "net/http" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/easy-cloud-Knet/KWS_Core/internal/metrics" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -16,11 +15,7 @@ func NewHandler() *Handler { } func (H *Handler) DefaultMetric() http.Handler { - reg := prometheus.NewRegistry() + metricRegister := metrics.Initialize() - reg.MustRegister( - collectors.NewGoCollector(), - collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), - ) - return promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) + return promhttp.HandlerFor(metricRegister, promhttp.HandlerOpts{}) } diff --git a/build/autoconfig.sh b/build/autoconfig.sh index 6465c71..b007c65 100755 --- a/build/autoconfig.sh +++ b/build/autoconfig.sh @@ -4,6 +4,9 @@ get_shell_type() { ps -p $$ -o cmd= | awk '{print $1}' | xargs basename } +sudo sysctl -w net.ipv4.ping_group_range="0 2147483647" +// this enables non-root users to ping without sudo, refer internal/metrics/ping.go for more details + curr_dir=$(pwd) shell_type=$(get_shell_type) diff --git a/go.mod b/go.mod index 84d2044..6d37cf8 100755 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ require ( require ( github.com/google/uuid v1.6.0 + github.com/prometheus-community/pro-bing v0.8.0 + github.com/prometheus/client_golang v1.23.2 github.com/shirou/gopsutil v3.21.11+incompatible go.uber.org/zap v1.27.0 libvirt.org/libvirt-go-xml v7.4.0+incompatible @@ -21,7 +23,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect @@ -30,7 +31,8 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect google.golang.org/protobuf v1.36.8 // indirect - libvirt.org/go/libvirtxml v1.12001.0 // indirect ) diff --git a/go.sum b/go.sum index 8ffeebe..697e1ee 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,24 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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/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/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-community/pro-bing v0.8.0 h1:CEY/g1/AgERRDjxw5P32ikcOgmrSuXs7xon7ovx6mNc= +github.com/prometheus-community/pro-bing v0.8.0/go.mod h1:Idyxz8raDO6TgkUN6ByiEGvWJNyQd40kN9ZUeho3lN0= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -20,10 +32,12 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +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/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= @@ -38,18 +52,21 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= libvirt.org/go/libvirt v1.10006.0 h1:VzbLKReneWBIiplgOvZHxMiLLJ0HxAyp4MMPcYTHJjY= libvirt.org/go/libvirt v1.10006.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= -libvirt.org/go/libvirtxml v1.12001.0 h1:UWCcBvPYXzCmwbEDFIJcVjuo00g9795ITaS4BygaNSg= -libvirt.org/go/libvirtxml v1.12001.0/go.mod h1:7Oq2BLDstLr/XtoQD8Fr3mfDNrzlI3utYKySXF2xkng= libvirt.org/libvirt-go-xml v7.4.0+incompatible h1:NaCRjbtz//xuTZOp1nDHbe0eu5BQlhIy5PPuc09EWtU= libvirt.org/libvirt-go-xml v7.4.0+incompatible/go.mod h1:FL+H1+hKNWDdkKQGGS4sGCZJ3pGWcjt6VbxZvPlQJkY= diff --git a/internal/metrics/Readme.md b/internal/metrics/Readme.md new file mode 100644 index 0000000..05db843 --- /dev/null +++ b/internal/metrics/Readme.md @@ -0,0 +1,46 @@ +## matrics + +Purpose +------- +Manages registration of custom Prometheus collectors. +`Initialize()` creates a registry and enrolls all collectors at once. + +Adding a new collector +---------------------- +1. Create a package under `internal/matrics//`. +2. Implement the `Matrics` interface. + +```go +type Matrics interface { + Enroll(prometheus.Registerer) error +} +``` + +3. Inside `Enroll`, create and register your GaugeVec/CounterVec, then start a collection goroutine. + +```go +func (c *Collector) Enroll(reg prometheus.Registerer) error { + c.gauge = prometheus.NewGaugeVec(...) + if err := reg.Register(c.gauge); err != nil { + return err + } + go c.collect() + return nil +} +``` + +4. Add the collector to `matricsList` in `init.go`. + +```go +var matricsList []Matrics = []Matrics{ + &ping.Collector{}, + &yourpkg.Collector{}, // add here +} +``` + +Rules +----- +- Return `error` from `Enroll` on failure. `Initialize` handles the panic. +- Errors inside goroutines should skip the current cycle and wait for the next — do not crash the server. +- Use `Register` + error return instead of `MustRegister`. +- Follow the metric naming convention: `__` (e.g. `ping_rtt_seconds`). diff --git a/internal/metrics/init.go b/internal/metrics/init.go new file mode 100644 index 0000000..6d7509c --- /dev/null +++ b/internal/metrics/init.go @@ -0,0 +1,30 @@ +package metrics + +import ( + "github.com/easy-cloud-Knet/KWS_Core/internal/metrics/ping" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" +) + +type Metrics interface { + Enroll(prometheus.Registerer) error +} + +var metricsList []Metrics = []Metrics{ + &ping.Collector{}, +} + +func Initialize() *prometheus.Registry { + reg := prometheus.NewRegistry() + + reg.MustRegister( + collectors.NewGoCollector(), + collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), + ) + for _, m := range metricsList { + if err := m.Enroll(reg); err != nil { + panic(err) + } + } + return reg +} diff --git a/internal/metrics/ping/ping.go b/internal/metrics/ping/ping.go new file mode 100644 index 0000000..3387bbd --- /dev/null +++ b/internal/metrics/ping/ping.go @@ -0,0 +1,56 @@ +package ping + +import ( + "time" + + probing "github.com/prometheus-community/pro-bing" + "github.com/prometheus/client_golang/prometheus" +) + +type Collector struct { + rtt *prometheus.GaugeVec + dropRate *prometheus.GaugeVec +} + +func (c *Collector) Enroll(reg prometheus.Registerer) error { + c.rtt = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "ping_rtt_seconds", + Help: "Average round trip time in seconds", + }, []string{"destination"}) + + c.dropRate = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "ping_drop_rate", + Help: "Packet loss rate (0.0-100.0)", + }, []string{"destination"}) + + if err := reg.Register(c.rtt); err != nil { + return err + } + if err := reg.Register(c.dropRate); err != nil { + return err + } + + go c.collect() + return nil +} + +func (c *Collector) collect() { + for { + c.ping("8.8.8.8") + time.Sleep(10 * time.Second) + } +} + +func (c *Collector) ping(target string) { + pinger, err := probing.NewPinger(target) + if err != nil { + return + } + pinger.Count = 3 + if err := pinger.Run(); err != nil { + return + } + stats := pinger.Statistics() + c.rtt.WithLabelValues(target).Set(stats.AvgRtt.Seconds()) + c.dropRate.WithLabelValues(target).Set(stats.PacketLoss) +} From 2ef3f72eb15447f0ed1858bedc29dff15eaf35a5 Mon Sep 17 00:00:00 2001 From: Kwon Date: Wed, 6 May 2026 17:02:09 +0900 Subject: [PATCH 2/3] Reconcile added for pingger --- internal/metrics/ping/ping.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/metrics/ping/ping.go b/internal/metrics/ping/ping.go index 3387bbd..476d8e7 100644 --- a/internal/metrics/ping/ping.go +++ b/internal/metrics/ping/ping.go @@ -46,7 +46,8 @@ func (c *Collector) ping(target string) { if err != nil { return } - pinger.Count = 3 + pinger.Count = 10 + pinger.Timeout = 5 * time.Second if err := pinger.Run(); err != nil { return } From 4c5f67619c82c5745f01920d6be85788964c2b1f Mon Sep 17 00:00:00 2001 From: Kwon Date: Wed, 6 May 2026 17:21:40 +0900 Subject: [PATCH 3/3] minimized duplictated log message --- internal/server/middleware/log.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/server/middleware/log.go b/internal/server/middleware/log.go index e2c0e99..998e507 100644 --- a/internal/server/middleware/log.go +++ b/internal/server/middleware/log.go @@ -19,17 +19,20 @@ func (rw *responseWriter) WriteHeader(code int) { func LoggerMiddleware(next http.Handler, logger *zap.Logger) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - start := time.Now() + if r.URL.Path == "/metrics" { + next.ServeHTTP(w, r) + return + } + start := time.Now() wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK} next.ServeHTTP(wrapped, r) - elapsed := time.Since(start) logger.Info("http response sent", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.Int("status", wrapped.statusCode), - zap.Duration("duration", elapsed), + zap.Duration("duration", time.Since(start)), ) }) }