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

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

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

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

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

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

- 包体积（解压后）：69.1 kB，40 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.12\.0
- 运行依赖：1 个
- peer 依赖：winston ^3\.19\.0
- 首次发布：2026-10-11T09:42:17.872Z
- 最新版本发布：2026-10-31T21:45:03.185Z
- 已发布版本数：63（近 12 个月 63 个）

## 中文简介

温斯顿内核记录器端口的适配器，每行都有跟踪和执行关联。

## 安装

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

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

## README

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

# @ddd-ts-kit /温斯顿

内核 `Logger` 港口 [温斯顿](https://github.com/winstonjs/winston)的JSON行 标准输出，每行正在进行的工作的相关字段，凭据 已编辑，关闭的冲洗。中没有 `domain/` 或者 `application/` 进口这 包，或记录任何东西：组合根绑定适配器和基础设施， 表示和方面通过端口编写。

```
npm i @ddd-ts-kit/winston winston
```

对温斯顿3.19写的。

域和处理程序不记录任何内容：它们必须记录的是事件或错误。这个 适配器的 `infrastructure/`的控制者 `presentation/` 这些方面(`logged`， `loggingMiddleware`（作业调度器）接收内核 `Logger`，和组成根 结合 `WinstonLogger` 一次。记录日志的代码与 `@ddd-ts-kit/pino` 或者 `StdoutLogger`:看 [从适配器进行日志记录](https://npmcn.com/package/%40ddd-ts-kit/pino/README.md#logging-from-an-adapter).

## 作文根

```ts
// platform/observability/create-logger.ts
import type { ExecutionScope, LogLevel } from '@ddd-ts-kit/kernel';
import { traceLogFields } from '@ddd-ts-kit/tracing';
import { createWinstonLogger, type WinstonLogger } from '@ddd-ts-kit/winston';

export interface LogSettings {
  readonly level: LogLevel; // LOG_LEVEL
  readonly format: 'json' | 'pretty'; // LOG_FORMAT
  readonly service: string;
  readonly environment: string;
  readonly scrubMessages: boolean; // LOG_SCRUB_MESSAGES
}

export function createLogger(scope: ExecutionScope, settings: LogSettings): WinstonLogger {
  return createWinstonLogger({ ...settings, fields: () => traceLogFields(scope) });
}
```

```ts
// main.ts
import { tracing } from '@/platform/observability/tracing'; // first
import { createLogger } from '@/platform/observability/create-logger';
import { AsyncLocalStorageExecutionScope } from '@ddd-ts-kit/kernel';
import { kernelModule } from '@ddd-ts-kit/di';
import { createApplication } from '@ddd-ts-kit/di/tsyringe';

const scope = new AsyncLocalStorageExecutionScope();
const logger = createLogger(scope, config.log);

const app = createApplication({
  modules: [kernelModule({ executionScope: scope, logger }), ...contexts],
});

process.once('SIGTERM', () => void shutdown());

async function shutdown(): Promise<void> {
  await app.stop(); // flushes the error reporter, then the logger, last: that ends the winston logger
  await tracing.shutdown();
}
```

`init --logger winston` 写这个 `create-logger.ts` (它回答了。 `NoopLogger` 当给出no时 设置（这是工具和测试保持沉默的方式）； `platform/composition/lay-foundations.ts` 调用一次，然后 `composeApplication` 将记录器交给 `kernelModule({ logger })`.

应用程序的一行，如下所示：

```json
{"actorId":"user-1","context":"shipping","correlationId":"9f2c","environment":"production","level":"info","message":"order shipped","origin":"http","service":"shop-api","span_id":"00f067aa0ba902b7","tenantId":"acme","time":"2026-03-01T10:00:00.000Z","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736"}
```

![The path of one call to logger.info through winston: below the threshold the adapter stops after one comparison; otherwise it describes the errors among the fields and keeps level and message out of them; winston merges the fields of the line, the fields of the children and its defaultMeta; then the formats run in order: correlationFields fills the keys nobody gave from fields(), redactCredentials hides the credential keys, isoTime writes time, json or the pretty line serializes; each transport writes the line: the console by default, or the files, syslog or stream you give](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/winston-path-of-a-line.svg)

**相关性。** `fields` 按温斯顿格式在每一行读取：no call 站点传递一个id。 `traceLogFields(scope)` 回答跨度内的跟踪和跨度id，以及 在执行范围内，关联id、因果id、起源、参与者和 租户。这个包不依赖于跟踪包：any `() => LogFields` 合适, `() => logFieldsOf(scope.current())` 包括在内。抛出的函数不给出字段，永远不会 失去了线。这条线的田地赢得了孩子们的田地，孩子们赢得了 `fields`: 格式只能填满没人给的钥匙。 `StdoutLogger` 和 `@ddd-ts-kit/pino` 意见不同 第二步（这里） `fields` 赢得一个孩子)，所以不要在孩子身上绑定一个键 `fields` 回答日志记录器是否可能被交换。

![Which value a key gets when it is given twice, from the weakest to the strongest: with pino and StdoutLogger, service, environment and base, then the children, then fields(), then the line, which wins; with winston, fields(), then the children, then the line, then service, environment and base, which win](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/pino-which-value-wins.svg)

**一个你自己的例子。** 已经配置了winston的项目会继续这样做 接受它想要的格式：

```ts
import { CREDENTIAL_KEYS, type ExecutionScope } from '@ddd-ts-kit/kernel';
import { traceLogFields } from '@ddd-ts-kit/tracing';
import winston from 'winston';
import { WinstonLogger, correlationFields, redactCredentials } from '@ddd-ts-kit/winston';

declare const scope: ExecutionScope; // the one the composition root creates

const instance = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    correlationFields(() => traceLogFields(scope)),
    redactCredentials([...CREDENTIAL_KEYS, 'iban']), // after the formats that add fields
    winston.format.timestamp(),
    winston.format.json(),
  ),
  transports: [new winston.transports.Console()],
});

export const logger = new WinstonLogger(instance);
```

`createWinstonLogger` 写下下面的瞬间 `time`，通过其名称和消息获取级别 下 `message`，比如 `StdoutLogger` 方面的包和喜欢 `@ddd-ts-kit/pino`： 一个仪表板上写着3。一行的键被排序出来：winston的JSON格式 使用稳定的stringifier进行序列化。

**你自己的交通工具** 不需要自己配置温斯顿： `transports` 取代了 控制台，所以当行仍然必须到达标准输出时也命名它。

```ts
import { createWinstonLogger } from '@ddd-ts-kit/winston';
import winston from 'winston';

export const logger = createWinstonLogger({
  service: 'shop-api',
  transports: [
    new winston.transports.Console(), // the standard output, as by default
    new winston.transports.File({ filename: 'logs/errors.log', level: 'error' }), // errors only, kept on disk
  ],
});
```

每个传输都有自己的过滤器 `level` 格式运行后：上面的文件接收到 `error` 行，控制台的每一行从日志记录器的阈值开始。

## 发展模式

```ts
createWinstonLogger({ level: 'debug', format: 'pretty', fields: () => traceLogFields(scope) });
```

```
10:00:00.000 info order shipped { context: 'shipping', service: 'shop-api', correlationId: '9f2c' }
```

这个 `pretty` 格式是由winston自己的组合子(`colorize`， `printf`)，在… 处理：没有其他的包，当地的时间，在颜色的水平，领域作为节点 在几行上打印一个对象，一个堆栈。从配置中选择格式 （`LOG_FORMAT=pretty` 在 `.env`， `json` 其他地方)。

## 隐私

日志线是个人数据泄露的地方：它们被写在任何地方，每个人都可以阅读并保存 太久。在适配器中完成了三件事，因此调用站点不必记住它们。

**凭据已编辑。** 通过温斯顿格式，在行，子和的字段中 `fields` 一样的。这些键的值被替换为 `[redacted]`，在一行的顶端 下一层(`headers.authorization`， `body.password`):

`password`， `passwd`， `authorization`， `cookie`， `set-cookie`， `token`， `secret`， `apiKey`， `api_key`， `x-api-key`， `access_token`， `refresh_token`， `id_token`， `accessToken`， `refreshToken`， `idToken`， `client_secret`， `clientSecret`， `client-secret`， `private_key`， `privateKey`

`redact: ['iban', 'ssn']` 添加到列表中。无论名字是什么情况，都是匹配的 （`Authorization`， `X-Api-Key`),如 `isCredentialKey` 果仁做了，却什么也没看 对于一个以上的层次，也不是在一个列表中：记录你想要的部分，而不是整个请求。这个 您传递的对象永远不会写入：当必须隐藏密钥时，将复制嵌套对象。

**作为字段传递的错误** （`logger.error('payment failed', { error })`: `LogValue` 的 该港口允许船舶驶入。 `Error`)写为 `describeError` 内核的描述，是一样的 从端口的每个记录器中：它的类为 `type`,它的 `code` 当 这是一个 `DomainError`,它的 `message`，它自己 `stack`，那么…… `cause` 同样的方法，直到 八个深。链的每个错误都写一次。的字段中查找错误 行，不在对象内嵌套。 `CREDENTIAL_KEYS`， `REDACTED` 和 `SCRUBBED_MESSAGE` 是 内核的也一样。

