@tumbaland/backend-core 1.53.0 → 1.55.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAIxD,KAAK,YAAY,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AA+B1D,eAAO,MAAM,gBAAgB,GAAI,QAAQ,OAAO,KAAG,OAAO,CAAC,OAAO,CAAyB,CAAC;AAE5F;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAEvE"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAIxD,KAAK,YAAY,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAmD1D,eAAO,MAAM,gBAAgB,GAAI,QAAQ,OAAO,KAAG,OAAO,CAAC,OAAO,CAAyB,CAAC;AAE5F;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAEvE"}
@@ -17,6 +17,16 @@ const mongoose_1 = __importDefault(require("mongoose"));
17
17
  */
18
18
  exports.USER_SESSIONS_COLLECTION = 'user_sessions';
19
19
  const OBJECT_ID = /^[a-f\d]{24}$/i;
20
+ /**
21
+ * Checks running right now, by session. Opening a thread sends several requests
22
+ * at once, each of which authenticates, and each would ask the database the same
23
+ * question across the internet. They share one answer instead.
24
+ *
25
+ * Only while the question is being asked: nothing is kept once it has been
26
+ * answered, so a session revoked a moment later is refused on the very next
27
+ * request, exactly as before.
28
+ */
29
+ const inFlight = new Map();
20
30
  /**
21
31
  * Whether a verified session token still stands: for a token with a `sid`,
22
32
  * its session belongs to the token's user and has neither been revoked nor
@@ -36,12 +46,21 @@ const checkSessionInDatabase = async (claims) => {
36
46
  // Refuse rather than queue an authentication query while the database is down.
37
47
  if (mongoose_1.default.connection.readyState !== 1)
38
48
  return false;
39
- const session = await mongoose_1.default.connection.collection(exports.USER_SESSIONS_COLLECTION).findOne({
49
+ const key = `${id}:${sid}`;
50
+ const running = inFlight.get(key);
51
+ if (running)
52
+ return running;
53
+ const lookup = mongoose_1.default.connection
54
+ .collection(exports.USER_SESSIONS_COLLECTION)
55
+ .findOne({
40
56
  _id: new mongoose_1.default.Types.ObjectId(sid),
41
57
  userId: new mongoose_1.default.Types.ObjectId(id),
42
58
  expiresAt: { $gt: new Date() }
43
- }, { projection: { _id: 1 }, readPreference: 'primary', maxTimeMS: 3000 });
44
- return session !== null;
59
+ }, { projection: { _id: 1 }, readPreference: 'primary', maxTimeMS: 3000 })
60
+ .then((session) => session !== null)
61
+ .finally(() => inFlight.delete(key));
62
+ inFlight.set(key, lookup);
63
+ return lookup;
45
64
  };
46
65
  let sessionCheck = checkSessionInDatabase;
47
66
  const isCurrentSession = (claims) => sessionCheck(claims);
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":";;;;;;AAqDA,oEAEC;AAvDD,wDAAgC;AAEhC;;;;;;;;GAQG;AACU,QAAA,wBAAwB,GAAG,eAAe,CAAC;AAExD,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAInC;;;;;GAKG;AACH,MAAM,sBAAsB,GAAiB,KAAK,EAAE,MAAM,EAAE,EAAE;IAC5D,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,MAAyC,CAAC;IAC9D,4EAA4E;IAC5E,uDAAuD;IACvD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,+EAA+E;IAC/E,IAAI,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvD,MAAM,OAAO,GAAG,MAAM,kBAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,gCAAwB,CAAC,CAAC,OAAO,CACpF;QACE,GAAG,EAAE,IAAI,kBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QACrC,MAAM,EAAE,IAAI,kBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvC,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,EAAE;KAC/B,EACD,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CACvE,CAAC;IACF,OAAO,OAAO,KAAK,IAAI,CAAC;AAC1B,CAAC,CAAC;AAEF,IAAI,YAAY,GAAiB,sBAAsB,CAAC;AAEjD,MAAM,gBAAgB,GAAG,CAAC,MAAe,EAAoB,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AAA/E,QAAA,gBAAgB,oBAA+D;AAE5F;;;;GAIG;AACH,SAAgB,4BAA4B,CAAC,KAAoB;IAC/D,YAAY,GAAG,KAAK,IAAI,sBAAsB,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":";;;;;;AAyEA,oEAEC;AA3ED,wDAAgC;AAEhC;;;;;;;;GAQG;AACU,QAAA,wBAAwB,GAAG,eAAe,CAAC;AAExD,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAInC;;;;;;;;GAQG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA4B,CAAC;AAErD;;;;;GAKG;AACH,MAAM,sBAAsB,GAAiB,KAAK,EAAE,MAAM,EAAE,EAAE;IAC5D,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,MAAyC,CAAC;IAC9D,4EAA4E;IAC5E,uDAAuD;IACvD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,+EAA+E;IAC/E,IAAI,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvD,MAAM,GAAG,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAE5B,MAAM,MAAM,GAAG,kBAAQ,CAAC,UAAU;SAC/B,UAAU,CAAC,gCAAwB,CAAC;SACpC,OAAO,CACN;QACE,GAAG,EAAE,IAAI,kBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QACrC,MAAM,EAAE,IAAI,kBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvC,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,EAAE;KAC/B,EACD,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CACvE;SACA,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC;SACnC,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,IAAI,YAAY,GAAiB,sBAAsB,CAAC;AAEjD,MAAM,gBAAgB,GAAG,CAAC,MAAe,EAAoB,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AAA/E,QAAA,gBAAgB,oBAA+D;AAE5F;;;;GAIG;AACH,SAAgB,4BAA4B,CAAC,KAAoB;IAC/D,YAAY,GAAG,KAAK,IAAI,sBAAsB,CAAC;AACjD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { EventEmitter } from 'events';
2
+ /** Idempotent: a client that is already observed is left alone. */
3
+ export declare function observeCommands(client: EventEmitter): void;
4
+ //# sourceMappingURL=commandMonitoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandMonitoring.d.ts","sourceRoot":"","sources":["../../src/database/commandMonitoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA2D3C,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAgD1D"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.observeCommands = observeCommands;
4
+ const api_1 = require("@opentelemetry/api");
5
+ const metrics_1 = require("../metrics");
6
+ const tracing_1 = require("../tracing");
7
+ /**
8
+ * Times every query the MongoDB driver sends, whoever sent it.
9
+ *
10
+ * It listens to the driver's own command events (`monitorCommands`, switched on in
11
+ * `poolOptions`) instead of wrapping Mongoose, so it needs no plugin registered
12
+ * before the models are compiled and sees queries made through the driver
13
+ * directly as well. Each query becomes one observation in
14
+ * `database_query_duration_seconds`, and, inside a traced request, one child span.
15
+ *
16
+ * Only the operation and the collection are recorded. The filter and the
17
+ * documents are not: they hold what people wrote.
18
+ */
19
+ /** Data commands. The handshake, pings and cursor housekeeping are left out. */
20
+ const OPERATIONS = new Set([
21
+ 'find',
22
+ 'aggregate',
23
+ 'insert',
24
+ 'update',
25
+ 'delete',
26
+ 'findAndModify',
27
+ 'count',
28
+ 'distinct',
29
+ 'getMore'
30
+ ]);
31
+ const observed = new WeakSet();
32
+ /** The collection a command is about; `getMore` names it in `collection`. */
33
+ function collectionOf(commandName, command) {
34
+ const name = commandName === 'getMore' ? command.collection : command[commandName];
35
+ // A database-level aggregate has `aggregate: 1`.
36
+ return typeof name === 'string' ? name : 'none';
37
+ }
38
+ /** Idempotent: a client that is already observed is left alone. */
39
+ function observeCommands(client) {
40
+ if (observed.has(client))
41
+ return;
42
+ observed.add(client);
43
+ const inFlight = new Map();
44
+ client.on('commandStarted', (event) => {
45
+ if (!OPERATIONS.has(event.commandName))
46
+ return;
47
+ const operation = event.commandName;
48
+ const collection = collectionOf(operation, event.command);
49
+ // Only inside a request that is being traced: a query with no request
50
+ // around it would be a trace of one span.
51
+ const parent = api_1.context.active();
52
+ const span = api_1.trace.getSpan(parent)
53
+ ? (0, tracing_1.getTracer)().startSpan(`mongodb.${operation} ${collection}`, {
54
+ kind: api_1.SpanKind.CLIENT,
55
+ attributes: {
56
+ 'db.system': 'mongodb',
57
+ 'db.operation.name': operation,
58
+ 'db.collection.name': collection
59
+ }
60
+ }, parent)
61
+ : undefined;
62
+ inFlight.set(event.requestId, { operation, collection, span });
63
+ });
64
+ const finish = (event, status) => {
65
+ const started = inFlight.get(event.requestId);
66
+ if (!started)
67
+ return;
68
+ inFlight.delete(event.requestId);
69
+ metrics_1.databaseQueryDuration.labels(started.operation, started.collection).observe(event.duration / 1000);
70
+ metrics_1.databaseQueriesTotal.labels(started.operation, started.collection, status).inc();
71
+ if (started.span) {
72
+ if (status === 'error')
73
+ started.span.setStatus({ code: api_1.SpanStatusCode.ERROR });
74
+ started.span.end();
75
+ }
76
+ };
77
+ client.on('commandSucceeded', (event) => finish(event, 'success'));
78
+ client.on('commandFailed', (event) => finish(event, 'error'));
79
+ }
80
+ //# sourceMappingURL=commandMonitoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandMonitoring.js","sourceRoot":"","sources":["../../src/database/commandMonitoring.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,31 @@
1
+ import { ConnectOptions } from 'mongoose';
2
+ /**
3
+ * How the pool behaves toward a database that is across the internet.
4
+ *
5
+ * Measured, not assumed: on the Pi a database lookup took about 17 ms on a warm
6
+ * connection and 850 to 1,370 ms on a cold one, so the distance to Atlas is not
7
+ * the cost. Opening a connection is. It is a TCP and TLS handshake and an
8
+ * authentication, from a small machine, and the driver defaults make a burst of
9
+ * requests pay for it badly:
10
+ *
11
+ * - It keeps no connections open, and an idle one is eventually dropped by
12
+ * something between here and Atlas, so the first request after a quiet spell
13
+ * starts from nothing.
14
+ * - It opens at most two connections at a time. Opening a thread sends five or
15
+ * six requests at once, so they queued behind each other, a second or so per
16
+ * pair, and the thread took three to seven seconds.
17
+ *
18
+ * So:
19
+ * - `minPoolSize` keeps enough connections open to cover that burst.
20
+ * - `maxConnecting` lets the pool open several at once when it does need more.
21
+ * - `maxIdleTimeMS` retires a connection that has sat idle for a few minutes,
22
+ * before something else kills it, and the pool opens a fresh one in its place.
23
+ * - `family: 4` skips the IPv6 half of every hostname lookup. Atlas is IPv4, and
24
+ * a resolver that is slow to answer for AAAA delays every new connection.
25
+ *
26
+ * Each can be set per deployment, for a database that is somewhere else.
27
+ */
28
+ export declare function poolOptions(env?: NodeJS.ProcessEnv): ConnectOptions;
1
29
  /**
2
30
  * Connects once and throws on failure. There is deliberately no retry loop here.
3
31
  *
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GAAU,cAAc,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAchF,CAAC;AAwCF,eAAO,MAAM,YAAY,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,IAAI,CASrE,CAAC"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAYpD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CAchF;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GAAU,cAAc,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAchF,CAAC;AA6DF,eAAO,MAAM,YAAY,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,IAAI,CASrE,CAAC"}
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.disconnectDB = exports.connectDB = void 0;
7
+ exports.poolOptions = poolOptions;
7
8
  const mongoose_1 = __importDefault(require("mongoose"));
8
9
  const logger_1 = __importDefault(require("../logging/logger"));
10
+ const commandMonitoring_1 = require("./commandMonitoring");
9
11
  let connected = false;
10
12
  /**
11
13
  * Set while a connection attempt is in flight so concurrent callers await that
@@ -13,6 +15,45 @@ let connected = false;
13
15
  */
