npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jintianxiayu/logger

v1.1.0

Published

A Winston-based named logger factory with YAML configuration, masking and async trace context.

Readme

@jintianxiayu/logger

@jintianxiayu/logger 是一个面向 Node.js 应用的 Winston 日志封装。它提供命名日志器、YAML/对象配置、 Console 与轮转文件输出、结构化脱敏、异步 traceId 上下文,以及可等待日志刷新的显式关闭流程。

目录

要求与安装

  • Node.js 20 或更高版本
  • CommonJS 运行时;包内包含 TypeScript 类型声明
pnpm add @jintianxiayu/logger

也可以使用 npm install @jintianxiayu/logger。本仓库自身使用 pnpm 11。

快速开始

下面的示例显式关闭进程错误接管,适合由应用或框架统一处理未捕获异常的场景:

import { LoggerContext, LoggerFactory } from '@jintianxiayu/logger';

async function main(): Promise<void> {
    LoggerFactory.init({
        root: {
            console: {
                enabled: true,
                format: 'json',
            },
        },
        processErrors: {
            uncaughtException: false,
            unhandledRejection: false,
            exitOnError: false,
        },
    });

    const logger = LoggerFactory.getLogger('orders');

    await LoggerContext.withContext({ traceId: 'order-20260902-001' }, async () => {
        logger.info('Order created', {
            orderId: 'order-001',
            password: 'masked-before-output',
        });
    });

    await LoggerFactory.shutdown();
}

main().catch((error: unknown) => {
    process.stderr.write(`${String(error)}\n`);
    process.exitCode = 1;
});

CommonJS 调用方可以使用同一公共入口:

const { LoggerFactory, LoggerContext } = require('@jintianxiayu/logger');

配置

配置来源与优先级

LoggerFactory.init() 接受配置对象或 YAML 文件路径:

LoggerFactory.init({ root: { level: 'debug' } });
LoggerFactory.init('./config/logger.yaml');

配置来源按以下顺序选择:

  1. 传给 LoggerFactory.init(source) 的对象或路径;
  2. LOGGER_CONFIG_PATH 环境变量指定的 YAML 路径;
  3. 内置默认配置。

相对路径以 process.cwd() 为基准。显式来源无效时会直接抛错,不会回退到环境变量或默认值。 首次成功初始化后,后续 init() 调用保持幂等,不会重新读取或切换配置。未显式调用 init() 时, 第一次 getLogger() 会按相同规则延迟初始化。

多环境级别 Profile

在同一份 logger.yaml 中定义环境差异:

root:
    level: info
profiles:
    dev:
        loggers:
            orders:
                level: debug
    qa:
        loggers:
            orders:
                level: debug
    prod:
        loggers:
            orders:
                level: info

在启动进程前设置 LOGGER_CONFIG_PATH 为该文件路径,并设置 LOGGER_PROFILE=dev、qa 或 prod。 应用照常调用 LoggerFactory.init() 和 LoggerFactory.getLogger('orders'):dev/qa 输出 debug,prod 从 info 开始输出。

配置来源仍按上节选择;选定文档后,LOGGER_PROFILE 选择该文档内的策略,包括显式传给 init() 的对象或路径。 合并顺序为内置 root、基础 root、基础命名 logger、所选 Profile 的命名 logger level。 Profile 中新声明的 logger 继承基础 root 的其他字段,未匹配的 logger 及 root 保持原策略。

  • profiles 可选,类型为 Record<string, LoggerLevelProfile>。每个 Profile 仅允许可选 loggers 映射; 每个覆盖项为 LoggerLevelOverride,仅含必填 level: 'debug' | 'info' | 'warn' | 'error'。
  • 名称区分大小写、非空且不能含首尾空白;logger 名称按完整字符串匹配,支持 @jintianxiayu/cache-decorator,不支持通配符。
  • 不设置 LOGGER_PROFILE 时使用基础配置,不根据 NODE_ENV 自动选择。空字符串、首尾空白、未知 Profile 均导致初始化失败。
  • 空 Profile(如 prod: {})表示沿用基础配置;覆盖项 { orders: {} } 因缺少 level 而非法。
  • 全部 Profile 都会校验,包括未选中的策略。未知字段、重复 YAML 键或非法级别会导致初始化失败;不支持在 Profile 中配置 root、输出或脱敏等其他字段。
  • 配置只在首次成功初始化时固化;修改文件、环境变量或再次调用 init() 不会热更新。首次 getLogger() 的延迟初始化遵循相同规则。

