feat(alerting): ClickHouseLogStore.countLogs for log-pattern evaluator
Adds countLogs(LogSearchRequest) — no FINAL, no cursor/sort/limit — reusing the same WHERE-clause logic as search() for tenant, env, app, level, q, logger, source, exchangeId, and time-range filters. Also extends ClickHouseTestHelper with executeInitSqlWithProjections() and makes the script runner non-fatal for ADD/MATERIALIZE PROJECTION. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -256,6 +256,84 @@ public class ClickHouseLogStore implements LogIndex {
|
||||
return new LogSearchResponse(results, nextCursor, hasMore, levelCounts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts log entries matching the given request — no {@code FINAL}, no cursor/sort/limit.
|
||||
* Intended for alerting evaluators (LogPatternEvaluator) which tolerate brief duplicate counts.
|
||||
*/
|
||||
public long countLogs(LogSearchRequest request) {
|
||||
List<String> conditions = new ArrayList<>();
|
||||
List<Object> params = new ArrayList<>();
|
||||
conditions.add("tenant_id = ?");
|
||||
params.add(tenantId);
|
||||
|
||||
if (request.environment() != null && !request.environment().isEmpty()) {
|
||||
conditions.add("environment = ?");
|
||||
params.add(request.environment());
|
||||
}
|
||||
|
||||
if (request.application() != null && !request.application().isEmpty()) {
|
||||
conditions.add("application = ?");
|
||||
params.add(request.application());
|
||||
}
|
||||
|
||||
if (request.instanceId() != null && !request.instanceId().isEmpty()) {
|
||||
conditions.add("instance_id = ?");
|
||||
params.add(request.instanceId());
|
||||
}
|
||||
|
||||
if (request.exchangeId() != null && !request.exchangeId().isEmpty()) {
|
||||
conditions.add("(exchange_id = ?" +
|
||||
" OR (mapContains(mdc, 'cameleer.exchangeId') AND mdc['cameleer.exchangeId'] = ?)" +
|
||||
" OR (mapContains(mdc, 'camel.exchangeId') AND mdc['camel.exchangeId'] = ?))");
|
||||
params.add(request.exchangeId());
|
||||
params.add(request.exchangeId());
|
||||
params.add(request.exchangeId());
|
||||
}
|
||||
|
||||
if (request.q() != null && !request.q().isEmpty()) {
|
||||
String term = "%" + escapeLike(request.q()) + "%";
|
||||
conditions.add("(message ILIKE ? OR stack_trace ILIKE ?)");
|
||||
params.add(term);
|
||||
params.add(term);
|
||||
}
|
||||
|
||||
if (request.logger() != null && !request.logger().isEmpty()) {
|
||||
conditions.add("logger_name ILIKE ?");
|
||||
params.add("%" + escapeLike(request.logger()) + "%");
|
||||
}
|
||||
|
||||
if (request.sources() != null && !request.sources().isEmpty()) {
|
||||
String placeholders = String.join(", ", Collections.nCopies(request.sources().size(), "?"));
|
||||
conditions.add("source IN (" + placeholders + ")");
|
||||
for (String s : request.sources()) {
|
||||
params.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.levels() != null && !request.levels().isEmpty()) {
|
||||
String placeholders = String.join(", ", Collections.nCopies(request.levels().size(), "?"));
|
||||
conditions.add("level IN (" + placeholders + ")");
|
||||
for (String lvl : request.levels()) {
|
||||
params.add(lvl.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (request.from() != null) {
|
||||
conditions.add("timestamp >= parseDateTime64BestEffort(?, 3)");
|
||||
params.add(request.from().toString());
|
||||
}
|
||||
|
||||
if (request.to() != null) {
|
||||
conditions.add("timestamp <= parseDateTime64BestEffort(?, 3)");
|
||||
params.add(request.to().toString());
|
||||
}
|
||||
|
||||
String where = String.join(" AND ", conditions);
|
||||
String sql = "SELECT count() FROM logs WHERE " + where; // NO FINAL
|
||||
Long result = jdbc.queryForObject(sql, Long.class, params.toArray());
|
||||
return result != null ? result : 0L;
|
||||
}
|
||||
|
||||
private Map<String, Long> queryLevelCounts(String baseWhere, List<Object> baseParams) {
|
||||
String sql = "SELECT level, count() AS cnt FROM logs WHERE " + baseWhere + " GROUP BY level";
|
||||
Map<String, Long> counts = new LinkedHashMap<>();
|
||||
|
||||
Reference in New Issue
Block a user