feat: progressive drill-down dashboard with RED metrics and SLA compliance (#94)
Three-level dashboard driven by sidebar selection: - L1 (no selection): all-apps overview with health table, per-app charts - L2 (app selected): route performance table, error velocity, top errors - L3 (route selected): processor table, latency heatmap data, bottleneck KPI Backend: 3 new endpoints (timeseries/by-app, timeseries/by-route, errors/top), per-app SLA settings (app_settings table, V12 migration), exact SLA compliance from executions hypertable, error velocity with acceleration detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package com.cameleer3.server.app.dto;
|
||||
|
||||
import com.cameleer3.server.core.admin.AppSettings;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "Per-application dashboard settings")
|
||||
public record AppSettingsRequest(
|
||||
@NotNull @Min(1)
|
||||
@Schema(description = "SLA duration threshold in milliseconds")
|
||||
Integer slaThresholdMs,
|
||||
|
||||
@NotNull @Min(0) @Max(100)
|
||||
@Schema(description = "Error rate % threshold for warning (yellow) health dot")
|
||||
Double healthErrorWarn,
|
||||
|
||||
@NotNull @Min(0) @Max(100)
|
||||
@Schema(description = "Error rate % threshold for critical (red) health dot")
|
||||
Double healthErrorCrit,
|
||||
|
||||
@NotNull @Min(0) @Max(100)
|
||||
@Schema(description = "SLA compliance % threshold for warning (yellow) health dot")
|
||||
Double healthSlaWarn,
|
||||
|
||||
@NotNull @Min(0) @Max(100)
|
||||
@Schema(description = "SLA compliance % threshold for critical (red) health dot")
|
||||
Double healthSlaCrit
|
||||
) {
|
||||
|
||||
public AppSettings toSettings(String appId) {
|
||||
Instant now = Instant.now();
|
||||
return new AppSettings(appId, slaThresholdMs, healthErrorWarn, healthErrorCrit,
|
||||
healthSlaWarn, healthSlaCrit, now, now);
|
||||
}
|
||||
|
||||
public List<String> validate() {
|
||||
List<String> errors = new ArrayList<>();
|
||||
if (healthErrorWarn != null && healthErrorCrit != null
|
||||
&& healthErrorWarn > healthErrorCrit) {
|
||||
errors.add("healthErrorWarn must be <= healthErrorCrit");
|
||||
}
|
||||
if (healthSlaWarn != null && healthSlaCrit != null
|
||||
&& healthSlaWarn < healthSlaCrit) {
|
||||
errors.add("healthSlaWarn must be >= healthSlaCrit (higher SLA = healthier)");
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,6 @@ public record RouteMetrics(
|
||||
@NotNull double p99DurationMs,
|
||||
@NotNull double errorRate,
|
||||
@NotNull double throughputPerSec,
|
||||
@NotNull List<Double> sparkline
|
||||
@NotNull List<Double> sparkline,
|
||||
double slaCompliance
|
||||
) {}
|
||||
|
||||
Reference in New Issue
Block a user