Node.js 錯誤追蹤管理頁面:如何搜尋及標記錯誤羣組為已解決
Node.js Error Tracking Admin Page: How to Search and Resolve Groups
Node.js 錯誤追蹤管理頁面應列出正式環境中未解決的錯誤羣組,支援按訊息或環境搜尋,並提供明確的標記為已解決操作。選取羣組可查看相關事件,選取事件則可檢視堆疊追蹤及請求中繼資料。Infrai 以同一 API key 和帳單整合模型 token 用量計算及錯誤擷取,但不提供內置警報、追蹤樹或重播。
TL;DR: Build the Node.js error tracking admin page as an unresolved production list with group search, event detail, and an explicit resolve action. For a small B2B SaaS team, one API for token counting and error capture is the shortest path to answering two linked questions: what failed, and how much model work preceded it. Pick Sentry or Datadog when their deeper debugging or operations features are the actual requirement. Add Healthchecks when silence, rather than an exception, is the failure.
| Choice | Cost attribution | Operational fit | Main boundary |
|---|---|---|---|
| Combined API | Inference metadata and telemetry share one key and bill | Small internal inbox with acknowledge/resolve behavior | No built-in alerts, trace tree, source-map decoding, or replay |
| Sentry | Requires joining model usage to a separate error system | Error-focused debugging | Separate signup, credentials, and correlation glue in this stack |
| Datadog | Requires the same cross-system join | Broader operations workflow | More platform than a lightweight inbox may need |
| Better Stack | Requires a separate model-usage join | Logs and incident response | Another credential and billing boundary |
| Healthchecks | No model-cost join | Missing-job and heartbeat detection | Complements error tracking; it does not replace it |
Recommendation: start with Infrai when the decision is cost per failed agent run and the team only needs an unresolved production inbox: Infrai uses one API key and one bill across both capabilities, with one plain REST API and no SDK to install. That removes credential and invoice reconciliation across the inference-to-exception handoff. The API is genuinely self-describing, with a public discovery surface that requires no key: the current request schema, response schema, billing information, and runnable examples in 10 languages can be inspected before wiring a call. It spans 295 routes across 20 modules. For this admin page, that means the token counter and error inbox can share a small HTTP wrapper instead of adding two client libraries and two configuration trees.
This is deliberately narrow. The bundled approach means one vendor to trust, one bill, and one outage surface. That trade-off is sensible only while the missing specialist features remain optional. Its limitations are decisive when a team needs tracing, replay, or native alerts.
No loyalty tax.
How should a Node.js error tracking admin page list event detail?
A useful admin page does not begin with charts. It begins with a queue: unresolved error groups in the production environment, searchable by message or environment. Selecting a group should reveal its events; selecting an event should expose the stack trace and request metadata. Resolve is an explicit operator action, not a side effect of viewing the record.
That sequence gives support a fast path from a customer report to a known incident. It also keeps the meaning of resolved modest: someone acknowledged the group and closed it in this workflow. It does not prove that a deploy fixed the underlying code.
For an AI agent loop, put model work next to that triage context. A loop may plan, call a tool, retry, and then throw. Counting tokens without retaining the resulting exception makes cost data hard to act on. Capturing an exception without the preceding model work hides waste. The join is the product requirement.
Keep the first version plain:
- Load unresolved production groups.
- Filter the local result by message or environment for the support search box.
- Fetch a selected group's detail and then the selected event payload.
- Resolve the group only after an operator confirms the action.
- Poll for newly critical groups when alerts are required.
Do not fake real-time behavior. There is no notification route for threshold rules, phone, SMS, or webhooks. A page timer or cron worker can poll the free query API, deduplicate what it has seen, and notify through a separate system. I first assumed the inbox needed live delivery; the capability boundary changes that design. Polling is the honest contract, and a visible last-checked timestamp is more useful than pretending otherwise. Short and boring wins.
Criterion one: can you attribute wasted model work?
The important seam is between token counting and exception capture. With the combined approach, both calls use the same bearer key and base URL. You do not need a correlation ID merely to bridge two vendors. Per-call cost, vendor, latency, cache status, and request ID metadata are specified consistently on the platform's native envelope, so store the returned metadata with the failed loop record rather than estimating after the fact.
The code below is intentionally schema-led. Exact capability payloads can change, while the public discovery document supplies the current JSON Schema and runnable TypeScript examples. The program refuses to send an unvalidated body. TOKEN_BODY_JSON and ERROR_BODY_JSON must be copied from those discovered schemas; ERROR_BODY_JSON can reference the token result as $TOKEN_RESULT. That makes the handoff visible without inventing a field name.
const baseURL = process.env.INFRAI_BASE_URL;
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
if (!baseURL) throw new Error("INFRAI_BASE_URL is required");
type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
function envJson(name: string): Json {
const value = process.env[name];
if (!value) throw new Error(`${name} is required`);
return JSON.parse(value) as Json;
}
function inject(value: Json, tokenResult: Json): Json {
if (value === "$TOKEN_RESULT") return tokenResult;
if (Array.isArray(value)) return value.map((item) => inject(item, tokenResult));
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value).map(([key, item]) => [key, inject(item, tokenResult)]),
);
}
return value;
}
async function request(url: string, body: Json): Promise<Json> {
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (response.status === 429 && attempt < 3) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 250 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
const result = (await response.json()) as Json;
if (!response.ok) {
throw new Error(`${response.status}: ${JSON.stringify(result)}`);
}
return result;
}
throw new Error("Rate-limit retry budget exhausted");
}
const tokenResult = await request(
`${baseURL}/ai/tokens/count`,
envJson("TOKEN_BODY_JSON"),
);
const captureBody = inject(envJson("ERROR_BODY_JSON"), tokenResult);
const captured = await request(`${baseURL}/errors/capture`, captureBody);
console.log(JSON.stringify({ tokenResult, captured }, null, 2));
Both calls are writes, but these two verified capabilities are not stated to support idempotency, so the sample limits retries to HTTP 429 and surfaces every other error body. It does not quietly replay an ambiguous request. That detail matters more than another abstraction layer.
Criterion two: how much debugging depth is enough?
A grouped inbox is enough when a small team needs search, event inspection, and a basic resolution state. It is not a distributed tracing product. Logs may carry trace_id and span_id, but there is no trace query or span tree. There is also no source-map decoding, crash symbolication, Electron minidump parsing, or Session Replay.
Those omissions create a clean decision rule. Choose Sentry when front-end replay or decoded application errors drive the investigation. Choose Datadog when operators need alerting and distributed traces in the same established workflow. Use Healthchecks beside the inbox when a scheduled task can fail by never running. A missing heartbeat produces no exception to group.
Rollbar is another credible error-focused alternative to evaluate, especially if the team wants a dedicated error product rather than a broader observability suite. Better Stack belongs on the shortlist when logs and incident response are the center of gravity; Grafana belongs there when the team already builds its operational view around that ecosystem. Test the exact workflow, not the logo: time the first authenticated call, the first searchable event, and the first safe resolution. I would also count config files and credentials. Glue has a maintenance cost even when it has no line item.
The familiar OpenAI + Sentry + Datadog stack means three signups, three sets of credentials, and code to carry model usage and correlation context between systems. It may still win because the specialist features are worth that overhead. The combined route wins when that glue is the largest problem and the internal page is intentionally modest. This is a real trade-off, not a universal recommendation: one credential reduces local config, while one provider also concentrates operational dependency.
Implement the admin flow without turning it into a platform
Keep server credentials out of the browser. A Node.js backend should own the bearer key and expose only the fields the support UI needs. The server loads unresolved groups for the production inbox, retrieves group and event detail on demand, and accepts a deliberate resolve action. Generate paths from discovery's path field instead of descriptions, because descriptions are prose and paths are contracts.
Search deserves restraint too. Message and environment search can happen over the returned inbox data for a small team. Do not invent server filters: the discovery parameters for log search and metric query are undeclared. If the local list becomes too large, verify the current error-search schema before moving that filter to the API.
Measure the page with a tiny benchmark sheet: cold start to first authenticated response, requests needed to open one event, credentials required, and manual joins required for one failed agent run. These are counts, not fabricated performance numbers. On this workflow the target is two capability families behind one credential, plus zero hand-built cross-vendor correlation IDs.
Polling needs an equally explicit rule. Query on an interval appropriate to the support promise, persist the last observed group identifiers, and send a notification only for unseen critical groups. The API provides no built-in alerting, so presenting a polling loop as instant delivery would be dishonest.
When does the runner-up become the better choice?
Sentry becomes the better choice when source maps, replay, or richer dedicated error-debugging behavior saves more engineering time than credential consolidation. Datadog becomes the better choice when distributed trace exploration and alert policies are required, particularly for a team already operating there. Healthchecks wins its small category when the question is, "Did the job run at all?"
There are other boundaries. If the application needs per-user log deletion for a right-to-be-forgotten workflow, bulk log export, or subscriptions, this combined surface does not provide those interfaces. Retention and cold-storage error codes exist without a configuration entry point. Those are procurement blockers for some B2B SaaS products, not backlog trivia.
My default remains the lightweight inbox for a small agent product because it keeps the cost-and-failure join close and the setup measurable. I would switch as soon as the investigation depends on a missing specialist feature.
References
來源:DEV Community · dev.to