应在任何依赖首次获取 logger 前提供配置;依赖可能在导入时获取 logger,因此建议在进程启动环境中提供上述变量。 先升级所有读取此文件的消费者,再启用 profiles:旧版会拒绝该新字段。回滚旧版时恢复旧格式配置并清除 LOGGER_PROFILE; 仅在新版停用 Profile 时,清除该变量即可恢复基础策略。

配置项说明

以下基础配置项都是可选的(Profile 覆盖项的 level 必填)。root 在内置默认值上合并,loggers.<name> 再深度继承合并后的 root,只覆盖 自己显式声明的字段。

顶层配置

| 配置项 | 类型 | 默认值 | 说明 | | --------------- | ------------------------------------ | ------------------- | ----------------------------------------------------------------- | | root | LoggerOptions | 内置根配置 | 定义所有日志器共享的级别、调用位置和输出方式 | | profiles | Record<string, LoggerLevelProfile> | {} | 通过 LOGGER_PROFILE 选择的命名日志器级别策略 | | loggers | Record<string, LoggerOptions> | {} | 按名称覆盖 root;名称必须非空且不含首尾空白,并且区分大小写 | | masking | SensitiveMaskingConfig | 启用内置策略 | 定义应用于所有日志器和输出通道的元数据脱敏规则 | | processErrors | 配置对象 | 三个开关均为 true | 控制 Winston 对未捕获异常、未处理拒绝和进程退出的处理;字段见下表 |

getLogger() 会移除传入名称的首尾空白,并为同一个规范化名称返回同一实例。例如 database 与 database 返回同一个日志器,而 Database 是另一个名称。

日志器配置

以下配置可用于 root 和任意 loggers.<name>。命名日志器未声明的值继承 root。

| 配置项 | 类型 | root 默认值 | 说明 | | -------------------- | -------------------------------- | ------------- | ---------------------------------------------------------- | | level | debug \| info \| warn \| error | info | 最低输出级别;例如 info 会输出 info、warn 和 error | | captureLogPosition | boolean | true | 是否允许采集调用位置;只有启用的输出实际需要位置时才会采集 | | console | ConsoleConfig | 见下表 | 控制标准输出的启用状态、格式、颜色和模板 | | file | FileConfig | 见下表 | 控制轮转文件输出、目录、文件名和保留策略 |

Console 配置

| 配置项 | 类型 | root 默认值 | 说明 | | ----------------- | --------------- | --------------- | ------------------------------------------------------------ | | console.enabled | boolean | true | 是否启用 Console 输出 | | console.colors | boolean | false | 是否为 Plain 输出的日志级别添加 ANSI 颜色;JSON 输出忽略该项 | | console.format | plain \| json | plain | Console 输出格式;JSON 始终输出无 ANSI 颜色的单行对象 | | console.pattern | string | 内置 Plain 模板 | Plain 输出模板;仅在最终格式为 plain 时校验和使用 |

内置 Plain 模板为:

%{timestamp} %{level} [%{name}] [%{traceId}] %{log_position}: %{message} %{meta}

模板支持的占位符及缺失值行为见输出格式。

File 配置

