mirror of
https://github.com/hanzoai/status.git
synced 2026-10-30 18:21:48 +00:00
zip's registration is a verb method on the group, so a route that is not a typed operation says which method it answers with. The security package's own two-method router goes with it: it existed so both the app and a sub-router could satisfy one name, and restating a routing surface is what let the two disagree — the methods it named no longer exist. The app spells itself as a group, which registers at exactly the paths it did. The toolchain and the pin move together, because below zip v1.36.50 the JSON encoder was selected on a build tag: a repo whose go directive rises first writes a wire that is neither the old release's nor the new one's until the pin catches up. metrics_test.go still does not compile, before and after: it compares a metric registry against a gatherer from a different metrics library, so the two Gather signatures disagree. That is its own change. Co-authored-by: Hanzo Dev <[email protected]>
233 lines
8.6 KiB
Go
233 lines
8.6 KiB
Go
package metrics
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
metric "github.com/luxfi/metric"
|
|
"hanzo.ai/status/config"
|
|
"hanzo.ai/status/config/endpoint"
|
|
"hanzo.ai/status/config/suite"
|
|
)
|
|
|
|
const namespace = "gatus" // The prefix of the metrics
|
|
|
|
var (
|
|
resultTotal metric.CounterVec
|
|
resultDurationSeconds metric.GaugeVec
|
|
resultConnectedTotal metric.CounterVec
|
|
resultCodeTotal metric.CounterVec
|
|
resultCertificateExpirationSeconds metric.GaugeVec
|
|
resultDomainExpirationSeconds metric.GaugeVec
|
|
resultEndpointSuccess metric.GaugeVec
|
|
|
|
// Suite metrics
|
|
suiteResultTotal metric.CounterVec
|
|
suiteResultDurationSeconds metric.GaugeVec
|
|
suiteResultSuccess metric.GaugeVec
|
|
|
|
// Track if metrics have been initialized to prevent duplicate registration
|
|
metricsInitialized bool
|
|
currentRegisterer metric.Registerer
|
|
)
|
|
|
|
// UnregisterPrometheusMetrics unregisters all previously registered metrics
|
|
func UnregisterPrometheusMetrics() {
|
|
if !metricsInitialized || currentRegisterer == nil {
|
|
return
|
|
}
|
|
|
|
// Unregister all metrics if they exist
|
|
if resultTotal != nil {
|
|
currentRegisterer.Unregister(resultTotal)
|
|
}
|
|
if resultDurationSeconds != nil {
|
|
currentRegisterer.Unregister(resultDurationSeconds)
|
|
}
|
|
if resultConnectedTotal != nil {
|
|
currentRegisterer.Unregister(resultConnectedTotal)
|
|
}
|
|
if resultCodeTotal != nil {
|
|
currentRegisterer.Unregister(resultCodeTotal)
|
|
}
|
|
if resultCertificateExpirationSeconds != nil {
|
|
currentRegisterer.Unregister(resultCertificateExpirationSeconds)
|
|
}
|
|
if resultDomainExpirationSeconds != nil {
|
|
currentRegisterer.Unregister(resultDomainExpirationSeconds)
|
|
}
|
|
if resultEndpointSuccess != nil {
|
|
currentRegisterer.Unregister(resultEndpointSuccess)
|
|
}
|
|
|
|
// Unregister suite metrics
|
|
if suiteResultTotal != nil {
|
|
currentRegisterer.Unregister(suiteResultTotal)
|
|
}
|
|
if suiteResultDurationSeconds != nil {
|
|
currentRegisterer.Unregister(suiteResultDurationSeconds)
|
|
}
|
|
if suiteResultSuccess != nil {
|
|
currentRegisterer.Unregister(suiteResultSuccess)
|
|
}
|
|
|
|
metricsInitialized = false
|
|
currentRegisterer = nil
|
|
}
|
|
|
|
func InitializePrometheusMetrics(cfg *config.Config, reg metric.Registerer) {
|
|
// If metrics are already initialized, unregister them first
|
|
if metricsInitialized {
|
|
UnregisterPrometheusMetrics()
|
|
}
|
|
|
|
if reg == nil {
|
|
reg = metric.DefaultRegisterer
|
|
}
|
|
|
|
// Store the registerer for later unregistration
|
|
currentRegisterer = reg
|
|
|
|
extraLabels := cfg.GetUniqueExtraMetricLabels()
|
|
resultTotal = metric.NewCounterVec(metric.CounterOpts{
|
|
Namespace: namespace,
|
|
Name: "results_total",
|
|
Help: "Number of results per endpoint",
|
|
}, append([]string{"key", "group", "name", "type", "success"}, extraLabels...))
|
|
reg.MustRegister(resultTotal)
|
|
|
|
resultDurationSeconds = metric.NewGaugeVec(metric.GaugeOpts{
|
|
Namespace: namespace,
|
|
Name: "results_duration_seconds",
|
|
Help: "Duration of the request in seconds",
|
|
}, append([]string{"key", "group", "name", "type"}, extraLabels...))
|
|
reg.MustRegister(resultDurationSeconds)
|
|
|
|
resultConnectedTotal = metric.NewCounterVec(metric.CounterOpts{
|
|
Namespace: namespace,
|
|
Name: "results_connected_total",
|
|
Help: "Total number of results in which a connection was successfully established",
|
|
}, append([]string{"key", "group", "name", "type"}, extraLabels...))
|
|
reg.MustRegister(resultConnectedTotal)
|
|
|
|
resultCodeTotal = metric.NewCounterVec(metric.CounterOpts{
|
|
Namespace: namespace,
|
|
Name: "results_code_total",
|
|
Help: "Total number of results by code",
|
|
}, append([]string{"key", "group", "name", "type", "code"}, extraLabels...))
|
|
reg.MustRegister(resultCodeTotal)
|
|
|
|
resultCertificateExpirationSeconds = metric.NewGaugeVec(metric.GaugeOpts{
|
|
Namespace: namespace,
|
|
Name: "results_certificate_expiration_seconds",
|
|
Help: "Number of seconds until the certificate expires",
|
|
}, append([]string{"key", "group", "name", "type"}, extraLabels...))
|
|
reg.MustRegister(resultCertificateExpirationSeconds)
|
|
|
|
resultDomainExpirationSeconds = metric.NewGaugeVec(metric.GaugeOpts{
|
|
Namespace: namespace,
|
|
Name: "results_domain_expiration_seconds",
|
|
Help: "Number of seconds until the domain expires",
|
|
}, append([]string{"key", "group", "name", "type"}, extraLabels...))
|
|
reg.MustRegister(resultDomainExpirationSeconds)
|
|
|
|
resultEndpointSuccess = metric.NewGaugeVec(metric.GaugeOpts{
|
|
Namespace: namespace,
|
|
Name: "results_endpoint_success",
|
|
Help: "Displays whether or not the endpoint was a success",
|
|
}, append([]string{"key", "group", "name", "type"}, extraLabels...))
|
|
reg.MustRegister(resultEndpointSuccess)
|
|
|
|
// Suite metrics
|
|
suiteResultTotal = metric.NewCounterVec(metric.CounterOpts{
|
|
Namespace: namespace,
|
|
Name: "suite_results_total",
|
|
Help: "Total number of suite executions",
|
|
}, append([]string{"key", "group", "name", "success"}, extraLabels...))
|
|
reg.MustRegister(suiteResultTotal)
|
|
|
|
suiteResultDurationSeconds = metric.NewGaugeVec(metric.GaugeOpts{
|
|
Namespace: namespace,
|
|
Name: "suite_results_duration_seconds",
|
|
Help: "Duration of suite execution in seconds",
|
|
}, append([]string{"key", "group", "name"}, extraLabels...))
|
|
reg.MustRegister(suiteResultDurationSeconds)
|
|
|
|
suiteResultSuccess = metric.NewGaugeVec(metric.GaugeOpts{
|
|
Namespace: namespace,
|
|
Name: "suite_results_success",
|
|
Help: "Whether the suite execution was successful (1) or not (0)",
|
|
}, append([]string{"key", "group", "name"}, extraLabels...))
|
|
reg.MustRegister(suiteResultSuccess)
|
|
|
|
// Mark as initialized
|
|
metricsInitialized = true
|
|
}
|
|
|
|
// PublishMetricsForEndpoint publishes metrics for the given endpoint and its result.
|
|
// These metrics will be exposed at /metrics if the metrics are enabled
|
|
func PublishMetricsForEndpoint(ep *endpoint.Endpoint, result *endpoint.Result, extraLabels []string) {
|
|
var labelValues []string
|
|
for _, label := range extraLabels {
|
|
if value, ok := ep.ExtraLabels[label]; ok {
|
|
labelValues = append(labelValues, value)
|
|
} else {
|
|
labelValues = append(labelValues, "")
|
|
}
|
|
}
|
|
endpointType := ep.Type()
|
|
resultTotal.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType), strconv.FormatBool(result.Success)}, labelValues...)...).Inc()
|
|
resultDurationSeconds.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType)}, labelValues...)...).Set(result.Duration.Seconds())
|
|
if result.Connected {
|
|
resultConnectedTotal.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType)}, labelValues...)...).Inc()
|
|
}
|
|
if result.DNSRCode != "" {
|
|
resultCodeTotal.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType), result.DNSRCode}, labelValues...)...).Inc()
|
|
}
|
|
if result.HTTPStatus != 0 {
|
|
resultCodeTotal.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType), strconv.Itoa(result.HTTPStatus)}, labelValues...)...).Inc()
|
|
}
|
|
if result.CertificateExpiration != 0 {
|
|
resultCertificateExpirationSeconds.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType)}, labelValues...)...).Set(result.CertificateExpiration.Seconds())
|
|
}
|
|
if result.DomainExpiration != 0 {
|
|
resultDomainExpirationSeconds.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType)}, labelValues...)...).Set(result.DomainExpiration.Seconds())
|
|
}
|
|
if result.Success {
|
|
resultEndpointSuccess.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType)}, labelValues...)...).Set(1)
|
|
} else {
|
|
resultEndpointSuccess.WithLabelValues(append([]string{ep.Key(), ep.Group, ep.Name, string(endpointType)}, labelValues...)...).Set(0)
|
|
}
|
|
}
|
|
|
|
// PublishMetricsForSuite publishes metrics for the given suite and its result.
|
|
// These metrics will be exposed at /metrics if the metrics are enabled
|
|
func PublishMetricsForSuite(s *suite.Suite, result *suite.Result, extraLabels []string) {
|
|
if !metricsInitialized {
|
|
return
|
|
}
|
|
var labelValues []string
|
|
// For now, suites don't have ExtraLabels, so we'll use empty values
|
|
// This maintains consistency with endpoint metrics structure
|
|
for range extraLabels {
|
|
labelValues = append(labelValues, "")
|
|
}
|
|
// Publish suite execution counter
|
|
suiteResultTotal.WithLabelValues(
|
|
append([]string{s.Key(), s.Group, s.Name, strconv.FormatBool(result.Success)}, labelValues...)...,
|
|
).Inc()
|
|
// Publish suite duration
|
|
suiteResultDurationSeconds.WithLabelValues(
|
|
append([]string{s.Key(), s.Group, s.Name}, labelValues...)...,
|
|
).Set(result.Duration.Seconds())
|
|
// Publish suite success status
|
|
if result.Success {
|
|
suiteResultSuccess.WithLabelValues(
|
|
append([]string{s.Key(), s.Group, s.Name}, labelValues...)...,
|
|
).Set(1)
|
|
} else {
|
|
suiteResultSuccess.WithLabelValues(
|
|
append([]string{s.Key(), s.Group, s.Name}, labelValues...)...,
|
|
).Set(0)
|
|
}
|
|
}
|