Add Prometheus metrics, JSON logs and provider health checks
- GET /metrics (bearer server.metrics_token): build info, users, sessions, pending requests, per-model request/token/cost counters, provider_up - CODECRAFT_LOG_FORMAT=json for one JSON object per log line - Providers are probed every 5 minutes via their model list; status shown in the provider settings with a manual re-check Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
10d4b64289
commit
b5de531d2e
12 files changed
+244
-6
No files matched your search
@@ -371,7 +371,9 @@ function cleanModel(m: ModelRow) {
|
||||
return out;
|
||||
}
|
||||
|
||||
function ProviderEditor({ name, p, reload }: { name: string; p: Json; reload: () => void }) {
|
||||
type ProviderStatus = { up: boolean; error?: string; latency_ms: number; checked_at: number };
|
||||
|
||||
function ProviderEditor({ name, p, reload, status }: { name: string; p: Json; reload: () => void; status?: ProviderStatus }) {
|
||||
const t = useT();
|
||||
const [form, setForm] = useState({ ...p, api_key: p.api_key === MASK ? "" : p.api_key, admin_key: "" });
|
||||
const [priceNote, setPriceNote] = useState<string>();
|
||||
@@ -436,6 +438,15 @@ function ProviderEditor({ name, p, reload }: { name: string; p: Json; reload: ()
|
||||
{name}
|
||||
<span className="chip">{form.kind}</span>
|
||||
<span className="chip">{t("settings.modelCount", { n: models.length || discovered.length })}</span>
|
||||
{status && (
|
||||
<span
|
||||
className={"chip " + (status.up ? "ok" : "warn")}
|
||||
title={(status.error ?? "") + " · " + new Date(status.checked_at).toLocaleTimeString()}
|
||||
>
|
||||
<Icon name={status.up ? "check" : "warning"} size={12} />
|
||||
{status.up ? t("settings.providerUp", { ms: status.latency_ms }) : t("settings.providerDown")}
|
||||
</span>
|
||||
)}
|
||||
<span className="spacer" />
|
||||
<button className="small" onClick={() => setOpen(!open)}>
|
||||
{open ? t("common.collapse") : t("common.edit")}
|
||||
@@ -584,6 +595,18 @@ function Providers({ cfg, reload }: { cfg: ConfigResponse; reload: () => void })
|
||||
const [preset, setPreset] = useState("anthropic");
|
||||
const { run, view } = useSaver(reload);
|
||||
const providers = cfg.config.providers ?? {};
|
||||
const [status, setStatus] = useState<Record<string, ProviderStatus>>({});
|
||||
const [checking, setChecking] = useState(false);
|
||||
useEffect(() => {
|
||||
get<Record<string, ProviderStatus>>("/providers/status").then(setStatus).catch(() => {});
|
||||
}, []);
|
||||
const recheck = () => {
|
||||
setChecking(true);
|
||||
post<Record<string, ProviderStatus>>("/providers/status")
|
||||
.then(setStatus)
|
||||
.catch(() => {})
|
||||
.finally(() => setChecking(false));
|
||||
};
|
||||
|
||||
const add = () => {
|
||||
const n = name.trim() || preset;
|
||||
@@ -599,9 +622,12 @@ function Providers({ cfg, reload }: { cfg: ConfigResponse; reload: () => void })
|
||||
<>
|
||||
<div className="row" style={{ marginBottom: 12 }}>
|
||||
<button onClick={() => post("/providers/reload").then(reload)}>{t("settings.reloadProviders")}</button>
|
||||
<button onClick={recheck} disabled={checking}>
|
||||
{checking ? t("settings.providerChecking") : t("settings.providerCheck")}
|
||||
</button>
|
||||
</div>
|
||||
{Object.entries(providers).map(([n, p]) => (
|
||||
<ProviderEditor key={n + JSON.stringify(p)} name={n} p={p} reload={reload} />
|
||||
<ProviderEditor key={n + JSON.stringify(p)} name={n} p={p} reload={reload} status={status[n]} />
|
||||
))}
|
||||
<div className="panel">
|
||||
<h3>{t("settings.addProvider")}</h3>
|
||||
|
||||
Reference in new issue
Block a user