| 配置项 | 类型 | root 默认值 | 说明 | | ------------------ | ------------------ | --------------- | ----------------------------------------------------------------------- | | file.enabled | boolean | false | 是否启用轮转文件输出 | | file.format | plain \| json | json | 文件内容格式;JSON 为单行对象,Plain 使用 file.pattern | | file.pattern | string | 内置 Plain 模板 | Plain 文件模板;仅在最终格式为 plain 时校验和使用 | | file.dirname | string | ./logs | 日志目录;相对路径以 process.cwd() 为基准解析 | | file.filename | string | app.log | 轮转文件名;未包含 %DATE% 时会在扩展名前自动插入日期占位符 | | file.datePattern | string | YYYY-MM-DD | 传给轮转文件传输的日期格式 | | file.maxSize | number \| string | 10m | 单个文件的大小上限;接受正数字节数或带可选 k、m、g 单位的正数文本 | | file.maxFiles | number \| string | 7d | 保留的文件数或天数;接受正整数、数字文本,或 7d 形式的天数文本 |

例如,默认文件名最终会生成类似 app-2026-09-02.log 的文件。同一物理文件目标不能混用不同输出格式, 也不能声明冲突的轮转配置,否则初始化失败。

脱敏配置

| 配置项 | 类型 | 默认值 | 说明 | | ----------------- | ---------------------- | ------ | -------------------------------------------------------------------------- | | masking.enabled | boolean | true | 是否对元数据递归应用内置和自定义字段策略 | | masking.fields | SensitiveFieldConfig | {} | 字符串新增或覆盖掩码模板;false 取消指定字段的策略;字段匹配不区分大小写 |

SensitiveFieldConfig 的定义为 Record<string, string | false>。

自定义模板支持的语法和内置敏感字段见敏感字段脱敏。

进程错误配置

| 配置项 | 类型 | 默认值 | 说明 | | ---------------------------------- | --------- | ------ | --------------------------------------- | | processErrors.uncaughtException | boolean | true | 是否让根输出处理未捕获异常 | | processErrors.unhandledRejection | boolean | true | 是否让根输出处理未处理的 Promise 拒绝 | | processErrors.exitOnError | boolean | true | 是否由 Winston 在处理进程错误后退出进程 |

启用 uncaughtException 或 unhandledRejection 时,root.console 与 root.file 必须至少启用一个。 如果应用框架自行管理致命错误和退出流程,应显式关闭相应选项。

路径、文件名、日期格式、Plain 模板和脱敏模板等字符串配置不能为空。未知字段、错误类型、无效级别或格式、 错误模板和冲突文件目标都会在初始化阶段被拒绝。

日志与上下文

日志器支持四个级别:debug、info、warn 和 error。

const logger = LoggerFactory.getLogger('database');

logger.debug('Query started', { sqlId: 'query-001' });
logger.info('Query completed', { durationMs: 12 });
logger.warn('Pool utilization is high', { utilization: 0.9 });
logger.error('Query failed', new Error('connection lost'));

message 必须是字符串;运行时收到非字符串消息会抛出 TypeError。一个普通对象元数据参数会直接成为 meta;其他参数组合会放入 meta.args。Error、日期、循环引用和常见非 JSON 值会先转换为稳定的 可序列化结构,元数据处理失败时会降级为安全占位值,不中断主日志调用。

LoggerContext.withContext() 使用异步上下文隔离并继承嵌套值:

await LoggerContext.withContext({ traceId: 'request-001', tenant: 'north' }, async () => {
    logger.info('Handling request');
    const tenant = LoggerContext.get<string>('tenant');
});

只有非空字符串 traceId 会自动进入日志事件;其他上下文字段可通过 LoggerContext.get() 读取,但不会 自动输出。并发异步调用链之间的上下文相互隔离,回调完成、抛错或拒绝后会恢复父级上下文。

输出格式

JSON 输出为单行对象,固定包含 timestamp、level、name 和 message,并按实际数据添加 traceId、logPosition 与 meta。JSON 输出不会包含 ANSI 颜色。

