feat(alerting): JVM_METRIC evaluator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package com.cameleer.server.app.alerting.eval;
|
||||
|
||||
import com.cameleer.server.core.alerting.AggregationOp;
|
||||
import com.cameleer.server.core.alerting.AlertRule;
|
||||
import com.cameleer.server.core.alerting.ConditionKind;
|
||||
import com.cameleer.server.core.alerting.JvmMetricCondition;
|
||||
import com.cameleer.server.core.storage.MetricsQueryStore;
|
||||
import com.cameleer.server.core.storage.model.MetricTimeSeries;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalDouble;
|
||||
|
||||
@Component
|
||||
public class JvmMetricEvaluator implements ConditionEvaluator<JvmMetricCondition> {
|
||||
|
||||
private final MetricsQueryStore metricsStore;
|
||||
|
||||
public JvmMetricEvaluator(MetricsQueryStore metricsStore) {
|
||||
this.metricsStore = metricsStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionKind kind() { return ConditionKind.JVM_METRIC; }
|
||||
|
||||
@Override
|
||||
public EvalResult evaluate(JvmMetricCondition c, AlertRule rule, EvalContext ctx) {
|
||||
String agentId = c.scope() != null ? c.scope().agentId() : null;
|
||||
if (agentId == null) return EvalResult.Clear.INSTANCE;
|
||||
|
||||
Map<String, List<MetricTimeSeries.Bucket>> series = metricsStore.queryTimeSeries(
|
||||
agentId,
|
||||
List.of(c.metric()),
|
||||
ctx.now().minusSeconds(c.windowSeconds()),
|
||||
ctx.now(),
|
||||
1
|
||||
);
|
||||
|
||||
List<MetricTimeSeries.Bucket> buckets = series.get(c.metric());
|
||||
if (buckets == null || buckets.isEmpty()) return EvalResult.Clear.INSTANCE;
|
||||
|
||||
OptionalDouble aggregated = aggregate(buckets, c.aggregation());
|
||||
if (aggregated.isEmpty()) return EvalResult.Clear.INSTANCE;
|
||||
|
||||
double actual = aggregated.getAsDouble();
|
||||
|
||||
boolean fire = switch (c.comparator()) {
|
||||
case GT -> actual > c.threshold();
|
||||
case GTE -> actual >= c.threshold();
|
||||
case LT -> actual < c.threshold();
|
||||
case LTE -> actual <= c.threshold();
|
||||
case EQ -> actual == c.threshold();
|
||||
};
|
||||
|
||||
if (!fire) return EvalResult.Clear.INSTANCE;
|
||||
|
||||
return new EvalResult.Firing(actual, c.threshold(),
|
||||
Map.of(
|
||||
"metric", c.metric(),
|
||||
"agent", Map.of("id", agentId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private OptionalDouble aggregate(List<MetricTimeSeries.Bucket> buckets, AggregationOp op) {
|
||||
return switch (op) {
|
||||
case MAX -> buckets.stream().mapToDouble(MetricTimeSeries.Bucket::value).max();
|
||||
case MIN -> buckets.stream().mapToDouble(MetricTimeSeries.Bucket::value).min();
|
||||
case AVG -> buckets.stream().mapToDouble(MetricTimeSeries.Bucket::value).average();
|
||||
case LATEST -> buckets.stream()
|
||||
.max(java.util.Comparator.comparing(MetricTimeSeries.Bucket::time))
|
||||
.map(b -> OptionalDouble.of(b.value()))
|
||||
.orElse(OptionalDouble.empty());
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user