MikroORM 日志系统完全指南:从 debug 模式到自定义 Logger 的深度实践
后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载本篇指南以 MikroORM v6.6 官方文档 docs/versioned_docs/version-6.6/logging.md 为骨架结合 packages/core/src/logging 下的源码实现展开。你将学会一行配置开启全量 SQL 日志、按命名空间Namespace精确过滤输出、用ignoreDeprecations管理弃用警告、按查询粒度覆盖调试模式以及通过logger/loggerFactory/loggerContext三件套将 ORM 日志接入自己的日志体系。读完即可在生产与开发环境中对 MikroORM 的每一条查询做到看得见、控得住、接得进。开启 debug 模式一行配置看清所有 SQL对于开发阶段最简单的做法是在初始化时打开debug开关return MikroORM.init({ debug: true, });打开后MikroORM 会默认使用console.log()输出所有执行过的查询。从源码看Configuration.ts 中默认的logger就是console.log.bind(console)因此无需任何额外配置即可在终端看到如下输出[query] select e0.* from author as e0 where e0.name ? limit ? [took 2 ms] [query] begin [took 1 ms] [query] insert into author (name, email, created_at, updated_at, terms_accepted) values (?, ?, ?, ?, ?) [took 2 ms] [query] commit [took 2 ms]每条记录都带[query]命名空间前缀、实际执行的 SQL以及[took X ms]的耗时信息。注意 SQL 中的?占位符默认情况下参数值不会直接内联进语句若想看到参数需要同时开启query-params命名空间见下文。debug 模式对于排查**实体发现entity discovery**问题同样非常有用。开启后初始化阶段会输出每一个被处理实体的信息[discovery] ORM entity discovery started [discovery] - processing entity Author [discovery] - using cached metadata for entity Author [discovery] - processing entity Book [discovery] - processing entity BookTag [discovery] - entity discovery finished after 13 ms这段输出能帮你快速定位实体是否被扫描到、是否命中了元数据缓存、扫描耗时多少是排查实体未被识别 / 重复定义 / 元数据缓存失效等问题的第一手证据。关闭彩色输出MikroORM 的默认日志输出带有颜色命名空间为灰色、label 为青色、错误为红色、警告为黄色见 colors.ts 中的 ANSI 转义实现。若你的日志系统或 CI 环境不支持 ANSI 颜色可以通过配置项colors: false关闭return MikroORM.init({ colors: false, });也可以使用环境变量控制四个变量按 colors.ts 中的优先级逻辑生效环境变量作用MIKRO_ORM_NO_COLOR强制关闭颜色NO_COLOR通用的无颜色约定同样生效MIKRO_ORM_COLORS值为true/t/1时开启颜色FORCE_COLOR值为true/t/1时强制开启颜色从源码看颜色是否启用由enabled()函数决定只要NO_COLOR或MIKRO_ORM_NO_COLOR为真颜色即关闭否则再看FORCE_COLOR与MIKRO_ORM_COLORS这两个为未定义时默认开启的语义。另外当颜色被禁用时Configuration.ts 会自动将 highlighter 降级为NullHighlighter避免高亮逻辑与颜色输出产生冲突。Logger Namespaces按需过滤日志debug选项除了布尔值还可以是一个命名空间数组用来只输出你关心的那几类日志return MikroORM.init({ debug: [query], // 现在只会输出查询日志 });在 v6.6 中一共有 6 个命名空间命名空间输出内容query所有执行的 SQL / Mongo 查询语句query-params查询参数绑定值schemaSchema 生成与同步相关日志discovery实体发现过程info一般信息deprecated弃用警告一个重要的约束query-params必须与query同时提供才会生效因为参数是附着在查询语句输出上的。版本差异说明在仓库当前主分支文档 docs/docs/logging.md 中命名空间已扩展为 7 个新增了slow-query慢查询日志配合slowQueryThreshold使用见文末补充章节。v6.6 的LoggerNamespace类型定义在 Logger.ts 中可查。从实现上看命名空间过滤的核心逻辑在 DefaultLogger.ts 的isEnabled()方法中当debugMode为true时全部放行为数组时仅放行数组中包含的命名空间deprecated命名空间则额外受ignoreDeprecations控制见下节。处理弃用警告Deprecation Warnings即使没有开启任何 debug 模式默认 logger 也会在控制台输出deprecated命名空间的弃用消息。弃用意味着该 API 计划在未来的主版本中移除消息中通常会给出替代方案提示你在升级主版本前完成迁移。如果你暂时不想看到这些警告可以全局忽略return MikroORM.init({ ignoreDeprecations: true, // 不再输出任何弃用警告但升级时可能会惊喜 });更精细的做法是只忽略部分警告。每个弃用警告都有一个标签如D0001你可以按标签逐个屏蔽同时继续收到其他未知的弃用提醒return MikroORM.init({ ignoreDeprecations: [D0001], // 只忽略标签为 D0001 的警告其他照常输出 });完整的弃用错误清单可参阅 Configuration 文档中的 Deprecated Warnings 小节。底层实现同样位于isEnabled()当ignoreDeprecations为数组时会检查context.label是否命中数组中的某个标签命中则静默DefaultLogger.ts。Highlighters让 SQL 日志更易读早期版本使用 Highlight.js 为 CLI 输出的 SQL、Mongo 查询、迁移与实体生成结果做语法高亮。该库功能没问题但体积庞大对使用 webpack 打包或部署到 Lambda 的场景造成了明显的性能负担。因此从 v4 起高亮默认关闭改为提供两个可选的高亮器需自行安装import { SqlHighlighter } from mikro-orm/sql-highlighter; MikroORM.init({ highlighter: new SqlHighlighter(), // ... });MongoDB 用户则使用mikro-orm/mongo-highlighter包中的MongoHighlighter。高亮器在 DefaultLogger.logQuery() 中通过this.highlighter?.highlight(context.query)对查询语句进行渲染若未配置默认NullHighlighter则原样输出。Logger Customization日志的深度定制除了开/关与过滤MikroORM 还提供了一系列细粒度定制能力覆盖给查询打标、单条查询覆盖配置、更换输出函数、乃至完全自定义 Logger等场景。Query Labels给查询打上来源标签当你在多个调用点反复执行同类查询时很难分辨某条日志出自哪段业务代码。此时可以使用FindOptions中的logging.label为查询输出附加一个标签const author await em.findOne(Author, { id: 1 }, { logging: { label: Author Retrieval - /authors/me } }); // [query] (Author Retrieval - /authors/me) select a0.* from Author as a0 where a0.id 1 limit 1 [took 21 ms]标签以青色括号形式出现在命名空间与 SQL 之间。适用场景包括定位EntityManager.find/EntityManager.findOne的调用来源、排查冗余重复查询等。相关类型定义见 Logger.ts 中的LoggingOptions PickLogContext, label | enabled | debugMode。单条查询覆盖debugMode或关闭日志FindOptions.logging还支持enabled与debugMode两个属性用于在单条查询级别覆盖全局配置// MikroORM.init({ debug: true }); const author await em.findOne(Author, { id: 1 }, { logging: { enabled: false } }); // 覆盖全局配置这条查询不输出任何日志 // ... // MikroORM.init({ debug: false }); const author await em.findOne(Author, { id: 1 }, { logging: { enabled: true } }); // 覆盖全局配置这条查询强制输出日志 // ... // MikroORM.init({ debug: [query-labels] }); const author await em.findOne(Author, { id: 1 }, { logging: { debugMode: [query] } }); // 覆盖全局配置仅对这条查询按 [query] 命名空间输出这套机制在源码中的实现路径非常清晰isEnabled()会优先读取context.enabled若显式给出则直接作为开关结果否则回退到context.debugMode ?? this.debugModeDefaultLogger.ts。也就是说LogContext中的enabled与debugMode字段是查询级覆盖全局配置的唯一入口。使用自定义 logger 函数最简单的高度定制是替换输出函数本身。通过logger选项你可以把 ORM 的所有日志转发到自己的日志工具return MikroORM.init({ debug: true, logger: msg myCustomLogger.log(msg), });logger的默认值即console.log.bind(console)Configuration.ts替换后所有命名空间的消息都会流经你的函数。使用自定义LoggerFactory如果需要对记什么、怎么记做更全面的控制可以使用loggerFactory选项配合以下三种方式之一继承DefaultLogger或SimpleLoggerDefaultLogger与SimpleLogger都从mikro-orm/core导出其中SimpleLogger输出不带颜色其源码即重写了log与logQuery去掉了颜色包装见 SimpleLogger.ts。class CustomLogger extends DefaultLogger { log(namespace: LoggerNamespace, message: string, context?: LogContext) { // 用自己的实现输出 console.log([${namespace}] (${context.label}) ${message}); // 或者复用 DefaultLogger 的实现 super.log(namespace, message, context) } } return MikroORM.init({ debug: true, loggerFactory: (options) new CustomLogger(options), });若改用SimpleLogger只需把基类替换即可class CustomLogger extends SimpleLogger { // ... }从零实现Logger接口你也可以完全不继承内置实现直接实现Logger接口import { Logger, LoggerOptions, MikroORM, Configuration } from mikro-orm/core; class MyLogger implements Logger { // ... } const orm await MikroORM.init({ debug: true, loggerFactory: (options) new MyLogger(options), });Logger接口在 Logger.ts 中定义如下interface Logger { log(namespace: LoggerNamespace, message: string, context?: LogContext): void; error(namespace: LoggerNamespace, message: string, context?: LogContext): void; warn(namespace: LoggerNamespace, message: string, context?: LogContext): void; logQuery(context: LogContext): void; setDebugMode(debugMode: boolean | LoggerNamespace[]): void; isEnabled(namespace: LoggerNamespace, context?: LogContext): boolean; } type LoggerNamespace query | query-params | schema | discovery | info | deprecated; interface LogContext extends Dictionary { query?: string; label?: string; params?: unknown[]; took?: number; level?: info | warning | error; enabled?: boolean; debugMode?: LoggerNamespace[]; connection?: { type?: string; name?: string; }; }理解这些字段对实现自定义 Logger 至关重要log/error/warn通用日志入口。error与warn在 DefaultLogger.ts 中通过给context.level注入error/warning再调用log实现并分别渲染为红色 / 黄色。logQuery查询专用入口接收包含query、took、results、affected等字段的LogContext。DefaultLogger会在 SQL 后追加[took X ms, N results, M rows affected]形式的元信息若配置了读副本read replicas还会追加(via replica connection name)提示DefaultLogger.ts。setDebugMode供 ORM 在运行时动态调整 debug 模式如配置变更后调用。isEnabled决定某命名空间是否输出是实现命名空间过滤的关键钩子。需要特别注意的是LogContext是Dictionary的扩展因此任何自定义键值都可以挂在上面——这正好支撑了下面要讲的loggerContext机制。通过loggerContext向自定义 Logger 传递附加上下文如果你实现了自己的LoggerFactory又需要在日志实现中读取额外的业务上下文可以使用FindOptions.loggerContext注入任意键值对const res await em.findAll(Author, { loggerContext: { meaningOfLife: 42 } }); // ... class CustomLogger extends DefaultLogger { log(namespace: LoggerNamespace, message: string, context?: LogContext) { console.log(context?.meaningOfLife); // 42 } }上下文也可以在EntityManager层面设置例如通过em.fork()让一个分支的所有查询共享同一上下文const fork em.fork({ loggerContext: { meaningOfLife: 42 }, }); const res await fork.findAll(Author); // 与上一个示例效果相同提示EntityManager的默认 logger context 中包含 fork 的id因此借助它你可以分辨每条查询出自哪个EntityManager实例。深入底层DefaultLogger 的消息管线为了让自定义 Logger 与默认行为保持兼容理解 DefaultLogger.ts 的完整消息管线会很有帮助命名空间过滤log()先调用isEnabled()不通过直接返回deprecated命名空间单独受ignoreDeprecations控制。消息清洗将换行与多余空格压缩、trim()掉首尾空白。级别着色error渲染为红色、warning渲染为黄色受colors.enabled()控制可被环境变量关闭。标签插入若context.label存在以青色(label)前缀插入。写出最终消息为[命名空间] (label) message交给writer即logger选项提供的函数。配置侧Configuration.ts 中默认loggerFactory ?? DefaultLogger.create并以debugMode: options.debug、highlighter、writer: options.logger组装 LoggerOptions 实例化。换言之你配置的debug、colors、highlighter、logger四项会统一汇入LoggerOptionsLogger.ts再由loggerFactory负责构建最终实例。附慢查询日志当前主版本新增能力虽然 v6.6 文档尚未收录但仓库当前主文档 docs/docs/logging.md 已加入慢查询日志能力可作为升级参考。通过slowQueryThreshold毫秒配置阈值超过阈值的查询会自动经slow-query命名空间输出——无论debug是否开启return MikroORM.init({ slowQueryThreshold: 200, // 记录耗时 ≥ 200ms 的查询 });[slow-query] select * from user where id 1 [took 450 ms]慢查询以warning级别输出失败的慢查询以error级别输出阈值设为0则每条查询都会被视为慢查询。如需把慢查询单独导出例如写入文件或上报监控可使用与loggerFactory形状一致的slowQueryLoggerFactoryreturn MikroORM.init({ slowQueryThreshold: 200, slowQueryLoggerFactory: options new DefaultLogger({ ...options, writer: msg fs.appendFileSync(slow-queries.log, msg \n), }), });实现上慢查询日志条目会以context.enabled true发出以绕过 debug 模式检查因此自定义 Logger 的isEnabled()必须尊重context.enabledDefaultLogger正是这么做的慢查询日志才能正常输出。相关配置项定义可查阅 Configuration.ts。小结MikroORM 的日志体系是一套分层可插拔的设计debug布尔值或命名空间数组决定了输出范围colors与四个环境变量控制颜色ignoreDeprecations管理弃用警告highlighter增强可读性而logger/loggerFactory/loggerContext则让你能把日志无缝接入既有日志体系甚至实现从零构建的Logger。配合 DefaultLogger.ts、SimpleLogger.ts 与 Logger.ts 三个核心源码文件你可以清楚地推演每一条日志从产生到落地的完整路径进而在开发排障与生产监控两个场景中都游刃有余。赞分享后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载相关推荐OpenXR-SDK-Source与主流引擎集成Unity、Unreal Engine开发流程详解OpenXR SDK Source与主流引擎集成Unity、Unreal Engine开发流程详解 OpenXR SDK Source是Khronos GroTypeORM 日志系统完全指南日志开关、内置日志器与自定义 Logger 实战详解TypeORM 日志系统完全指南日志开关、内置日志器与自定义 Logger 实战详解 TypeORM 内置了一套面向 Node.js 的数据库日志体系可覆盖后端数据库ORMRealm Swift SDK 日志系统完全指南日志级别、自定义 Logger 与性能调优Realm Swift SDK 日志系统完全指南日志级别、自定义 Logger 与性能调优 本文以 Realm Swift SDKrealm swift 仓数据库移动开发嵌入式数据库上一篇Django Import/Export 框架入门教程下一篇Kconfiglib 项目使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考