Plain 输出可以通过 pattern 组合以下占位符:

| 占位符 | 输出内容 | 缺失值与特殊行为 | | ----------------- | ---------------------------------------------------- | ------------------------------------------------------- | | %{timestamp} | 日志事件生成时的 ISO 8601 UTC 时间 | 始终存在 | | %{level} | debug、info、warn 或 error | Console 的 Plain 输出启用 colors 时会包含 ANSI 颜色码 | | %{name} | LoggerFactory.getLogger(name) 对应的命名日志器名称 | 进程异常和未处理拒绝使用 root | | %{traceId} | 当前 LoggerContext 中的非空字符串 traceId | 未设置或不是非空字符串时输出 - | | %{log_position} | 发起日志调用的规范化源码位置,格式为 file:line | 未采集到位置时输出 -;不包含列号 | | %{message} | 传给日志方法的字符串消息 | 始终存在;消息中的占位符文本不会再次展开 | | %{meta} | 完成规范化和脱敏后的元数据 JSON | 没有元数据时输出 -;序列化失败时输出安全占位对象 |

占位符名称区分大小写,可以与任意普通文本组合并重复使用。未知占位符或未闭合的 %{...} 会在 初始化阶段触发配置错误。只有 pattern 本身会进行占位符替换,message 和 meta 中的同名文本均按 原始业务数据输出。

缺失的可选值在 Plain 输出中显示为 -。当 captureLogPosition 启用且输出格式需要调用位置时, logPosition 使用 file:line,不包含列号。

调用位置统一使用 / 作为路径分隔符。项目内文件相对于 Logger 首次成功初始化时的工作目录输出; 依赖文件从最后一个 node_modules 目录段之后输出,因此 pnpm 物理存储目录不会进入日志。例如:

@jintianxiayu/http-client-decorator/dist/middlewares/debug.js:17

规范化后的路径部分超过 120 个字符且至少包含六段时,中间目录压缩为 ...,保留前三段和 最后两段,例如 src/modules/order/.../services/handler.js:42。压缩结果允许碰撞,不附加哈希; 包名、目录名和文件名不会被逐段截断。无法识别项目或依赖归属时保留规范化后的原始路径。

Console 和 File 可以分别选择 plain 或 json。文件输出使用日期/大小轮转;同一物理文件目标不能 混用不同格式或冲突的轮转配置。

敏感字段脱敏

脱敏在所有输出通道之前递归执行,字段名匹配不区分大小写,并且不会修改调用方传入的对象。

内置策略包括:

  • password、token、authorization、cookie 等凭证字段完整替换为 ********;
  • phone、creditCard、bankAccount、idNumber 等号码字段只保留末四位;
  • email 在格式有效时保留前两个字符和域名,否则完整遮盖。

自定义模板支持普通文本以及 {firstN}、{lastN}、{domain},其中 N 是正整数。动态模板遇到过短值、 复杂对象或无效邮箱时会使用完整掩码。模板语法在初始化阶段校验。

当业务字段与内置字段同名、但确定不承载敏感信息时,可以显式取消该字段的脱敏:

masking:
    enabled: true
    fields:
        password: false
        account: '***{last4}'

配置对象使用相同语义:

LoggerFactory.init({
    masking: {
        fields: {
            password: false,
            account: '***{last4}',
        },
    },
});

false 必须是布尔值且不能加引号;'false' 是合法的字符串模板,会把匹配值替换为文本 false。 退出规则按完整字段名大小写不敏感地匹配,因此 password: false 同时匹配 Password,但不会连带取消 passwd、pwd 等其他内置别名,也不会按前缀匹配 passwordHash。

字段策略是应用级配置,对所有命名 Logger、Console/File 输出和任意嵌套层级生效。如果退出字段的值是对象, 该对象仍会递归复制并检查内部字段;例如外层 password 退出后,内部 token 仍按内置策略脱敏。