14
16
  let pending = null;
15
17
  let listenersRegistered = false;
18
+ /**
19
+ * How the pool behaves toward a database that is across the internet.
20
+ *
21
+ * Measured, not assumed: on the Pi a database lookup took about 17 ms on a warm
22
+ * connection and 850 to 1,370 ms on a cold one, so the distance to Atlas is not
23
+ * the cost. Opening a connection is. It is a TCP and TLS handshake and an
24
+ * authentication, from a small machine, and the driver defaults make a burst of
25
+ * requests pay for it badly:
26
+ *
27
+ * - It keeps no connections open, and an idle one is eventually dropped by
28
+ * something between here and Atlas, so the first request after a quiet spell
29
+ * starts from nothing.
30
+ * - It opens at most two connections at a time. Opening a thread sends five or
31
+ * six requests at once, so they queued behind each other, a second or so per
32
+ * pair, and the thread took three to seven seconds.
33
+ *
34
+ * So:
35
+ * - `minPoolSize` keeps enough connections open to cover that burst.
36
+ * - `maxConnecting` lets the pool open several at once when it does need more.
37
+ * - `maxIdleTimeMS` retires a connection that has sat idle for a few minutes,
38
+ * before something else kills it, and the pool opens a fresh one in its place.
39
+ * - `family: 4` skips the IPv6 half of every hostname lookup. Atlas is IPv4, and
40
+ * a resolver that is slow to answer for AAAA delays every new connection.
41
+ *
42
+ * Each can be set per deployment, for a database that is somewhere else.
43
+ */
44
+ function poolOptions(env = process.env) {
45
+ const number = (value, fallback) => value !== undefined && value.trim() !== '' && Number.isFinite(Number(value)) ? Number(value) : fallback;
46
+ const family = number(env.MONGODB_IP_FAMILY, 4);
47
+ return {
48
+ minPoolSize: number(env.MONGODB_MIN_POOL_SIZE, 5),
49
+ maxConnecting: number(env.MONGODB_MAX_CONNECTING, 5),
50
+ maxIdleTimeMS: number(env.MONGODB_MAX_IDLE_MS, 180_000),
51
+ family: family === 6 ? 6 : family === 0 ? 0 : 4,
52
+ // The driver announces every command it sends; commandMonitoring.ts turns
53
+ // those into query timings and spans.
54
+ monitorCommands: true
55
+ };
56
+ }
16
57
  /**
17
58
  * Connects once and throws on failure. There is deliberately no retry loop here.
18
59
  *
@@ -45,10 +86,12 @@ const connectDB = async (serviceName, uri) => {
45
86
  exports.connectDB = connectDB;
46
87
  async function connectOnce(mongoUri, serviceName) {
47
88
  try {
48
- await mongoose_1.default.connect(mongoUri);
89
+ await mongoose_1.default.connect(mongoUri, poolOptions());
49
90
  connected = true;
91
+ (0, commandMonitoring_1.observeCommands)(mongoose_1.default.connection.getClient());
50
92
  registerConnectionListeners(serviceName);
51
93
  logger_1.default.info('MongoDB connected', { service: serviceName });
94
+ await logRoundTrip(serviceName);
52
95
  }
53
96
  catch (error) {
54
97
  logger_1.default.error('Failed to connect to MongoDB', {
@@ -58,6 +101,26 @@ async function connectOnce(mongoUri, serviceName) {
58
101
  throw error;
59
102
  }
60
103
  }
104
+ /**
105
+ * How long one round trip to the database takes from here, once, at start.
106
+ *
107
+ * Every query is at least one of these, and a request makes several, so it is the
108
+ * number that explains whether a slow request was slow because of the database's
109
+ * distance or because of something else. Best effort: it never fails a start.
110
+ */
111
+ async function logRoundTrip(serviceName) {
112
+ try {
113
+ const db = mongoose_1.default.connection.db;
114
+ if (!db)
115
+ return;
116
+ const started = performance.now();
117
+ await db.admin().ping();
118
+ logger_1.default.info('MongoDB round trip', { service: serviceName, ms: Math.round(performance.now() - started) });
119
+ }
120
+ catch {
121
+ // A start is not worth failing for a measurement.
122
+ }
123
+ }
61
124
  /**
62
125
  * Mongoose reconnects on its own once an initial connection has succeeded, so
63
126
  * these only report state — but without them a drop is silent and the only
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AA8E9B,QAAA,MAAM,MAAM,gBAsBV,CAAC;AAEH,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AA4F9B,QAAA,MAAM,MAAM,gBAsBV,CAAC;AAEH,eAAe,MAAM,CAAC"}
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const winston_1 = __importDefault(require("winston"));
7
+ const api_1 = require("@opentelemetry/api");
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const fs_1 = __importDefault(require("fs"));
9
10
  // Define log levels
@@ -30,8 +31,19 @@ const colors = {
30
31
  winston_1.default.addColors(colors);
31
32
  // Define format for console (human-readable)
32
33
  const consoleFormat = winston_1.default.format.combine(winston_1.default.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), winston_1.default.format.colorize({ all: true }), winston_1.default.format.printf((info) => `${info.timestamp} ${info.level}: ${String(info.message)}`));
34
+ // Puts the trace id of the request being served on the line, so a log line found
35
+ // in Kibana leads to its trace in Jaeger, and the other way round. Lines logged
36
+ // outside a traced request (startup, background work) are left as they are.
37
+ const traceContext = winston_1.default.format((info) => {
38
+ const spanContext = api_1.trace.getSpan(api_1.context.active())?.spanContext();
39
+ if (spanContext && api_1.trace.isSpanContextValid(spanContext)) {
40
+ info.traceId = spanContext.traceId;
41
+ info.spanId = spanContext.spanId;
42
+ }
43
+ return info;
44
+ });
33
45
  // Define JSON format for production (structured logging)
34
- const jsonFormat = winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.errors({ stack: true }), winston_1.default.format.json());
46
+ const jsonFormat = winston_1.default.format.combine(traceContext(), winston_1.default.format.timestamp(), winston_1.default.format.errors({ stack: true }), winston_1.default.format.json());
35
47
  // Choose format based on environment
36
48
  const isProduction = process.env.NODE_ENV === 'production';
37
49
  const logFormat = isProduction ? jsonFormat : consoleFormat;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,16 @@ export declare const businessMetrics: {
13
13
  paymentsProcessed: promClient.Counter<"method" | "status">;
14
14
  };
15
15
  export declare const metricsHandler: (_req: Request, res: Response) => Promise<void>;
16
+ /**
17
+ * The route as it is written in the code ("/api/chat/conversations/:id"), not the
18
+ * URL that was asked for.
19
+ *
20
+ * A label per distinct URL is a time series per distinct URL: every id, every
21
+ * query string and every scanner probing for /wp-login.php adds series that
22
+ * Prometheus keeps in memory, and a chart "by route" turns into thousands of
23
+ * lines. A request no route matched (404s, probes) is one `unmatched` series.
24
+ */
25
+ export declare const routeLabel: (req: Request) => string;
16
26
  export declare const metricsMiddleware: (req: Request, res: Response, next: NextFunction) => void;
