sourcemap view
This commit is contained in:
1 parent
412e430cac
commit
97743ea21c
10 files changed
+452
-147
No files matched your search
@@ -0,0 +1,93 @@
|
||||
import { AlertTriangle, ArrowRight } from "lucide-react"
|
||||
import React from "react"
|
||||
import { useObservabilityData } from "../hooks/useObservabilityData"
|
||||
import { useIssues } from "../hooks/useIssues"
|
||||
import { getServiceName, isSpanError } from "../lib/otel-utils"
|
||||
|
||||
/** Full-width alert strip docked under the top bar; renders nothing when there is nothing anomalous. */
|
||||
export const AnomalyRadarBar: React.FC = () => {
|
||||
const { logs, traces, showErrorsOnly, setShowErrorsOnly, setActiveTab } = useObservabilityData()
|
||||
const { issues } = useIssues()
|
||||
|
||||
const errorStats = React.useMemo(() => {
|
||||
let errorCount = 0
|
||||
const services = new Set<string>()
|
||||
const note = (name: string) => { if (name !== "unknown-service") services.add(name) }
|
||||
|
||||
logs.forEach((l) =>
|
||||
(l.scopeLogs ?? []).forEach((sl) =>
|
||||
(sl.logRecords ?? []).forEach((r) => {
|
||||
if ((r.severityNumber ?? 0) >= 17) {
|
||||
errorCount++
|
||||
note(getServiceName(l.resource?.attributes ?? [], r.attributes ?? []))
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
traces.forEach((t) =>
|
||||
(t.resourceSpans ?? []).forEach((rs) =>
|
||||
(rs.scopeSpans ?? []).forEach((ss) =>
|
||||
(ss.spans ?? []).forEach((s) => {
|
||||
if (isSpanError(s)) {
|
||||
errorCount++
|
||||
note(getServiceName(rs.resource?.attributes ?? [], s.attributes ?? []))
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
return { errorCount, services: Array.from(services) }
|
||||
}, [logs, traces])
|
||||
|
||||
const topOpenIssue = React.useMemo(
|
||||
() => issues.filter((i) => !i.status || i.status === "open").sort((a, b) => b.count - a.count)[0] ?? null,
|
||||
[issues],
|
||||
)
|
||||
|
||||
if (errorStats.errorCount === 0 && !topOpenIssue) return null
|
||||
|
||||
const services = errorStats.services.slice(0, 3).join(", ") + (errorStats.services.length > 3 ? ` +${errorStats.services.length - 3}` : "")
|
||||
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
className="sticky top-[58px] z-30 flex flex-wrap items-center gap-x-4 gap-y-1.5 border-b-2 border-ds-critical bg-ds-critical-bg px-6 py-2 font-[family-name:var(--font-ui)]"
|
||||
>
|
||||
<span className="inline-flex shrink-0 items-center gap-1.5 text-[11px] font-bold uppercase tracking-[0.1em] text-ds-critical">
|
||||
<AlertTriangle className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
Anomaly radar
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-[12.5px] text-ds-text">
|
||||
{errorStats.errorCount > 0 && (
|
||||
<><strong className="font-semibold">{errorStats.errorCount} errors</strong> in the active window{services && <span className="text-ds-text-2"> · {services}</span>}</>
|
||||
)}
|
||||
{topOpenIssue && (
|
||||
<span className="text-ds-text-2">
|
||||
{errorStats.errorCount > 0 ? " · top issue " : ""}
|
||||
<span className="font-medium text-ds-text">{topOpenIssue.title}</span> ×{topOpenIssue.count} ({topOpenIssue.service})
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
{!showErrorsOnly && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowErrorsOnly(true)}
|
||||
className="inline-flex h-7 items-center rounded-[8px] border border-ds-critical/40 px-2.5 text-[12px] font-medium text-ds-critical hover:bg-ds-critical/10"
|
||||
>
|
||||
Isolate errors
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab("tab0")}
|
||||
className="inline-flex h-7 items-center gap-1 rounded-[8px] bg-ds-critical px-2.5 text-[12px] font-semibold text-white hover:opacity-90"
|
||||
>
|
||||
Triage issues <ArrowRight className="h-3 w-3" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnomalyRadarBar
|
||||
Reference in new issue
Block a user