package checker import ( "encoding/json" "fmt" "time" sdk "git.happydns.org/checker-sdk-go/checker" ) // ExtractMetrics implements sdk.CheckerMetricsReporter. It consumes the raw // observation, derives consensus on the fly, and emits time-series for // dashboards. Severity counters are computed from the rule states carried // in the ReportContext rather than re-derived from raw data. func (p *resolverPropagationProvider) ExtractMetrics(ctx sdk.ReportContext, collectedAt time.Time) ([]sdk.CheckMetric, error) { var data ResolverPropagationData if err := json.Unmarshal(ctx.Data(), &data); err != nil { return nil, fmt.Errorf("resolver-propagation: decoding observation: %w", err) } deriveView(&data) var out []sdk.CheckMetric zone := data.Zone rollups := []struct { name string val float64 }{ {"resolver_propagation_resolvers_total", float64(data.Stats.TotalResolvers)}, {"resolver_propagation_resolvers_reachable", float64(data.Stats.ReachableResolvers)}, {"resolver_propagation_unfiltered_agreeing", float64(data.Stats.UnfilteredAgreeing)}, {"resolver_propagation_regions_covered", float64(data.Stats.CountriesCovered)}, {"resolver_propagation_run_duration_ms", float64(data.RunDurationMs)}, } for _, r := range rollups { out = append(out, sdk.CheckMetric{ Name: r.name, Value: r.val, Labels: map[string]string{"zone": zone}, Timestamp: collectedAt, }) } var crit, warn, info int for _, st := range ctx.States() { switch st.Status { case sdk.StatusCrit: crit++ case sdk.StatusWarn: warn++ case sdk.StatusInfo: info++ } } for _, x := range []struct { name string val int }{ {"resolver_propagation_findings_critical", crit}, {"resolver_propagation_findings_warning", warn}, {"resolver_propagation_findings_info", info}, } { out = append(out, sdk.CheckMetric{ Name: x.name, Value: float64(x.val), Labels: map[string]string{"zone": zone}, Timestamp: collectedAt, }) } for id, rv := range data.Resolvers { labels := map[string]string{ "zone": zone, "resolver": id, "ip": rv.IP, "region": rv.Region, "transport": string(rv.Transport), } up := float64(0) if rv.Reachable { up = 1 } out = append(out, sdk.CheckMetric{ Name: "resolver_propagation_resolver_up", Value: up, Labels: labels, Timestamp: collectedAt, }) var total, n int64 for _, p := range rv.Probes { if p.Error != "" { continue } total += p.LatencyMs n++ } if n > 0 { out = append(out, sdk.CheckMetric{ Name: "resolver_propagation_resolver_latency_ms", Value: float64(total / n), Unit: "ms", Labels: labels, Timestamp: collectedAt, }) } } for key, v := range data.RRsets { out = append(out, sdk.CheckMetric{ Name: "resolver_propagation_rrset_signatures", Value: float64(len(v.Groups)), Labels: map[string]string{ "zone": zone, "rrset": key, }, Timestamp: collectedAt, }) } return out, nil }