snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
68
internal/metrics/accessors.go
Normal file
68
internal/metrics/accessors.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package metrics
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
// Proxy metrics - exported for use by proxy package.
|
||||
//
|
||||
// ProxyCallDurationSummary holds the legacy Summary instrument by default;
|
||||
// when prometheus.native_histograms is enabled it is a no-op observer (see
|
||||
// internal noopObserverVec), so callers can continue to construct cached
|
||||
// observers and call .Observe() without nil-checks. ProxyCallDurationHistogram
|
||||
// is the canonical companion — prefer it for new queries and OTel export.
|
||||
var (
|
||||
ProxyCallDurationSummary prometheus.ObserverVec
|
||||
ProxyCallDurationHistogram *prometheus.HistogramVec
|
||||
ProxyCallErrorCount *prometheus.CounterVec
|
||||
ProxyCallInflightRequests *prometheus.GaugeVec
|
||||
)
|
||||
|
||||
// API metrics - exported for use by api package.
|
||||
//
|
||||
// APICommandDurationSummary follows the same pattern as ProxyCallDurationSummary
|
||||
// (real Summary by default, no-op when native_histograms is enabled).
|
||||
// APICommandDurationHistogram is the canonical companion.
|
||||
//
|
||||
// RPCDurationSummary follows the same pattern. RPCDurationHistogram is the
|
||||
// companion Histogram exposed as "rpc_duration_seconds_histogram" —
|
||||
// unconditional, with native (sparse, exponential) schema when
|
||||
// native_histograms is enabled.
|
||||
var (
|
||||
APICommandErrorsTotal *prometheus.CounterVec
|
||||
APICommandDurationSummary prometheus.ObserverVec
|
||||
APICommandDurationHistogram *prometheus.HistogramVec
|
||||
RPCDurationSummary prometheus.ObserverVec
|
||||
RPCDurationHistogram *prometheus.HistogramVec
|
||||
)
|
||||
|
||||
// Consumer metrics - exported for use by consuming package
|
||||
var (
|
||||
ConsumerProcessedTotal *prometheus.CounterVec
|
||||
ConsumerErrorsTotal *prometheus.CounterVec
|
||||
)
|
||||
|
||||
// Shared poll proxy metrics - exported for use by proxy package
|
||||
var (
|
||||
SharedPollProxyRequestItems *prometheus.HistogramVec
|
||||
SharedPollProxyResponseItems *prometheus.HistogramVec
|
||||
)
|
||||
|
||||
// Middleware metrics - exported for use by middleware package
|
||||
var (
|
||||
ConnLimitReached prometheus.Counter
|
||||
HTTPRequestsTotal *prometheus.CounterVec
|
||||
)
|
||||
|
||||
// Postgres broker metrics exported for use by pgmapbroker and pgstreambroker.
|
||||
//
|
||||
// All carry a postgres_ name prefix: these are Postgres-specific (outbox,
|
||||
// partitions, row cleanup), not generic broker concepts. Metrics emitted by both
|
||||
// PG brokers are split per kind (broker_* for stream, map_broker_* for map) so
|
||||
// the two never share a series. Cleanup is stream-only today.
|
||||
var (
|
||||
BrokerPostgresCleanupRemovedTotal *prometheus.CounterVec
|
||||
|
||||
BrokerPostgresOutboxCursorLagSeconds *prometheus.GaugeVec
|
||||
MapBrokerPostgresOutboxCursorLagSeconds *prometheus.GaugeVec
|
||||
BrokerPostgresPartitions *prometheus.GaugeVec
|
||||
MapBrokerPostgresPartitions *prometheus.GaugeVec
|
||||
)
|
||||
145
internal/metrics/graphite/graphite.go
Normal file
145
internal/metrics/graphite/graphite.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package graphite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/FZambia/eagle"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var re = regexp.MustCompile("[[:^ascii:]]")
|
||||
|
||||
// PreparePathComponent cleans string to be used as Graphite metric path.
|
||||
func PreparePathComponent(s string) string {
|
||||
s = re.ReplaceAllLiteralString(s, "_")
|
||||
return strings.ReplaceAll(s, ".", "_")
|
||||
}
|
||||
|
||||
// Exporter to Graphite.
|
||||
type Exporter struct {
|
||||
prefix string
|
||||
address string
|
||||
timeout time.Duration
|
||||
tags bool
|
||||
closeOnce sync.Once
|
||||
closeCh chan struct{}
|
||||
sink chan eagle.Metrics
|
||||
eagle *eagle.Eagle
|
||||
}
|
||||
|
||||
// Config for Graphite Exporter.
|
||||
type Config struct {
|
||||
Address string
|
||||
Gatherer prometheus.Gatherer
|
||||
Interval time.Duration
|
||||
Prefix string
|
||||
Tags bool
|
||||
}
|
||||
|
||||
// New creates new Graphite Exporter.
|
||||
func New(c Config) *Exporter {
|
||||
exporter := &Exporter{
|
||||
prefix: c.Prefix,
|
||||
address: c.Address,
|
||||
timeout: time.Second,
|
||||
tags: c.Tags,
|
||||
closeCh: make(chan struct{}),
|
||||
sink: make(chan eagle.Metrics),
|
||||
}
|
||||
exporter.eagle = eagle.New(eagle.Config{
|
||||
Gatherer: c.Gatherer,
|
||||
Interval: c.Interval,
|
||||
Sink: exporter.sink,
|
||||
})
|
||||
return exporter
|
||||
}
|
||||
|
||||
func (e *Exporter) Run(ctx context.Context) error {
|
||||
defer func() { _ = e.close() }()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case metrics := <-e.sink:
|
||||
_ = e.exportOnce(metrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops exporter.
|
||||
func (e *Exporter) close() error {
|
||||
e.closeOnce.Do(func() {
|
||||
close(e.closeCh)
|
||||
_ = e.eagle.Close()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Exporter) exportOnce(metrics eagle.Metrics) error {
|
||||
conn, err := net.DialTimeout("tcp", e.address, e.timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
_ = e.write(conn, metrics)
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeTags(labels []string) string {
|
||||
if len(labels) == 0 {
|
||||
return ""
|
||||
}
|
||||
var tagParts []string
|
||||
for i := 0; i < len(labels); i += 2 {
|
||||
tagParts = append(tagParts, fmt.Sprintf("%s=%s", labels[i], labels[i+1]))
|
||||
}
|
||||
return ";" + strings.Join(tagParts, ";")
|
||||
}
|
||||
|
||||
func (e *Exporter) write(w io.Writer, metrics eagle.Metrics) error {
|
||||
now := time.Now().Unix()
|
||||
for _, item := range metrics.Items {
|
||||
for _, metricValue := range item.Values {
|
||||
parts := []string{e.prefix}
|
||||
if item.Namespace != "" {
|
||||
parts = append(parts, item.Namespace)
|
||||
}
|
||||
if item.Subsystem != "" {
|
||||
parts = append(parts, item.Subsystem)
|
||||
}
|
||||
if item.Name != "" {
|
||||
parts = append(parts, item.Name)
|
||||
}
|
||||
if metricValue.Name != "" {
|
||||
parts = append(parts, metricValue.Name)
|
||||
}
|
||||
if !e.tags {
|
||||
parts = append(parts, metricValue.Labels...)
|
||||
}
|
||||
key := strings.Join(parts, ".")
|
||||
|
||||
if e.tags {
|
||||
key += makeTags(metricValue.Labels)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if item.Type == eagle.MetricTypeCounter {
|
||||
_, err = fmt.Fprintf(w, "%s %d %d\n", key, int64(metricValue.Value), now)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w, "%s %f %d\n", key, metricValue.Value, now)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
22
internal/metrics/graphite/graphite_test.go
Normal file
22
internal/metrics/graphite/graphite_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package graphite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPreparePathComponent(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in, out string
|
||||
}{
|
||||
{in: "service", out: "service"},
|
||||
{in: "service.local", out: "service_local"},
|
||||
{in: "service.prod.", out: "service_prod_"},
|
||||
{in: "приvет", out: "___v__"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if want, got := tc.out, PreparePathComponent(tc.in); want != got {
|
||||
t.Fatalf("error, got sanitized string %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
internal/metrics/helpers.go
Normal file
40
internal/metrics/helpers.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// API metric helper functions - these were previously in the api package
|
||||
|
||||
// IncAPIError increments the API error counter with a numeric error code.
|
||||
func IncAPIError(protocol string, method string, code uint32) {
|
||||
APICommandErrorsTotal.WithLabelValues(protocol, method, strconv.FormatUint(uint64(code), 10)).Inc()
|
||||
}
|
||||
|
||||
// IncAPIErrorStringCode increments the API error counter with a string error code.
|
||||
func IncAPIErrorStringCode(protocol string, method string, code string) {
|
||||
APICommandErrorsTotal.WithLabelValues(protocol, method, code).Inc()
|
||||
}
|
||||
|
||||
// ObserveAPICommand observes the duration of an API command.
|
||||
func ObserveAPICommand(started time.Time, protocol string, method string) {
|
||||
duration := time.Since(started).Seconds()
|
||||
APICommandDurationSummary.WithLabelValues(protocol, method).Observe(duration)
|
||||
APICommandDurationHistogram.WithLabelValues(protocol, method).Observe(duration)
|
||||
}
|
||||
|
||||
// ObserveRPC observes the duration of an RPC call.
|
||||
func ObserveRPC(started time.Time, protocol string, method string) {
|
||||
duration := time.Since(started).Seconds()
|
||||
RPCDurationSummary.WithLabelValues(protocol, method).Observe(duration)
|
||||
RPCDurationHistogram.WithLabelValues(protocol, method).Observe(duration)
|
||||
}
|
||||
|
||||
// Consumer metric helper functions - these were previously in the consuming package
|
||||
|
||||
// InitConsumerMetrics initializes consumer metrics with zero values for the given consumer name.
|
||||
func InitConsumerMetrics(consumerName string) {
|
||||
ConsumerProcessedTotal.WithLabelValues(consumerName).Add(0)
|
||||
ConsumerErrorsTotal.WithLabelValues(consumerName).Add(0)
|
||||
}
|
||||
400
internal/metrics/metrics.go
Normal file
400
internal/metrics/metrics.go
Normal file
@@ -0,0 +1,400 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const defaultMetricsNamespace = "centrifugo"
|
||||
|
||||
// Config contains metrics configuration.
|
||||
type Config struct {
|
||||
// Namespace is the prometheus namespace for all metrics. If empty, defaults to "centrifugo".
|
||||
Namespace string
|
||||
// ConstLabels are labels that will be added to all metrics as constant labels.
|
||||
// These are useful for adding environment, region, or other deployment-specific labels.
|
||||
ConstLabels map[string]string
|
||||
// Registerer is the prometheus registerer to use. If nil, prometheus.DefaultRegisterer is used.
|
||||
Registerer prometheus.Registerer
|
||||
// NativeHistograms switches Histogram instruments to Prometheus native
|
||||
// (sparse, exponential) schema with no explicit buckets exposed. Designed
|
||||
// for OpenTelemetry export via the client_golang Prometheus bridge.
|
||||
// Text-format scrapes lose _bucket series; use protobuf scrape format.
|
||||
NativeHistograms bool
|
||||
}
|
||||
|
||||
// nativeHistogramOpts returns opts unchanged when native is false. When true,
|
||||
// it enables Prometheus native histogram schema with no explicit buckets —
|
||||
// the metric exposes only _count, _sum, and the native histogram chunk.
|
||||
func nativeHistogramOpts(opts prometheus.HistogramOpts, native bool) prometheus.HistogramOpts {
|
||||
if !native {
|
||||
return opts
|
||||
}
|
||||
opts.Buckets = nil
|
||||
opts.NativeHistogramBucketFactor = 1.1
|
||||
opts.NativeHistogramMaxBucketNumber = 200
|
||||
opts.NativeHistogramMinResetDuration = time.Hour
|
||||
return opts
|
||||
}
|
||||
|
||||
// noopObserverVec implements prometheus.ObserverVec with all no-op methods.
|
||||
// Assigned to Summary accessors when NativeHistograms is true so that
|
||||
// callers (proxy handlers, helpers) can still construct cached observers
|
||||
// and call .Observe() without nil checks — the calls become no-ops and no
|
||||
// metric data is recorded for the disabled Summary instrument.
|
||||
type noopObserverVec struct{}
|
||||
|
||||
func (noopObserverVec) Describe(chan<- *prometheus.Desc) {}
|
||||
func (noopObserverVec) Collect(chan<- prometheus.Metric) {}
|
||||
func (noopObserverVec) WithLabelValues(...string) prometheus.Observer { return noopObserver{} }
|
||||
func (noopObserverVec) With(prometheus.Labels) prometheus.Observer { return noopObserver{} }
|
||||
func (noopObserverVec) GetMetricWith(prometheus.Labels) (prometheus.Observer, error) {
|
||||
return noopObserver{}, nil
|
||||
}
|
||||
func (noopObserverVec) GetMetricWithLabelValues(...string) (prometheus.Observer, error) {
|
||||
return noopObserver{}, nil
|
||||
}
|
||||
func (noopObserverVec) CurryWith(prometheus.Labels) (prometheus.ObserverVec, error) {
|
||||
return noopObserverVec{}, nil
|
||||
}
|
||||
func (noopObserverVec) MustCurryWith(prometheus.Labels) prometheus.ObserverVec {
|
||||
return noopObserverVec{}
|
||||
}
|
||||
|
||||
type noopObserver struct{}
|
||||
|
||||
func (noopObserver) Observe(float64) {}
|
||||
|
||||
// Registry holds all Centrifugo metrics.
|
||||
type Registry struct {
|
||||
config Config
|
||||
|
||||
// Proxy metrics
|
||||
proxyCallDurationSummary prometheus.ObserverVec
|
||||
proxyCallDurationHistogram *prometheus.HistogramVec
|
||||
proxyCallErrorCount *prometheus.CounterVec
|
||||
proxyCallInflightRequests *prometheus.GaugeVec
|
||||
|
||||
// API metrics
|
||||
apiCommandErrorsTotal *prometheus.CounterVec
|
||||
apiCommandDurationSummary prometheus.ObserverVec
|
||||
apiCommandDurationHistogram *prometheus.HistogramVec
|
||||
// rpcDurationSummary is the legacy Summary by default; no-op when
|
||||
// NativeHistograms is true. rpcDurationHistogram is the new companion
|
||||
// Histogram exposed as "rpc_duration_seconds_histogram" — unconditional,
|
||||
// with native schema when NativeHistograms is true.
|
||||
rpcDurationSummary prometheus.ObserverVec
|
||||
rpcDurationHistogram *prometheus.HistogramVec
|
||||
|
||||
// Consumer metrics
|
||||
consumerProcessedTotal *prometheus.CounterVec
|
||||
consumerErrorsTotal *prometheus.CounterVec
|
||||
|
||||
// Shared poll proxy metrics
|
||||
sharedPollProxyRequestItems *prometheus.HistogramVec
|
||||
sharedPollProxyResponseItems *prometheus.HistogramVec
|
||||
|
||||
// Middleware metrics
|
||||
connLimitReached prometheus.Counter
|
||||
httpRequestsTotal *prometheus.CounterVec
|
||||
|
||||
// PostgreSQL-specific broker metrics. Cleanup is Postgres-specific here (only
|
||||
// the PG stream broker prunes rows, with PG table names in the "pass" label),
|
||||
// so it carries the postgres_ prefix like the rest. Metrics emitted by both
|
||||
// PG brokers are split per kind so stream and map never share a series.
|
||||
brokerPostgresCleanupRemovedTotal *prometheus.CounterVec
|
||||
brokerPostgresOutboxCursorLagSeconds *prometheus.GaugeVec
|
||||
mapBrokerPostgresOutboxCursorLagSeconds *prometheus.GaugeVec
|
||||
brokerPostgresPartitions *prometheus.GaugeVec
|
||||
mapBrokerPostgresPartitions *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
// Init initializes the metrics registry with the provided configuration.
|
||||
// It creates all metrics and registers them with the provided registerer.
|
||||
// If registerer is nil, prometheus.DefaultRegisterer is used.
|
||||
// Returns an error if metric registration fails.
|
||||
func Init(cfg Config) error {
|
||||
reg, err := newRegistry(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Populate exported variables for backward compatibility
|
||||
ProxyCallDurationSummary = reg.proxyCallDurationSummary
|
||||
ProxyCallDurationHistogram = reg.proxyCallDurationHistogram
|
||||
ProxyCallErrorCount = reg.proxyCallErrorCount
|
||||
ProxyCallInflightRequests = reg.proxyCallInflightRequests
|
||||
|
||||
APICommandErrorsTotal = reg.apiCommandErrorsTotal
|
||||
APICommandDurationSummary = reg.apiCommandDurationSummary
|
||||
APICommandDurationHistogram = reg.apiCommandDurationHistogram
|
||||
RPCDurationSummary = reg.rpcDurationSummary
|
||||
RPCDurationHistogram = reg.rpcDurationHistogram
|
||||
|
||||
ConsumerProcessedTotal = reg.consumerProcessedTotal
|
||||
ConsumerErrorsTotal = reg.consumerErrorsTotal
|
||||
|
||||
SharedPollProxyRequestItems = reg.sharedPollProxyRequestItems
|
||||
SharedPollProxyResponseItems = reg.sharedPollProxyResponseItems
|
||||
|
||||
ConnLimitReached = reg.connLimitReached
|
||||
HTTPRequestsTotal = reg.httpRequestsTotal
|
||||
|
||||
BrokerPostgresCleanupRemovedTotal = reg.brokerPostgresCleanupRemovedTotal
|
||||
BrokerPostgresOutboxCursorLagSeconds = reg.brokerPostgresOutboxCursorLagSeconds
|
||||
MapBrokerPostgresOutboxCursorLagSeconds = reg.mapBrokerPostgresOutboxCursorLagSeconds
|
||||
BrokerPostgresPartitions = reg.brokerPostgresPartitions
|
||||
MapBrokerPostgresPartitions = reg.mapBrokerPostgresPartitions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newRegistry(cfg Config) (*Registry, error) {
|
||||
registerer := cfg.Registerer
|
||||
if registerer == nil {
|
||||
registerer = prometheus.DefaultRegisterer
|
||||
}
|
||||
|
||||
metricsNamespace := cfg.Namespace
|
||||
if metricsNamespace == "" {
|
||||
metricsNamespace = defaultMetricsNamespace
|
||||
}
|
||||
|
||||
constLabels := prometheus.Labels(cfg.ConstLabels)
|
||||
|
||||
m := &Registry{
|
||||
config: cfg,
|
||||
}
|
||||
|
||||
// Proxy metrics
|
||||
if cfg.NativeHistograms {
|
||||
m.proxyCallDurationSummary = noopObserverVec{}
|
||||
} else {
|
||||
m.proxyCallDurationSummary = prometheus.NewSummaryVec(prometheus.SummaryOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "proxy",
|
||||
Name: "duration_seconds",
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.99: 0.001, 0.999: 0.0001},
|
||||
Help: "DEPRECATED — use centrifugo_proxy_duration_seconds_histogram. Will be removed in Centrifugo v7. Duration of proxy call.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"protocol", "type", "name"})
|
||||
}
|
||||
|
||||
m.proxyCallDurationHistogram = prometheus.NewHistogramVec(nativeHistogramOpts(prometheus.HistogramOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "proxy",
|
||||
Name: "duration_seconds_histogram",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
Help: "Histogram of duration of proxy call.",
|
||||
ConstLabels: constLabels,
|
||||
}, cfg.NativeHistograms), []string{"protocol", "type", "name"})
|
||||
|
||||
m.proxyCallErrorCount = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "proxy",
|
||||
Name: "errors",
|
||||
Help: "Proxy call error count.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"protocol", "type", "name"})
|
||||
|
||||
m.proxyCallInflightRequests = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "proxy",
|
||||
Name: "inflight_requests",
|
||||
Help: "Number of inflight proxy requests.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"protocol", "type", "name"})
|
||||
|
||||
// API metrics
|
||||
m.apiCommandErrorsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "api",
|
||||
Name: "command_errors_total",
|
||||
Help: "Total errors in API commands.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"protocol", "method", "error"})
|
||||
|
||||
if cfg.NativeHistograms {
|
||||
m.apiCommandDurationSummary = noopObserverVec{}
|
||||
} else {
|
||||
m.apiCommandDurationSummary = prometheus.NewSummaryVec(prometheus.SummaryOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "api",
|
||||
Name: "command_duration_seconds",
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.99: 0.001, 0.999: 0.0001},
|
||||
Help: "DEPRECATED — use centrifugo_api_command_duration_seconds_histogram. Will be removed in Centrifugo v7. Duration of API per command.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"protocol", "method"})
|
||||
}
|
||||
|
||||
m.apiCommandDurationHistogram = prometheus.NewHistogramVec(nativeHistogramOpts(prometheus.HistogramOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "api",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
Name: "command_duration_seconds_histogram",
|
||||
Help: "Histogram of duration of API per command.",
|
||||
ConstLabels: constLabels,
|
||||
}, cfg.NativeHistograms), []string{"protocol", "method"})
|
||||
|
||||
if cfg.NativeHistograms {
|
||||
m.rpcDurationSummary = noopObserverVec{}
|
||||
} else {
|
||||
m.rpcDurationSummary = prometheus.NewSummaryVec(prometheus.SummaryOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "api",
|
||||
Name: "rpc_duration_seconds",
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.99: 0.001, 0.999: 0.0001},
|
||||
Help: "DEPRECATED — use centrifugo_api_rpc_duration_seconds_histogram. Will be removed in Centrifugo v7. Duration of API per command.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"protocol", "method"})
|
||||
}
|
||||
|
||||
m.rpcDurationHistogram = prometheus.NewHistogramVec(nativeHistogramOpts(prometheus.HistogramOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "api",
|
||||
Name: "rpc_duration_seconds_histogram",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
Help: "Histogram of duration of API per command.",
|
||||
ConstLabels: constLabels,
|
||||
}, cfg.NativeHistograms), []string{"protocol", "method"})
|
||||
|
||||
// Consumer metrics
|
||||
m.consumerProcessedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "consumers",
|
||||
Name: "messages_processed_total",
|
||||
Help: "Total number of processed messages in consumer",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"consumer_name"})
|
||||
|
||||
m.consumerErrorsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "consumers",
|
||||
Name: "errors_total",
|
||||
Help: "Total number of errors in consumer",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"consumer_name"})
|
||||
|
||||
// Shared poll proxy metrics
|
||||
m.sharedPollProxyRequestItems = prometheus.NewHistogramVec(nativeHistogramOpts(prometheus.HistogramOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "shared_poll_proxy",
|
||||
Name: "request_items",
|
||||
Help: "Number of items per shared poll proxy request.",
|
||||
Buckets: []float64{1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
|
||||
ConstLabels: constLabels,
|
||||
}, cfg.NativeHistograms), []string{"name"})
|
||||
|
||||
m.sharedPollProxyResponseItems = prometheus.NewHistogramVec(nativeHistogramOpts(prometheus.HistogramOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "shared_poll_proxy",
|
||||
Name: "response_items",
|
||||
Help: "Number of items per shared poll proxy response.",
|
||||
Buckets: []float64{1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
|
||||
ConstLabels: constLabels,
|
||||
}, cfg.NativeHistograms), []string{"name"})
|
||||
|
||||
// Middleware metrics
|
||||
m.connLimitReached = prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "node",
|
||||
Name: "client_connection_limit",
|
||||
Help: "Number of refused requests due to node client connection limit.",
|
||||
ConstLabels: constLabels,
|
||||
})
|
||||
|
||||
m.httpRequestsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "node",
|
||||
Name: "incoming_http_requests_total",
|
||||
Help: "Number of incoming HTTP requests",
|
||||
ConstLabels: constLabels,
|
||||
},
|
||||
[]string{"path", "method", "status"},
|
||||
)
|
||||
|
||||
// Postgres-specific broker metrics carry a postgres_ name prefix. Cleanup is
|
||||
// Postgres-specific here: only the PG stream broker prunes rows (the "pass"
|
||||
// label is the PG table being cleaned), so it is not the generic cleanup
|
||||
// metric. Metrics emitted by both PG brokers are split per kind below.
|
||||
m.brokerPostgresCleanupRemovedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "broker",
|
||||
Name: "postgres_cleanup_removed_total",
|
||||
Help: "Total rows removed by each Postgres cleanup pass.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"broker_name", "pass"})
|
||||
|
||||
m.brokerPostgresOutboxCursorLagSeconds = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "broker",
|
||||
Name: "postgres_outbox_cursor_lag_seconds",
|
||||
Help: "Time between the outbox cursor's row created_at and now, per shard.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"broker_name", "shard"})
|
||||
|
||||
m.mapBrokerPostgresOutboxCursorLagSeconds = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "map_broker",
|
||||
Name: "postgres_outbox_cursor_lag_seconds",
|
||||
Help: "Time between the outbox cursor's row created_at and now, per shard.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"broker_name", "shard"})
|
||||
|
||||
m.brokerPostgresPartitions = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "broker",
|
||||
Name: "postgres_partitions",
|
||||
Help: "Count of stream/history table partitions.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"broker_name"})
|
||||
|
||||
m.mapBrokerPostgresPartitions = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: metricsNamespace,
|
||||
Subsystem: "map_broker",
|
||||
Name: "postgres_partitions",
|
||||
Help: "Count of stream/history table partitions.",
|
||||
ConstLabels: constLabels,
|
||||
}, []string{"broker_name"})
|
||||
|
||||
// Register all metrics
|
||||
var alreadyRegistered prometheus.AlreadyRegisteredError
|
||||
|
||||
collectors := []prometheus.Collector{
|
||||
m.proxyCallDurationSummary,
|
||||
m.proxyCallDurationHistogram,
|
||||
m.proxyCallErrorCount,
|
||||
m.proxyCallInflightRequests,
|
||||
m.apiCommandErrorsTotal,
|
||||
m.apiCommandDurationSummary,
|
||||
m.apiCommandDurationHistogram,
|
||||
m.rpcDurationSummary,
|
||||
m.rpcDurationHistogram,
|
||||
m.consumerProcessedTotal,
|
||||
m.consumerErrorsTotal,
|
||||
m.sharedPollProxyRequestItems,
|
||||
m.sharedPollProxyResponseItems,
|
||||
m.connLimitReached,
|
||||
m.httpRequestsTotal,
|
||||
m.brokerPostgresCleanupRemovedTotal,
|
||||
m.brokerPostgresOutboxCursorLagSeconds,
|
||||
m.mapBrokerPostgresOutboxCursorLagSeconds,
|
||||
m.brokerPostgresPartitions,
|
||||
m.mapBrokerPostgresPartitions,
|
||||
}
|
||||
|
||||
for _, collector := range collectors {
|
||||
err := registerer.Register(collector)
|
||||
if err != nil {
|
||||
// Ignore if already registered (allows re-initialization in tests)
|
||||
if !errors.As(err, &alreadyRegistered) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
Reference in New Issue
Block a user