17
27
  export { register };
18
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,QAAA,MAAM,QAAQ,iEAA4B,CAAC;AAY3C,eAAO,MAAM,mBAAmB,0DAK9B,CAAC;AAEH,eAAO,MAAM,iBAAiB,wDAI5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,0BAG5B,CAAC;AAEH,eAAO,MAAM,qBAAqB,kDAKhC,CAAC;AAEH,eAAO,MAAM,oBAAoB,2DAI/B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;CAsB3B,CAAC;AAGF,eAAO,MAAM,cAAc,GAAU,MAAM,OAAO,EAAE,KAAK,QAAQ,kBAQhE,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAyBhF,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,QAAA,MAAM,QAAQ,iEAA4B,CAAC;AAc3C,eAAO,MAAM,mBAAmB,0DAS9B,CAAC;AAEH,eAAO,MAAM,iBAAiB,wDAK5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,0BAI5B,CAAC;AAEH,eAAO,MAAM,qBAAqB,kDAMhC,CAAC;AAEH,eAAO,MAAM,oBAAoB,2DAK/B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;CA0B3B,CAAC;AAGF,eAAO,MAAM,cAAc,GAAU,MAAM,OAAO,EAAE,KAAK,QAAQ,kBAQhE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,OAAO,KAAG,MAOzC,CAAC;AAUF,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAiChF,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.register = exports.metricsMiddleware = exports.metricsHandler = exports.businessMetrics = exports.databaseQueriesTotal = exports.databaseQueryDuration = exports.activeConnections = exports.httpRequestsTotal = exports.httpRequestDuration = void 0;
6
+ exports.register = exports.metricsMiddleware = exports.routeLabel = exports.metricsHandler = exports.businessMetrics = exports.databaseQueriesTotal = exports.databaseQueryDuration = exports.activeConnections = exports.httpRequestsTotal = exports.httpRequestDuration = void 0;
7
7
  const prom_client_1 = __importDefault(require("prom-client"));
