# @ddd-ts-kit/sentry 中文文档

规范地址：https://npmcn.com/package/%40ddd-ts-kit/sentry/

版本：0\.106\.0
npm 元数据更新时间：2026-10-31T21:44:30.616Z
中文译文更新时间：2026-10-31T23:25:55.727Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40ddd-ts-kit/sentry
原文对照：https://npmcn.com/package/%40ddd-ts-kit/sentry/?lang=original
项目主页：https://gbourgeat.github.io/ddd-ts-kit/packages/sentry/
源码仓库：https://github.com/gbourgeat/ddd-ts-kit

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：89.1 kB，52 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.12\.0
- 运行依赖：1 个
- peer 依赖：@sentry/node ^11\.6\.0，@opentelemetry/api ^1\.9\.0
- 首次发布：2026-10-11T09:41:54.626Z
- 最新版本发布：2026-10-31T21:44:30.307Z
- 已发布版本数：63（近 12 个月 63 个）

## 中文简介

向内核ErrorReporter端口后面的Sentry或GlitchTip报告错误：只有意外的失败，与跟踪相关，在发送之前清除。

## 安装

```sh
npm install @ddd-ts-kit/sentry
```

也可以使用：`pnpm add @ddd-ts-kit/sentry`、`yarn add @ddd-ts-kit/sentry`、`bun add @ddd-ts-kit/sentry`

## README

**英语** · [法语](https://npmcn.com/package/%40ddd-ts-kit/sentry/README.fr.md)

# @ddd-ts-kit /哨兵

内核 `ErrorReporter` 在Sentry或GlitchTip上，它们使用相同的协议：一个适配器， 只有DSN不同。只有意想不到的失败，每一次失败都留下了痕迹 离开流程。中没有 `domain/` 或者 `application/` 导入此包或报告 处理程序抛出的任何东西，以及它们周围的东西都会报告。

```
npm i @ddd-ts-kit/sentry @sentry/node @opentelemetry/api
```

写对 `@sentry/node` 11：它所依赖的选项`enableOpenTelemetrySetup`， `dataCollection`)在以前的专业中不存在。

![Where the Sentry reporter sits: handlers and aggregates throw and report nothing; five capture points around them, the command and query buses, the HTTP pipeline, the job scheduler, DeadLetterReports on the hooks of the event providers and reportProcessFailures on the process, hand an unexpected failure to the kernel ErrorReporter, once; the composition root binds the SentryErrorReporter of initSentry to it; the event is scrubbed, then passed to the beforeSend of the project, then sent to Sentry or GlitchTip; its trace id leads to the trace in the tracing backend](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/sentry-capture-points.svg)

用例的代码永远不会看到报告器。它扔;它周围的东西决定它是否存在 失败是一个事件，用正在进行的工作和手的标识符来描述它 它变成了内核 `ErrorReporter`这个包实现的唯一端口。

## 作文根

```ts
// platform/observability/errors.ts: imported second, right after platform/observability/tracing.ts
import { reportProcessFailures } from '@ddd-ts-kit/kernel';
import { initSentry } from '@ddd-ts-kit/sentry';
import { loadConfig } from '@/platform/config/load-config';

// { dsn?, environment, release?, scrubMessages } of the validated configuration.
// Without a DSN nothing starts and nothing is reported.
export const errors = initSentry(loadConfig(process.env).errorReporting);

reportProcessFailures(errors); // uncaughtException and unhandledRejection: reported, then exit 1
```

```ts
// main.ts
import { tracing } from '@/platform/observability/tracing'; // first
import { errors } from '@/platform/observability/errors'; // second
import { DeadLetterReports, ExecutionScope } from '@ddd-ts-kit/kernel';
import { kernelModule } from '@ddd-ts-kit/di';
import { createApplication } from '@ddd-ts-kit/di/tsyringe';
import { reportHttpErrors } from '@ddd-ts-kit/http';
import { RabbitMqEventBus } from '@ddd-ts-kit/rabbitmq';

const deadLetters = new DeadLetterReports(errors);
const bus = new RabbitMqEventBus({ url, onFailure: (failure) => deadLetters.onFailure(failure) });

const app = createApplication({
  modules: [
    kernelModule({
      errorReporter: errors, // buses and jobs report through it, app.stop() flushes it
      eventBus: bus,
      eventSubscribers: { forContext: (name) => deadLetters.observing(bus.forConsumer(name)) },
    }),
    ...contexts,
  ],
});

const http = { onError: reportHttpErrors(errors), executionScope: app.resolve(ExecutionScope) };

process.on('SIGTERM', () => void app.stop().then(() => tracing.shutdown()));
```

