diff --git a/checker/evaluate.go b/checker/evaluate.go index 8cef1aa..f64b2f1 100644 --- a/checker/evaluate.go +++ b/checker/evaluate.go @@ -23,7 +23,6 @@ package checker import ( "fmt" - "strings" "time" ) @@ -53,48 +52,43 @@ type Metric struct { Timestamp time.Time `json:"timestamp"` } -// EvaluateResult holds the evaluation outcome. +// EvaluateResult holds the evaluation outcome for a single target. type EvaluateResult struct { + Address string `json:"address"` Status int `json:"status"` Message string `json:"message"` Code string `json:"code"` } const ( - StatusOK = 1 - StatusWarn = 3 - StatusCrit = 4 + StatusUnknown = 0 + StatusOK = 1 + StatusWarn = 3 + StatusCrit = 4 ) -// Evaluate checks the ping data against the given thresholds. -// StatusUnknown indicates the check could not be performed. -const StatusUnknown = 0 - -func Evaluate(data *PingData, warningRTT, criticalRTT, warningPacketLoss, criticalPacketLoss float64) EvaluateResult { +// Evaluate checks the ping data against the given thresholds and returns one +// result per target. +func Evaluate(data *PingData, warningRTT, criticalRTT, warningPacketLoss, criticalPacketLoss float64) []EvaluateResult { if len(data.Targets) == 0 { - return EvaluateResult{ - Status: StatusUnknown, - Message: "No targets to ping", - Code: "ping_no_targets", - } + return nil } - overallStatus := StatusOK - var summaryParts []string - + results := make([]EvaluateResult, 0, len(data.Targets)) for _, target := range data.Targets { + status := StatusOK if target.PacketLoss >= criticalPacketLoss || target.RTTAvg >= criticalRTT { - overallStatus = StatusCrit - } else if (target.PacketLoss >= warningPacketLoss || target.RTTAvg >= warningRTT) && overallStatus < StatusWarn { - overallStatus = StatusWarn + status = StatusCrit + } else if target.PacketLoss >= warningPacketLoss || target.RTTAvg >= warningRTT { + status = StatusWarn } - summaryParts = append(summaryParts, fmt.Sprintf("%s: %.1fms avg, %.0f%% loss", target.Address, target.RTTAvg, target.PacketLoss)) - } - - return EvaluateResult{ - Status: overallStatus, - Message: strings.Join(summaryParts, " | "), - Code: "ping_result", + results = append(results, EvaluateResult{ + Address: target.Address, + Status: status, + Message: fmt.Sprintf("%.1fms avg, %.0f%% loss", target.RTTAvg, target.PacketLoss), + Code: "ping_result", + }) } + return results } diff --git a/checker/rule.go b/checker/rule.go index 5932cb1..f54d76a 100644 --- a/checker/rule.go +++ b/checker/rule.go @@ -106,14 +106,14 @@ func (r *pingRule) ValidateOptions(opts sdk.CheckerOptions) error { return nil } -func (r *pingRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) sdk.CheckState { +func (r *pingRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { var data PingData if err := obs.Get(ctx, ObservationKeyPing, &data); err != nil { - return sdk.CheckState{ + return []sdk.CheckState{{ Status: sdk.StatusError, Message: fmt.Sprintf("Failed to get ping data: %v", err), Code: "ping_error", - } + }} } warningRTT := sdk.GetFloatOption(opts, "warningRTT", 100) @@ -121,26 +121,44 @@ func (r *pingRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts warningPacketLoss := sdk.GetFloatOption(opts, "warningPacketLoss", 10) criticalPacketLoss := sdk.GetFloatOption(opts, "criticalPacketLoss", 50) - result := Evaluate(&data, warningRTT, criticalRTT, warningPacketLoss, criticalPacketLoss) - - var status sdk.Status - switch result.Status { - case StatusOK: - status = sdk.StatusOK - case StatusWarn: - status = sdk.StatusWarn - case StatusCrit: - status = sdk.StatusCrit - default: - status = sdk.StatusUnknown + results := Evaluate(&data, warningRTT, criticalRTT, warningPacketLoss, criticalPacketLoss) + if len(results) == 0 { + return []sdk.CheckState{{ + Status: sdk.StatusInfo, + Message: "No targets to ping", + Code: "ping_no_targets", + }} } - return sdk.CheckState{ - Status: status, - Message: result.Message, - Code: result.Code, - Meta: map[string]any{ - "targets": data.Targets, - }, + targetByAddr := make(map[string]PingTargetResult, len(data.Targets)) + for _, t := range data.Targets { + targetByAddr[t.Address] = t } + + out := make([]sdk.CheckState, 0, len(results)) + for _, r := range results { + var status sdk.Status + switch r.Status { + case StatusOK: + status = sdk.StatusOK + case StatusWarn: + status = sdk.StatusWarn + case StatusCrit: + status = sdk.StatusCrit + default: + status = sdk.StatusUnknown + } + + state := sdk.CheckState{ + Status: status, + Subject: r.Address, + Message: r.Message, + Code: r.Code, + } + if t, ok := targetByAddr[r.Address]; ok { + state.Meta = map[string]any{"target": t} + } + out = append(out, state) + } + return out } diff --git a/go.mod b/go.mod index 02a7c39..8e1e592 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module git.happydns.org/checker-ping go 1.25.0 require ( + git.happydns.org/checker-sdk-go v1.2.0 git.happydns.org/happyDomain v0.7.0 github.com/prometheus-community/pro-bing v0.8.0 ) require ( - git.happydns.org/checker-sdk-go v0.0.1 github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect diff --git a/go.sum b/go.sum index 4a5626b..630d66c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.happydns.org/checker-sdk-go v0.0.1 h1:4RxCJr73HWKxjOyU/6NJMO8lXJmH0gMLA68EzTqLbQI= -git.happydns.org/checker-sdk-go v0.0.1/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= +git.happydns.org/checker-sdk-go v1.2.0 h1:v4MpKAz0W3PwP+bxx3pya8w893sVH5xTD1of1cc0TV8= +git.happydns.org/checker-sdk-go v1.2.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= git.happydns.org/happyDomain v0.7.0 h1:NV82/NbcSeRm0+IUZqaK3Vu9Ovl5+vv4AigUJZMdwws= git.happydns.org/happyDomain v0.7.0/go.mod h1:5tgkmqFE65kK359rY49V++49wgZ0gco+Gh9X6tbL+bY= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=