8
8
  // Create a Registry which registers the metrics
9
9
  const register = new prom_client_1.default.Registry();
@@ -15,48 +15,62 @@ register.setDefaultLabels({
15
15
  });
16
16
  // Enable the collection of default metrics
17
17
  prom_client_1.default.collectDefaultMetrics({ register });
18
- // Custom metrics
18
+ // Custom metrics. Each names `register` explicitly: left out, prom-client adds a
19
+ // metric to its global registry, which is not the one /metrics serves, and the
20
+ // metric is counted but never shown.
19
21
  exports.httpRequestDuration = new prom_client_1.default.Histogram({
22
+ registers: [register],
20
23
  name: 'http_request_duration_seconds',
21
24
  help: 'Duration of HTTP requests in seconds',
22
25
  labelNames: ['method', 'route', 'status_code'],
23
- buckets: [0.1, 0.5, 1, 2, 5, 10]
26
+ // Starts at 5 ms: with the database on the same machine most requests finish
27
+ // in well under 100 ms, and a first bucket of 0.1 s would hold nearly all of
28
+ // them and make every percentile read "under 100 ms".
29
+ buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
24
30
  });
25
31
  exports.httpRequestsTotal = new prom_client_1.default.Counter({
32
+ registers: [register],
26
33
  name: 'http_requests_total',
27
34
  help: 'Total number of HTTP requests',
28
35
  labelNames: ['method', 'route', 'status_code']
29
36
  });
