Skip to content

Commit

Permalink
feat: adding metrics package
Browse files Browse the repository at this point in the history
  • Loading branch information
Iusupov Anton committed Jul 30, 2024
1 parent 40ed2d8 commit a34181b
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 10 deletions.
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/go-pg/pg/v10 v10.12.0
github.com/gofrs/uuid v4.2.0+incompatible
github.com/joho/godotenv v1.5.1
github.com/prometheus/client_golang v1.19.1
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.7.1
github.com/valyala/fasthttp v1.44.0
Expand All @@ -16,6 +17,8 @@ require (

require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-pg/zerochecker v0.2.0 // indirect
Expand All @@ -27,6 +30,9 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
Expand All @@ -41,6 +47,7 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/sys v0.18.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
Expand Down
32 changes: 22 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
Expand Down Expand Up @@ -108,8 +112,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
Expand Down Expand Up @@ -145,8 +149,8 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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=
Expand All @@ -155,8 +159,6 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
Expand All @@ -173,10 +175,18 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
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/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
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/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
Expand Down Expand Up @@ -532,10 +542,12 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
Expand Down
Empty file added metrics/.coverignore
Empty file.
63 changes: 63 additions & 0 deletions metrics/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package metrics

import (
"net/http"
"sync/atomic"

"github.com/gateway-fm/scriptorium/clog"
)

type HealthChecker struct {
isReady atomic.Value
isHealthy atomic.Value
logger clog.CLog
}

func NewHealthChecker(logger clog.CLog) *HealthChecker {
hc := &HealthChecker{
logger: logger,
}
hc.isReady.Store(false)
hc.isHealthy.Store(true)
return hc
}

func (hc *HealthChecker) SetReady(ready bool) {
hc.isReady.Store(ready)
}

func (hc *HealthChecker) SetHealthy(healthy bool) {
hc.isHealthy.Store(healthy)
}

func (hc *HealthChecker) LivenessHandler(w http.ResponseWriter, r *http.Request) {
if hc.isHealthy.Load().(bool) {
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("ok"))
if err != nil {
hc.logger.ErrorCtx(r.Context(), err, "Failed to write liveness response")
}
} else {
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write([]byte("unhealthy"))
if err != nil {
hc.logger.ErrorCtx(r.Context(), err, "Failed to write liveness response")
}
}
}

func (hc *HealthChecker) ReadinessHandler(w http.ResponseWriter, r *http.Request) {
if hc.isReady.Load().(bool) {
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("ok"))
if err != nil {
hc.logger.ErrorCtx(r.Context(), err, "Failed to write readiness response")
}
} else {
w.WriteHeader(http.StatusServiceUnavailable)
_, err := w.Write([]byte("not ready"))
if err != nil {
hc.logger.ErrorCtx(r.Context(), err, "Failed to write readiness response")
}
}
}
9 changes: 9 additions & 0 deletions metrics/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package metrics

import "github.com/prometheus/client_golang/prometheus"

type Registry interface {
Inc(name string)
RecordDuration(name string, labels []string) *prometheus.HistogramVec
PrometheusRegistry() *prometheus.Registry
}
86 changes: 86 additions & 0 deletions metrics/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package metrics

import (
"strings"
"sync"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
)

type registry struct {
Subsystem string
Namespace string
PromRegistry *prometheus.Registry

metricsMu sync.Mutex
counters map[string]prometheus.Counter
histograms map[string]*prometheus.HistogramVec
}

func NewRegistry(subsystem, namespace string) Registry {
r := &registry{
Subsystem: subsystem,
Namespace: namespace,
PromRegistry: prometheus.NewRegistry(),
counters: make(map[string]prometheus.Counter),
histograms: make(map[string]*prometheus.HistogramVec),
}

registerMetrics(r)

return r
}

func (r *registry) sanitizeMetricName(name string) string {
return strings.ReplaceAll(strings.ReplaceAll(name, ".", "_"), "-", "_")
}

func (r *registry) Inc(name string) {
r.metricsMu.Lock()
defer r.metricsMu.Unlock()

sanitized := r.sanitizeMetricName(name)
counter, exists := r.counters[sanitized]
if !exists {
counter = prometheus.NewCounter(prometheus.CounterOpts{
Subsystem: r.Subsystem,
Namespace: r.Namespace,
Name: sanitized,
})
r.PromRegistry.MustRegister(counter)
r.counters[sanitized] = counter
}
counter.Inc()
}

func (r *registry) RecordDuration(name string, labels []string) *prometheus.HistogramVec {
r.metricsMu.Lock()
defer r.metricsMu.Unlock()

sanitized := r.sanitizeMetricName(name)
histogram, exists := r.histograms[sanitized]
if !exists {
histogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Subsystem: r.Subsystem,
Namespace: r.Namespace,
Name: sanitized,
Buckets: prometheus.DefBuckets,
}, labels)
r.PromRegistry.MustRegister(histogram)
r.histograms[sanitized] = histogram
}
return histogram
}

func (r *registry) PrometheusRegistry() *prometheus.Registry {
return r.PromRegistry
}

func registerMetrics(registry *registry) {
registry.PromRegistry.MustRegister(
collectors.NewGoCollector(
collectors.WithGoCollectorMemStatsMetricsDisabled(),
collectors.WithGoCollectorRuntimeMetrics(collectors.MetricsScheduler),
))
}
99 changes: 99 additions & 0 deletions metrics/series.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package metrics

import (
"context"
"fmt"
)

type (
SeriesType string

Series struct {
st SeriesType
name string
operation string
}

seriesContextKey struct{}
)

func (st SeriesType) String() string {
return string(st)
}

const (
SeriesTypeRPCHandler SeriesType = "rpc_handler"
SeriesTypeApiHandler SeriesType = "api_handler"
SeriesTypeUseCase SeriesType = "use_case"
SeriesTypeClient SeriesType = "client"
SeriesTypeDB SeriesType = "postgres"
SeriesTypeDatabusConsumer SeriesType = "databus_consumer"
)

func NewSeries(st SeriesType, name string) Series {
return Series{
st: st,
name: name,
operation: "undefined",
}
}

func FromContext(ctx context.Context) Series {
series, ok := ctx.Value(seriesContextKey{}).(Series)
if !ok {
return Series{}
}

return series
}

func (s Series) WithOperation(ctx context.Context, operation string) (context.Context, Series) {
series := FromContext(ctx)

if s.st == series.st &&
s.name == series.name {
series = Series{
st: s.st,
name: s.name,
operation: series.appendOperation(operation),
}

return series.ToContext(ctx), series
}

series = Series{
st: s.st,
name: s.name,
operation: operation,
}

return series.ToContext(ctx), series
}

func (s Series) ToContext(ctx context.Context) context.Context {
return context.WithValue(ctx, seriesContextKey{}, s)
}

func (s Series) Success() string {
return fmt.Sprintf("%s_%s_%s_success", s.st.String(), s.name, s.operation)
}

func (s Series) Error(errCode string) string {
return fmt.Sprintf("%s.%s.%s.error.%s", s.st.String(), s.name, s.operation, errCode)
}

func (s Series) Duration() string {
return fmt.Sprintf("%s.%s.%s.duration", s.st.String(), s.name, s.operation)
}

func (s Series) Info(code string) string {
return fmt.Sprintf("%s.%s.%s.info.%s", s.st.String(), s.name, s.operation, code)
}

func (s Series) Operation() string {
return s.operation
}

func (s Series) appendOperation(operation string) string {
return s.operation + "_" + operation
}
Loading

0 comments on commit a34181b

Please sign in to comment.