它有三行字： `initSentry`， `errorReporter: errors`， `onError: reportHttpErrors(errors)`. 流程处理程序和死信各占一行，供需要它们的用户使用。

`init --errors sentry` 在它的文件中精确地写入这些片段： `platform/observability/errors.ts` 作为 以上, `SENTRY_DSN`， `SENTRY_ENVIRONMENT` (默认值: `NODE_ENV`)， `SENTRY_RELEASE` 和 `SENTRY_SCRUB_MESSAGES` 读到 `platform/config/load-config.ts`； `errors` 交的 `main.ts` 来 `composeApplication`，它的值是 `kernelModule({ errorReporter })`； `reportHttpErrors` 在 `platform/auth/authentication/http-boundary.ts`； `DeadLetterReports` 在 `platform/events/create-event-provider.ts` （经纪人）进 `platform/events/start-outbox-relay.ts` （被宣布死亡的出版物）。

**加载顺序。** `platform/observability/tracing.ts` 保留第一个导入：OpenTelemetry补丁 `http` 和 驱动程序加载时，所以它必须在它们之前运行。 `initSentry` 没有补丁，所以 它的位置对于跟踪没有改变，无论两个顺序中哪个先开始（两个顺序都是） 测试)。它是第二个，所以当应用程序的其余部分加载时已经出现了故障 报道了 `reportProcessFailures`.

## 报道了什么

只是没人计划的事。一个 `DomainError` 是生意上的拒绝，是回答，而不是事件。

