diff --git a/config/config.go b/config/config.go index 24666605..5e40413c 100644 --- a/config/config.go +++ b/config/config.go @@ -33,8 +33,8 @@ import ( "github.com/alecthomas/units" "github.com/miekg/dns" + configmetrics "github.com/prometheus/blackbox_exporter/internal/metrics/config" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/common/config" ) @@ -90,18 +90,17 @@ type SafeConfig struct { } func NewSafeConfig(reg prometheus.Registerer) *SafeConfig { - configReloadSuccess := promauto.With(reg).NewGauge(prometheus.GaugeOpts{ - Namespace: "blackbox_exporter", - Name: "config_last_reload_successful", - Help: "Blackbox exporter config loaded successfully.", - }) + configReloadSuccessMetric := configmetrics.NewLastReloadSuccessful() + configReloadSecondsMetric := configmetrics.NewLastReloadSuccessTimestampSeconds() - configReloadSeconds := promauto.With(reg).NewGauge(prometheus.GaugeOpts{ - Namespace: "blackbox_exporter", - Name: "config_last_reload_success_timestamp_seconds", - Help: "Timestamp of the last successful configuration reload.", - }) - return &SafeConfig{C: &Config{}, configReloadSuccess: configReloadSuccess, configReloadSeconds: configReloadSeconds} + reg.MustRegister(configReloadSuccessMetric) + reg.MustRegister(configReloadSecondsMetric) + + return &SafeConfig{ + C: &Config{}, + configReloadSuccess: configReloadSuccessMetric, + configReloadSeconds: configReloadSecondsMetric, + } } func (sc *SafeConfig) ReloadConfig(confFile string, logger *slog.Logger) (err error) { diff --git a/internal/metrics/blackbox/metric_blackbox_module_unknown.go b/internal/metrics/blackbox/metric_blackbox_module_unknown.go new file mode 100644 index 00000000..68b41da2 --- /dev/null +++ b/internal/metrics/blackbox/metric_blackbox_module_unknown.go @@ -0,0 +1,27 @@ +package blackbox + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Count of unknown modules requested by probes +type ModuleUnknown struct { + prometheus.Counter +} + +func NewModuleUnknown() ModuleUnknown { + return ModuleUnknown{Counter: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "blackbox_module_unknown_total", + Help: "Count of unknown modules requested by probes", + })} +} + +func (m ModuleUnknown) Register(regs ...prometheus.Registerer) ModuleUnknown { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/config/metric_config_last_reload_success_timestamp_seconds.go b/internal/metrics/config/metric_config_last_reload_success_timestamp_seconds.go new file mode 100644 index 00000000..eb70e0e1 --- /dev/null +++ b/internal/metrics/config/metric_config_last_reload_success_timestamp_seconds.go @@ -0,0 +1,27 @@ +package config + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Timestamp of the last successful configuration reload +type LastReloadSuccessTimestampSeconds struct { + prometheus.Gauge +} + +func NewLastReloadSuccessTimestampSeconds() LastReloadSuccessTimestampSeconds { + return LastReloadSuccessTimestampSeconds{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "blackbox_exporter_config_last_reload_success_timestamp_seconds", + Help: "Timestamp of the last successful configuration reload", + })} +} + +func (m LastReloadSuccessTimestampSeconds) Register(regs ...prometheus.Registerer) LastReloadSuccessTimestampSeconds { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/config/metric_config_last_reload_successful.go b/internal/metrics/config/metric_config_last_reload_successful.go new file mode 100644 index 00000000..e91aae28 --- /dev/null +++ b/internal/metrics/config/metric_config_last_reload_successful.go @@ -0,0 +1,27 @@ +package config + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Blackbox exporter config loaded successfully +type LastReloadSuccessful struct { + prometheus.Gauge +} + +func NewLastReloadSuccessful() LastReloadSuccessful { + return LastReloadSuccessful{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "blackbox_exporter_config_last_reload_successful", + Help: "Blackbox exporter config loaded successfully", + })} +} + +func (m LastReloadSuccessful) Register(regs ...prometheus.Registerer) LastReloadSuccessful { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/dns/metric_dns_probe_additional_rrs.go b/internal/metrics/dns/metric_dns_probe_additional_rrs.go new file mode 100644 index 00000000..1714a7e3 --- /dev/null +++ b/internal/metrics/dns/metric_dns_probe_additional_rrs.go @@ -0,0 +1,27 @@ +package dns + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns number of entries in the additional resource record list +type ProbeAdditionalRrs struct { + prometheus.Gauge +} + +func NewProbeAdditionalRrs() ProbeAdditionalRrs { + return ProbeAdditionalRrs{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_dns_additional_rrs", + Help: "Returns number of entries in the additional resource record list", + })} +} + +func (m ProbeAdditionalRrs) Register(regs ...prometheus.Registerer) ProbeAdditionalRrs { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/dns/metric_dns_probe_answer_rrs.go b/internal/metrics/dns/metric_dns_probe_answer_rrs.go new file mode 100644 index 00000000..49ae1ac3 --- /dev/null +++ b/internal/metrics/dns/metric_dns_probe_answer_rrs.go @@ -0,0 +1,27 @@ +package dns + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns number of entries in the answer resource record list +type ProbeAnswerRrs struct { + prometheus.Gauge +} + +func NewProbeAnswerRrs() ProbeAnswerRrs { + return ProbeAnswerRrs{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_dns_answer_rrs", + Help: "Returns number of entries in the answer resource record list", + })} +} + +func (m ProbeAnswerRrs) Register(regs ...prometheus.Registerer) ProbeAnswerRrs { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/dns/metric_dns_probe_authority_rrs.go b/internal/metrics/dns/metric_dns_probe_authority_rrs.go new file mode 100644 index 00000000..adf5eedd --- /dev/null +++ b/internal/metrics/dns/metric_dns_probe_authority_rrs.go @@ -0,0 +1,27 @@ +package dns + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns number of entries in the authority resource record list +type ProbeAuthorityRrs struct { + prometheus.Gauge +} + +func NewProbeAuthorityRrs() ProbeAuthorityRrs { + return ProbeAuthorityRrs{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_dns_authority_rrs", + Help: "Returns number of entries in the authority resource record list", + })} +} + +func (m ProbeAuthorityRrs) Register(regs ...prometheus.Registerer) ProbeAuthorityRrs { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/dns/metric_dns_probe_duration_seconds.go b/internal/metrics/dns/metric_dns_probe_duration_seconds.go new file mode 100644 index 00000000..cd036a1a --- /dev/null +++ b/internal/metrics/dns/metric_dns_probe_duration_seconds.go @@ -0,0 +1,35 @@ +package dns + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Duration of DNS request by phase +type ProbeDurationSeconds struct { + *prometheus.GaugeVec + extra ProbeDurationSecondsExtra +} + +func NewProbeDurationSeconds() ProbeDurationSeconds { + labels := []string{other.AttrPhase("").Key()} + return ProbeDurationSeconds{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_dns_duration_seconds", + Help: "Duration of DNS request by phase", + }, labels)} +} + +func (m ProbeDurationSeconds) With(phase other.AttrPhase, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(phase.Value()) +} + +// Deprecated: Use [ProbeDurationSeconds.With] instead +func (m ProbeDurationSeconds) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeDurationSecondsExtra struct { +} diff --git a/internal/metrics/dns/metric_dns_probe_query_succeeded.go b/internal/metrics/dns/metric_dns_probe_query_succeeded.go new file mode 100644 index 00000000..7058ee12 --- /dev/null +++ b/internal/metrics/dns/metric_dns_probe_query_succeeded.go @@ -0,0 +1,27 @@ +package dns + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Displays whether or not the query was executed successfully +type ProbeQuerySucceeded struct { + prometheus.Gauge +} + +func NewProbeQuerySucceeded() ProbeQuerySucceeded { + return ProbeQuerySucceeded{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_dns_query_succeeded", + Help: "Displays whether or not the query was executed successfully", + })} +} + +func (m ProbeQuerySucceeded) Register(regs ...prometheus.Registerer) ProbeQuerySucceeded { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/dns/metric_dns_probe_serial.go b/internal/metrics/dns/metric_dns_probe_serial.go new file mode 100644 index 00000000..0cd0353e --- /dev/null +++ b/internal/metrics/dns/metric_dns_probe_serial.go @@ -0,0 +1,27 @@ +package dns + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns the serial number of the zone +type ProbeSerial struct { + prometheus.Gauge +} + +func NewProbeSerial() ProbeSerial { + return ProbeSerial{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_dns_serial", + Help: "Returns the serial number of the zone", + })} +} + +func (m ProbeSerial) Register(regs ...prometheus.Registerer) ProbeSerial { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/generate.go b/internal/metrics/generate.go new file mode 100644 index 00000000..c83d309c --- /dev/null +++ b/internal/metrics/generate.go @@ -0,0 +1,4 @@ +package metrics + +//go:generate weaver registry generate --registry=../../semconv --templates=/home/tbraack/work/promconv/templates --param module=github.com/prometheus/blackbox_exporter/internal/metrics go . +//go:generate gofmt -s -w . diff --git a/internal/metrics/grpc/metric_grpc_probe_duration_seconds.go b/internal/metrics/grpc/metric_grpc_probe_duration_seconds.go new file mode 100644 index 00000000..d21a104e --- /dev/null +++ b/internal/metrics/grpc/metric_grpc_probe_duration_seconds.go @@ -0,0 +1,35 @@ +package grpc + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Duration of gRPC request by phase +type ProbeDurationSeconds struct { + *prometheus.GaugeVec + extra ProbeDurationSecondsExtra +} + +func NewProbeDurationSeconds() ProbeDurationSeconds { + labels := []string{other.AttrPhase("").Key()} + return ProbeDurationSeconds{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_grpc_duration_seconds", + Help: "Duration of gRPC request by phase", + }, labels)} +} + +func (m ProbeDurationSeconds) With(phase other.AttrPhase, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(phase.Value()) +} + +// Deprecated: Use [ProbeDurationSeconds.With] instead +func (m ProbeDurationSeconds) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeDurationSecondsExtra struct { +} diff --git a/internal/metrics/grpc/metric_grpc_probe_healthcheck_response.go b/internal/metrics/grpc/metric_grpc_probe_healthcheck_response.go new file mode 100644 index 00000000..02b09f20 --- /dev/null +++ b/internal/metrics/grpc/metric_grpc_probe_healthcheck_response.go @@ -0,0 +1,35 @@ +package grpc + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Response HealthCheck response +type ProbeHealthcheckResponse struct { + *prometheus.GaugeVec + extra ProbeHealthcheckResponseExtra +} + +func NewProbeHealthcheckResponse() ProbeHealthcheckResponse { + labels := []string{other.AttrServingStatus("").Key()} + return ProbeHealthcheckResponse{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_grpc_healthcheck_response", + Help: "Response HealthCheck response", + }, labels)} +} + +func (m ProbeHealthcheckResponse) With(servingStatus other.AttrServingStatus, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(servingStatus.Value()) +} + +// Deprecated: Use [ProbeHealthcheckResponse.With] instead +func (m ProbeHealthcheckResponse) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeHealthcheckResponseExtra struct { +} diff --git a/internal/metrics/grpc/metric_grpc_probe_ssl.go b/internal/metrics/grpc/metric_grpc_probe_ssl.go new file mode 100644 index 00000000..8049b0c2 --- /dev/null +++ b/internal/metrics/grpc/metric_grpc_probe_ssl.go @@ -0,0 +1,27 @@ +package grpc + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Indicates if SSL was used for the connection +type ProbeSsl struct { + prometheus.Gauge +} + +func NewProbeSsl() ProbeSsl { + return ProbeSsl{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_grpc_ssl", + Help: "Indicates if SSL was used for the connection", + })} +} + +func (m ProbeSsl) Register(regs ...prometheus.Registerer) ProbeSsl { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/grpc/metric_grpc_probe_status_code.go b/internal/metrics/grpc/metric_grpc_probe_status_code.go new file mode 100644 index 00000000..9de9f7d6 --- /dev/null +++ b/internal/metrics/grpc/metric_grpc_probe_status_code.go @@ -0,0 +1,27 @@ +package grpc + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Response gRPC status code +type ProbeStatusCode struct { + prometheus.Gauge +} + +func NewProbeStatusCode() ProbeStatusCode { + return ProbeStatusCode{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_grpc_status_code", + Help: "Response gRPC status code", + })} +} + +func (m ProbeStatusCode) Register(regs ...prometheus.Registerer) ProbeStatusCode { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_content_length.go b/internal/metrics/http/metric_http_probe_content_length.go new file mode 100644 index 00000000..1ccded62 --- /dev/null +++ b/internal/metrics/http/metric_http_probe_content_length.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Length of http content response +type ProbeContentLength struct { + prometheus.Gauge +} + +func NewProbeContentLength() ProbeContentLength { + return ProbeContentLength{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_content_length", + Help: "Length of http content response", + })} +} + +func (m ProbeContentLength) Register(regs ...prometheus.Registerer) ProbeContentLength { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_duration_seconds.go b/internal/metrics/http/metric_http_probe_duration_seconds.go new file mode 100644 index 00000000..e2565a6e --- /dev/null +++ b/internal/metrics/http/metric_http_probe_duration_seconds.go @@ -0,0 +1,35 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Duration of http request by phase, summed over all redirects +type ProbeDurationSeconds struct { + *prometheus.GaugeVec + extra ProbeDurationSecondsExtra +} + +func NewProbeDurationSeconds() ProbeDurationSeconds { + labels := []string{other.AttrPhase("").Key()} + return ProbeDurationSeconds{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_http_duration_seconds", + Help: "Duration of http request by phase, summed over all redirects", + }, labels)} +} + +func (m ProbeDurationSeconds) With(phase other.AttrPhase, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(phase.Value()) +} + +// Deprecated: Use [ProbeDurationSeconds.With] instead +func (m ProbeDurationSeconds) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeDurationSecondsExtra struct { +} diff --git a/internal/metrics/http/metric_http_probe_failed_due_to_cel.go b/internal/metrics/http/metric_http_probe_failed_due_to_cel.go new file mode 100644 index 00000000..fc7d1b97 --- /dev/null +++ b/internal/metrics/http/metric_http_probe_failed_due_to_cel.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Indicates if probe failed due to CEL expression not matching +type ProbeFailedDueToCel struct { + prometheus.Gauge +} + +func NewProbeFailedDueToCel() ProbeFailedDueToCel { + return ProbeFailedDueToCel{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_failed_due_to_cel", + Help: "Indicates if probe failed due to CEL expression not matching", + })} +} + +func (m ProbeFailedDueToCel) Register(regs ...prometheus.Registerer) ProbeFailedDueToCel { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_last_modified_timestamp_seconds.go b/internal/metrics/http/metric_http_probe_last_modified_timestamp_seconds.go new file mode 100644 index 00000000..350dbd0e --- /dev/null +++ b/internal/metrics/http/metric_http_probe_last_modified_timestamp_seconds.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns the Last-Modified HTTP response header in unixtime +type ProbeLastModifiedTimestampSeconds struct { + prometheus.Gauge +} + +func NewProbeLastModifiedTimestampSeconds() ProbeLastModifiedTimestampSeconds { + return ProbeLastModifiedTimestampSeconds{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_last_modified_timestamp_seconds", + Help: "Returns the Last-Modified HTTP response header in unixtime", + })} +} + +func (m ProbeLastModifiedTimestampSeconds) Register(regs ...prometheus.Registerer) ProbeLastModifiedTimestampSeconds { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_redirects.go b/internal/metrics/http/metric_http_probe_redirects.go new file mode 100644 index 00000000..777442e0 --- /dev/null +++ b/internal/metrics/http/metric_http_probe_redirects.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// The number of redirects +type ProbeRedirects struct { + prometheus.Gauge +} + +func NewProbeRedirects() ProbeRedirects { + return ProbeRedirects{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_redirects", + Help: "The number of redirects", + })} +} + +func (m ProbeRedirects) Register(regs ...prometheus.Registerer) ProbeRedirects { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_ssl.go b/internal/metrics/http/metric_http_probe_ssl.go new file mode 100644 index 00000000..ba102a04 --- /dev/null +++ b/internal/metrics/http/metric_http_probe_ssl.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Indicates if SSL was used for the final redirect +type ProbeSsl struct { + prometheus.Gauge +} + +func NewProbeSsl() ProbeSsl { + return ProbeSsl{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_ssl", + Help: "Indicates if SSL was used for the final redirect", + })} +} + +func (m ProbeSsl) Register(regs ...prometheus.Registerer) ProbeSsl { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_status_code.go b/internal/metrics/http/metric_http_probe_status_code.go new file mode 100644 index 00000000..01946614 --- /dev/null +++ b/internal/metrics/http/metric_http_probe_status_code.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Response HTTP status code +type ProbeStatusCode struct { + prometheus.Gauge +} + +func NewProbeStatusCode() ProbeStatusCode { + return ProbeStatusCode{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_status_code", + Help: "Response HTTP status code", + })} +} + +func (m ProbeStatusCode) Register(regs ...prometheus.Registerer) ProbeStatusCode { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_uncompressed_body_length.go b/internal/metrics/http/metric_http_probe_uncompressed_body_length.go new file mode 100644 index 00000000..06eff160 --- /dev/null +++ b/internal/metrics/http/metric_http_probe_uncompressed_body_length.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Length of uncompressed response body +type ProbeUncompressedBodyLength struct { + prometheus.Gauge +} + +func NewProbeUncompressedBodyLength() ProbeUncompressedBodyLength { + return ProbeUncompressedBodyLength{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_uncompressed_body_length", + Help: "Length of uncompressed response body", + })} +} + +func (m ProbeUncompressedBodyLength) Register(regs ...prometheus.Registerer) ProbeUncompressedBodyLength { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/http/metric_http_probe_version.go b/internal/metrics/http/metric_http_probe_version.go new file mode 100644 index 00000000..18ffcdaa --- /dev/null +++ b/internal/metrics/http/metric_http_probe_version.go @@ -0,0 +1,27 @@ +package http + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns the version of HTTP of the probe response +type ProbeVersion struct { + prometheus.Gauge +} + +func NewProbeVersion() ProbeVersion { + return ProbeVersion{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_http_version", + Help: "Returns the version of HTTP of the probe response", + })} +} + +func (m ProbeVersion) Register(regs ...prometheus.Registerer) ProbeVersion { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/icmp/metric_icmp_probe_duration_seconds.go b/internal/metrics/icmp/metric_icmp_probe_duration_seconds.go new file mode 100644 index 00000000..7aa97839 --- /dev/null +++ b/internal/metrics/icmp/metric_icmp_probe_duration_seconds.go @@ -0,0 +1,35 @@ +package icmp + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Duration of icmp request by phase +type ProbeDurationSeconds struct { + *prometheus.GaugeVec + extra ProbeDurationSecondsExtra +} + +func NewProbeDurationSeconds() ProbeDurationSeconds { + labels := []string{other.AttrPhase("").Key()} + return ProbeDurationSeconds{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_icmp_duration_seconds", + Help: "Duration of icmp request by phase", + }, labels)} +} + +func (m ProbeDurationSeconds) With(phase other.AttrPhase, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(phase.Value()) +} + +// Deprecated: Use [ProbeDurationSeconds.With] instead +func (m ProbeDurationSeconds) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeDurationSecondsExtra struct { +} diff --git a/internal/metrics/icmp/metric_icmp_probe_reply_hop_limit.go b/internal/metrics/icmp/metric_icmp_probe_reply_hop_limit.go new file mode 100644 index 00000000..be6efec3 --- /dev/null +++ b/internal/metrics/icmp/metric_icmp_probe_reply_hop_limit.go @@ -0,0 +1,27 @@ +package icmp + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Replied packet hop limit (TTL for ipv4) +type ProbeReplyHopLimit struct { + prometheus.Gauge +} + +func NewProbeReplyHopLimit() ProbeReplyHopLimit { + return ProbeReplyHopLimit{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_icmp_reply_hop_limit", + Help: "Replied packet hop limit (TTL for ipv4)", + })} +} + +func (m ProbeReplyHopLimit) Register(regs ...prometheus.Registerer) ProbeReplyHopLimit { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/metrics.md b/internal/metrics/metrics.md new file mode 100644 index 00000000..dbe4e861 --- /dev/null +++ b/internal/metrics/metrics.md @@ -0,0 +1,508 @@ +# Metrics + + +## blackbox + +### blackbox_module_unknown_total + +```py +# TYPE blackbox_module_unknown_total COUNTER +blackbox_module_unknown_total{} +``` + +Count of unknown modules requested by probes + + + + + +## config + +### blackbox_exporter_config_last_reload_success_timestamp_seconds + +```py +# TYPE blackbox_exporter_config_last_reload_success_timestamp_seconds GAUGE +blackbox_exporter_config_last_reload_success_timestamp_seconds{} +``` + +Timestamp of the last successful configuration reload + + + + +### blackbox_exporter_config_last_reload_successful + +```py +# TYPE blackbox_exporter_config_last_reload_successful GAUGE +blackbox_exporter_config_last_reload_successful{} +``` + +Blackbox exporter config loaded successfully + + + + + +## dns + +### probe_dns_additional_rrs + +```py +# TYPE probe_dns_additional_rrs GAUGE +probe_dns_additional_rrs{} +``` + +Returns number of entries in the additional resource record list + + + + +### probe_dns_answer_rrs + +```py +# TYPE probe_dns_answer_rrs GAUGE +probe_dns_answer_rrs{} +``` + +Returns number of entries in the answer resource record list + + + + +### probe_dns_authority_rrs + +```py +# TYPE probe_dns_authority_rrs GAUGE +probe_dns_authority_rrs{} +``` + +Returns number of entries in the authority resource record list + + + + +### probe_dns_duration_seconds + +```py +# TYPE probe_dns_duration_seconds GAUGE +probe_dns_duration_seconds{phase} +``` + +Duration of DNS request by phase + + +|Attribute|Type|Description| +|-|-|-| +| phase | `resolve` \| `connect` \| `request` \| `tls` \| `processing` \| `transfer` \| `setup` \| `rtt` \| `check` | Probe phase | + + + + +### probe_dns_query_succeeded + +```py +# TYPE probe_dns_query_succeeded GAUGE +probe_dns_query_succeeded{} +``` + +Displays whether or not the query was executed successfully + + + + +### probe_dns_serial + +```py +# TYPE probe_dns_serial GAUGE +probe_dns_serial{} +``` + +Returns the serial number of the zone + + + + + +## grpc + +### probe_grpc_duration_seconds + +```py +# TYPE probe_grpc_duration_seconds GAUGE +probe_grpc_duration_seconds{phase} +``` + +Duration of gRPC request by phase + + +|Attribute|Type|Description| +|-|-|-| +| phase | `resolve` \| `connect` \| `request` \| `tls` \| `processing` \| `transfer` \| `setup` \| `rtt` \| `check` | Probe phase | + + + + +### probe_grpc_healthcheck_response + +```py +# TYPE probe_grpc_healthcheck_response GAUGE +probe_grpc_healthcheck_response{serving_status} +``` + +Response HealthCheck response + + +|Attribute|Type|Description| +|-|-|-| +| serving_status | `SERVING` \| `NOT_SERVING` \| `UNKNOWN` \| `SERVICE_UNKNOWN` | gRPC health check serving status | + + + + +### probe_grpc_ssl + +```py +# TYPE probe_grpc_ssl GAUGE +probe_grpc_ssl{} +``` + +Indicates if SSL was used for the connection + + + + +### probe_grpc_status_code + +```py +# TYPE probe_grpc_status_code GAUGE +probe_grpc_status_code{} +``` + +Response gRPC status code + + + + + +## http + +### probe_failed_due_to_cel + +```py +# TYPE probe_failed_due_to_cel GAUGE +probe_failed_due_to_cel{} +``` + +Indicates if probe failed due to CEL expression not matching + + + + +### probe_http_content_length + +```py +# TYPE probe_http_content_length GAUGE +probe_http_content_length{} +``` + +Length of http content response + + + + +### probe_http_duration_seconds + +```py +# TYPE probe_http_duration_seconds GAUGE +probe_http_duration_seconds{phase} +``` + +Duration of http request by phase, summed over all redirects + + +|Attribute|Type|Description| +|-|-|-| +| phase | `resolve` \| `connect` \| `request` \| `tls` \| `processing` \| `transfer` \| `setup` \| `rtt` \| `check` | Probe phase | + + + + +### probe_http_last_modified_timestamp_seconds + +```py +# TYPE probe_http_last_modified_timestamp_seconds GAUGE +probe_http_last_modified_timestamp_seconds{} +``` + +Returns the Last-Modified HTTP response header in unixtime + + + + +### probe_http_redirects + +```py +# TYPE probe_http_redirects GAUGE +probe_http_redirects{} +``` + +The number of redirects + + + + +### probe_http_ssl + +```py +# TYPE probe_http_ssl GAUGE +probe_http_ssl{} +``` + +Indicates if SSL was used for the final redirect + + + + +### probe_http_status_code + +```py +# TYPE probe_http_status_code GAUGE +probe_http_status_code{} +``` + +Response HTTP status code + + + + +### probe_http_uncompressed_body_length + +```py +# TYPE probe_http_uncompressed_body_length GAUGE +probe_http_uncompressed_body_length{} +``` + +Length of uncompressed response body + + + + +### probe_http_version + +```py +# TYPE probe_http_version GAUGE +probe_http_version{} +``` + +Returns the version of HTTP of the probe response + + + + + +## icmp + +### probe_icmp_duration_seconds + +```py +# TYPE probe_icmp_duration_seconds GAUGE +probe_icmp_duration_seconds{phase} +``` + +Duration of icmp request by phase + + +|Attribute|Type|Description| +|-|-|-| +| phase | `resolve` \| `connect` \| `request` \| `tls` \| `processing` \| `transfer` \| `setup` \| `rtt` \| `check` | Probe phase | + + + + +### probe_icmp_reply_hop_limit + +```py +# TYPE probe_icmp_reply_hop_limit GAUGE +probe_icmp_reply_hop_limit{} +``` + +Replied packet hop limit (TTL for ipv4) + + + + + +## probe + +### probe_dns_lookup_time_seconds + +```py +# TYPE probe_dns_lookup_time_seconds GAUGE +probe_dns_lookup_time_seconds{} +``` + +Returns the time taken for probe dns lookup in seconds + + + + +### probe_duration_seconds + +```py +# TYPE probe_duration_seconds GAUGE +probe_duration_seconds{} +``` + +Returns how long the probe took to complete in seconds + + + + +### probe_failed_due_to_regex + +```py +# TYPE probe_failed_due_to_regex GAUGE +probe_failed_due_to_regex{} +``` + +Indicates if probe failed due to regex + + + + +### probe_ip_addr_hash + +```py +# TYPE probe_ip_addr_hash GAUGE +probe_ip_addr_hash{} +``` + +Specifies the hash of IP address. It's useful to detect if the IP address changes. + + + + +### probe_ip_protocol + +```py +# TYPE probe_ip_protocol GAUGE +probe_ip_protocol{} +``` + +Specifies whether probe ip protocol is IP4 or IP6 + + + + +### probe_success + +```py +# TYPE probe_success GAUGE +probe_success{} +``` + +Displays whether or not the probe was a success + + + + + +## ssl + +### probe_ssl_earliest_cert_expiry + +```py +# TYPE probe_ssl_earliest_cert_expiry GAUGE +probe_ssl_earliest_cert_expiry{} +``` + +Returns earliest SSL cert expiry date + + + + +### probe_ssl_last_chain_expiry_timestamp_seconds + +```py +# TYPE probe_ssl_last_chain_expiry_timestamp_seconds GAUGE +probe_ssl_last_chain_expiry_timestamp_seconds{} +``` + +Returns last SSL chain expiry timestamp + + + + +### probe_ssl_last_chain_info + +```py +# TYPE probe_ssl_last_chain_info GAUGE +probe_ssl_last_chain_info{fingerprint_sha256, subject, issuer, subjectalternative, serialnumber} +``` + +Contains SSL leaf certificate information + + +|Attribute|Type|Description| +|-|-|-| +| fingerprint_sha256 | string | SHA256 fingerprint of the certificate | +| subject | string | Subject of the certificate | +| issuer | string | Issuer of the certificate | +| subjectalternative | string | Subject alternative names of the certificate | +| serialnumber | string | Serial number of the certificate | + + + + + +## tcp + +### probe_expect_info + +```py +# TYPE probe_expect_info GAUGE +probe_expect_info{} +``` + +Explicit content matched + + + + + +## tls + +### probe_tls_cipher_info + +```py +# TYPE probe_tls_cipher_info GAUGE +probe_tls_cipher_info{cipher} +``` + +Contains TLS cipher information + + +|Attribute|Type|Description| +|-|-|-| +| cipher | string | TLS cipher suite | + + + + +### probe_tls_version_info + +```py +# TYPE probe_tls_version_info GAUGE +probe_tls_version_info{version} +``` + +Contains TLS version information + + +|Attribute|Type|Description| +|-|-|-| +| version | string | TLS version | + + + + diff --git a/internal/metrics/other/attributes.go b/internal/metrics/other/attributes.go new file mode 100644 index 00000000..7bc1c94e --- /dev/null +++ b/internal/metrics/other/attributes.go @@ -0,0 +1,104 @@ +package other + +// TLS cipher suite +type AttrCipher string // cipher + +func (AttrCipher) Stable() {} +func (AttrCipher) Recommended() {} +func (AttrCipher) Key() string { return "cipher" } +func (a AttrCipher) Value() string { return string(a) } + +// SHA256 fingerprint of the certificate +type AttrFingerprintSha256 string // fingerprint_sha256 + +func (AttrFingerprintSha256) Stable() {} +func (AttrFingerprintSha256) Recommended() {} +func (AttrFingerprintSha256) Key() string { return "fingerprint_sha256" } +func (a AttrFingerprintSha256) Value() string { return string(a) } + +// Hop limit (TTL for IPv4) of the replied packet +type AttrHopLimit string // hop_limit + +func (AttrHopLimit) Stable() {} +func (AttrHopLimit) Recommended() {} +func (AttrHopLimit) Key() string { return "hop_limit" } +func (a AttrHopLimit) Value() string { return string(a) } + +// Issuer of the certificate +type AttrIssuer string // issuer + +func (AttrIssuer) Stable() {} +func (AttrIssuer) Recommended() {} +func (AttrIssuer) Key() string { return "issuer" } +func (a AttrIssuer) Value() string { return string(a) } + +// Probe phase +type AttrPhase string // phase + +func (AttrPhase) Stable() {} +func (AttrPhase) Recommended() {} +func (AttrPhase) Key() string { return "phase" } +func (a AttrPhase) Value() string { return string(a) } + +const PhaseResolve AttrPhase = "resolve" +const PhaseConnect AttrPhase = "connect" +const PhaseRequest AttrPhase = "request" +const PhaseTLS AttrPhase = "tls" +const PhaseProcessing AttrPhase = "processing" +const PhaseTransfer AttrPhase = "transfer" +const PhaseSetup AttrPhase = "setup" +const PhaseRTT AttrPhase = "rtt" +const PhaseCheck AttrPhase = "check" + +// Serial number of the certificate +type AttrSerialnumber string // serialnumber + +func (AttrSerialnumber) Stable() {} +func (AttrSerialnumber) Recommended() {} +func (AttrSerialnumber) Key() string { return "serialnumber" } +func (a AttrSerialnumber) Value() string { return string(a) } + +// gRPC health check serving status +type AttrServingStatus string // serving_status + +func (AttrServingStatus) Stable() {} +func (AttrServingStatus) Recommended() {} +func (AttrServingStatus) Key() string { return "serving_status" } +func (a AttrServingStatus) Value() string { return string(a) } + +const ServingStatusServing AttrServingStatus = "SERVING" +const ServingStatusNotServing AttrServingStatus = "NOT_SERVING" +const ServingStatusUnknown AttrServingStatus = "UNKNOWN" +const ServingStatusServiceUnknown AttrServingStatus = "SERVICE_UNKNOWN" + +// gRPC status code +type AttrStatusCode string // status_code + +func (AttrStatusCode) Stable() {} +func (AttrStatusCode) Recommended() {} +func (AttrStatusCode) Key() string { return "status_code" } +func (a AttrStatusCode) Value() string { return string(a) } + +// Subject of the certificate +type AttrSubject string // subject + +func (AttrSubject) Stable() {} +func (AttrSubject) Recommended() {} +func (AttrSubject) Key() string { return "subject" } +func (a AttrSubject) Value() string { return string(a) } + +// Subject alternative names of the certificate +type AttrSubjectalternative string // subjectalternative + +func (AttrSubjectalternative) Stable() {} +func (AttrSubjectalternative) Recommended() {} +func (AttrSubjectalternative) Key() string { return "subjectalternative" } +func (a AttrSubjectalternative) Value() string { return string(a) } + +// TLS version +type AttrVersion string // version + +func (AttrVersion) Stable() {} +func (AttrVersion) Recommended() {} +func (AttrVersion) Key() string { return "version" } +func (a AttrVersion) Value() string { return string(a) } diff --git a/internal/metrics/probe/metric_probe_dns_lookup_time_seconds.go b/internal/metrics/probe/metric_probe_dns_lookup_time_seconds.go new file mode 100644 index 00000000..c62e6a69 --- /dev/null +++ b/internal/metrics/probe/metric_probe_dns_lookup_time_seconds.go @@ -0,0 +1,27 @@ +package probe + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns the time taken for probe dns lookup in seconds +type DnsLookupTimeSeconds struct { + prometheus.Gauge +} + +func NewDnsLookupTimeSeconds() DnsLookupTimeSeconds { + return DnsLookupTimeSeconds{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_dns_lookup_time_seconds", + Help: "Returns the time taken for probe dns lookup in seconds", + })} +} + +func (m DnsLookupTimeSeconds) Register(regs ...prometheus.Registerer) DnsLookupTimeSeconds { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/probe/metric_probe_duration_seconds.go b/internal/metrics/probe/metric_probe_duration_seconds.go new file mode 100644 index 00000000..bb3a6931 --- /dev/null +++ b/internal/metrics/probe/metric_probe_duration_seconds.go @@ -0,0 +1,27 @@ +package probe + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns how long the probe took to complete in seconds +type DurationSeconds struct { + prometheus.Gauge +} + +func NewDurationSeconds() DurationSeconds { + return DurationSeconds{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_duration_seconds", + Help: "Returns how long the probe took to complete in seconds", + })} +} + +func (m DurationSeconds) Register(regs ...prometheus.Registerer) DurationSeconds { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/probe/metric_probe_failed_due_to_regex.go b/internal/metrics/probe/metric_probe_failed_due_to_regex.go new file mode 100644 index 00000000..2f2018aa --- /dev/null +++ b/internal/metrics/probe/metric_probe_failed_due_to_regex.go @@ -0,0 +1,27 @@ +package probe + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Indicates if probe failed due to regex +type FailedDueToRegex struct { + prometheus.Gauge +} + +func NewFailedDueToRegex() FailedDueToRegex { + return FailedDueToRegex{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_failed_due_to_regex", + Help: "Indicates if probe failed due to regex", + })} +} + +func (m FailedDueToRegex) Register(regs ...prometheus.Registerer) FailedDueToRegex { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/probe/metric_probe_ip_addr_hash.go b/internal/metrics/probe/metric_probe_ip_addr_hash.go new file mode 100644 index 00000000..22e00f33 --- /dev/null +++ b/internal/metrics/probe/metric_probe_ip_addr_hash.go @@ -0,0 +1,27 @@ +package probe + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Specifies the hash of IP address. It's useful to detect if the IP address changes. +type IpAddrHash struct { + prometheus.Gauge +} + +func NewIpAddrHash() IpAddrHash { + return IpAddrHash{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_ip_addr_hash", + Help: "Specifies the hash of IP address. It's useful to detect if the IP address changes.", + })} +} + +func (m IpAddrHash) Register(regs ...prometheus.Registerer) IpAddrHash { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/probe/metric_probe_ip_protocol.go b/internal/metrics/probe/metric_probe_ip_protocol.go new file mode 100644 index 00000000..d5b7ae6c --- /dev/null +++ b/internal/metrics/probe/metric_probe_ip_protocol.go @@ -0,0 +1,27 @@ +package probe + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Specifies whether probe ip protocol is IP4 or IP6 +type IpProtocol struct { + prometheus.Gauge +} + +func NewIpProtocol() IpProtocol { + return IpProtocol{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_ip_protocol", + Help: "Specifies whether probe ip protocol is IP4 or IP6", + })} +} + +func (m IpProtocol) Register(regs ...prometheus.Registerer) IpProtocol { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/probe/metric_probe_success.go b/internal/metrics/probe/metric_probe_success.go new file mode 100644 index 00000000..6c109b13 --- /dev/null +++ b/internal/metrics/probe/metric_probe_success.go @@ -0,0 +1,27 @@ +package probe + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Displays whether or not the probe was a success +type Success struct { + prometheus.Gauge +} + +func NewSuccess() Success { + return Success{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_success", + Help: "Displays whether or not the probe was a success", + })} +} + +func (m Success) Register(regs ...prometheus.Registerer) Success { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/ssl/metric_ssl_probe_earliest_cert_expiry.go b/internal/metrics/ssl/metric_ssl_probe_earliest_cert_expiry.go new file mode 100644 index 00000000..fb0282b8 --- /dev/null +++ b/internal/metrics/ssl/metric_ssl_probe_earliest_cert_expiry.go @@ -0,0 +1,27 @@ +package ssl + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns earliest SSL cert expiry date +type ProbeEarliestCertExpiry struct { + prometheus.Gauge +} + +func NewProbeEarliestCertExpiry() ProbeEarliestCertExpiry { + return ProbeEarliestCertExpiry{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_ssl_earliest_cert_expiry", + Help: "Returns earliest SSL cert expiry date", + })} +} + +func (m ProbeEarliestCertExpiry) Register(regs ...prometheus.Registerer) ProbeEarliestCertExpiry { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/ssl/metric_ssl_probe_last_chain_expiry_timestamp_seconds.go b/internal/metrics/ssl/metric_ssl_probe_last_chain_expiry_timestamp_seconds.go new file mode 100644 index 00000000..4fa48bd4 --- /dev/null +++ b/internal/metrics/ssl/metric_ssl_probe_last_chain_expiry_timestamp_seconds.go @@ -0,0 +1,27 @@ +package ssl + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Returns last SSL chain expiry timestamp +type ProbeLastChainExpiryTimestampSeconds struct { + prometheus.Gauge +} + +func NewProbeLastChainExpiryTimestampSeconds() ProbeLastChainExpiryTimestampSeconds { + return ProbeLastChainExpiryTimestampSeconds{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_expiry_timestamp_seconds", + Help: "Returns last SSL chain expiry timestamp", + })} +} + +func (m ProbeLastChainExpiryTimestampSeconds) Register(regs ...prometheus.Registerer) ProbeLastChainExpiryTimestampSeconds { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/ssl/metric_ssl_probe_last_chain_info.go b/internal/metrics/ssl/metric_ssl_probe_last_chain_info.go new file mode 100644 index 00000000..094ce0bc --- /dev/null +++ b/internal/metrics/ssl/metric_ssl_probe_last_chain_info.go @@ -0,0 +1,35 @@ +package ssl + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Contains SSL leaf certificate information +type ProbeLastChainInfo struct { + *prometheus.GaugeVec + extra ProbeLastChainInfoExtra +} + +func NewProbeLastChainInfo() ProbeLastChainInfo { + labels := []string{other.AttrFingerprintSha256("").Key(), other.AttrIssuer("").Key(), other.AttrSerialnumber("").Key(), other.AttrSubject("").Key(), other.AttrSubjectalternative("").Key()} + return ProbeLastChainInfo{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_info", + Help: "Contains SSL leaf certificate information", + }, labels)} +} + +func (m ProbeLastChainInfo) With(fingerprintSha256 other.AttrFingerprintSha256, issuer other.AttrIssuer, serialnumber other.AttrSerialnumber, subject other.AttrSubject, subjectalternative other.AttrSubjectalternative, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(fingerprintSha256.Value(), issuer.Value(), serialnumber.Value(), subject.Value(), subjectalternative.Value()) +} + +// Deprecated: Use [ProbeLastChainInfo.With] instead +func (m ProbeLastChainInfo) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeLastChainInfoExtra struct { +} diff --git a/internal/metrics/tcp/metric_tcp_probe_expect_info.go b/internal/metrics/tcp/metric_tcp_probe_expect_info.go new file mode 100644 index 00000000..e2085ab2 --- /dev/null +++ b/internal/metrics/tcp/metric_tcp_probe_expect_info.go @@ -0,0 +1,27 @@ +package tcp + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Explicit content matched +type ProbeExpectInfo struct { + prometheus.Gauge +} + +func NewProbeExpectInfo() ProbeExpectInfo { + return ProbeExpectInfo{Gauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "probe_expect_info", + Help: "Explicit content matched", + })} +} + +func (m ProbeExpectInfo) Register(regs ...prometheus.Registerer) ProbeExpectInfo { + if regs == nil { + prometheus.DefaultRegisterer.MustRegister(m) + } + for _, reg := range regs { + reg.MustRegister(m) + } + return m +} diff --git a/internal/metrics/tls/metric_tls_probe_cipher.go b/internal/metrics/tls/metric_tls_probe_cipher.go new file mode 100644 index 00000000..62786c84 --- /dev/null +++ b/internal/metrics/tls/metric_tls_probe_cipher.go @@ -0,0 +1,35 @@ +package tls + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Contains TLS cipher information +type ProbeCipher struct { + *prometheus.GaugeVec + extra ProbeCipherExtra +} + +func NewProbeCipher() ProbeCipher { + labels := []string{other.AttrCipher("").Key()} + return ProbeCipher{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_tls_cipher_info", + Help: "Contains TLS cipher information", + }, labels)} +} + +func (m ProbeCipher) With(cipher other.AttrCipher, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(cipher.Value()) +} + +// Deprecated: Use [ProbeCipher.With] instead +func (m ProbeCipher) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeCipherExtra struct { +} diff --git a/internal/metrics/tls/metric_tls_probe_version.go b/internal/metrics/tls/metric_tls_probe_version.go new file mode 100644 index 00000000..febee143 --- /dev/null +++ b/internal/metrics/tls/metric_tls_probe_version.go @@ -0,0 +1,35 @@ +package tls + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +import ( + "github.com/prometheus/blackbox_exporter/internal/metrics/other" +) + +// Contains TLS version information +type ProbeVersion struct { + *prometheus.GaugeVec + extra ProbeVersionExtra +} + +func NewProbeVersion() ProbeVersion { + labels := []string{other.AttrVersion("").Key()} + return ProbeVersion{GaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "probe_tls_version_info", + Help: "Contains TLS version information", + }, labels)} +} + +func (m ProbeVersion) With(version other.AttrVersion, extras ...interface{}) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(version.Value()) +} + +// Deprecated: Use [ProbeVersion.With] instead +func (m ProbeVersion) WithLabelValues(lvs ...string) prometheus.Gauge { + return m.GaugeVec.WithLabelValues(lvs...) +} + +type ProbeVersionExtra struct { +} diff --git a/main.go b/main.go index 19897f55..4ed55b24 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus/client_golang/prometheus" versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version" - "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/promslog" "github.com/prometheus/common/promslog/flag" @@ -41,6 +40,7 @@ import ( "gopkg.in/yaml.v3" "github.com/prometheus/blackbox_exporter/config" + "github.com/prometheus/blackbox_exporter/internal/metrics/blackbox" "github.com/prometheus/blackbox_exporter/prober" ) @@ -56,10 +56,7 @@ var ( routePrefix = kingpin.Flag("web.route-prefix", "Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url.").PlaceHolder("").String() toolkitFlags = webflag.AddFlags(kingpin.CommandLine, ":9115") - moduleUnknownCounter = promauto.NewCounter(prometheus.CounterOpts{ - Name: "blackbox_module_unknown_total", - Help: "Count of unknown modules requested by probes", - }) + moduleUnknownCounter = blackbox.NewModuleUnknown().Register() ) func init() { diff --git a/prober/dns.go b/prober/dns.go index 3d301b9b..a07e1226 100644 --- a/prober/dns.go +++ b/prober/dns.go @@ -25,6 +25,9 @@ import ( pconfig "github.com/prometheus/common/config" "github.com/prometheus/blackbox_exporter/config" + + metrics "github.com/prometheus/blackbox_exporter/internal/metrics/dns" + "github.com/prometheus/blackbox_exporter/internal/metrics/other" ) // validRRs checks a slice of RRs received from the server against a DNSRRValidator. @@ -125,29 +128,14 @@ func validRcode(rcode int, valid []string, logger *slog.Logger) bool { func ProbeDNS(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) bool { var dialProtocol string - probeDNSDurationGaugeVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "probe_dns_duration_seconds", - Help: "Duration of DNS request by phase", - }, []string{"phase"}) - probeDNSAnswerRRSGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_dns_answer_rrs", - Help: "Returns number of entries in the answer resource record list", - }) - probeDNSAuthorityRRSGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_dns_authority_rrs", - Help: "Returns number of entries in the authority resource record list", - }) - probeDNSAdditionalRRSGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_dns_additional_rrs", - Help: "Returns number of entries in the additional resource record list", - }) - probeDNSQuerySucceeded := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_dns_query_succeeded", - Help: "Displays whether or not the query was executed successfully", - }) - for _, lv := range []string{"resolve", "connect", "request"} { - probeDNSDurationGaugeVec.WithLabelValues(lv) + probeDNSDurationGaugeVec := metrics.NewProbeDurationSeconds() + probeDNSAnswerRRSGauge := metrics.NewProbeAnswerRrs() + probeDNSAuthorityRRSGauge := metrics.NewProbeAuthorityRrs() + probeDNSAdditionalRRSGauge := metrics.NewProbeAdditionalRrs() + probeDNSQuerySucceeded := metrics.NewProbeQuerySucceeded() + for _, lv := range []other.AttrPhase{other.PhaseResolve, other.PhaseConnect, other.PhaseRequest} { + probeDNSDurationGaugeVec.With(lv) } registry.MustRegister(probeDNSDurationGaugeVec) @@ -201,7 +189,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry logger.Error("Error resolving address", "err", err) return false } - probeDNSDurationGaugeVec.WithLabelValues("resolve").Add(lookupTime) + probeDNSDurationGaugeVec.With(other.PhaseResolve).Add(lookupTime) targetIP := net.JoinHostPort(ip.String(), port) if ip.IP.To4() == nil { @@ -267,8 +255,8 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry // exchange messages with the server _after_ the connection is created. // We compute the connection time as the total time for the operation // minus the time for the actual request rtt. - probeDNSDurationGaugeVec.WithLabelValues("connect").Set((time.Since(requestStart) - rtt).Seconds()) - probeDNSDurationGaugeVec.WithLabelValues("request").Set(rtt.Seconds()) + probeDNSDurationGaugeVec.With(other.PhaseConnect).Set((time.Since(requestStart) - rtt).Seconds()) + probeDNSDurationGaugeVec.With(other.PhaseRequest).Set(rtt.Seconds()) if err != nil { logger.Error("Error while sending a DNS query", "err", err) return false @@ -281,10 +269,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry probeDNSQuerySucceeded.Set(1) if qt == dns.TypeSOA { - probeDNSSOAGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_dns_serial", - Help: "Returns the serial number of the zone", - }) + probeDNSSOAGauge = metrics.NewProbeSerial() registry.MustRegister(probeDNSSOAGauge) for _, a := range response.Answer { diff --git a/prober/grpc.go b/prober/grpc.go index fe8d1eca..265db41d 100644 --- a/prober/grpc.go +++ b/prober/grpc.go @@ -22,6 +22,10 @@ import ( "time" "github.com/prometheus/blackbox_exporter/config" + metrics "github.com/prometheus/blackbox_exporter/internal/metrics/grpc" + "github.com/prometheus/blackbox_exporter/internal/metrics/other" + "github.com/prometheus/blackbox_exporter/internal/metrics/ssl" + "github.com/prometheus/blackbox_exporter/internal/metrics/tls" "github.com/prometheus/client_golang/prometheus" pconfig "github.com/prometheus/common/config" "google.golang.org/grpc" @@ -75,47 +79,16 @@ func (c *gRPCHealthCheckClient) Check(ctx context.Context, service string) (bool } func ProbeGRPC(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) (success bool) { - var ( - durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "probe_grpc_duration_seconds", - Help: "Duration of gRPC request by phase", - }, []string{"phase"}) - - isSSLGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_grpc_ssl", - Help: "Indicates if SSL was used for the connection", - }) - - statusCodeGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_grpc_status_code", - Help: "Response gRPC status code", - }) - - healthCheckResponseGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "probe_grpc_healthcheck_response", - Help: "Response HealthCheck response", - }, []string{"serving_status"}) - - probeSSLEarliestCertExpiryGauge = prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) - - probeTLSVersion = prometheus.NewGaugeVec( - probeTLSInfoGaugeOpts, - []string{"version"}, - ) - - probeSSLLastInformation = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "probe_ssl_last_chain_info", - Help: "Contains SSL leaf certificate information", - }, - []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative", "serialnumber"}, - ) + durationGaugeVec = metrics.NewProbeDurationSeconds() + isSSLGauge = metrics.NewProbeSsl() + statusCodeGauge = metrics.NewProbeStatusCode() + healthCheckResponseGaugeVec = metrics.NewProbeHealthcheckResponse() + probeSSLEarliestCertExpiryGauge = ssl.NewProbeEarliestCertExpiry() + probeTLSVersion = tls.NewProbeVersion() + probeSSLLastInformation = ssl.NewProbeLastChainInfo() ) - - for _, lv := range []string{"resolve"} { - durationGaugeVec.WithLabelValues(lv) - } + durationGaugeVec.With(other.PhaseResolve) registry.MustRegister(durationGaugeVec) registry.MustRegister(isSSLGauge) @@ -149,7 +122,7 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr logger.Error("Error resolving address", "err", err) return false } - durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) + durationGaugeVec.With(other.PhaseResolve).Add(lookupTime) checkStart := time.Now() if len(tlsConfig.ServerName) == 0 { // If there is no `server_name` in tls_config, use @@ -180,7 +153,6 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr } conn, err := grpc.NewClient(target, opts...) - if err != nil { logger.Error("did not connect", "err", err) } @@ -188,13 +160,18 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr client := NewGrpcHealthCheckClient(conn) defer conn.Close() ok, statusCode, serverPeer, servingStatus, err := client.Check(context.Background(), module.GRPC.Service) - durationGaugeVec.WithLabelValues("check").Add(time.Since(checkStart).Seconds()) + durationGaugeVec.With(other.PhaseCheck).Add(time.Since(checkStart).Seconds()) - for servingStatusName := range grpc_health_v1.HealthCheckResponse_ServingStatus_value { - healthCheckResponseGaugeVec.WithLabelValues(servingStatusName).Set(float64(0)) + for _, ss := range []other.AttrServingStatus{ + other.ServingStatusServing, + other.ServingStatusNotServing, + other.ServingStatusUnknown, + other.ServingStatusServiceUnknown, + } { + healthCheckResponseGaugeVec.With(ss).Set(float64(0)) } if servingStatus != "" { - healthCheckResponseGaugeVec.WithLabelValues(servingStatus).Set(float64(1)) + healthCheckResponseGaugeVec.With(other.AttrServingStatus(servingStatus)).Set(float64(1)) } if serverPeer != nil { @@ -203,8 +180,14 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeSSLLastInformation) isSSLGauge.Set(float64(1)) probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(&tlsInfo.State).Unix())) - probeTLSVersion.WithLabelValues(getTLSVersion(&tlsInfo.State)).Set(1) - probeSSLLastInformation.WithLabelValues(getFingerprint(&tlsInfo.State), getSubject(&tlsInfo.State), getIssuer(&tlsInfo.State), getDNSNames(&tlsInfo.State), getSerialNumber(&tlsInfo.State)).Set(1) + probeTLSVersion.With(getTLSVersion(&tlsInfo.State)).Set(1) + probeSSLLastInformation.With( + getFingerprint(&tlsInfo.State), + getIssuer(&tlsInfo.State), + getSerialNumber(&tlsInfo.State), + getSubject(&tlsInfo.State), + getDNSNames(&tlsInfo.State), + ).Set(1) } else { isSSLGauge.Set(float64(0)) } diff --git a/prober/handler.go b/prober/handler.go index 6a51967b..1ef77612 100644 --- a/prober/handler.go +++ b/prober/handler.go @@ -25,6 +25,7 @@ import ( "time" "github.com/prometheus/blackbox_exporter/config" + "github.com/prometheus/blackbox_exporter/internal/metrics/probe" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/expfmt" @@ -73,14 +74,8 @@ func Handler(w http.ResponseWriter, r *http.Request, c *config.Config, logger *s defer cancel() r = r.WithContext(ctx) - probeSuccessGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_success", - Help: "Displays whether or not the probe was a success", - }) - probeDurationGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_duration_seconds", - Help: "Returns how long the probe took to complete in seconds", - }) + probeSuccessGauge := probe.NewSuccess() + probeDurationGauge := probe.NewDurationSeconds() target := params.Get("target") if target == "" { diff --git a/prober/http.go b/prober/http.go index 28243116..64d6caf1 100644 --- a/prober/http.go +++ b/prober/http.go @@ -43,6 +43,11 @@ import ( "golang.org/x/net/publicsuffix" "github.com/prometheus/blackbox_exporter/config" + httpmetrics "github.com/prometheus/blackbox_exporter/internal/metrics/http" + "github.com/prometheus/blackbox_exporter/internal/metrics/other" + "github.com/prometheus/blackbox_exporter/internal/metrics/probe" + sslmetrics "github.com/prometheus/blackbox_exporter/internal/metrics/ssl" + tlsmetrics "github.com/prometheus/blackbox_exporter/internal/metrics/tls" ) func matchRegularExpressions(reader io.Reader, httpConfig config.HTTPProbe, logger *slog.Logger) bool { @@ -291,74 +296,21 @@ var userAgentDefaultHeader = fmt.Sprintf("Blackbox Exporter/%s", version.Version func ProbeHTTP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) (success bool) { var redirects int var ( - durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "probe_http_duration_seconds", - Help: "Duration of http request by phase, summed over all redirects", - }, []string{"phase"}) - contentLengthGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_content_length", - Help: "Length of http content response", - }) - bodyUncompressedLengthGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_uncompressed_body_length", - Help: "Length of uncompressed response body", - }) - redirectsGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_redirects", - Help: "The number of redirects", - }) - - isSSLGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_ssl", - Help: "Indicates if SSL was used for the final redirect", - }) - - statusCodeGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_status_code", - Help: "Response HTTP status code", - }) - - probeSSLEarliestCertExpiryGauge = prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) - - probeSSLLastChainExpiryTimestampSeconds = prometheus.NewGauge(sslChainExpiryInTimeStampGaugeOpts) - - probeSSLLastInformation = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "probe_ssl_last_chain_info", - Help: "Contains SSL leaf certificate information", - }, - []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative", "serialnumber"}, - ) - - probeTLSVersion = prometheus.NewGaugeVec( - probeTLSInfoGaugeOpts, - []string{"version"}, - ) - - probeTLSCipher = prometheus.NewGaugeVec( - probeTLSCipherGaugeOpts, - []string{"cipher"}, - ) - - probeHTTPVersionGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_version", - Help: "Returns the version of HTTP of the probe response", - }) - - probeFailedDueToRegex = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_failed_due_to_regex", - Help: "Indicates if probe failed due to regex", - }) - - probeFailedDueToCEL = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_failed_due_to_cel", - Help: "Indicates if probe failed due to CEL expression not matching", - }) - - probeHTTPLastModified = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_http_last_modified_timestamp_seconds", - Help: "Returns the Last-Modified HTTP response header in unixtime", - }) + durationGaugeVec = httpmetrics.NewProbeDurationSeconds() + contentLengthGauge = httpmetrics.NewProbeContentLength() + bodyUncompressedLengthGauge = httpmetrics.NewProbeUncompressedBodyLength() + redirectsGauge = httpmetrics.NewProbeRedirects() + isSSLGauge = httpmetrics.NewProbeSsl() + statusCodeGauge = httpmetrics.NewProbeStatusCode() + probeSSLEarliestCertExpiryGauge = sslmetrics.NewProbeEarliestCertExpiry() + probeSSLLastChainExpiryTimestampSeconds = sslmetrics.NewProbeLastChainExpiryTimestampSeconds() + probeSSLLastInformation = sslmetrics.NewProbeLastChainInfo() + probeTLSVersion = tlsmetrics.NewProbeVersion() + probeTLSCipher = tlsmetrics.NewProbeCipher() + probeHTTPVersionGauge = httpmetrics.NewProbeVersion() + probeFailedDueToRegex = probe.NewFailedDueToRegex() + probeFailedDueToCEL = httpmetrics.NewProbeFailedDueToCel() + probeHTTPLastModified = httpmetrics.NewProbeLastModifiedTimestampSeconds() ) registry.MustRegister(durationGaugeVec) @@ -393,7 +345,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr if !module.HTTP.SkipResolvePhaseWithProxy || module.HTTP.HTTPClientConfig.ProxyURL.URL == nil || module.HTTP.HTTPClientConfig.ProxyFromEnvironment { var lookupTime float64 ip, lookupTime, err = chooseProtocol(ctx, module.HTTP.IPProtocol, module.HTTP.IPProtocolFallback, targetHost, registry, logger) - durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) + durationGaugeVec.With(other.PhaseResolve).Add(lookupTime) if err != nil { logger.Error("Error resolving address", "err", err) return false @@ -521,8 +473,8 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr } request = request.WithContext(httptrace.WithClientTrace(request.Context(), trace)) - for _, lv := range []string{"connect", "tls", "processing", "transfer"} { - durationGaugeVec.WithLabelValues(lv) + for _, lv := range []other.AttrPhase{other.PhaseConnect, other.PhaseTLS, other.PhaseProcessing, other.PhaseTransfer} { + durationGaugeVec.With(lv) } resp, err := client.Do(request) @@ -684,7 +636,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr ) // We get the duration for the first request from chooseProtocol. if i != 0 { - durationGaugeVec.WithLabelValues("resolve").Add(trace.dnsDone.Sub(trace.start).Seconds()) + durationGaugeVec.With(other.PhaseResolve).Add(trace.dnsDone.Sub(trace.start).Seconds()) } // Continue here if we never got a connection because a request failed. if trace.gotConn.IsZero() { @@ -692,34 +644,40 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr } if trace.tls { // dnsDone must be set if gotConn was set. - durationGaugeVec.WithLabelValues("connect").Add(trace.connectDone.Sub(trace.dnsDone).Seconds()) - durationGaugeVec.WithLabelValues("tls").Add(trace.tlsDone.Sub(trace.tlsStart).Seconds()) + durationGaugeVec.With(other.PhaseConnect).Add(trace.connectDone.Sub(trace.dnsDone).Seconds()) + durationGaugeVec.With(other.PhaseTLS).Add(trace.tlsDone.Sub(trace.tlsStart).Seconds()) } else { - durationGaugeVec.WithLabelValues("connect").Add(trace.gotConn.Sub(trace.dnsDone).Seconds()) + durationGaugeVec.With(other.PhaseConnect).Add(trace.gotConn.Sub(trace.dnsDone).Seconds()) } // Continue here if we never got a response from the server. if trace.responseStart.IsZero() { continue } - durationGaugeVec.WithLabelValues("processing").Add(trace.responseStart.Sub(trace.gotConn).Seconds()) + durationGaugeVec.With(other.PhaseProcessing).Add(trace.responseStart.Sub(trace.gotConn).Seconds()) // Continue here if we never read the full response from the server. // Usually this means that request either failed or was redirected. if trace.end.IsZero() { continue } - durationGaugeVec.WithLabelValues("transfer").Add(trace.end.Sub(trace.responseStart).Seconds()) + durationGaugeVec.With(other.PhaseTransfer).Add(trace.end.Sub(trace.responseStart).Seconds()) } if resp.TLS != nil { isSSLGauge.Set(float64(1)) registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeTLSCipher, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(resp.TLS).Unix())) - probeTLSVersion.WithLabelValues(getTLSVersion(resp.TLS)).Set(1) - probeTLSCipher.WithLabelValues(getTLSCipher(resp.TLS)).Set(1) + probeTLSVersion.With(other.AttrVersion(getTLSVersion(resp.TLS))).Set(1) + probeTLSCipher.With(other.AttrCipher(getTLSCipher(resp.TLS))).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(resp.TLS).Unix())) - probeSSLLastInformation.WithLabelValues(getFingerprint(resp.TLS), getSubject(resp.TLS), getIssuer(resp.TLS), getDNSNames(resp.TLS), getSerialNumber(resp.TLS)).Set(1) + probeSSLLastInformation.With( + other.AttrFingerprintSha256(getFingerprint(resp.TLS)), + other.AttrIssuer(getIssuer(resp.TLS)), + other.AttrSerialnumber(getSerialNumber(resp.TLS)), + other.AttrSubject(getSubject(resp.TLS)), + other.AttrSubjectalternative(getDNSNames(resp.TLS)), + ).Set(1) if httpConfig.FailIfSSL { logger.Error("Final request was over SSL") success = false diff --git a/prober/icmp.go b/prober/icmp.go index 2f07e6b0..f31a27a7 100644 --- a/prober/icmp.go +++ b/prober/icmp.go @@ -30,6 +30,8 @@ import ( "golang.org/x/net/ipv6" "github.com/prometheus/blackbox_exporter/config" + metrics "github.com/prometheus/blackbox_exporter/internal/metrics/icmp" + "github.com/prometheus/blackbox_exporter/internal/metrics/other" ) var ( @@ -70,21 +72,13 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr v4RawConn *ipv4.RawConn hopLimitFlagSet = true - durationGaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "probe_icmp_duration_seconds", - Help: "Duration of icmp request by phase", - }, []string{"phase"}) - - hopLimitGauge = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_icmp_reply_hop_limit", - Help: "Replied packet hop limit (TTL for ipv4)", - }) + durationGaugeVec = metrics.NewProbeDurationSeconds() + hopLimitGauge = metrics.NewProbeReplyHopLimit() ) - for _, lv := range []string{"resolve", "setup", "rtt"} { - durationGaugeVec.WithLabelValues(lv) + for _, lv := range []other.AttrPhase{other.PhaseResolve, other.PhaseSetup, other.PhaseRTT} { + durationGaugeVec.With(lv) } - registry.MustRegister(durationGaugeVec) dstIPAddr, lookupTime, err := chooseProtocol(ctx, module.ICMP.IPProtocol, module.ICMP.IPProtocolFallback, target, registry, logger) @@ -93,7 +87,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr logger.Error("Error resolving address", "err", err) return false } - durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) + durationGaugeVec.With(other.PhaseResolve).Add(lookupTime) var srcIP net.IP if len(module.ICMP.SourceIPAddress) > 0 { @@ -228,7 +222,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr return } - durationGaugeVec.WithLabelValues("setup").Add(time.Since(setupStart).Seconds()) + durationGaugeVec.With(other.PhaseSetup).Add(time.Since(setupStart).Seconds()) logger.Info("Writing out packet") rttStart := time.Now() @@ -363,7 +357,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr rb[3] = 0 } if bytes.Equal(rb[:n], wb) { - durationGaugeVec.WithLabelValues("rtt").Add(time.Since(rttStart).Seconds()) + durationGaugeVec.With(other.PhaseRTT).Add(time.Since(rttStart).Seconds()) if hopLimit >= 0 { hopLimitGauge.Set(hopLimit) registry.MustRegister(hopLimitGauge) diff --git a/prober/tcp.go b/prober/tcp.go index f2f1396d..6cb5c63b 100644 --- a/prober/tcp.go +++ b/prober/tcp.go @@ -21,10 +21,12 @@ import ( "log/slog" "net" + "github.com/prometheus/blackbox_exporter/config" + "github.com/prometheus/blackbox_exporter/internal/metrics/probe" + "github.com/prometheus/blackbox_exporter/internal/metrics/ssl" + tlsmetrics "github.com/prometheus/blackbox_exporter/internal/metrics/tls" "github.com/prometheus/client_golang/prometheus" pconfig "github.com/prometheus/common/config" - - "github.com/prometheus/blackbox_exporter/config" ) func dialTCP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) (net.Conn, error) { @@ -106,23 +108,11 @@ func probeExpectInfo(registry *prometheus.Registry, qr *config.QueryResponse, by } func ProbeTCP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) bool { - probeSSLEarliestCertExpiry := prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) - probeSSLLastChainExpiryTimestampSeconds := prometheus.NewGauge(sslChainExpiryInTimeStampGaugeOpts) - probeSSLLastInformation := prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "probe_ssl_last_chain_info", - Help: "Contains SSL leaf certificate information", - }, - []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative", "serialnumber"}, - ) - probeTLSVersion := prometheus.NewGaugeVec( - probeTLSInfoGaugeOpts, - []string{"version"}, - ) - probeFailedDueToRegex := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_failed_due_to_regex", - Help: "Indicates if probe failed due to regex", - }) + probeSSLEarliestCertExpiry := ssl.NewProbeEarliestCertExpiry() + probeSSLLastChainExpiryTimestampSeconds := ssl.NewProbeLastChainExpiryTimestampSeconds() + probeSSLLastInformation := ssl.NewProbeLastChainInfo() + probeTLSVersion := tlsmetrics.NewProbeVersion() + probeFailedDueToRegex := probe.NewFailedDueToRegex() registry.MustRegister(probeFailedDueToRegex) deadline, _ := ctx.Deadline() @@ -145,9 +135,15 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry state := conn.(*tls.Conn).ConnectionState() registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) - probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) + probeTLSVersion.With(getTLSVersion(&state)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) - probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state), getSerialNumber(&state)).Set(1) + probeSSLLastInformation.With( + getFingerprint(&state), + getIssuer(&state), + getSerialNumber(&state), + getSubject(&state), + getDNSNames(&state), + ).Set(1) } scanner := bufio.NewScanner(conn) for i, qr := range module.TCP.QueryResponse { @@ -214,9 +210,15 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry state := tlsConn.ConnectionState() registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) - probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) + probeTLSVersion.With(getTLSVersion(&state)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) - probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state), getSerialNumber(&state)).Set(1) + probeSSLLastInformation.With( + getFingerprint(&state), + getIssuer(&state), + getSerialNumber(&state), + getSubject(&state), + getDNSNames(&state), + ).Set(1) } } return true diff --git a/prober/tls.go b/prober/tls.go index 0589036e..ea732ec4 100644 --- a/prober/tls.go +++ b/prober/tls.go @@ -20,6 +20,8 @@ import ( "fmt" "strings" "time" + + "github.com/prometheus/blackbox_exporter/internal/metrics/other" ) func getEarliestCertExpiry(state *tls.ConnectionState) time.Time { @@ -32,25 +34,25 @@ func getEarliestCertExpiry(state *tls.ConnectionState) time.Time { return earliest } -func getFingerprint(state *tls.ConnectionState) string { +func getFingerprint(state *tls.ConnectionState) other.AttrFingerprintSha256 { cert := state.PeerCertificates[0] fingerprint := sha256.Sum256(cert.Raw) - return hex.EncodeToString(fingerprint[:]) + return other.AttrFingerprintSha256(hex.EncodeToString(fingerprint[:])) } -func getSubject(state *tls.ConnectionState) string { +func getSubject(state *tls.ConnectionState) other.AttrSubject { cert := state.PeerCertificates[0] - return cert.Subject.String() + return other.AttrSubject(cert.Subject.String()) } -func getIssuer(state *tls.ConnectionState) string { +func getIssuer(state *tls.ConnectionState) other.AttrIssuer { cert := state.PeerCertificates[0] - return cert.Issuer.String() + return other.AttrIssuer(cert.Issuer.String()) } -func getDNSNames(state *tls.ConnectionState) string { +func getDNSNames(state *tls.ConnectionState) other.AttrSubjectalternative { cert := state.PeerCertificates[0] - return strings.Join(cert.DNSNames, ",") + return other.AttrSubjectalternative(strings.Join(cert.DNSNames, ",")) } func getLastChainExpiry(state *tls.ConnectionState) time.Time { @@ -70,26 +72,26 @@ func getLastChainExpiry(state *tls.ConnectionState) time.Time { return lastChainExpiry } -func getSerialNumber(state *tls.ConnectionState) string { +func getSerialNumber(state *tls.ConnectionState) other.AttrSerialnumber { cert := state.PeerCertificates[0] // Using `cert.SerialNumber.Text(16)` will drop the leading zeros when converting the SerialNumber to String, see https://github.com/mozilla/tls-observatory/pull/245. // To avoid that, we format in lowercase the bytes with `%x` to base 16, with lower-case letters for a-f, see https://go.dev/play/p/Fylce70N2Zl. - return fmt.Sprintf("%x", cert.SerialNumber.Bytes()) + return other.AttrSerialnumber(fmt.Sprintf("%x", cert.SerialNumber.Bytes())) } -func getTLSVersion(state *tls.ConnectionState) string { +func getTLSVersion(state *tls.ConnectionState) other.AttrVersion { switch state.Version { case tls.VersionTLS10: - return "TLS 1.0" + return other.AttrVersion("TLS 1.0") case tls.VersionTLS11: - return "TLS 1.1" + return other.AttrVersion("TLS 1.1") case tls.VersionTLS12: - return "TLS 1.2" + return other.AttrVersion("TLS 1.2") case tls.VersionTLS13: - return "TLS 1.3" + return other.AttrVersion("TLS 1.3") default: - return "unknown" + return other.AttrVersion("unknown") } } diff --git a/prober/utils.go b/prober/utils.go index 3dc4153c..720be9ba 100644 --- a/prober/utils.go +++ b/prober/utils.go @@ -21,6 +21,7 @@ import ( "net" "time" + "github.com/prometheus/blackbox_exporter/internal/metrics/probe" "github.com/prometheus/client_golang/prometheus" ) @@ -32,23 +33,15 @@ var protocolToGauge = map[string]float64{ // Returns the IP for the IPProtocol and lookup time. func chooseProtocol(ctx context.Context, IPProtocol string, fallbackIPProtocol bool, target string, registry *prometheus.Registry, logger *slog.Logger) (ip *net.IPAddr, lookupTime float64, err error) { var fallbackProtocol string - probeDNSLookupTimeSeconds := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_dns_lookup_time_seconds", - Help: "Returns the time taken for probe dns lookup in seconds", - }) - - probeIPProtocolGauge := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_ip_protocol", - Help: "Specifies whether probe ip protocol is IP4 or IP6", - }) - - probeIPAddrHash := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "probe_ip_addr_hash", - Help: "Specifies the hash of IP address. It's useful to detect if the IP address changes.", - }) - registry.MustRegister(probeIPProtocolGauge) - registry.MustRegister(probeDNSLookupTimeSeconds) - registry.MustRegister(probeIPAddrHash) + probeDNSLookupTimeSeconds := probe.NewDnsLookupTimeSeconds() + probeIPProtocolGauge := probe.NewIpProtocol() + probeIPAddrHash := probe.NewIpAddrHash() + + registry.MustRegister( + probeIPProtocolGauge, + probeDNSLookupTimeSeconds, + probeIPAddrHash, + ) if IPProtocol == "ip6" || IPProtocol == "" { IPProtocol = "ip6" diff --git a/prober/utils_test.go b/prober/utils_test.go index bc85d23d..d84ef01d 100644 --- a/prober/utils_test.go +++ b/prober/utils_test.go @@ -28,6 +28,7 @@ import ( "testing" "time" + "github.com/prometheus/blackbox_exporter/internal/metrics/other" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/promslog" @@ -256,7 +257,7 @@ func TestGetSerialNumber(t *testing.T) { tests := []struct { name string serialNumber *big.Int - expected string + expected other.AttrSerialnumber }{ { name: "Serial number with leading zeros", diff --git a/semconv/blackbox/metrics.yaml b/semconv/blackbox/metrics.yaml new file mode 100644 index 00000000..9043dc37 --- /dev/null +++ b/semconv/blackbox/metrics.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.blackbox.module.unknown + type: metric + metric_name: blackbox_module_unknown_total + brief: "Count of unknown modules requested by probes" + instrument: "counter" + unit: "1" + stability: stable diff --git a/semconv/config/metrics.yaml b/semconv/config/metrics.yaml new file mode 100644 index 00000000..8765a8b8 --- /dev/null +++ b/semconv/config/metrics.yaml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.config.last.reload.successful + type: metric + metric_name: blackbox_exporter_config_last_reload_successful + brief: "Blackbox exporter config loaded successfully" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.config.last.reload.success.timestamp.seconds + type: metric + metric_name: blackbox_exporter_config_last_reload_success_timestamp_seconds + brief: "Timestamp of the last successful configuration reload" + instrument: "gauge" + unit: "s" + stability: stable diff --git a/semconv/dns/metrics.yaml b/semconv/dns/metrics.yaml new file mode 100644 index 00000000..fb13f3ba --- /dev/null +++ b/semconv/dns/metrics.yaml @@ -0,0 +1,52 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.dns.probe.duration.seconds + type: metric + metric_name: probe_dns_duration_seconds + brief: "Duration of DNS request by phase" + instrument: "gauge" + unit: "s" + attributes: + - ref: phase + requirement_level: required + stability: stable + + - id: metric.dns.probe.answer.rrs + type: metric + metric_name: probe_dns_answer_rrs + brief: "Returns number of entries in the answer resource record list" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.dns.probe.authority.rrs + type: metric + metric_name: probe_dns_authority_rrs + brief: "Returns number of entries in the authority resource record list" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.dns.probe.additional.rrs + type: metric + metric_name: probe_dns_additional_rrs + brief: "Returns number of entries in the additional resource record list" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.dns.probe.query.succeeded + type: metric + metric_name: probe_dns_query_succeeded + brief: "Displays whether or not the query was executed successfully" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.dns.probe.serial + type: metric + metric_name: probe_dns_serial + brief: "Returns the serial number of the zone" + instrument: "gauge" + unit: "1" + stability: stable diff --git a/semconv/grpc/metrics.yaml b/semconv/grpc/metrics.yaml new file mode 100644 index 00000000..2d3710de --- /dev/null +++ b/semconv/grpc/metrics.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.grpc.probe.duration.seconds + type: metric + metric_name: probe_grpc_duration_seconds + brief: "Duration of gRPC request by phase" + instrument: "gauge" + unit: "s" + attributes: + - ref: phase + requirement_level: required + stability: stable + + - id: metric.grpc.probe.ssl + type: metric + metric_name: probe_grpc_ssl + brief: "Indicates if SSL was used for the connection" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.grpc.probe.status.code + type: metric + metric_name: probe_grpc_status_code + brief: "Response gRPC status code" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.grpc.probe.healthcheck.response + type: metric + metric_name: probe_grpc_healthcheck_response + brief: "Response HealthCheck response" + instrument: "gauge" + unit: "1" + attributes: + - ref: serving_status + requirement_level: required + stability: stable diff --git a/semconv/grpc/registry.yaml b/semconv/grpc/registry.yaml new file mode 100644 index 00000000..3ee5281a --- /dev/null +++ b/semconv/grpc/registry.yaml @@ -0,0 +1,24 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: registry.grpc + type: attribute_group + brief: gRPC attributes + attributes: + - id: status_code + type: int + stability: stable + brief: "gRPC status code" + examples: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + - id: serving_status + type: + members: + - { id: "serving", value: "SERVING", stability: stable } + - { id: "not_serving", value: "NOT_SERVING", stability: stable } + - { id: "unknown", value: "UNKNOWN", stability: stable } + - { + id: "service_unknown", + value: "SERVICE_UNKNOWN", + stability: stable, + } + stability: stable + brief: "gRPC health check serving status" diff --git a/semconv/http/metrics.yaml b/semconv/http/metrics.yaml new file mode 100644 index 00000000..b667785c --- /dev/null +++ b/semconv/http/metrics.yaml @@ -0,0 +1,76 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.http.probe.duration.seconds + type: metric + metric_name: probe_http_duration_seconds + brief: "Duration of http request by phase, summed over all redirects" + instrument: "gauge" + unit: "s" + attributes: + - ref: phase + requirement_level: required + stability: stable + + - id: metric.http.probe.content.length + type: metric + metric_name: probe_http_content_length + brief: "Length of http content response" + instrument: "gauge" + unit: "By" + stability: stable + + - id: metric.http.probe.uncompressed.body.length + type: metric + metric_name: probe_http_uncompressed_body_length + brief: "Length of uncompressed response body" + instrument: "gauge" + unit: "By" + stability: stable + + - id: metric.http.probe.redirects + type: metric + metric_name: probe_http_redirects + brief: "The number of redirects" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.http.probe.ssl + type: metric + metric_name: probe_http_ssl + brief: "Indicates if SSL was used for the final redirect" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.http.probe.status.code + type: metric + metric_name: probe_http_status_code + brief: "Response HTTP status code" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.http.probe.version + type: metric + metric_name: probe_http_version + brief: "Returns the version of HTTP of the probe response" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.http.probe.failed.due.to.cel + type: metric + metric_name: probe_failed_due_to_cel + brief: "Indicates if probe failed due to CEL expression not matching" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.http.probe.last.modified.timestamp.seconds + type: metric + metric_name: probe_http_last_modified_timestamp_seconds + brief: "Returns the Last-Modified HTTP response header in unixtime" + instrument: "gauge" + unit: "s" + stability: stable diff --git a/semconv/icmp/metrics.yaml b/semconv/icmp/metrics.yaml new file mode 100644 index 00000000..2544db45 --- /dev/null +++ b/semconv/icmp/metrics.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.icmp.probe.duration.seconds + type: metric + metric_name: probe_icmp_duration_seconds + brief: "Duration of icmp request by phase" + instrument: "gauge" + unit: "s" + attributes: + - ref: phase + requirement_level: required + stability: stable + + - id: metric.icmp.probe.reply.hop.limit + type: metric + metric_name: probe_icmp_reply_hop_limit + brief: "Replied packet hop limit (TTL for ipv4)" + instrument: "gauge" + unit: "1" + stability: stable diff --git a/semconv/icmp/registry.yaml b/semconv/icmp/registry.yaml new file mode 100644 index 00000000..d3784fd3 --- /dev/null +++ b/semconv/icmp/registry.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: registry.icmp + type: attribute_group + brief: ICMP attributes + attributes: + - id: hop_limit + type: int + stability: stable + brief: "Hop limit (TTL for IPv4) of the replied packet" + examples: [64, 128, 255] diff --git a/semconv/other/registry.yaml b/semconv/other/registry.yaml new file mode 100644 index 00000000..72dc685c --- /dev/null +++ b/semconv/other/registry.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: registry.other + type: attribute_group + brief: Unqualified attributes + attributes: + - id: phase + type: + members: + - { id: "resolve", value: "resolve", stability: stable } + - { id: "connect", value: "connect", stability: stable } + - { id: "request", value: "request", stability: stable } + - { id: "tls", value: "tls", stability: stable } + - { id: "processing", value: "processing", stability: stable } + - { id: "transfer", value: "transfer", stability: stable } + - { id: "setup", value: "setup", stability: stable } + - { id: "rtt", value: "rtt", stability: stable } + - { id: "check", value: "check", stability: stable } + stability: stable + brief: "Probe phase" diff --git a/semconv/probe/metrics.yaml b/semconv/probe/metrics.yaml new file mode 100644 index 00000000..57420b55 --- /dev/null +++ b/semconv/probe/metrics.yaml @@ -0,0 +1,49 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.probe.success + type: metric + metric_name: probe_success + brief: "Displays whether or not the probe was a success" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.probe.duration.seconds + type: metric + metric_name: probe_duration_seconds + brief: "Returns how long the probe took to complete in seconds" + instrument: "gauge" + unit: "s" + stability: stable + + - id: metric.probe.dns.lookup.time.seconds + type: metric + metric_name: probe_dns_lookup_time_seconds + brief: "Returns the time taken for probe dns lookup in seconds" + instrument: "gauge" + unit: "s" + stability: stable + + - id: metric.probe.ip.protocol + type: metric + metric_name: probe_ip_protocol + brief: "Specifies whether probe ip protocol is IP4 or IP6" + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.probe.ip.addr.hash + type: metric + metric_name: probe_ip_addr_hash + brief: "Specifies the hash of IP address. It's useful to detect if the IP address changes." + instrument: "gauge" + unit: "1" + stability: stable + + - id: metric.probe.failed.due.to.regex + type: metric + metric_name: probe_failed_due_to_regex + brief: "Indicates if probe failed due to regex" + instrument: "gauge" + unit: "1" + stability: stable diff --git a/semconv/tcp/metrics.yaml b/semconv/tcp/metrics.yaml new file mode 100644 index 00000000..46736ef1 --- /dev/null +++ b/semconv/tcp/metrics.yaml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.tcp.probe.expect.info + type: metric + metric_name: probe_expect_info + brief: "Explicit content matched" + instrument: "gauge" + unit: "1" + note: "This metric uses dynamic labels based on the TCP module configuration" + stability: stable diff --git a/semconv/tls/metrics.yaml b/semconv/tls/metrics.yaml new file mode 100644 index 00000000..2d8cc720 --- /dev/null +++ b/semconv/tls/metrics.yaml @@ -0,0 +1,58 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: metric.ssl.probe.earliest.cert.expiry + type: metric + metric_name: probe_ssl_earliest_cert_expiry + brief: "Returns earliest SSL cert expiry date" + instrument: "gauge" + unit: "s" + stability: stable + + - id: metric.ssl.probe.last.chain.expiry.timestamp.seconds + type: metric + metric_name: probe_ssl_last_chain_expiry_timestamp_seconds + brief: "Returns last SSL chain expiry timestamp" + instrument: "gauge" + unit: "s" + stability: stable + + - id: metric.ssl.probe.last.chain.info + type: metric + metric_name: probe_ssl_last_chain_info + brief: "Contains SSL leaf certificate information" + instrument: "gauge" + unit: "1" + attributes: + - ref: fingerprint_sha256 + requirement_level: required + - ref: subject + requirement_level: required + - ref: issuer + requirement_level: required + - ref: subjectalternative + requirement_level: required + - ref: serialnumber + requirement_level: required + stability: stable + + - id: metric.tls.probe.version + type: metric + metric_name: probe_tls_version_info + brief: "Contains TLS version information" + instrument: "gauge" + unit: "1" + attributes: + - ref: version + requirement_level: required + stability: stable + + - id: metric.tls.probe.cipher + type: metric + metric_name: probe_tls_cipher_info + brief: "Contains TLS cipher information" + instrument: "gauge" + unit: "1" + attributes: + - ref: cipher + requirement_level: required + stability: stable diff --git a/semconv/tls/registry.yaml b/semconv/tls/registry.yaml new file mode 100644 index 00000000..b0f8203b --- /dev/null +++ b/semconv/tls/registry.yaml @@ -0,0 +1,48 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/open-telemetry/weaver/refs/heads/main/schemas/semconv.schema.json +groups: + - id: registry.tls + type: attribute_group + brief: TLS attributes + attributes: + - id: fingerprint_sha256 + type: string + stability: stable + brief: "SHA256 fingerprint of the certificate" + examples: + ["e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"] + + - id: subject + type: string + stability: stable + brief: "Subject of the certificate" + examples: ["CN=example.com,O=Example Corp,L=San Francisco,ST=CA,C=US"] + + - id: issuer + type: string + stability: stable + brief: "Issuer of the certificate" + examples: ["CN=Example CA,O=Example Corp,C=US"] + + - id: subjectalternative + type: string + stability: stable + brief: "Subject alternative names of the certificate" + examples: ["DNS:example.com,DNS:www.example.com"] + + - id: serialnumber + type: string + stability: stable + brief: "Serial number of the certificate" + examples: ["1234567890abcdef"] + + - id: version + type: string + stability: stable + brief: "TLS version" + examples: ["TLS 1.3", "TLS 1.2"] + + - id: cipher + type: string + stability: stable + brief: "TLS cipher suite" + examples: ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES256-GCM-SHA384"]