132 lines
4.1 KiB
Go
132 lines
4.1 KiB
Go
package metrics
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
outcomeDomain "proxy-pool/internal/domain/outcome"
|
|
proxyDomain "proxy-pool/internal/domain/proxy"
|
|
)
|
|
|
|
// GatewayCollector exposes fixed-cardinality request-path outcome metrics.
|
|
// Proxy, route, destination, client and credential values are never labels.
|
|
type GatewayCollector struct {
|
|
outcomes *prometheus.CounterVec
|
|
dropped prometheus.Counter
|
|
invariants *prometheus.CounterVec
|
|
}
|
|
|
|
var (
|
|
_ outcomeDomain.MetricsObserver = (*GatewayCollector)(nil)
|
|
_ proxyDomain.CapacityInvariantObserver = (*GatewayCollector)(nil)
|
|
)
|
|
|
|
func NewGatewayCollector(registerer prometheus.Registerer) (*GatewayCollector, error) {
|
|
if registerer == nil {
|
|
return nil, ErrInvalidDependencies
|
|
}
|
|
outcomes, err := registerCounterVec(registerer, prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: "proxy_pool", Subsystem: "gateway", Name: "outcomes_total",
|
|
Help: "Number of Gateway proxy attempts by furthest completed stage and result.",
|
|
}, []string{"stage", "result"}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dropped, err := registerCounter(registerer, prometheus.NewCounter(prometheus.CounterOpts{
|
|
Namespace: "proxy_pool", Subsystem: "gateway", Name: "outcome_queue_dropped_total",
|
|
Help: "Number of Gateway outcome observations dropped after the local queue was full.",
|
|
}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
invariants, err := registerCounterVec(registerer, prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: "proxy_pool", Subsystem: "gateway", Name: "capacity_invariant_violations_total",
|
|
Help: "Number of invalid Gateway local proxy capacity lifecycle transitions.",
|
|
}, []string{"operation"}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &GatewayCollector{outcomes: outcomes, dropped: dropped, invariants: invariants}, nil
|
|
}
|
|
|
|
func (collector *GatewayCollector) Observe(event outcomeDomain.Event) {
|
|
if collector == nil || collector.outcomes == nil || !validGatewayStage(event.Stage) {
|
|
return
|
|
}
|
|
result := "failure"
|
|
if event.Success {
|
|
result = "success"
|
|
}
|
|
collector.outcomes.WithLabelValues(gatewayStageLabel(event.Stage), result).Inc()
|
|
}
|
|
|
|
func (collector *GatewayCollector) ObserveDropped() {
|
|
if collector == nil || collector.dropped == nil {
|
|
return
|
|
}
|
|
collector.dropped.Inc()
|
|
}
|
|
|
|
func (collector *GatewayCollector) ObserveCapacityInvariant(event proxyDomain.CapacityInvariant) {
|
|
if collector == nil || collector.invariants == nil || !validCapacityInvariantViolation(event.Violation) {
|
|
return
|
|
}
|
|
collector.invariants.WithLabelValues(string(event.Violation)).Inc()
|
|
}
|
|
|
|
func registerCounter(registerer prometheus.Registerer, candidate prometheus.Counter) (prometheus.Counter, error) {
|
|
if err := registerer.Register(candidate); err == nil {
|
|
return candidate, nil
|
|
} else {
|
|
var registered prometheus.AlreadyRegisteredError
|
|
if !errors.As(err, ®istered) {
|
|
return nil, fmt.Errorf("register counter: %w", err)
|
|
}
|
|
existing, ok := registered.ExistingCollector.(prometheus.Counter)
|
|
if !ok {
|
|
return nil, fmt.Errorf("register counter: existing collector has unexpected type")
|
|
}
|
|
return existing, nil
|
|
}
|
|
}
|
|
|
|
func validGatewayStage(stage outcomeDomain.Stage) bool {
|
|
switch stage {
|
|
case outcomeDomain.StageDial, outcomeDomain.StageProxyHandshake,
|
|
outcomeDomain.StageResponseHeaders, outcomeDomain.StageTunnel:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func gatewayStageLabel(stage outcomeDomain.Stage) string {
|
|
switch stage {
|
|
case outcomeDomain.StageDial:
|
|
return "DIAL"
|
|
case outcomeDomain.StageProxyHandshake:
|
|
return "PROXY_HANDSHAKE"
|
|
case outcomeDomain.StageResponseHeaders:
|
|
return "RESPONSE_HEADERS"
|
|
case outcomeDomain.StageTunnel:
|
|
return "TUNNEL"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func validCapacityInvariantViolation(violation proxyDomain.CapacityInvariantViolation) bool {
|
|
switch violation {
|
|
case proxyDomain.CapacityInvariantCommitAlreadyCommitted,
|
|
proxyDomain.CapacityInvariantCommitFinished,
|
|
proxyDomain.CapacityInvariantCancelFinished,
|
|
proxyDomain.CapacityInvariantReleaseBeforeCommit,
|
|
proxyDomain.CapacityInvariantReleaseFinished:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|