**意外错误的消息可以被丢弃。** 错误的消息是第一位的 个人数据出现的地方：司机引用了他拒绝的那一行。默认保留它，因为 一条日志线上面写着 `[message removed]` 花费了所有的调查。只有一个选择 这个项目自己写的信息保存了下来，那些是它的 `DomainError`年代;类、代码、堆栈和 Cause为其他的保留，堆栈失去它的第一行，这重复了这个消息：

```ts
createWinstonLogger({ scrubMessages: true }); // or new WinstonLogger(instance, { scrubMessages: true })
```

这是 `scrubMessages` 端口的每个记录器(`StdoutLogger`皮诺，温斯顿， `InMemoryLogger`)和的一个 `@ddd-ts-kit/sentry`，用同样的规则，同样的 `SCRUBBED_MESSAGE` 同样的权衡：当日志离开你的手或没有时打开它 操作符应该读取那里的数据。日志线本身的信息是你的： 写 `order shipped` 并将标识符放在字段中。

该工具包本身从不向记录器传递错误 `logged` 方面, `loggingMiddleware` 和 作业调度器写入 `errorType` 和 `errorCode` (`identifyError` （内核），永远不会 一个信息，随便什么 `scrubMessages` 说，错误会出现在 `ErrorReporter`。这个 上面的描述是针对您自己的代码选择记录的错误： `logger.error('payment failed', { error })`.

## 水平

| 港口 | 温斯顿(`npm` 水平) |  |
| --- | --- | --- |
| `debug` | `debug` | 在它下面，温斯顿也有 `silly` |
| `info` | `info` | 默认阈值； `http` 和 `verbose` 站之间 `info` 和 `debug` |
| `warn` | `warn` |  |
| `error` | `error` |  |

名字是一样的 `cli` 的水平。的实例。 `syslog` 水平没有 `warn`： 说出它去了哪里，否则这行就被删掉了，因为温斯顿的写作水平和它不一样 不知道。

```ts
import { WinstonLogger } from '@ddd-ts-kit/winston';
import winston from 'winston';

const instance = winston.createLogger({
  levels: winston.config.syslog.levels, // emerg, alert, crit, error, warning, notice, info, debug
  transports: [new winston.transports.Console()],
});

export const logger = new WinstonLogger(instance, { levels: { warn: 'warning' } }); // logger.warn() writes at 'warning'
```

低于阈值的一行需要进行一次比较：适配器请求 `isLevelEnabled` 首先, 因为温斯顿一个人就能在传送机下线前运行所有格式。这个 运行时更改实例的阈值(`instance.level = 'debug'`)后面跟着the 适配器和它的孩子。 `isLevelEnabled(level)` 的端口回答了同样的问题 映射的关卡：在建造需要花费的领域之前询问它。一旦记录器被刷新 答案 `false` 对于每个关卡。

`child(fields)` 是温斯顿的孩子吗？田野被捆绑一次，温斯顿就把它们合并在一起 每行的字段。当温斯顿被直接召唤时，有三件事与他不同 目的：一个名为 `level` 或者 `message` 永远不要替换级别或消息的 行;消息永远不会作为格式字符串(`%s`）; 和 `createWinstonLogger` 一个 字段命名 `time` 被该行的瞬间覆盖。有一件事是温斯顿的： `service`， `environment` 和 `base` 是它的 `defaultMeta`，赢得了一场比赛 相同的名字在一条线上或在一个孩子，其中其他两个记录器的工具包让线和 孩子赢了。

## 冲洗

`flush(): Promise<void>` 结束Winston记录器，并在其每个传输完成后进行解析 写的是递给他们的 `finish` 事件)。它从不拒绝，第二个电话会接 同样的承诺。 `app.stop()` 最后在模块停止和错误报告器之后调用它 已被刷新：也在停止日志。没有应用di包，您可以在 那个地方。

| 后 `flush()` 解决 | `@ddd-ts-kit/winston` | `@ddd-ts-kit/pino` |
| --- | --- | --- |
| 记录一行 | 删除，没有错误 | 写好了，像以前一样 |
| `isLevelEnabled(level)` | `false` 对于每个关卡 | 门槛，和以前一样 |
| 第二个 `flush()` | 同样的承诺 | 新面孔 |

端口在关闭时调用一次刷新，并且不指定之后记录的内容。 这里的刷新是最终的：温斯顿记录器不会重新打开，所以之后记录的内容， 通过这个记录器或它的子记录器之一，在没有错误的情况下删除 `isLevelEnabled` 答案 `false`． 关于关机的任何必须记录的内容都在之前记录了 `app.stop()` 的回报。在你自己开的小溪上运输 (一个套接字，一个文件流给 `Stream` 运输部已经把自己的生产线交给了这条小溪 当 `flush()` 解析：结束该流是你的。

## 表演

winston通过Node流、格式链及其传输写入每一行 而在调用过程中：大约7微秒一行在哪里 `@ddd-ts-kit/pino` 需要3 （节点24，一个子节点，四个相关字段，三个字段，一个丢弃的接收器）等等 在这里，每行需要一个字段的副本，每个级别需要一个合并 子，格式阅读 `fields`，编校传递顶级值，即时， 和 `JSON.stringify` 通过安全序列化器。对于每行几百行，这算不了什么 第二，可以看到数以万计：在那里，降低水平或采取 `@ddd-ts-kit/pino`.

适配器不会缓冲任何东西，因此在传输之外的崩溃中不会丢失任何东西 它自己没有写。日志调用永远不会抛出无法读取或序列化的字段(a Getter（抛出）给出没有它们的行，标记 `logError: "unserializable fields"`， 和 `createWinstonLogger` 将其格式保存为流内相同的规则。

## 哪一个

采取 `StdoutLogger` 标准输出中的JSON行都是您的 需要：不依赖，线条形状相同。采取 `@ddd-ts-kit/pino` 当日志量很重要时 或者当框架已经说pino （fasttify）时。球队已经跑了，带上温斯顿 它的传输和格式（文件与旋转，syslog，一个供应商传输），并希望保持 他们。这三个实现一个端口，并由一个合约套件持有：交换是其中的一行 组成根。

## NestJS

Nest也有自己的 `LoggerService`，由框架用于其自己的消息，并由 `new Logger(context)` 在Nest类中。该工具包的应用程序代码从不需要它：处理程序和 适配器接收内核 `Logger`． 要通过同一个实例发送Nest自身记录的内容， 给它一座几行字的桥；这个工具包没有发货，因为它会将这个包绑定到Nest；

```ts
const nest = logger.child({ context: 'nest' });
const bridge: LoggerService = {
  log: (message: unknown) => nest.info(String(message)),
  warn: (message: unknown) => nest.warn(String(message)),
  error: (message: unknown) => nest.error(String(message)),
  debug: (message: unknown) => nest.debug(String(message)),
};

const app = await NestFactory.create(AppModule, { logger: bridge });
```

`nest-winston` 当Nest类记录大量日志时，另一种选择是：给它一个winston实例 构建并封装同一个实例 `new WinstonLogger(instance)` 为了工具箱。

## 在测试中

这个包没有任何内容：测试应用程序绑定 `InMemoryLogger` 核函数的 `entries` 是数据。要使端口的另一个适配器具有相同的行为，请运行合约：

```ts
import { Writable } from 'node:stream';
import { beforeEach, describe, expect, it } from 'vitest';
import { LOG_LEVELS } from '@ddd-ts-kit/kernel';
import { loggerContract, type LoggedLine } from '@ddd-ts-kit/testing/contracts';
import { createWinstonLogger } from '@ddd-ts-kit/winston';
import winston from 'winston';

loggerContract({ describe, it, expect, beforeEach }, {
  name: 'WinstonLogger',
  createLogger: (setup) => { // { level, fields, scrubMessages }
    const written: string[] = [];
    const stream = new Writable({
      write: (chunk, _encoding, done) => {
        written.push(String(chunk));
        done();
      },
    });
    const logger = createWinstonLogger({ ...setup, transports: [new winston.transports.Stream({ stream })] });

    return { logger, lines: () => written.map((line) => lineOf(JSON.parse(line))) };
  },
});

/** A written line as the contract reads it: the level by its name, the message, the other fields. */
function lineOf({ level, message, time: _time, ...fields }: Record<string, unknown>): LoggedLine {
  const known = LOG_LEVELS.find((one) => one === level);

  if (known === undefined) {
    throw new Error(`Not a level of the port: ${String(level)}`);
  }

  return { level: known, message: String(message), fields };
}
```

## 常见问题

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

温斯顿内核记录器端口的适配器，每行都有跟踪和执行关联。

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

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

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

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

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

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

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

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

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

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

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

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

