@platformatic/basic 2.5.4 → 2.5.5-alpha.2

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/lib/base.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { deepmerge } from '@platformatic/utils'
2
+ import { collectMetrics } from '@platformatic/metrics'
2
3
  import { parseCommandString } from 'execa'
3
4
  import { spawn } from 'node:child_process'
4
5
  import { once } from 'node:events'
@@ -12,7 +13,7 @@ import { cleanBasePath } from './utils.js'
12
13
  import { ChildManager } from './worker/child-manager.js'
13
14
 
14
15
  export class BaseStackable {
15
- #childManager
16
+ childManager
16
17
  #subprocess
17
18
  #subprocessStarted
18
19
 
@@ -29,6 +30,10 @@ export class BaseStackable {
29
30
  this.graphqlSchema = null
30
31
  this.isEntrypoint = options.context.isEntrypoint
31
32
  this.isProduction = options.context.isProduction
33
+ this.metricsRegistry = null
34
+ this.startHttpTimer = null
35
+ this.endHttpTimer = null
36
+ this.clientWs = null
32
37
 
33
38
  // Setup the logger
34
39
  const pinoOptions = {
@@ -85,13 +90,6 @@ export class BaseStackable {
85
90
  return this
86
91
  }
87
92
 
88
- async collectMetrics () {
89
- return {
90
- defaultMetrics: true,
91
- httpMetrics: false
92
- }
93
- }
94
-
95
93
  async getOpenapiSchema () {
96
94
  return this.openapiSchema
97
95
  }
@@ -136,7 +134,7 @@ export class BaseStackable {
136
134
 
137
135
  this.logger.debug(`Executing "${command}" ...`)
138
136
 
139
- this.#childManager = new ChildManager({
137
+ this.childManager = new ChildManager({
140
138
  logger: this.logger,
141
139
  loader,
142
140
  scripts,
@@ -153,7 +151,7 @@ export class BaseStackable {
153
151
  })
154
152
 
155
153
  try {
156
- await this.#childManager.inject()
154
+ await this.childManager.inject()
157
155
 
158
156
  const subprocess = this.spawn(command)
159
157
 
@@ -182,15 +180,15 @@ export class BaseStackable {
182
180
  throw error
183
181
  }
184
182
  } finally {
185
- await this.#childManager.eject()
186
- await this.#childManager.close()
183
+ await this.childManager.eject()
184
+ await this.childManager.close()
187
185
  }
188
186
  }
189
187
 
190
188
  async startWithCommand (command, loader) {
191
189
  const config = this.configManager.current
192
190
  const basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
193
- this.#childManager = new ChildManager({
191
+ this.childManager = new ChildManager({
194
192
  logger: this.logger,
195
193
  loader,
196
194
  context: {
@@ -206,12 +204,12 @@ export class BaseStackable {
206
204
  }
207
205
  })
208
206
 
209
- this.#childManager.on('config', config => {
207
+ this.childManager.on('config', config => {
210
208
  this.subprocessConfig = config
211
209
  })
212
210
 
213
211
  try {
214
- await this.#childManager.inject()
212
+ await this.childManager.inject()
215
213
 
216
214
  this.subprocess = this.spawn(command)
217
215
 
@@ -234,35 +232,36 @@ export class BaseStackable {
234
232
 
235
233
  this.#subprocessStarted = true
236
234
  } catch (e) {
237
- this.#childManager.close('SIGKILL')
235
+ this.childManager.close('SIGKILL')
238
236
  throw new Error(`Cannot execute command "${command}": executable not found`)
239
237
  } finally {
240
- await this.#childManager.eject()
238
+ await this.childManager.eject()
241
239
  }
242
240
 
243
241
  // If the process exits prematurely, terminate the thread with the same code
244
242
  this.subprocess.on('exit', code => {
245
243
  if (this.#subprocessStarted && typeof code === 'number' && code !== 0) {
246
- this.#childManager.close('SIGKILL')
244
+ this.childManager.close('SIGKILL')
247
245
  process.exit(code)
248
246
  }
249
247
  })
250
248
 
251
- const [url] = await once(this.#childManager, 'url')
249
+ const [url, clientWs] = await once(this.childManager, 'url')
252
250
  this.url = url
251
+ this.clientWs = clientWs
253
252
  }
254
253
 
255
254
  async stopCommand () {
256
255
  this.#subprocessStarted = false
257
256
  const exitPromise = once(this.subprocess, 'exit')
258
257
 
259
- this.#childManager.close(this.subprocessTerminationSignal ?? 'SIGINT')
258
+ this.childManager.close(this.subprocessTerminationSignal ?? 'SIGINT')
260
259
  this.subprocess.kill(this.subprocessTerminationSignal ?? 'SIGINT')
261
260
  await exitPromise
262
261
  }
263
262
 
264
263
  getChildManager () {
265
- return this.#childManager
264
+ return this.childManager
266
265
  }
267
266
 
268
267
  spawn (command) {
@@ -273,4 +272,44 @@ export class BaseStackable {
273
272
  ? spawn(command, { cwd: this.root, shell: true, windowsVerbatimArguments: true })
274
273
  : spawn(executable, args, { cwd: this.root })
275
274
  }
275
+
276
+ async collectMetrics () {
277
+ let metricsConfig = this.options.context.metricsConfig
278
+ if (metricsConfig !== false) {
279
+ metricsConfig = {
280
+ defaultMetrics: true,
281
+ httpMetrics: true,
282
+ ...metricsConfig
283
+ }
284
+
285
+ if (this.childManager && this.clientWs) {
286
+ await this.childManager.send(this.clientWs, 'collectMetrics', {
287
+ serviceId: this.id,
288
+ metricsConfig
289
+ })
290
+ return
291
+ }
292
+
293
+ const { registry, startHttpTimer, endHttpTimer } = await collectMetrics(
294
+ this.id,
295
+ metricsConfig
296
+ )
297
+
298
+ this.metricsRegistry = registry
299
+ this.startHttpTimer = startHttpTimer
300
+ this.endHttpTimer = endHttpTimer
301
+ }
302
+ }
303
+
304
+ async getMetrics ({ format } = {}) {
305
+ if (this.childManager && this.clientWs) {
306
+ return this.childManager.send(this.clientWs, 'getMetrics', { format })
307
+ }
308
+
309
+ if (!this.metricsRegistry) return null
310
+
311
+ return format === 'json'
312
+ ? await this.metricsRegistry.getMetricsAsJSON()
313
+ : await this.metricsRegistry.metrics()
314
+ }
276
315
  }
@@ -44,6 +44,7 @@ export class ChildManager extends ITC {
44
44
  #socketPath
45
45
  #clients
46
46
  #requests
47
+ #currentSender
47
48
  #currentClient
48
49
  #listener
49
50
  #originalNodeOptions
@@ -97,11 +98,14 @@ export class ChildManager extends ITC {
97
98
 
98
99
  ws.on('message', raw => {
99
100
  try {
101
+ this.#currentSender = ws
100
102
  const message = JSON.parse(raw)
101
103
  this.#requests.set(message.reqId, ws)
102
104
  this.#listener(message)
103
105
  } catch (error) {
104
106
  this.#handleUnexpectedError(error, 'Handling a message failed.', exitCodes.MANAGER_MESSAGE_HANDLING_FAILED)
107
+ } finally {
108
+ this.#currentSender = null
105
109
  }
106
110
  })
107
111
 
@@ -182,6 +186,10 @@ export class ChildManager extends ITC {
182
186
  register(this.#loader, { data: this.#context })
183
187
  }
184
188
 
189
+ emit (...args) {
190
+ super.emit(...args, this.#currentSender)
191
+ }
192
+
185
193
  send (client, name, message) {
186
194
  this.#currentClient = client
187
195
  return super.send(name, message)
@@ -1,6 +1,7 @@
1
1
  import { ITC } from '@platformatic/itc'
2
2
  import { setupNodeHTTPTelemetry } from '@platformatic/telemetry'
3
3
  import { createPinoWritable, ensureLoggableError } from '@platformatic/utils'
4
+ import { collectMetrics } from '@platformatic/metrics'
4
5
  import { tracingChannel } from 'node:diagnostics_channel'
5
6
  import { once } from 'node:events'
6
7
  import { readFile } from 'node:fs/promises'
@@ -81,10 +82,22 @@ export class ChildProcess extends ITC {
81
82
  #socket
82
83
  #child
83
84
  #logger
85
+ #metricsRegistry
84
86
  #pendingMessages
85
87
 
86
88
  constructor () {
87
- super({ throwOnMissingHandler: false, name: `${process.env.PLT_MANAGER_ID}-child-process` })
89
+ super({
90
+ throwOnMissingHandler: false,
91
+ name: `${process.env.PLT_MANAGER_ID}-child-process`,
92
+ handlers: {
93
+ collectMetrics: (...args) => {
94
+ return this.#collectMetrics(...args)
95
+ },
96
+ getMetrics: (...args) => {
97
+ return this.#getMetrics(...args)
98
+ },
99
+ }
100
+ })
88
101
 
89
102
  /* c8 ignore next */
90
103
  const protocol = platform() === 'win32' ? 'ws+unix:' : 'ws+unix://'
@@ -152,6 +165,21 @@ export class ChildProcess extends ITC {
152
165
  this.#socket.close()
153
166
  }
154
167
 
168
+ async #collectMetrics ({ serviceId, metricsConfig }) {
169
+ const { registry } = await collectMetrics(serviceId, metricsConfig)
170
+ this.#metricsRegistry = registry
171
+ }
172
+
173
+ async #getMetrics ({ format } = {}) {
174
+ if (!this.#metricsRegistry) return null
175
+
176
+ const res = format === 'json'
177
+ ? await this.#metricsRegistry.getMetricsAsJSON()
178
+ : await this.#metricsRegistry.metrics()
179
+
180
+ return res
181
+ }
182
+
155
183
  #setupLogger () {
156
184
  // Since this is executed by user code, make sure we only override this in the main thread
157
185
  // The rest will be intercepted by the BaseStackable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/basic",
3
- "version": "2.5.4",
3
+ "version": "2.5.5-alpha.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,10 +23,11 @@
23
23
  "split2": "^4.2.0",
24
24
  "undici": "^6.19.5",
25
25
  "ws": "^8.18.0",
26
- "@platformatic/config": "2.5.4",
27
- "@platformatic/itc": "2.5.4",
28
- "@platformatic/telemetry": "2.5.4",
29
- "@platformatic/utils": "2.5.4"
26
+ "@platformatic/config": "2.5.5-alpha.2",
27
+ "@platformatic/telemetry": "2.5.5-alpha.2",
28
+ "@platformatic/itc": "2.5.5-alpha.2",
29
+ "@platformatic/metrics": "2.5.5-alpha.2",
30
+ "@platformatic/utils": "2.5.5-alpha.2"
30
31
  },
31
32
  "devDependencies": {
32
33
  "borp": "^0.17.0",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/basic/2.5.4.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/basic/2.5.5-alpha.2.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Stackable",
5
5
  "type": "object",