// Package metrix implements stats-related functionality. package metrix import ( "net/http" "strconv" "time" "github.com/smallstep/certificates/authority/provisioner" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) // New initializes and returns a new [Meter]. func New() (m *Meter) { initializedAt := time.Now() m = &Meter{ uptime: prometheus.NewGaugeFunc( prometheus.GaugeOpts(opts( "", "uptime_seconds", "Number of seconds since service start", )), func() float64 { return float64(time.Since(initializedAt) / time.Second) }, ), ssh: newProvisionerInstruments("ssh"), x509: newProvisionerInstruments("x509"), kms: &kms{ signed: prometheus.NewCounter(prometheus.CounterOpts(opts("kms", "signed", "Number of KMS-backed signatures"))), errors: prometheus.NewCounter(prometheus.CounterOpts(opts("kms", "errors", "Number of KMS-related errors"))), }, } reg := prometheus.NewRegistry() reg.MustRegister( m.uptime, m.ssh.rekeyed, m.ssh.renewed, m.ssh.signed, m.x509.rekeyed, m.x509.renewed, m.x509.signed, m.kms.signed, m.kms.errors, ) h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{ Registry: reg, Timeout: 5 * time.Second, MaxRequestsInFlight: 10, }) mux := http.NewServeMux() mux.Handle("/metrics", h) m.Handler = mux return } // Meter wraps the functionality of a Prometheus-compatible HTTP handler. type Meter struct { http.Handler uptime prometheus.GaugeFunc ssh *provisionerInstruments x509 *provisionerInstruments kms *kms } // SSHRekeyed implements [authority.Meter] for [Meter]. func (m *Meter) SSHRekeyed(p provisioner.Interface, err error) { incrProvisionerCounter(m.ssh.rekeyed, p, err) } // SSHRenewed implements [authority.Meter] for [Meter]. func (m *Meter) SSHRenewed(p provisioner.Interface, err error) { incrProvisionerCounter(m.ssh.renewed, p, err) } // SSHSigned implements [authority.Meter] for [Meter]. func (m *Meter) SSHSigned(p provisioner.Interface, err error) { incrProvisionerCounter(m.ssh.signed, p, err) } // SSHAuthorized implements [authority.Meter] for [Meter]. func (m *Meter) SSHWebhookAuthorized(p provisioner.Interface, err error) { incrProvisionerCounter(m.ssh.webhookAuthorized, p, err) } // SSHEnriched implements [authority.Meter] for [Meter]. func (m *Meter) SSHWebhookEnriched(p provisioner.Interface, err error) { incrProvisionerCounter(m.ssh.webhookEnriched, p, err) } // X509Rekeyed implements [authority.Meter] for [Meter]. func (m *Meter) X509Rekeyed(p provisioner.Interface, err error) { incrProvisionerCounter(m.x509.rekeyed, p, err) } // X509Renewed implements [authority.Meter] for [Meter]. func (m *Meter) X509Renewed(p provisioner.Interface, err error) { incrProvisionerCounter(m.x509.renewed, p, err) } // X509Signed implements [authority.Meter] for [Meter]. func (m *Meter) X509Signed(p provisioner.Interface, err error) { incrProvisionerCounter(m.x509.signed, p, err) } // X509Authorized implements [authority.Meter] for [Meter]. func (m *Meter) X509WebhookAuthorized(p provisioner.Interface, err error) { incrProvisionerCounter(m.x509.webhookAuthorized, p, err) } // X509Enriched implements [authority.Meter] for [Meter]. func (m *Meter) X509WebhookEnriched(p provisioner.Interface, err error) { incrProvisionerCounter(m.x509.webhookEnriched, p, err) } func incrProvisionerCounter(cv *prometheus.CounterVec, p provisioner.Interface, err error) { var name string if p != nil { name = p.GetName() } cv.WithLabelValues(name, strconv.FormatBool(err == nil)).Inc() } // KMSSigned implements [authority.Meter] for [Meter]. func (m *Meter) KMSSigned(err error) { if err == nil { m.kms.signed.Inc() } else { m.kms.errors.Inc() } } // provisionerInstruments wraps the counters exported by provisioners. type provisionerInstruments struct { rekeyed *prometheus.CounterVec renewed *prometheus.CounterVec signed *prometheus.CounterVec webhookAuthorized *prometheus.CounterVec webhookEnriched *prometheus.CounterVec } func newProvisionerInstruments(subsystem string) *provisionerInstruments { return &provisionerInstruments{ rekeyed: newCounterVec(subsystem, "rekeyed_total", "Number of certificates rekeyed", "provisioner", "success", ), renewed: newCounterVec(subsystem, "renewed_total", "Number of certificates renewed", "provisioner", "success", ), signed: newCounterVec(subsystem, "signed_total", "Number of certificates signed", "provisioner", "success", ), webhookAuthorized: newCounterVec(subsystem, "webhook_authorized_total", "Number of authorizing webhooks called", "provisioner", "success", ), webhookEnriched: newCounterVec(subsystem, "webhook_enriched_total", "Number of enriching webhooks called", "provisioner", "success", ), } } type kms struct { signed prometheus.Counter errors prometheus.Counter } func newCounterVec(subsystem, name, help string, labels ...string) *prometheus.CounterVec { opts := opts(subsystem, name, help) return prometheus.NewCounterVec(prometheus.CounterOpts(opts), labels) } func opts(subsystem, name, help string) prometheus.Opts { return prometheus.Opts{ Namespace: "step_ca", Subsystem: subsystem, Name: name, Help: help, } }