包含字段值 false 的配置需要支持该能力的 Logger 版本。旧版会在初始化阶段将其作为非字符串模板拒绝, 因此部署时应先升级所有读取该配置的 Logger 实例,再启用字段退出;回滚旧版前应先移除这些布尔值。

脱敏只处理元数据字段,不解析 message 文本。不要把令牌、密码或个人信息拼接进日志消息。 显式退出可能使原始值进入全部日志输出;仅应在确认该字段名不承载敏感信息,并评估所有同名字段来源后使用。

进程错误与关闭

默认配置会让 Winston 捕获未处理的异常和 Promise 拒绝,并启用 exitOnError。如果应用框架已有自己的 致命错误处理和退出流程,应在初始化配置中显式关闭相应选项。启用任一进程错误处理器时,根日志器必须 至少启用一个 Console 或 File 输出。

本库不注册 SIGINT 或 SIGTERM 处理器。应用应在自己的生命周期钩子中显式等待关闭:

await LoggerFactory.shutdown({ timeout: 5_000 });
  • 默认超时为 5 秒,timeout 必须是正整数毫秒值;
  • 多次调用共享同一个关闭结果,不会重复结束 Winston 流;
  • 关闭会等待 Winston finish,并等待轮转文件流关闭;日志器、传输或文件流报错以及超时都会拒绝 Promise;
  • 开始关闭后,已有日志器的新写入会被忽略,且不能再初始化或获取日志器;默认工厂在同一进程中不能重新打开。

shutdown() 只能确认本库交给 Winston 传输的日志已完成本地关闭流程,不代表外部日志采集或存储平台已经持久化。

公共 API

包根入口只导出以下运行时符号:

| 符号 | 用途 | | --------------------------------------------- | --------------------------------------------------- | | LoggerFactory.init(source?) | 使用对象、YAML 路径、环境变量或默认值初始化单例工厂 | | LoggerFactory.getLogger(name) | 获取缓存的命名日志器;必要时延迟初始化 | | LoggerFactory.shutdown(options?) | 停止接收新日志并等待当前传输完成关闭 | | LoggerContext.withContext(values, callback) | 在同步或异步回调范围内合并上下文 | | LoggerContext.get(key) | 读取当前异步调用链中的上下文字段 |

LoggerFactory.getLogger() 返回的日志器实现 LoggerInterface:

interface LoggerInterface {
    debug(message: string, ...meta: unknown[]): void;
    info(message: string, ...meta: unknown[]): void;
    warn(message: string, ...meta: unknown[]): void;
    error(message: string, ...meta: unknown[]): void;
}

公开类型还包括 ConsoleConfig、FileConfig、LoggerConfig、LoggerInterface、LoggerOptions、 LoggerLevelOverride、LoggerLevelProfile、LogLevelName、SensitiveFieldConfig、SensitiveMaskingConfig 和 ShutdownOptions。内部加载器、格式器、 传输工厂和生命周期错误类不属于包根公共 API,请勿从 dist/ 子路径导入。

常见问题

配置文件没有生效

确认 LoggerFactory.init() 只在首次成功初始化前调用。已经初始化的工厂不会热加载配置。相对配置路径和 日志目录都以进程启动时的 process.cwd() 为基准。

关闭时超时或报错

shutdown() 会等待控制台/文件传输及轮转文件流。先检查磁盘权限、日志目录、被其他进程占用的文件, 以及传入的超时时间。调用方必须处理关闭 Promise 的拒绝并决定最终退出策略。

日志里仍出现敏感信息

确认敏感值位于元数据对象中,并检查 masking.enabled 是否为 true。消息字符串不会被字段脱敏器解析; 自定义字段名按大小写不敏感规则匹配。

开发

pnpm install
pnpm run typecheck
pnpm run build
pnpm run lint
pnpm test

实现边界、格式与关闭流程的内部说明见 DESIGN.md。

许可证

MIT