30
37
  exports.activeConnections = new prom_client_1.default.Gauge({
38
+ registers: [register],
31
39
  name: 'active_connections',
32
40
  help: 'Number of active connections'
33
41
  });
34
42
  exports.databaseQueryDuration = new prom_client_1.default.Histogram({
43
+ registers: [register],
35
44
  name: 'database_query_duration_seconds',
36
45
  help: 'Duration of database queries in seconds',
37
46
  labelNames: ['operation', 'collection'],
38
- buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5]
47
+ buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 5]
39
48
  });
40
49
  exports.databaseQueriesTotal = new prom_client_1.default.Counter({
50
+ registers: [register],
41
51
  name: 'database_queries_total',
42
52
  help: 'Total number of database queries',
43
53
  labelNames: ['operation', 'collection', 'status']
44
54
  });
45
55
  exports.businessMetrics = {
46
56
  albumsCreated: new prom_client_1.default.Counter({
57
+ registers: [register],
47
58
  name: 'albums_created_total',
48
59
  help: 'Total number of albums created'
49
60
  }),
50
61
  filesUploaded: new prom_client_1.default.Counter({
62
+ registers: [register],
51
63
  name: 'files_uploaded_total',
52
64
  help: 'Total number of files uploaded',
53
65
  labelNames: ['file_type']
54
66
  }),
55
67
  usersRegistered: new prom_client_1.default.Counter({
68
+ registers: [register],
56
69
  name: 'users_registered_total',
57
70
  help: 'Total number of users registered'
58
71
  }),
59
72
  paymentsProcessed: new prom_client_1.default.Counter({
73
+ registers: [register],
60
74
  name: 'payments_processed_total',
61
75
  help: 'Total number of payments processed',
62
76
  labelNames: ['status', 'method']
@@ -74,24 +88,59 @@ const metricsHandler = async (_req, res) => {
74
88
  }
75
89
  };
76
90
  exports.metricsHandler = metricsHandler;
91
+ /**
92
+ * The route as it is written in the code ("/api/chat/conversations/:id"), not the
93
+ * URL that was asked for.
94
+ *
95
+ * A label per distinct URL is a time series per distinct URL: every id, every
96
+ * query string and every scanner probing for /wp-login.php adds series that
97
+ * Prometheus keeps in memory, and a chart "by route" turns into thousands of
98
+ * lines. A request no route matched (404s, probes) is one `unmatched` series.
99
+ */
100
+ const routeLabel = (req) => {
101
+ const path = req.route?.path;
102
+ // A route can also be a regular expression or a list; the expression is still
103
+ // one fixed label, and a list is rare enough to share the unmatched one.
104
+ const pattern = typeof path === 'string' ? path : path instanceof RegExp ? path.source : undefined;
105
+ if (pattern === undefined)
106
+ return 'unmatched';
107
+ return `${req.baseUrl ?? ''}${pattern}`;
108
+ };
109
+ exports.routeLabel = routeLabel;
110
+ /**
111
+ * A response the browser holds open to receive events (chat, a collage being
112
+ * edited). It lasts as long as the page does, up to half an hour.
113
+ */
114
+ const isEventStream = (res) => String(res.getHeader('Content-Type') ?? '').includes('text/event-stream');
77
115
  // Middleware to collect HTTP metrics
78
116
  const metricsMiddleware = (req, res, next) => {
79
117
  const start = Date.now();
80
- const { method, url } = req;
81
- // Increment active connections
118
+ const { method } = req;
82
119
  exports.activeConnections.inc();
120
+ // 'close' comes for every response, also one the client gave up on before it
121
+ // was sent; 'finish' only for one sent in full. Counting down on 'finish'
122
+ // left every abandoned request, and every stream the browser closed, counted
123
+ // as open for good.
124
+ let closed = false;
125
+ res.on('close', () => {
126
+ if (closed)
127
+ return;
128
+ closed = true;
129
+ exports.activeConnections.dec();
130
+ });
83
131
  res.on('finish', () => {
84
132
  const duration = (Date.now() - start) / 1000; // Convert to seconds
85
- const { statusCode } = res;
86
- // Record metrics
87
- exports.httpRequestDuration
88
- .labels(method, url, statusCode.toString())
89
- .observe(duration);
90
- exports.httpRequestsTotal
91
- .labels(method, url, statusCode.toString())
92
- .inc();
93
- // Decrement active connections
94
- exports.activeConnections.dec();
133
+ const status = res.statusCode.toString();
134
+ // Read now, not at the start: the router has not matched anything yet then.
135
+ const route = (0, exports.routeLabel)(req);
136
+ exports.httpRequestsTotal.labels(method, route, status).inc();
137
+ // A stream opened with GET is counted but not timed: its "duration" is how
138
+ // long the page stayed open, and a few of those would make every latency
139
+ // percentile read in minutes. A POST answered as a stream (an MCP tool
140
+ // call) is an ordinary request that happens to reply in events, so it is timed.
141
+ if (method === 'GET' && isEventStream(res))
142
+ return;
143
+ exports.httpRequestDuration.labels(method, route, status).observe(duration);
95
144
  });
96
145
  next();
97
146
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"requestLogger.d.ts","sourceRoot":"","sources":["../../src/middleware/requestLogger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI1D;;;GAGG;AACH,eAAO,MAAM,aAAa,yIAHM,CAAA,yBAS9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,wBAAwB,+DAAqC,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAoBlF,CAAC"}
1
+ {"version":3,"file":"requestLogger.d.ts","sourceRoot":"","sources":["../../src/middleware/requestLogger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAkB1D;;;GAGG;AACH,eAAO,MAAM,aAAa,yIAnBU,CAAC,yBAyBnC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,wBAAwB,+DAAqC,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAoBlF,CAAC"}
@@ -7,11 +7,22 @@ exports.simpleRequestLogger = exports.requestLoggerWithMetrics = exports.request
7
7
  const morgan_1 = __importDefault(require("morgan"));
8
8
  const logger_1 = __importDefault(require("../logging/logger"));
9
9
  const metrics_1 = require("../metrics");
10
+ const withoutQuery_1 = require("../utils/withoutQuery");
11
+ morgan_1.default.token('path', (req) => (0, withoutQuery_1.withoutQuery)(req.originalUrl ?? req.url));
12
+ morgan_1.default.token('referrer-path', (req) => {
13
+ const referrer = req.headers.referer ?? req.headers.referrer;
14
+ return (0, withoutQuery_1.withoutQuery)(Array.isArray(referrer) ? referrer[0] : referrer) || '-';
15
+ });
16
+ /**
17
+ * Apache's "combined" line, with the path and the referrer cut at the query
18
+ * string: a signed file link's token is in its query (see withoutQuery).
19
+ */
20
+ const COMBINED_WITHOUT_QUERY = ':remote-addr - :remote-user [:date[clf]] ":method :path HTTP/:http-version" :status :res[content-length] ":referrer-path" ":user-agent"';
10
21
  /**
11
22
  * Morgan middleware configured to use Winston logger
12
23
  * Logs HTTP requests in structured format
13
24
  */
14
- exports.requestLogger = (0, morgan_1.default)('combined', {
25
+ exports.requestLogger = (0, morgan_1.default)(COMBINED_WITHOUT_QUERY, {
15
26
  stream: {
16
27
  write: (message) => {
17
28
  logger_1.default.http(message.trim());
@@ -30,14 +41,14 @@ exports.requestLoggerWithMetrics = [exports.requestLogger, metrics_1.metricsMidd
30
41
  const simpleRequestLogger = (req, res, next) => {
31
42
  const start = Date.now();
32
43
  const correlationId = req.correlationId || 'unknown';
33
- logger_1.default.info(`→ ${req.method} ${req.url}`, {
44
+ logger_1.default.info(`→ ${req.method} ${(0, withoutQuery_1.withoutQuery)(req.url)}`, {
34
45
  correlationId,
35
46
  ip: req.ip,
36
47
  userAgent: req.get('User-Agent')
37
48
  });
38
49
  res.on('finish', () => {
39
50
  const duration = Date.now() - start;
40
- logger_1.default.info(`← ${req.method} ${req.url} ${res.statusCode} ${duration}ms`, {
51
+ logger_1.default.info(`← ${req.method} ${(0, withoutQuery_1.withoutQuery)(req.url)} ${res.statusCode} ${duration}ms`, {
41
52
  correlationId,
42
53
  statusCode: res.statusCode,
43
54
  duration
@@ -1 +1 @@
1
- {"version":3,"file":"requestLogger.js","sourceRoot":"","sources":["../../src/middleware/requestLogger.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,+DAAuC;AACvC,wCAA+C;AAE/C;;;GAGG;AACU,QAAA,aAAa,GAAG,IAAA,gBAAM,EAAC,UAAU,EAAE;IAC9C,MAAM,EAAE;QACN,KAAK,EAAE,CAAC,OAAe,EAAE,EAAE;YACzB,gBAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9B,CAAC;KACF;CACF,CAAC,CAAC;AAEH;;;GAGG;AACU,QAAA,wBAAwB,GAAG,CAAC,qBAAa,EAAE,2BAAiB,CAAC,CAAC;AAE3E;;;GAGG;AACI,MAAM,mBAAmB,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACrF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,IAAI,SAAS,CAAC;IAErD,gBAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,EAAE;QACxC,aAAa;QACb,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;KACjC,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACpC,gBAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,QAAQ,IAAI,EAAE;YACxE,aAAa;YACb,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ;SACT,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AApBW,QAAA,mBAAmB,uBAoB9B"}
1
+ {"version":3,"file":"requestLogger.js","sourceRoot":"","sources":["../../src/middleware/requestLogger.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,+DAAuC;AACvC,wCAA+C;AAC/C,wDAAqD;AAErD,gBAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAY,EAAE,EAAE,CAAC,IAAA,2BAAY,EAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AACjF,gBAAM,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,GAAY,EAAE,EAAE;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC7D,OAAO,IAAA,2BAAY,EAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,sBAAsB,GAC1B,yIAAyI,CAAC;AAE5I;;;GAGG;AACU,QAAA,aAAa,GAAG,IAAA,gBAAM,EAAC,sBAAsB,EAAE;IAC1D,MAAM,EAAE;QACN,KAAK,EAAE,CAAC,OAAe,EAAE,EAAE;YACzB,gBAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9B,CAAC;KACF;CACF,CAAC,CAAC;AAEH;;;GAGG;AACU,QAAA,wBAAwB,GAAG,CAAC,qBAAa,EAAE,2BAAiB,CAAC,CAAC;AAE3E;;;GAGG;AACI,MAAM,mBAAmB,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACrF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,IAAI,SAAS,CAAC;IAErD,gBAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,IAAA,2BAAY,EAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE;QACtD,aAAa;QACb,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;KACjC,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACpC,gBAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,IAAA,2BAAY,EAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,IAAI,QAAQ,IAAI,EAAE;YACtF,aAAa;YACb,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ;SACT,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AApBW,QAAA,mBAAmB,uBAoB9B"}
@@ -1,5 +1,6 @@
1
1
  import { Tracer, Span, Context, Attributes, AttributeValue } from '@opentelemetry/api';
2
2
  import { Request, Response, NextFunction } from 'express';
3
+ export declare const sampleRatio: (env?: NodeJS.ProcessEnv) => number;
3
4
  export declare const initTracer: (serviceName: string) => Tracer;
4
5
  export declare const getTracer: () => Tracer;
5
6
  export declare const startSpan: (operationName: string, parent?: Span | Context) => Span;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tracing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,MAAM,EACN,IAAI,EACJ,OAAO,EACP,UAAU,EACV,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQ1D,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,MAiBhD,CAAC;AAKF,eAAO,MAAM,SAAS,QAAO,MAK5B,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,eAAe,MAAM,EAAE,SAAS,IAAI,GAAG,OAAO,SASvE,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAmBhF,CAAC;AAGF,eAAO,MAAM,eAAe,GAAI,eAAe,MAAM,EAAE,QAAQ,IAAI,GAAG,OAAO,SAE5E,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,MAAM,IAAI,EAAE,OAAO,MAAM,EAAE,OAAO,UAAU,SAErE,CAAC;AAGF,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,KAAK,MAAM,EAAE,OAAO,cAAc,SAExE,CAAC;AAGF,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI;;CAIvC,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,KACrD,OAEF,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tracing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,MAAM,EACN,IAAI,EACJ,OAAO,EACP,UAAU,EACV,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAkB1D,eAAO,MAAM,WAAW,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,MAIlE,CAAC;AAaF,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,MAehD,CAAC;AAKF,eAAO,MAAM,SAAS,QAAO,MAK5B,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,eAAe,MAAM,EAAE,SAAS,IAAI,GAAG,OAAO,SASvE,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAoChF,CAAC;AAGF,eAAO,MAAM,eAAe,GAAI,eAAe,MAAM,EAAE,QAAQ,IAAI,GAAG,OAAO,SAE5E,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,MAAM,IAAI,EAAE,OAAO,MAAM,EAAE,OAAO,UAAU,SAErE,CAAC;AAGF,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,KAAK,MAAM,EAAE,OAAO,cAAc,SAExE,CAAC;AAGF,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI;;CAIvC,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,KACrD,OAEF,CAAC"}
@@ -1,24 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractSpanContext = exports.injectHeaders = exports.setSpanTag = exports.logToSpan = exports.createChildSpan = exports.tracingMiddleware = exports.startSpan = exports.getTracer = exports.initTracer = void 0;
3
+ exports.extractSpanContext = exports.injectHeaders = exports.setSpanTag = exports.logToSpan = exports.createChildSpan = exports.tracingMiddleware = exports.startSpan = exports.getTracer = exports.initTracer = exports.sampleRatio = void 0;
4
4
  const api_1 = require("@opentelemetry/api");
5
5
  const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
6
6
  const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
7
7
  const resources_1 = require("@opentelemetry/resources");
8
8
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
9
+ const metrics_1 = require("../metrics");
10
+ const withoutQuery_1 = require("../utils/withoutQuery");
11
+ // Share of requests that are traced and exported. Every span goes through
12
+ // Elasticsearch on the Pi, so a tenth is the default; set OTEL_TRACES_SAMPLER_ARG
13
+ // (0 to 1) to change it. A request that arrives already sampled by another
14
+ // service stays sampled, so a trace is whole or absent, never half.
15
+ const DEFAULT_SAMPLE_RATIO = 0.1;
16
+ const sampleRatio = (env = process.env) => {
17
+ const raw = env.OTEL_TRACES_SAMPLER_ARG;
18
+ const ratio = raw !== undefined && raw.trim() !== '' ? Number(raw) : NaN;
19
+ return Number.isFinite(ratio) && ratio >= 0 && ratio <= 1 ? ratio : DEFAULT_SAMPLE_RATIO;
20
+ };
21
+ exports.sampleRatio = sampleRatio;
9
22
  // Initialize OpenTelemetry tracer provider, exporting via OTLP/HTTP.
10
- // Endpoint resolution: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT > JAEGER_ENDPOINT > in-cluster collector default.
23
+ // Endpoint resolution: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT > JAEGER_ENDPOINT.
24
+ //
25
+ // With neither set there is no exporter at all. This used to fall back to an
26
+ // in-cluster `jaeger-collector` host, which is only a good default where one is
27
+ // running. Where it is not, every service tried to send its spans to a name that
28
+ // does not exist every few seconds, resolving it again each time. A failed
29
+ // lookup holds one of Node's few shared worker threads (the same ones that
30
+ // resolve every other hostname the service calls) until the resolver gives up.
31
+ // Spans are still created and passed along in a trace context; they simply have
32
+ // nowhere to be sent until an endpoint is named.
11
33
  const initTracer = (serviceName) => {
12
- const url = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
13
- process.env.JAEGER_ENDPOINT ||
14
- 'http://jaeger-collector:4318/v1/traces';
34
+ const url = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || process.env.JAEGER_ENDPOINT;
15
35
  const provider = new sdk_trace_node_1.NodeTracerProvider({
36
+ sampler: new sdk_trace_node_1.ParentBasedSampler({ root: new sdk_trace_node_1.TraceIdRatioBasedSampler((0, exports.sampleRatio)()) }),
16
37
  resource: (0, resources_1.resourceFromAttributes)({
17
38
  [semantic_conventions_1.ATTR_SERVICE_NAME]: serviceName,
18
39
  [semantic_conventions_1.ATTR_SERVICE_VERSION]: process.env.npm_package_version || '1.0.0',
19
40
  'deployment.environment': process.env.NODE_ENV || 'development',
20
41
  }),
21
- spanProcessors: [new sdk_trace_node_1.BatchSpanProcessor(new exporter_trace_otlp_http_1.OTLPTraceExporter({ url }))],
42
+ spanProcessors: url ? [new sdk_trace_node_1.BatchSpanProcessor(new exporter_trace_otlp_http_1.OTLPTraceExporter({ url }))] : [],
22
43
  });
23
44
  provider.register();
24
45
  return provider.getTracer(serviceName);
@@ -49,14 +70,33 @@ const tracingMiddleware = (req, res, next) => {
49
70
  const parentContext = api_1.propagation.extract(api_1.context.active(), req.headers);
50
71
  const span = (0, exports.getTracer)().startSpan(`${req.method} ${req.path}`, { kind: api_1.SpanKind.SERVER }, parentContext);
51
72
  span.setAttribute('http.method', req.method);
52
- span.setAttribute('http.url', req.url);
73
+ // Without the query: a signed file link carries its token there.
74
+ span.setAttribute('http.url', (0, withoutQuery_1.withoutQuery)(req.originalUrl ?? req.url));
53
75
  // Add span to request for use in handlers
54
76
  req.span = span;
55
- res.on('finish', () => {
77
+ // Ended on whichever comes first: 'finish' for a response sent in full,
78
+ // 'close' for one the client gave up on (a stream the page closed). A span
79
+ // that is never ended is never exported.
80
+ let ended = false;
81
+ const end = () => {
82
+ if (ended)
83
+ return;
84
+ ended = true;
85
+ // Named for the route, not the URL: "GET /api/items/:id" is one operation
86
+ // in the trace list, where the raw path would be one per id.
87
+ const route = (0, metrics_1.routeLabel)(req);
88
+ span.updateName(`${req.method} ${route}`);
89
+ span.setAttribute('http.route', route);
56
90
  span.setAttribute('http.status_code', res.statusCode);
91
+ if (!res.writableFinished)
92
+ span.setAttribute('http.aborted', true);
57
93
  span.end();
58
- });
59
- next();
94
+ };
95
+ res.on('finish', end);
96
+ res.on('close', end);
97
+ // Run the rest of the request with this span active, so the spans made below
98
+ // it (database queries, calls to other services) become its children.
99
+ api_1.context.with(api_1.trace.setSpan(parentContext, span), next);
60
100
  };
61
101
  exports.tracingMiddleware = tracingMiddleware;
62
102
  // Helper to create child spans
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tracing/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tracing/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A URL's path without its query string.
3
+ *
4
+ * For anything that is kept: log lines and trace spans. Signed file links carry
5
+ * their token in the query (`?token=...&expires=...`) and an OAuth redirect its
6
+ * code, and whoever can read the logs could use either until it expires.
7
+ */
8
+ export declare const withoutQuery: (url: string | undefined) => string;
9
+ //# sourceMappingURL=withoutQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withoutQuery.d.ts","sourceRoot":"","sources":["../../src/utils/withoutQuery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,MAA4C,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withoutQuery = void 0;
4
+ /**
5
+ * A URL's path without its query string.
6
+ *
7
+ * For anything that is kept: log lines and trace spans. Signed file links carry
8
+ * their token in the query (`?token=...&expires=...`) and an OAuth redirect its
9
+ * code, and whoever can read the logs could use either until it expires.
10
+ */
11
+ const withoutQuery = (url) => (url ?? '').split('?', 1)[0] ?? '';
12
+ exports.withoutQuery = withoutQuery;
13
+ //# sourceMappingURL=withoutQuery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withoutQuery.js","sourceRoot":"","sources":["../../src/utils/withoutQuery.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACI,MAAM,YAAY,GAAG,CAAC,GAAuB,EAAU,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAAvF,QAAA,YAAY,gBAA2E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tumbaland/backend-core",
3
- "version": "1.53.0",
3
+ "version": "1.55.0",
4
4
  "description": "Core shared functionality for Tumbaland backend services",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",