@absolutejs/errors
绝对JS 效果内在、哨兵等同的例外捕捉 下结.
createErrorTracker 是一个薄薄的装饰器
@absolutejs/audit, (中文(简体) ),
@absolutejs/telemetry, (中文(简体) ),
和一个耐用的问题商店. capture(error, context?) 是一个
Effect<CaptureOutcome, never> —— 说 无法捕捉错误本身
失败 - 一次就做六件事: - 不,不,不,不
- 计算马力 指纹 所以不同的错误是相同的 呼叫网站崩溃成一个"问题"
- 记录例外 活动 OTel 跨度当追踪器是 提供.
- 表示a 审计活动 (单位:千美元)
kind: 'errors.captured') (中文(简体) ). - 增加组 问题 + 将事件附加到持久
store和火灾onIssue关于一个新的/倒退的问题. - 推入进程 最新错误 LRU 缓冲器。 。 。 .
- 每个指印的凸起 计量计数器。 。 。 .
嵌入 Error.cause 链条被附加到所储存的堆栈并保存为
结构化 extra.errorCauses 条目,包括数据库等驱动字段
错误代码、严重程度、细节和常规。 外部错误继续驱动
指纹的产生会增加诊断 而不破坏不同的手术.
浏览器 ingest 在可选功能后重新计算其犬形指纹
prepare() 步骤。 因此,同一故障的出处
即使其原始内容与块名不同,它们也同时释放.
摄入的边界还重新刻出具有证书的场、无记名/JWT值,
badcrumb 文本, 以及 URL 查询/ 随机值, 在计划验证后但在此之前
指纹或缓冲。 默认允许这种深入防御的通行证;
createIngestEndpoint({ redact: false }) 仅供主机使用
换成等同的可信任的边界政策,而习惯
redact(event) 函数可以扩展内置策略.
错误- as- 值
每个扇出( 审计 / 跟踪 / 存储 /) onIssue)是一个信任边界.
每一个都被包住了 所以它的失败变成了一个 键入, 标记值
(单位:千美元)Data.TaggedError——从未被吞入
匿名计数器或一个 onError(unknown)成果报告逐一沉入
交货状态('ok' | 'failed' | 'skipped'),所以一个呼叫者可以作出反应
具体地说——重新测试商店,审计损失的页面,忽略一个片面
onIssue - 详尽无遗 switch (failure._tag)。 。 。 .
import { Effect } from "effect";
import { status } from "elysia";
import { createErrorTracker, createMemoryIssueStore } from "@absolutejs/errors";
import { tracerOrNoop } from "@absolutejs/telemetry";
const errors = createErrorTracker({
audit: broker, // @absolutejs/audit
tracer: tracerOrNoop(otelProvider, "app"),
store: createMemoryIssueStore(), // or @absolutejs/errors-postgres
project: "acme",
release: process.env.RELEASE,
environment: "production",
onIssue: (r) => alert(r.issue), // only on new / regression
});
// Effect API (primary):
const outcome = await Effect.runPromise(
errors.capture(e, {
tenant,
target: `order_${orderId}`,
tags: { component: "billing" },
}),
);
// Promise edge (for Promise-world consumers) — identical outcome:
const out = await errors.captureException(e);
if (out.failures.length > 0) {
for (const f of out.failures) {
switch (f._tag) {
case "StoreFailure":
retryLater(f.cause);
break; // f.cause: IssueStoreError
case "AuditSinkFailure":
page("audit lost", f.cause);
break;
case "TracerFailure":
case "OnIssueFailure":
case "FingerprintFailure":
/* tolerate */ break;
}
}
}
return status(
"Internal Server Error",
`Request failed. Reference: ${out.fingerprint}`,
);
API 密码
createErrorTracker(options?: {
audit?: { append: (event) => Promise<void> | void };
tracer?: { startSpan?: (name) => Span };
store?: IssueStore; // durable "Issues" surface
project?: string; // default 'default'
onIssue?: (r: IssueUpsertResult) => void | Promise<void>; // new/regression only
release?: string;
environment?: string;
fingerprint?: (error: Error, context: ErrorContext) => string | Promise<string>;
maxRecent?: number; // default 100
maxFingerprints?: number; // default 1000
clock?: () => number;
}) => ErrorTracker
capture(error, context?) => Effect<CaptureOutcome, never> // primary
captureException(error, context?) => Promise<CaptureOutcome> // Promise edge
recentErrors() => ReadonlyArray<CapturedError>
clearRecent() => void
metrics() => { captured, captureErrors, byFingerprint }
store? => IssueStore
type CaptureOutcome = {
fingerprint: string;
delivered: Record<'fingerprint'|'audit'|'tracer'|'store'|'onIssue', 'ok'|'failed'|'skipped'>;
issue?: IssueUpsertResult; // present iff the store delivered
failures: CaptureFailure[]; // typed, tagged; empty ⇒ fully delivered
};
ErrorContext 携带标准的哨兵式分门别类信封:
tenant, (中文(简体) ), target, (中文(简体) ), traceId, (中文(简体) ), spanId, (中文(简体) ), replayId, (中文(简体) ), tags, (中文(简体) ), extra, (中文(简体) ),
level,可选语义 groupingKey组合键
信息或源框可能改变的合成或集成失败
在释放之间:
await errors.captureException(providerError, {
groupingKey: "google-ads-tag-load",
tags: { provider: "google" },
});
该密钥在信任的错误边界被验证并散列 。 客户从不 直接选择存储的指纹,项目范围界定仍然隔离 其它相同的密钥.
Elysia 服务器集成
@absolutejs/errors/elysia 提供一个具有单独设置的服务器插件
用于两个错误路径:
import { createErrorTracker, createMemoryIssueStore } from "@absolutejs/errors";
import { errorsPlugin, handledError } from "@absolutejs/errors/elysia";
import { Elysia, status } from "elysia";
const tracker = createErrorTracker({
store: createMemoryIssueStore(), // use the Postgres adapter in production
});
const app = new Elysia()
.use(
errorsPlugin({
tracker,
server: {
context: ({ request }) => ({
tenant: request.headers.get("x-user-id") ?? undefined,
}),
},
ingest: {
path: "/ingest",
reporting: {
path: "/ingest/reports",
project: "web",
environment: "production",
release: process.env.RELEASE_SHA,
},
// authorize, limits, buffer, symbolication, and drain settings
},
}),
)
.get("/report", async () => {
try {
return await buildReport();
} catch (error) {
return handledError(
status(500, { message: "Unable to build the report." }),
error,
);
}
});
server 抓获、处理和未说明原因的5xx回复.
设定 captureReturned5xx 故意控制平面时的
反应,如准备状态 503 应通过健康保持可观察性
监测工作不成为一个例外问题。 上游收到
请求上下文、反应类型和状态; 扔出和明确处理
例外情况不受影响.
默认启用,可以禁用 server: false。 。 。 . ingest
挂载浏览器- 事件端点, 并选择输入; 通过 {} 要使用跟踪器
存储和默认值,或 false打开它 暴露任何路线.
ingest.reporting 挂载浏览器报告 API 接收器并转换崩溃,
同一经证实的问题的折旧、干预和政策执行
输油管。 发送回复信头
Reporting-Endpoints: default="/ingest/reports" 关于启用浏览器的文档
交货。 无法收集崩溃报告 ReportingObserver: 页面
JavaScript在浏览器-进程崩溃后不再运行.
此子路径只包含服务器, 包含效果支持的跟踪器/ 摄入器
运行时间。 浏览器代码应使用 @absolutejs/beacon (或
@absolutejs/observability)并且永远不导入 @absolutejs/errors/elysia。 。 。 .
仅转发服务器可省略 tracker 和提供 server.capture
直接; 仍然不变 errorsPlugin。 。 。 .
持久性问题(“问题”表面)
程序内缓冲器是用于分解的;对于持续可查询的
问题 产品(第一眼 / 最后眼 / 发生数 / 状态 /
受让人/回归探测)通过a store数字 :
import { createErrorTracker, createMemoryIssueStore } from "@absolutejs/errors";
// or, for durability:
// import { createPostgresIssueStore } from '@absolutejs/errors-postgres';
const errors = createErrorTracker({
project: "acme",
release: process.env.RELEASE,
store: createMemoryIssueStore(), // swap for the Postgres adapter
onIssue: (r) => {
// fires ONLY on new / regression
if (r.isNew || r.isRegression) alert(r.issue); // → @absolutejs/dispatch
},
});
每次抓取都会增加一个分组问题(按 (project, fingerprint)(中文(简体) )
并附上事件. onIssue 仅对价值的过渡开火
求人——a 新设 问题或问题 回归 联合国 resolved
问题再次出现, 这倒转回 unresolvedA级 resolved 问题
严重性不断升级,但从未降级; ignored 问题保持沉默.
存储写作是最佳的: 存储出局表面为输入
StoreFailure 内 outcome.failures (包装适配器)
IssueStoreError),递增 captureErrors永远不要被俘虏.
适配器是 效应本土 ——每种方法返回一个 Effect 带有
类型 IssueStoreError 频道 (IssueStoreSchemaError 编号 :
IssueStoreQueryError 编号 : IssueStoreSerializationError:
type IssueStore = {
record: (event: StoredEvent) => Effect<IssueUpsertResult, IssueStoreError>; // required
listIssues?: (filter?) => Effect<IssueRecord[], IssueStoreError>; // dashboard
getIssue?: (project, fingerprint) => Effect<Option<IssueRecord>, IssueStoreError>;
setState?: (project, fingerprint, 'unresolved'|'resolved'|'ignored') => Effect<void, IssueStoreError>;
assign?: (project, fingerprint, assignee | null) => Effect<void, IssueStoreError>;
listEvents?:(project, fingerprint, limit?) => Effect<StoredEvent[], IssueStoreError>;
};
createMemoryIssueStore() 是零失败的引用执行
(用于dev/测试/单一进程); @absolutejs/errors-postgres
是耐久的适配器——两者都尊重相同的语义.
StoredEvent 携带 traceId 编号 : spanId (-) @absolutejs/telemetry(中文(简体) )
财务报告和审定财务报表 replayId (-) @absolutejs/replay因此,仪表板可以交叉链接
一个问题 准确的追踪 和DOM重放. issueTitle() 编号 : issueCulprit()
以导出, 这样存储适配器从问题行中导出,而不连接到
内出.
handoffErrorContext(summary) 链接到
@absolutejs/handoff 通过有边框的标签和隐私安全
预测。 它不包括最新的有效载荷、电文、参考资料和
外相为相.
指纹
没有 groupingKey,默认指纹是 SHA-1 的 16-hex-char 前缀
error.name | normalized(message) | first-user-stack-frame
normalized() 将数字和引用的字符串字元脱去这样
user 'u_42' not found 财务报告和审定财务报表 user 'u_99' not found 组合在一起.
输入自定义 fingerprint 用于确定性测试或
域特定分组规则.
内存界限
maxRecent(默认) 100个)为进程内最近缓冲设置了上限.maxFingerprints(默认) 1 000个盖上byFingerprint反相图,所以一个能合成独特错误的攻击者不能 吹出过程内存. 超过封顶,老字号被赶出去 任意——计数器按设计大致.
许可证
BSL-1.1 与主机错误跟踪 SaaS 的命名例外
(哨兵, Bugsnag, Rollbar, Honeybadger, Airbrake, 数据犬错误)
追踪 见 LICENSE。更改日期 : 2030-05-31 (英语) • Apache 2.0.