@platformatic/runtime 2.5.3 → 2.5.5-alpha.1

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
@@ -5,7 +5,7 @@
5
5
  * and run json-schema-to-typescript to regenerate this file.
6
6
  */
7
7
 
8
- export type HttpsSchemasPlatformaticDevPlatformaticRuntime253Json = {
8
+ export type HttpsSchemasPlatformaticDevPlatformaticRuntime255Alpha1Json = {
9
9
  [k: string]: unknown;
10
10
  } & {
11
11
  $schema?: string;
package/lib/config.js CHANGED
@@ -140,9 +140,14 @@ async function _transformConfig (configManager, args) {
140
140
  }
141
141
 
142
142
  if (!hasValidEntrypoint) {
143
- throw typeof config.entrypoint !== 'undefined'
144
- ? new errors.InvalidEntrypointError(config.entrypoint)
145
- : new errors.MissingEntrypointError()
143
+ if (config.entrypoint) {
144
+ throw new errors.InvalidEntrypointError(config.entrypoint)
145
+ } else if (services.length >= 1) {
146
+ throw new errors.MissingEntrypointError()
147
+ }
148
+ // If there are no services, and no entrypoint it's an empty app.
149
+ // It won't start, but we should be able to parse and operate on it,
150
+ // like adding other services.
146
151
  }
147
152
 
148
153
  configManager.current.services = services
package/lib/runtime.js CHANGED
@@ -127,6 +127,9 @@ class Runtime extends EventEmitter {
127
127
  }
128
128
 
129
129
  async start () {
130
+ if (typeof this.#configManager.current.entrypoint === 'undefined') {
131
+ throw new errors.MissingEntrypointError()
132
+ }
130
133
  this.#updateStatus('starting')
131
134
 
132
135
  // Important: do not use Promise.all here since it won't properly manage dependencies
package/lib/worker/app.js CHANGED
@@ -10,7 +10,6 @@ const debounce = require('debounce')
10
10
 
11
11
  const errors = require('../errors')
12
12
  const defaultStackable = require('./default-stackable')
13
- const { collectMetrics } = require('./metrics')
14
13
  const { getServiceUrl, loadConfig, loadEmptyConfig } = require('../utils')
15
14
 
16
15
  class PlatformaticApp extends EventEmitter {
@@ -19,7 +18,6 @@ class PlatformaticApp extends EventEmitter {
19
18
  #listening
20
19
  #watch
21
20
  #fileWatcher
22
- #metricsRegistry
23
21
  #debouncedRestart
24
22
  #context
25
23
 
@@ -32,7 +30,6 @@ class PlatformaticApp extends EventEmitter {
32
30
  this.#listening = false
33
31
  this.stackable = null
34
32
  this.#fileWatcher = null
35
- this.#metricsRegistry = null
36
33
 
37
34
  this.#context = {
38
35
  serviceId: this.appConfig.id,
@@ -114,10 +111,9 @@ class PlatformaticApp extends EventEmitter {
114
111
  })
115
112
  this.stackable = this.#wrapStackable(stackable)
116
113
 
117
- const metricsConfig = this.#context.metricsConfig
118
- if (metricsConfig !== false) {
119
- this.#metricsRegistry = await collectMetrics(this.stackable, this.appConfig.id, metricsConfig)
120
- }
114
+ this.once('start', () => {
115
+ this.stackable.collectMetrics()
116
+ })
121
117
 
122
118
  this.#updateDispatcher()
123
119
  } catch (err) {
@@ -196,9 +192,7 @@ class PlatformaticApp extends EventEmitter {
196
192
  }
197
193
 
198
194
  async getMetrics ({ format }) {
199
- if (!this.#metricsRegistry) return null
200
-
201
- return format === 'json' ? this.#metricsRegistry.getMetricsAsJSON() : this.#metricsRegistry.metrics()
195
+ return this.stackable.getMetrics({ format })
202
196
  }
203
197
 
204
198
  #fetchServiceUrl (key, { parent, context: service }) {
@@ -16,10 +16,8 @@ const defaultStackable = {
16
16
  getOpenapiSchema: () => null,
17
17
  getGraphqlSchema: () => null,
18
18
  getMeta: () => ({}),
19
- collectMetrics: () => ({
20
- defaultMetrics: true,
21
- httpMetrics: true
22
- }),
19
+ collectMetrics: () => {},
20
+ getMetrics: () => null,
23
21
  inject: () => {
24
22
  throw new Error('Stackable inject not implemented')
25
23
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "2.5.3",
3
+ "version": "2.5.5-alpha.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -34,17 +34,17 @@
34
34
  "typescript": "^5.5.4",
35
35
  "undici-oidc-interceptor": "^0.5.0",
36
36
  "why-is-node-running": "^2.2.2",
37
- "@platformatic/composer": "2.5.3",
38
- "@platformatic/db": "2.5.3",
39
- "@platformatic/service": "2.5.3",
40
- "@platformatic/sql-graphql": "2.5.3",
41
- "@platformatic/sql-mapper": "2.5.3"
37
+ "@platformatic/composer": "2.5.5-alpha.1",
38
+ "@platformatic/service": "2.5.5-alpha.1",
39
+ "@platformatic/sql-graphql": "2.5.5-alpha.1",
40
+ "@platformatic/db": "2.5.5-alpha.1",
41
+ "@platformatic/sql-mapper": "2.5.5-alpha.1"
42
42
  },
43
43
  "dependencies": {
44
44
  "@fastify/error": "^4.0.0",
45
45
  "@fastify/websocket": "^11.0.0",
46
46
  "@hapi/topo": "^6.0.2",
47
- "@platformatic/http-metrics": "^0.2.0",
47
+ "@platformatic/http-metrics": "^0.2.1",
48
48
  "@watchable/unpromise": "^1.0.2",
49
49
  "boring-name-generator": "^1.0.3",
50
50
  "change-case-all": "^2.1.0",
@@ -69,13 +69,13 @@
69
69
  "undici": "^6.9.0",
70
70
  "undici-thread-interceptor": "^0.7.0",
71
71
  "ws": "^8.16.0",
72
- "@platformatic/basic": "2.5.3",
73
- "@platformatic/config": "2.5.3",
74
- "@platformatic/generators": "2.5.3",
75
- "@platformatic/itc": "2.5.3",
76
- "@platformatic/telemetry": "2.5.3",
77
- "@platformatic/ts-compiler": "2.5.3",
78
- "@platformatic/utils": "2.5.3"
72
+ "@platformatic/basic": "2.5.5-alpha.1",
73
+ "@platformatic/generators": "2.5.5-alpha.1",
74
+ "@platformatic/itc": "2.5.5-alpha.1",
75
+ "@platformatic/telemetry": "2.5.5-alpha.1",
76
+ "@platformatic/utils": "2.5.5-alpha.1",
77
+ "@platformatic/ts-compiler": "2.5.5-alpha.1",
78
+ "@platformatic/config": "2.5.5-alpha.1"
79
79
  },
80
80
  "scripts": {
81
81
  "test": "npm run lint && borp --concurrency=1 --timeout=180000 && tsd",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.5.3.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.5.5-alpha.1.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "type": "object",
5
5
  "properties": {
@@ -1,176 +0,0 @@
1
- 'use strict'
2
-
3
- const os = require('node:os')
4
- const { eventLoopUtilization } = require('node:perf_hooks').performance
5
- const { Registry, Gauge, Counter, collectDefaultMetrics } = require('prom-client')
6
- const collectHttpMetrics = require('@platformatic/http-metrics')
7
-
8
- async function collectMetrics (stackable, serviceId, opts = {}) {
9
- const registry = new Registry()
10
-
11
- const httpRequestCallbacks = []
12
- const httpResponseCallbacks = []
13
-
14
- const metricsConfig = await stackable.collectMetrics({
15
- registry,
16
- startHttpTimer: options => httpRequestCallbacks.forEach(cb => cb(options)),
17
- endHttpTimer: options => httpResponseCallbacks.forEach(cb => cb(options))
18
- })
19
-
20
- const labels = opts.labels ?? {}
21
- registry.setDefaultLabels({ ...labels, serviceId })
22
-
23
- if (metricsConfig.defaultMetrics) {
24
- collectDefaultMetrics({ register: registry })
25
- collectEluMetric(registry)
26
- await collectThreadCpuMetrics(registry)
27
- }
28
-
29
- if (metricsConfig.httpMetrics) {
30
- {
31
- const { startTimer, endTimer } = collectHttpMetrics(registry, {
32
- customLabels: ['telemetry_id'],
33
- getCustomLabels: req => {
34
- const telemetryId = req.headers?.['x-plt-telemetry-id'] ?? 'unknown'
35
- return { telemetry_id: telemetryId }
36
- }
37
- })
38
- httpRequestCallbacks.push(startTimer)
39
- httpResponseCallbacks.push(endTimer)
40
- }
41
-
42
- {
43
- // TODO: check if it's a nodejs environment
44
- // Needed for the Meraki metrics
45
- const { startTimer, endTimer } = collectHttpMetrics(registry, {
46
- customLabels: ['telemetry_id'],
47
- getCustomLabels: req => {
48
- const telemetryId = req.headers?.['x-plt-telemetry-id'] ?? 'unknown'
49
- return { telemetry_id: telemetryId }
50
- },
51
- histogram: {
52
- name: 'http_request_all_duration_seconds',
53
- help: 'request duration in seconds summary for all requests',
54
- collect: function () {
55
- process.nextTick(() => this.reset())
56
- }
57
- },
58
- summary: {
59
- name: 'http_request_all_summary_seconds',
60
- help: 'request duration in seconds histogram for all requests',
61
- collect: function () {
62
- process.nextTick(() => this.reset())
63
- }
64
- }
65
- })
66
- httpRequestCallbacks.push(startTimer)
67
- httpResponseCallbacks.push(endTimer)
68
- }
69
- }
70
-
71
- return registry
72
- }
73
-
74
- async function collectThreadCpuMetrics (registry) {
75
- // We need until we switch to 22 as thread-cpu-usage is ESM
76
- const { threadCpuUsage } = await import('thread-cpu-usage')
77
-
78
- let lastSample = process.hrtime.bigint()
79
- let lastUsage = threadCpuUsage()
80
-
81
- const threadCpuUserUsageCounterMetric = new Counter({
82
- name: 'thread_cpu_user_system_seconds_total',
83
- help: 'Total user CPU time spent in seconds for the current thread.',
84
- registers: [registry]
85
- })
86
-
87
- const threadCpuSystemUsageCounterMetric = new Counter({
88
- name: 'thread_cpu_system_seconds_total',
89
- help: 'Total system CPU time spent in seconds for the current thread.',
90
- registers: [registry]
91
- })
92
-
93
- const threadCpuPercentUsageGaugeMetric = new Gauge({
94
- name: 'thread_cpu_percent_usage',
95
- help: 'The thread CPU percent usage.',
96
- registers: [registry]
97
- })
98
-
99
- const threadCpuUsageCounterMetric = new Counter({
100
- name: 'thread_cpu_seconds_total',
101
- help: 'Total user and system CPU time spent in seconds for the current threads.',
102
- // Use this one metric's `collect` to set all metrics' values.
103
- collect () {
104
- const newSample = process.hrtime.bigint()
105
- const newUsage = threadCpuUsage()
106
- const user = newUsage.user - lastUsage.user
107
- const system = newUsage.system - lastUsage.system
108
- const elapsed = Number(newSample - lastSample)
109
-
110
- lastUsage = newUsage
111
- lastSample = newSample
112
-
113
- threadCpuSystemUsageCounterMetric.inc(system / 1e6)
114
- threadCpuUserUsageCounterMetric.inc(user / 1e6)
115
- threadCpuUsageCounterMetric.inc((user + system) / 1e6)
116
- threadCpuPercentUsageGaugeMetric.set((100 * ((user + system) * 1000)) / elapsed)
117
- },
118
- registers: [registry]
119
- })
120
-
121
- registry.registerMetric(threadCpuUserUsageCounterMetric)
122
- registry.registerMetric(threadCpuSystemUsageCounterMetric)
123
- registry.registerMetric(threadCpuUsageCounterMetric)
124
- registry.registerMetric(threadCpuPercentUsageGaugeMetric)
125
- }
126
-
127
- function collectEluMetric (register) {
128
- let startELU = eventLoopUtilization()
129
- const eluMetric = new Gauge({
130
- name: 'nodejs_eventloop_utilization',
131
- help: 'The event loop utilization as a fraction of the loop time. 1 is fully utilized, 0 is fully idle.',
132
- collect: () => {
133
- const endELU = eventLoopUtilization()
134
- const result = eventLoopUtilization(endELU, startELU).utilization
135
- eluMetric.set(result)
136
- startELU = endELU
137
- },
138
- registers: [register]
139
- })
140
- register.registerMetric(eluMetric)
141
-
142
- let previousIdleTime = 0
143
- let previousTotalTime = 0
144
- const cpuMetric = new Gauge({
145
- name: 'process_cpu_percent_usage',
146
- help: 'The process CPU percent usage.',
147
- collect: () => {
148
- const cpus = os.cpus()
149
- let idleTime = 0
150
- let totalTime = 0
151
-
152
- cpus.forEach(cpu => {
153
- for (const type in cpu.times) {
154
- totalTime += cpu.times[type]
155
- if (type === 'idle') {
156
- idleTime += cpu.times[type]
157
- }
158
- }
159
- })
160
-
161
- const idleDiff = idleTime - previousIdleTime
162
- const totalDiff = totalTime - previousTotalTime
163
-
164
- const usagePercent = 100 - (100 * idleDiff) / totalDiff
165
- const roundedUsage = Math.round(usagePercent * 100) / 100
166
- cpuMetric.set(roundedUsage)
167
-
168
- previousIdleTime = idleTime
169
- previousTotalTime = totalTime
170
- },
171
- registers: [register]
172
- })
173
- register.registerMetric(cpuMetric)
174
- }
175
-
176
- module.exports = { collectMetrics }