| 捕捉点 | 附加的 | 报告 | 不报告 |
| --- | --- | --- | --- |
| 命令和查询总线 | `kernelModule` (默认的公共汽车运载 `errorReportingMiddleware`） | 命令或查询失败，但没有 `DomainError` | 一个 `DomainError`;事件被消耗时的任何东西 |
| HTTP | `onError: reportHttpErrors(errors)` | 每个请求都得到了5xx， a `DomainError` 映射到一个5xx包含 | 4 xx |
| 安排工作 | `kernelModule` (默认调度器观察 `reportFailedRuns`） | 赛跑的结果是 `failed`，包括超时 | 一个 `DomainError`跳过或错过的事件 |
| 集成事件 | `DeadLetterReports` 在 `onFailure` 提供者的钩子 | 事件放弃：重试已用尽，有毒消息，发件箱中继的发布被宣布为死亡 | 传递失败并重新尝试 |
| 过程 | `reportProcessFailures(errors)` | `uncaughtException` 和 `unhandledRejection`,因为 `fatal` |  |

**一次失败，一次报告。** 看到错误对象的第一个捕获点报告它和 标志着它;它越过的边界后来承认它，也被称为 `cause` 一个错误 就这样吧。因此，在请求中失败的命令将由总线报告一次， 在命令的名称下，回答500的路由保持沉默。报告仍然 说明了它的来源： `origin` 是执行上下文的起源(`http`， `schedule`)， 关联id和跟踪会指向请求。

![One failure, one report: a request reaches the route, which sends a command on the bus; the handler throws an unexpected error; errorReportingMiddleware of the bus reports it under the name of the command and marks it, then lets it go on; the route answers 500 and reportHttpErrors recognises the marked error and stays silent; the tracker receives one event, with origin http, the correlation id and the trace id](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/sentry-one-failure-one-report.svg)

**事件。** 消费者被重新尝试：它的第一次失败是预料之中的，报告它们也是预料之中的 掩盖真实的事件。因此，总线中间件在使用事件时什么也不说， 和 `DeadLetterReports` 一旦其提供者放弃该事件，就报告该事件。鱼钩 提供者只说代码，所以 `observing(subscriber)` 记住每个处理程序抛出了什么： 报告携带真正的错误及其堆栈。没有它，或者是一个有毒的信息，报告 是一个 `DeadLetterError` 命名代码。事件隐藏在 `DomainError` 被报道为 `warning`它还是输给了它的消费者。

![An event whose handler keeps failing: at each attempt observing remembers what the handler threw and the provider calls onFailure; while the provider retries, onFailure reports nothing; at the last attempt the provider gives the event up, onFailure hears dead and reports once, with the error the handler really threw, origin event and the operation named after the event type and the consumer](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/sentry-event-given-up.svg)

同样的步骤，用手对内核的双子棋，作为对线路的测试：

```ts
import {
  DeadLetterReports,
  InMemoryErrorReporter,
  type IntegrationEvent,
  type IntegrationEventCallback,
  type IntegrationEventSubscriber,
} from '@ddd-ts-kit/kernel';

// A provider reduced to what matters here: it keeps the handler it is given.
const handlers = new Map<string, IntegrationEventCallback>();
const provider: IntegrationEventSubscriber = {
  subscribe: (type, handler) => {
    handlers.set(type, handler);

    return { unsubscribe: () => handlers.delete(type) };
  },
};

const reporter = new InMemoryErrorReporter();
const deadLetters = new DeadLetterReports(reporter);

deadLetters.observing(provider).subscribe('ordering.order.placed', async () => {
  throw new Error('ledger unavailable');
});

const event: IntegrationEvent = {
  id: 'evt-1',
  type: 'ordering.order.placed',
  version: 1,
  occurredAt: '2026-03-01T10:00:00.000Z',
  payload: {},
};
const deliver = handlers.get('ordering.order.placed');

// What the provider says of each failed delivery: codes and identifiers only.
const failure = { kind: 'handler', code: 'Error', eventId: 'evt-1', eventType: 'ordering.order.placed', consumer: 'billing' };

await deliver?.(event).catch(() => undefined); // attempt 1 fails: observing remembers the error
deadLetters.onFailure({ ...failure, attempt: 1 }); // retried: no report

await deliver?.(event).catch(() => undefined); // attempt 2, the last one, fails
deadLetters.onFailure({ ...failure, attempt: 2, dead: true }); // given up: one report

// One report: the Error('ledger unavailable') itself, origin 'event', operation 'ordering.order.placed billing'.
export const reports = reporter.reports;
```

**在处理程序中。** 没有什么要写的：处理程序抛出，它周围的捕获点报告。

```ts
import { DomainRefusal, type ErrorReporter } from '@ddd-ts-kit/kernel';

interface PaymentGateway {
  capture(paymentId: string): Promise<'captured' | 'declined'>;
}

interface CapturePaymentCommand {
  readonly paymentId: string;
}

// payments.codes.ts
export const PaymentsCodes = {
  PaymentDeclined: 'PAYMENT_DECLINED',
} as const;

// domain/errors/payment-declined.error.ts
export class PaymentDeclined extends DomainRefusal {
  readonly code = PaymentsCodes.PaymentDeclined;

  static forPayment(paymentId: string) {
    return PaymentDeclined.refusal(`Payment ${paymentId} was declined.`, { paymentId });
  }
}

// Instead of: a handler that reports by itself, and hides the failure from its caller.
export class CapturePaymentReportingItself {
  constructor(
    private readonly gateway: PaymentGateway,
    private readonly errors: ErrorReporter,
  ) {}

  async execute(command: CapturePaymentCommand): Promise<void> {
    try {
      await this.gateway.capture(command.paymentId);
    } catch (error) {
      this.errors.report(error, { origin: 'command', operation: 'CapturePayment', severity: 'error' });
    }
  }
}

// Write: a handler that throws. A refusal is a DomainError, answered and never reported;
// anything else goes up to the bus, which reports it once, with the actor, the tenant and the trace.
export class CapturePaymentHandler {
  constructor(private readonly gateway: PaymentGateway) {}

  async execute(command: CapturePaymentCommand): Promise<void> {
    if ((await this.gateway.capture(command.paymentId)) === 'declined') {
      throw PaymentDeclined.forPayment(command.paymentId);
    }
  }
}
```

## 与OpenTelemetry

`@ddd-ts-kit/tracing` 拥有跟踪程序提供程序、上下文管理器、传播程序和 auto-instrumentations。 `initSentry` 让他们独自一人：

-   `enableOpenTelemetrySetup: false`： SDK不注册任何跟踪程序提供程序和传播程序；
-   没有SDK的默认集成，并且 `enableRuntimeChannelInjection: false`没有第二 仪器的 `http` 或指司机；
-   否 `tracesSampleRate` 除非给定：否则不会将性能数据发送到跟踪器。

将两者联系起来的是：当报告失败时，报告程序获取跟踪id和 活动OpenTelemetry span的span id，并将它们放入 `contexts.trace` 事件的。 从Sentry或GlitchTip中的一个问题，跟踪id在跟踪后端打开跟踪。这个 跨度本身被导出一次，由工具包的出口商。

## 隐私

|  | 默认的 | 选择在 |
| --- | --- | --- |
| 请求 | 从来没有收集。如果其他东西添加了一个：方法、不带查询字符串的URL、既不携带凭据也不携带地址的标头。没有尸体，就没有饼干 | 一个项目 `beforeSend` |
| 用户 | 演员id，没有其他信息：没有电子邮件，没有姓名，没有地址 |  |
| 租户 | 不发送 | `tagTenant: true` |
| 局部变量 | 从来没有 |  |
| 面包屑 | 无自动记录：无话务台，无呼出 | `Sentry.addBreadcrumb` 项目决定的地方 |
| 异常消息 | 发送后，失败语句引用的值被编校 | `scrubMessages: true` 为每个非 `DomainError` |
| 为失败语句赋值引号 | 永远不要在消息中说明错误的原因或集成复制的内容(`extra`（上下文）：语句保留，包括占位符 |  |

**消息。** 消息是错误跟踪器中首先读取的内容，也是第一个读取的内容 个人数据泄露的地方：司机引用了他拒绝的那一行。默认保留它，因为 问题标题 `[message removed]` 浪费了所有的调查，因为追踪者是个 项目控制其访问和保留的内部工具。与 `scrubMessages: true` 只有 这个项目自己写的信息保存了下来，那些是它的 `DomainError`年代;输入，编码，堆栈 并为其他人留下痕迹。当跟踪器由第三方托管或 当数据足够敏感，操作人员不应该读取它时。的记录器 工具包(`StdoutLogger`， `@ddd-ts-kit/pino`， `@ddd-ts-kit/winston`)选择同一项下的选项 名字，同样的规则，同样的标记， `SCRUBBED_MESSAGE` 核的。

**失败语句的值。** 无论 `scrubMessages` 说, `scrubEvent` 替换的 `[redacted]` 驱动程序引用每个值，并保留语句，这是信号： Drizzle写后 `params:` 在一个 `DrizzleQueryError`;被侵犯的唯一的键 或外键约束(`Key (email)=([redacted]) already exists.`，在短信或邮件中 `detail`）; 行不为空或检查约束被拒绝(`Failing row contains ([redacted]).`）; PostgreSQL无法读取的值(`invalid input syntax for type integer: "[redacted]"`）; Prisma对它拒绝的请求所提出的论据；一个名为 `parameters`， `params` 或者 `bindings` 这是对误差的积分。了解一下PostgreSQL的引号 `$1`， TypeORM是服务器的消息，其表单如上面所示。

`beforeSend` 在擦除的事件上最后运行，并可能通过回答而丢弃它 `null`.

DSN留在SDK客户端中。一个 `SentryErrorReporter` 这是记录，传播或 连载显示 `SentryErrorReporter [client concealed]`;SDK的客户端，如果您登录 它自己显示了它的选项。

## 分组和标记

指纹从不包含消息，因此消息中的标识符不会解决一个问题 分成成千上万个问题 `DomainError` 是它的类和代码；任何其他错误都是 类、它的代码（如果有的话）以及它的第一帧的函数和文件（没有行） 数字。

标签: `origin`， `operation`， `correlation_id`， `bounded_context`， `error_code`, `tenant_id` 在请求。项目添加到的字段 `ErrorReportExtensions` 作为 `report` 上下文。

## 选项

| 选项 | 默认的 |  |
| --- | --- | --- |
| `dsn` | 无 | 没有它 `initSentry` 回答一个 `NoopErrorReporter` 什么都没有开始。 `SENTRY_DSN` 是不是读取后面的配置 |
| `environment`， `release` | 无 |  |
| `enabled` | `true` | `false` 什么也没报告，不管深空网络是什么 |
| `sampleRate` | 1 | 发送的错误事件的共享 |
| `tracesSampleRate` | 无 | 仅适用于通过Sentry API启动的跨度；套件的跨度从来没有到过那里 |
| `scrubMessages` | `false` | 看到“隐私” |
| `tagTenant` | `false` |  |
| `beforeSend` | 无 | 在试剂盒洗涤器之后运行 |
| `integrations` | 无 | 添加到链接错误和源上下文行 |

`flush(timeoutMs)` 答案 `true` 当没有东西可以送的时候。 `app.stop()` 用2来调用 秒; `reportProcessFailures` 退出前做。

## 开发小提示

GlitchTip 6作为PostgreSQL（14或更高版本）旁边的一个服务运行，并且由其推荐 文档,Valkey。开发映像引导用户、组织和项目 并打印DSN：

```yaml
services:
  glitchtip:
    image: glitchtip/glitchtip:6.2.6
    profiles: [errors]
    depends_on:
      glitchtip-postgres: { condition: service_healthy }
      glitchtip-valkey: { condition: service_started }
    ports: ['127.0.0.1:8000:8000']
    environment:
      SERVER_ROLE: all_in_one # web and worker in one process
      DATABASE_URL: postgres://postgres:postgres@glitchtip-postgres:5432/glitchtip
      VALKEY_URL: redis://glitchtip-valkey:6379
      SECRET_KEY: local-development-only-not-a-secret # never in production: openssl rand -hex 32
      GLITCHTIP_DOMAIN: http://localhost:8000
      EMAIL_URL: consolemail://
      DEFAULT_FROM_EMAIL: glitchtip@localhost
      DEBUG: 'true' # development only: required by the bootstrap below
      GLITCHTIP_BOOTSTRAP_DEV: 'true' # user test@example.com / admin_pass, organization, project, DSN
    healthcheck:
      test: [CMD, python, healthcheck.py]
      interval: 10s
      timeout: 6s
      retries: 6
      start_period: 180s # the first start migrates the database
  glitchtip-postgres:
    image: postgres:18-alpine
    profiles: [errors]
    environment: { POSTGRES_DB: glitchtip, POSTGRES_PASSWORD: postgres }
    volumes: ['glitchtip-postgres:/var/lib/postgresql']
    healthcheck:
      test: [CMD-SHELL, pg_isready -U postgres -d glitchtip]
      interval: 5s
      retries: 12
  glitchtip-valkey:
    image: valkey/valkey:9-alpine
    profiles: [errors]

volumes:
  glitchtip-postgres:
```

```
docker compose --profile errors up -d --wait
docker compose logs glitchtip | grep 'DSN:'
```

把DSN放进去 `SENTRY_DSN`;从堆栈的另一个容器中替换 `localhost` 与 `glitchtip`． 使用上述用户登录http://localhost:8000。

## 在测试中

这个包没有任何内容：测试应用程序绑定 `InMemoryErrorReporter`, `app.errors.reports` 列出总线和作业报告的内容，作为数据：每个条目是 `{ error, report }`，抛出时的错误和 `ErrorReport` 就是这样。

## 常见问题

### @ddd-ts-kit/sentry 是什么？

向内核ErrorReporter端口后面的Sentry或GlitchTip报告错误：只有意外的失败，与跟踪相关，在发送之前清除。

### 如何安装 @ddd-ts-kit/sentry？

在项目目录运行 npm install @ddd-ts-kit/sentry，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@sentry/node（^11\.6\.0）、@opentelemetry/api（^1\.9\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @ddd-ts-kit/sentry 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @ddd-ts-kit/sentry 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @ddd-ts-kit/sentry 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.12\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @ddd-ts-kit/sentry 可以商用吗？

@ddd-ts-kit/sentry 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @ddd-ts-kit/sentry 还在维护吗？

最新版本 0\.106\.0 发布于 2026/11/01（2 天前）；至今共发布 63 个版本，其中过去 12 个月发布了 63 个；首次发布于 2026/10/11。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

