@mikro-orm/core 7.0.0-dev.337 → 7.0.0-dev.338

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ import type { Platform } from '../platforms/Platform.js';
6
6
  import type { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
7
7
  import type { IsolationLevel } from '../enums.js';
8
8
  export declare abstract class Connection {
9
+ #private;
9
10
  protected readonly config: Configuration;
10
11
  protected readonly type: ConnectionType;
11
12
  protected metadata: MetadataStorage;
@@ -7,6 +7,12 @@ export class Connection {
7
7
  options;
8
8
  logger;
9
9
  connected = false;
10
+ get #connectionLabel() {
11
+ return {
12
+ type: this.type,
13
+ name: this.options.name || this.config.get('name') || this.options.host || this.options.dbName,
14
+ };
15
+ }
10
16
  constructor(config, options, type = 'write') {
11
17
  this.config = config;
12
18
  this.type = type;
@@ -120,28 +126,38 @@ export class Connection {
120
126
  const now = Date.now();
121
127
  try {
122
128
  const res = await cb();
123
- this.logQuery(query, {
124
- ...context,
125
- took: Date.now() - now,
126
- results: Array.isArray(res) ? res.length : undefined,
127
- affected: Utils.isPlainObject(res) ? res.affectedRows : undefined,
128
- });
129
+ const took = Date.now() - now;
130
+ const results = Array.isArray(res) ? res.length : undefined;
131
+ const affected = Utils.isPlainObject(res) ? res.affectedRows : undefined;
132
+ this.logQuery(query, { ...context, took, results, affected });
129
133
  return res;
130
134
  }
131
135
  catch (e) {
132
- this.logQuery(query, { ...context, took: Date.now() - now, level: 'error' });
136
+ const took = Date.now() - now;
137
+ this.logQuery(query, { ...context, took, level: 'error' });
133
138
  throw e;
134
139
  }
135
140
  }
136
141
  logQuery(query, context = {}) {
142
+ const connection = this.#connectionLabel;
137
143
  this.logger.logQuery({
138
144
  level: 'info',
139
- connection: {
140
- type: this.type,
141
- name: this.options.name || this.config.get('name') || this.options.host,
142
- },
145
+ connection,
143
146
  ...context,
144
147
  query,
145
148
  });
149
+ const threshold = this.config.get('slowQueryThreshold');
150
+ if (threshold != null && (context.took ?? 0) >= threshold) {
151
+ this.config.getSlowQueryLogger().logQuery({
152
+ ...context,
153
+ // `enabled: true` bypasses the debug-mode check in isEnabled(),
154
+ // ensuring slow query logs are always emitted regardless of the `debug` setting.
155
+ enabled: true,
156
+ level: context.level ?? 'warning',
157
+ namespace: 'slow-query',
158
+ connection,
159
+ query,
160
+ });
161
+ }
146
162
  }
147
163
  }
@@ -68,7 +68,8 @@ export class DefaultLogger {
68
68
  * @inheritDoc
69
69
  */
70
70
  logQuery(context) {
71
- if (!this.isEnabled('query', context)) {
71
+ const namespace = context.namespace ?? 'query';
72
+ if (!this.isEnabled(namespace, context)) {
72
73
  return;
73
74
  }
74
75
  /* v8 ignore next */
@@ -86,7 +87,7 @@ export class DefaultLogger {
86
87
  if (this.usesReplicas && context.connection) {
87
88
  msg += colors.cyan(` (via ${context.connection.type} connection '${context.connection.name}')`);
88
89
  }
89
- return this.log('query', msg, context);
90
+ return this.log(namespace, msg, context);
90
91
  }
91
92
  static create(options) {
92
93
  return new DefaultLogger(options);
@@ -22,10 +22,11 @@ export interface Logger {
22
22
  setDebugMode(debugMode: boolean | LoggerNamespace[]): void;
23
23
  isEnabled(namespace: LoggerNamespace, context?: LogContext): boolean;
24
24
  }
25
- export type LoggerNamespace = 'query' | 'query-params' | 'schema' | 'discovery' | 'info' | 'deprecated';
25
+ export type LoggerNamespace = 'query' | 'query-params' | 'schema' | 'discovery' | 'info' | 'deprecated' | 'slow-query';
26
26
  export interface LogContext extends Dictionary {
27
27
  query?: string;
28
28
  label?: string;
29
+ namespace?: LoggerNamespace;
29
30
  params?: readonly unknown[];
30
31
  took?: number;
31
32
  results?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.0.0-dev.337",
3
+ "version": "7.0.0-dev.338",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -36,6 +36,11 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
36
36
  * Gets Logger instance.
37
37
  */
38
38
  getLogger(): Logger;
39
+ /**
40
+ * Gets the logger instance for slow queries.
41
+ * Falls back to the main logger if no custom slow query logger factory is configured.
42
+ */
43
+ getSlowQueryLogger(): Logger;
39
44
  getDataloaderType(): DataloaderType;
40
45
  getSchema(skipDefaultSchema?: boolean): string | undefined;
41
46
  /**
@@ -671,6 +676,24 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
671
676
  * @default DefaultLogger.create
672
677
  */
673
678
  loggerFactory?: (options: LoggerOptions) => Logger;
679
+ /**
680
+ * Threshold in milliseconds for logging slow queries.
681
+ * Queries taking at least this long will be logged via the 'slow-query' namespace at warning level.
682
+ * Slow query logs are always emitted when the threshold is met, regardless of the `debug` setting.
683
+ * Set to `0` to log every query as slow.
684
+ * @default undefined (slow query logging disabled)
685
+ */
686
+ slowQueryThreshold?: number;
687
+ /**
688
+ * Factory function to create a custom logger instance for slow queries.
689
+ * Has the same shape as `loggerFactory`. When not provided, the main logger instance is used.
690
+ *
691
+ * Note: slow query log entries are emitted with `context.enabled = true` to bypass the
692
+ * debug-mode check. Custom logger implementations must respect `context.enabled` in their
693
+ * `isEnabled()` method (as `DefaultLogger` does) to ensure slow query logs are always emitted.
694
+ * @default undefined (falls back to main logger)
695
+ */
696
+ slowQueryLoggerFactory?: (options: LoggerOptions) => Logger;
674
697
  /**
675
698
  * Custom error handler for `em.findOneOrFail()` when no entity is found.
676
699
  * @param entityName - Name of the entity being queried
@@ -130,6 +130,7 @@ const DEFAULTS = {
130
130
  export class Configuration {
131
131
  #options;
132
132
  #logger;
133
+ #slowQueryLogger;
133
134
  #driver;
134
135
  #platform;
135
136
  #cache = new Map();
@@ -195,6 +196,20 @@ export class Configuration {
195
196
  getLogger() {
196
197
  return this.#logger;
197
198
  }
199
+ /**
200
+ * Gets the logger instance for slow queries.
201
+ * Falls back to the main logger if no custom slow query logger factory is configured.
202
+ */
203
+ getSlowQueryLogger() {
204
+ this.#slowQueryLogger ??=
205
+ this.#options.slowQueryLoggerFactory?.({
206
+ debugMode: this.#options.debug,
207
+ writer: this.#options.logger,
208
+ highlighter: this.#options.highlighter,
209
+ usesReplicas: (this.#options.replicas?.length ?? 0) > 0,
210
+ }) ?? this.#logger;
211
+ return this.#slowQueryLogger;
212
+ }
198
213
  getDataloaderType() {
199
214
  if (typeof this.#options.dataloader === 'boolean') {
200
215
  return this.#options.dataloader ? DataloaderType.ALL : DataloaderType.NONE;
@@ -337,6 +352,7 @@ export class Configuration {
337
352
  sync() {
338
353
  setEnv('MIKRO_ORM_COLORS', this.#options.colors);
339
354
  this.#logger.setDebugMode(this.#options.debug);
355
+ this.#slowQueryLogger = undefined;
340
356
  }
341
357
  validateOptions() {
342
358
  /* v8 ignore next */
package/utils/Utils.js CHANGED
@@ -123,7 +123,7 @@ export function parseJsonSafe(value) {
123
123
  }
124
124
  export class Utils {
125
125
  static PK_SEPARATOR = '~~~';
126
- static #ORM_VERSION = '7.0.0-dev.337';
126
+ static #ORM_VERSION = '7.0.0-dev.338';
127
127
  /**
128
128
  * Checks if the argument is instance of `Object`. Returns false for arrays.
129
129
  */