@platformatic/basic 3.13.0 → 3.14.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.
package/config.d.ts CHANGED
@@ -13,7 +13,20 @@ export interface PlatformaticBasicConfig {
13
13
  services?: {
14
14
  [k: string]: unknown;
15
15
  }[];
16
- workers?: number | string;
16
+ workers?:
17
+ | number
18
+ | string
19
+ | {
20
+ static?: number;
21
+ dynamic?: boolean;
22
+ minimum?: number;
23
+ maximum?: number;
24
+ total?: number;
25
+ maxMemory?: number;
26
+ cooldown?: number;
27
+ gracePeriod?: number;
28
+ [k: string]: unknown;
29
+ };
17
30
  workersRestartDelay?: number | string;
18
31
  logger?: {
19
32
  level: (
@@ -221,6 +234,37 @@ export interface PlatformaticBasicConfig {
221
234
  };
222
235
  plugins?: string[];
223
236
  timeout?: number | string;
237
+ /**
238
+ * Configuration for exporting metrics to an OTLP endpoint
239
+ */
240
+ otlpExporter?: {
241
+ /**
242
+ * Enable or disable OTLP metrics export
243
+ */
244
+ enabled?: boolean | string;
245
+ /**
246
+ * OTLP endpoint URL (e.g., http://collector:4318/v1/metrics)
247
+ */
248
+ endpoint: string;
249
+ /**
250
+ * Interval in milliseconds between metric pushes
251
+ */
252
+ interval?: number | string;
253
+ /**
254
+ * Additional HTTP headers for authentication
255
+ */
256
+ headers?: {
257
+ [k: string]: string;
258
+ };
259
+ /**
260
+ * Service name for OTLP resource attributes
261
+ */
262
+ serviceName?: string;
263
+ /**
264
+ * Service version for OTLP resource attributes
265
+ */
266
+ serviceVersion?: string;
267
+ };
224
268
  };
225
269
  telemetry?: {
226
270
  enabled?: boolean | string;
@@ -304,13 +348,28 @@ export interface PlatformaticBasicConfig {
304
348
  maxTotalMemory?: number;
305
349
  minWorkers?: number;
306
350
  maxWorkers?: number;
351
+ cooldownSec?: number;
352
+ gracePeriod?: number;
353
+ /**
354
+ * @deprecated
355
+ */
307
356
  scaleUpELU?: number;
357
+ /**
358
+ * @deprecated
359
+ */
308
360
  scaleDownELU?: number;
361
+ /**
362
+ * @deprecated
363
+ */
309
364
  timeWindowSec?: number;
365
+ /**
366
+ * @deprecated
367
+ */
310
368
  scaleDownTimeWindowSec?: number;
311
- cooldownSec?: number;
369
+ /**
370
+ * @deprecated
371
+ */
312
372
  scaleIntervalSec?: number;
313
- gracePeriod?: number;
314
373
  };
315
374
  inspectorOptions?: {
316
375
  host?: string;
package/lib/capability.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  kMetadata,
7
7
  kTimeout
8
8
  } from '@platformatic/foundation'
9
- import { client, collectMetrics, ensureMetricsGroup } from '@platformatic/metrics'
9
+ import { client, collectMetrics, ensureMetricsGroup, setupOtlpExporter } from '@platformatic/metrics'
10
10
  import { parseCommandString } from 'execa'
11
11
  import { spawn } from 'node:child_process'
12
12
  import EventEmitter, { once } from 'node:events'
@@ -50,7 +50,8 @@ export class BaseCapability extends EventEmitter {
50
50
  subprocessForceClose
51
51
  subprocessTerminationSignal
52
52
  logger
53
- metricsRegistr
53
+ metricsRegistry
54
+ otlpBridge
54
55
 
55
56
  #subprocessStarted
56
57
  #metricsCollected
@@ -69,7 +70,7 @@ export class BaseCapability extends EventEmitter {
69
70
  this.standardStreams = standardStreams
70
71
 
71
72
  this.applicationId = this.context.applicationId
72
- this.workerId = this.context.worker.count > 1 ? this.context.worker.index : undefined
73
+ this.workerId = this.context.worker.index
73
74
  this.telemetryConfig = this.context.telemetryConfig
74
75
  this.serverConfig = deepmerge(this.context.serverConfig ?? {}, config.server ?? {})
75
76
  this.openapiSchema = null
@@ -118,6 +119,7 @@ export class BaseCapability extends EventEmitter {
118
119
  this.registerGlobals({ prometheus: { client, registry: this.metricsRegistry } })
119
120
  }
120
121
 
122
+ this.otlpBridge = null
121
123
  this.#metricsCollected = false
122
124
  this.#pendingDependenciesWaits = new Set()
123
125
  }
@@ -156,6 +158,12 @@ export class BaseCapability extends EventEmitter {
156
158
  if (this.#pendingDependenciesWaits.size > 0) {
157
159
  await Promise.allSettled(this.#pendingDependenciesWaits)
158
160
  }
161
+
162
+ // Stop OTLP bridge if running
163
+ if (this.otlpBridge) {
164
+ this.otlpBridge.stop()
165
+ this.otlpBridge = null
166
+ }
159
167
  }
160
168
 
161
169
  build () {
@@ -620,6 +628,7 @@ export class BaseCapability extends EventEmitter {
620
628
 
621
629
  await this.#collectMetrics()
622
630
  this.#setHttpCacheMetrics()
631
+ await this.#setupOtlpExporter()
623
632
  }
624
633
 
625
634
  async #collectMetrics () {
@@ -736,6 +745,33 @@ export class BaseCapability extends EventEmitter {
736
745
  globalThis.platformatic.onActiveResourcesEventLoop = val => activeResourcesEventLoopMetric.set(val)
737
746
  }
738
747
 
748
+ async #setupOtlpExporter () {
749
+ const metricsConfig = this.context.metricsConfig
750
+ if (!metricsConfig || !metricsConfig.otlpExporter) {
751
+ return
752
+ }
753
+
754
+ // Wait for telemetry to be ready before loading promotel to avoid race condition
755
+ if (globalThis.platformatic?.telemetryReady) {
756
+ await globalThis.platformatic.telemetryReady
757
+ }
758
+
759
+ // Setup and start OTLP exporter bridge
760
+ this.otlpBridge = await setupOtlpExporter(
761
+ this.metricsRegistry,
762
+ metricsConfig.otlpExporter,
763
+ this.applicationId
764
+ )
765
+
766
+ if (this.otlpBridge) {
767
+ this.otlpBridge.start()
768
+ this.logger.info({
769
+ endpoint: metricsConfig.otlpExporter.endpoint,
770
+ interval: metricsConfig.otlpExporter.interval || 60000
771
+ }, 'OTLP metrics exporter started')
772
+ }
773
+ }
774
+
739
775
  async #invalidateHttpCache (opts = {}) {
740
776
  await globalThis[kITC].send('invalidateHttpCache', opts)
741
777
  }
@@ -100,6 +100,10 @@ export class ChildProcess extends ITC {
100
100
  }
101
101
 
102
102
  if (!handled) {
103
+ this.#logger.warn(
104
+ `Please register a "close" event handler in globalThis.platformatic.events for application "${this.applicationId}" to make sure resources have been closed properly and avoid exit timeouts.`
105
+ )
106
+
103
107
  // No user event, just exit without errors
104
108
  setImmediate(() => {
105
109
  process.exit(process.exitCode ?? 0)
@@ -327,7 +331,7 @@ export class ChildProcess extends ITC {
327
331
  pinoOptions.timestamp = buildPinoTimestamp(loggerOptions.timestamp)
328
332
  }
329
333
 
330
- if (loggerOptions.base !== null && typeof globalThis.platformatic.workerId !== 'undefined') {
334
+ if (loggerOptions.base !== null) {
331
335
  pinoOptions.base = {
332
336
  ...(pinoOptions.base ?? {}),
333
337
  pid: process.pid,
@@ -401,10 +405,7 @@ export class ChildProcess extends ITC {
401
405
  }
402
406
 
403
407
  #setupHandlers () {
404
- const errorLabel =
405
- typeof globalThis.platformatic.workerId !== 'undefined'
406
- ? `worker ${globalThis.platformatic.workerId} of the application "${globalThis.platformatic.applicationId}"`
407
- : `application "${globalThis.platformatic.applicationId}"`
408
+ const errorLabel = `worker ${globalThis.platformatic.workerId} of the application "${globalThis.platformatic.applicationId}"`
408
409
 
409
410
  function handleUnhandled (type, err) {
410
411
  this.#logger.error({ err: ensureLoggableError(err) }, `Child process for the ${errorLabel} threw an ${type}.`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/basic",
3
- "version": "3.13.0",
3
+ "version": "3.14.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -25,10 +25,10 @@
25
25
  "split2": "^4.2.0",
26
26
  "undici": "^7.0.0",
27
27
  "ws": "^8.18.0",
28
- "@platformatic/foundation": "3.13.0",
29
- "@platformatic/itc": "3.13.0",
30
- "@platformatic/telemetry": "3.13.0",
31
- "@platformatic/metrics": "3.13.0"
28
+ "@platformatic/itc": "3.14.0",
29
+ "@platformatic/foundation": "3.14.0",
30
+ "@platformatic/metrics": "3.14.0",
31
+ "@platformatic/telemetry": "3.14.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "cleaner-spec-reporter": "^0.5.0",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/basic/3.13.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/basic/3.14.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Basic Config",
5
5
  "type": "object",
@@ -71,11 +71,27 @@
71
71
  "workers": {
72
72
  "anyOf": [
73
73
  {
74
- "type": "number",
75
- "minimum": 1
74
+ "type": "number"
76
75
  },
77
76
  {
78
77
  "type": "string"
78
+ },
79
+ {
80
+ "type": "object",
81
+ "properties": {
82
+ "static": {
83
+ "type": "number",
84
+ "minimum": 1
85
+ },
86
+ "minimum": {
87
+ "type": "number",
88
+ "minimum": 1
89
+ },
90
+ "maximum": {
91
+ "type": "number",
92
+ "minimum": 0
93
+ }
94
+ }
79
95
  }
80
96
  ]
81
97
  },
@@ -296,6 +312,43 @@
296
312
  },
297
313
  {
298
314
  "type": "string"
315
+ },
316
+ {
317
+ "type": "object",
318
+ "properties": {
319
+ "static": {
320
+ "type": "number",
321
+ "minimum": 1
322
+ },
323
+ "dynamic": {
324
+ "type": "boolean",
325
+ "default": false
326
+ },
327
+ "minimum": {
328
+ "type": "number",
329
+ "minimum": 1
330
+ },
331
+ "maximum": {
332
+ "type": "number",
333
+ "minimum": 0
334
+ },
335
+ "total": {
336
+ "type": "number",
337
+ "minimum": 1
338
+ },
339
+ "maxMemory": {
340
+ "type": "number",
341
+ "minimum": 0
342
+ },
343
+ "cooldown": {
344
+ "type": "number",
345
+ "minimum": 0
346
+ },
347
+ "gracePeriod": {
348
+ "type": "number",
349
+ "minimum": 0
350
+ }
351
+ }
299
352
  }
300
353
  ]
301
354
  },
@@ -1072,6 +1125,58 @@
1072
1125
  }
1073
1126
  ],
1074
1127
  "default": 10000
1128
+ },
1129
+ "otlpExporter": {
1130
+ "type": "object",
1131
+ "description": "Configuration for exporting metrics to an OTLP endpoint",
1132
+ "properties": {
1133
+ "enabled": {
1134
+ "anyOf": [
1135
+ {
1136
+ "type": "boolean"
1137
+ },
1138
+ {
1139
+ "type": "string"
1140
+ }
1141
+ ],
1142
+ "description": "Enable or disable OTLP metrics export"
1143
+ },
1144
+ "endpoint": {
1145
+ "type": "string",
1146
+ "description": "OTLP endpoint URL (e.g., http://collector:4318/v1/metrics)"
1147
+ },
1148
+ "interval": {
1149
+ "anyOf": [
1150
+ {
1151
+ "type": "integer"
1152
+ },
1153
+ {
1154
+ "type": "string"
1155
+ }
1156
+ ],
1157
+ "default": 60000,
1158
+ "description": "Interval in milliseconds between metric pushes"
1159
+ },
1160
+ "headers": {
1161
+ "type": "object",
1162
+ "additionalProperties": {
1163
+ "type": "string"
1164
+ },
1165
+ "description": "Additional HTTP headers for authentication"
1166
+ },
1167
+ "serviceName": {
1168
+ "type": "string",
1169
+ "description": "Service name for OTLP resource attributes"
1170
+ },
1171
+ "serviceVersion": {
1172
+ "type": "string",
1173
+ "description": "Service version for OTLP resource attributes"
1174
+ }
1175
+ },
1176
+ "required": [
1177
+ "endpoint"
1178
+ ],
1179
+ "additionalProperties": false
1075
1180
  }
1076
1181
  },
1077
1182
  "additionalProperties": false
@@ -1231,35 +1336,40 @@
1231
1336
  "type": "number",
1232
1337
  "minimum": 1
1233
1338
  },
1339
+ "cooldownSec": {
1340
+ "type": "number",
1341
+ "minimum": 0
1342
+ },
1343
+ "gracePeriod": {
1344
+ "type": "number",
1345
+ "minimum": 0
1346
+ },
1234
1347
  "scaleUpELU": {
1235
1348
  "type": "number",
1236
1349
  "minimum": 0,
1237
- "maximum": 1
1350
+ "maximum": 1,
1351
+ "deprecated": true
1238
1352
  },
1239
1353
  "scaleDownELU": {
1240
1354
  "type": "number",
1241
1355
  "minimum": 0,
1242
- "maximum": 1
1356
+ "maximum": 1,
1357
+ "deprecated": true
1243
1358
  },
1244
1359
  "timeWindowSec": {
1245
1360
  "type": "number",
1246
- "minimum": 0
1361
+ "minimum": 0,
1362
+ "deprecated": true
1247
1363
  },
1248
1364
  "scaleDownTimeWindowSec": {
1249
1365
  "type": "number",
1250
- "minimum": 0
1251
- },
1252
- "cooldownSec": {
1253
- "type": "number",
1254
- "minimum": 0
1366
+ "minimum": 0,
1367
+ "deprecated": true
1255
1368
  },
1256
1369
  "scaleIntervalSec": {
1257
1370
  "type": "number",
1258
- "minimum": 0
1259
- },
1260
- "gracePeriod": {
1261
- "type": "number",
1262
- "minimum": 0
1371
+ "minimum": 0,
1372
+ "deprecated": true
1263
1373
  }
1264
1374
  },
1265
1375
  "additionalProperties": false