feat(alerting): LOG_PATTERN evaluator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package com.cameleer.server.app.alerting.eval;
|
||||
|
||||
import com.cameleer.server.app.search.ClickHouseLogStore;
|
||||
import com.cameleer.server.core.alerting.AlertRule;
|
||||
import com.cameleer.server.core.alerting.ConditionKind;
|
||||
import com.cameleer.server.core.alerting.LogPatternCondition;
|
||||
import com.cameleer.server.core.runtime.EnvironmentRepository;
|
||||
import com.cameleer.server.core.search.LogSearchRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class LogPatternEvaluator implements ConditionEvaluator<LogPatternCondition> {
|
||||
|
||||
private final ClickHouseLogStore logStore;
|
||||
private final EnvironmentRepository envRepo;
|
||||
|
||||
public LogPatternEvaluator(ClickHouseLogStore logStore, EnvironmentRepository envRepo) {
|
||||
this.logStore = logStore;
|
||||
this.envRepo = envRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionKind kind() { return ConditionKind.LOG_PATTERN; }
|
||||
|
||||
@Override
|
||||
public EvalResult evaluate(LogPatternCondition c, AlertRule rule, EvalContext ctx) {
|
||||
String envSlug = envRepo.findById(rule.environmentId())
|
||||
.map(e -> e.slug())
|
||||
.orElse(null);
|
||||
|
||||
String appSlug = c.scope() != null ? c.scope().appSlug() : null;
|
||||
|
||||
Instant from = ctx.now().minusSeconds(c.windowSeconds());
|
||||
Instant to = ctx.now();
|
||||
|
||||
// Build a stable cache key so identical queries within the same tick are coalesced.
|
||||
String cacheKey = String.join("|",
|
||||
envSlug == null ? "" : envSlug,
|
||||
appSlug == null ? "" : appSlug,
|
||||
c.level() == null ? "" : c.level(),
|
||||
c.pattern() == null ? "" : c.pattern(),
|
||||
from.toString(),
|
||||
to.toString()
|
||||
);
|
||||
|
||||
long count = ctx.tickCache().getOrCompute(cacheKey, () -> {
|
||||
var req = new LogSearchRequest(
|
||||
c.pattern(),
|
||||
c.level() != null ? List.of(c.level()) : List.of(),
|
||||
appSlug,
|
||||
null, // instanceId
|
||||
null, // exchangeId
|
||||
null, // logger
|
||||
envSlug,
|
||||
null, // sources
|
||||
from,
|
||||
to,
|
||||
null, // cursor
|
||||
1, // limit (count query; value irrelevant)
|
||||
"desc" // sort
|
||||
);
|
||||
return logStore.countLogs(req);
|
||||
});
|
||||
|
||||
if (count <= c.threshold()) return EvalResult.Clear.INSTANCE;
|
||||
|
||||
return new EvalResult.Firing(
|
||||
(double) count,
|
||||
(double) c.threshold(),
|
||||
Map.of(
|
||||
"app", Map.of("slug", appSlug == null ? "" : appSlug),
|
||||
"pattern", c.pattern() == null ? "" : c.pattern(),
|
||||
"level", c.level() == null ? "" : c.level()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user