@platformatic/control 1.24.0 → 1.26.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/lib/logs.js CHANGED
@@ -1,8 +1,18 @@
1
1
  'use strict'
2
2
 
3
3
  const { parseArgs } = require('node:util')
4
+ const { prettyFactory } = require('pino-pretty')
4
5
  const RuntimeApiClient = require('./runtime-api-client')
5
6
 
7
+ const pinoLogLevels = {
8
+ fatal: 60,
9
+ error: 50,
10
+ warn: 40,
11
+ info: 30,
12
+ debug: 20,
13
+ trace: 10
14
+ }
15
+
6
16
  async function streamRuntimeLogsCommand (argv) {
7
17
  const args = parseArgs({
8
18
  args: argv,
@@ -10,7 +20,7 @@ async function streamRuntimeLogsCommand (argv) {
10
20
  pid: { type: 'string', short: 'p' },
11
21
  name: { type: 'string', short: 'n' },
12
22
  level: { type: 'string', short: 'l', default: 'info' },
13
- pretty: { type: 'boolean', default: true },
23
+ pretty: { type: 'string', default: 'true' },
14
24
  service: { type: 'string', short: 's' }
15
25
  },
16
26
  strict: false
@@ -19,19 +29,30 @@ async function streamRuntimeLogsCommand (argv) {
19
29
  const client = new RuntimeApiClient()
20
30
  const runtime = await client.getMatchingRuntime(args)
21
31
 
22
- const options = {}
23
- if (args.level !== undefined) {
24
- options.level = args.level
25
- }
26
- if (args.pretty !== undefined) {
27
- options.pretty = args.pretty
28
- }
29
- if (args.service !== undefined) {
30
- options.serviceId = args.service
31
- }
32
-
33
- const logsStream = client.getRuntimeLogsStream(runtime.pid, options)
34
- logsStream.pipe(process.stdout)
32
+ const logLevelNumber = pinoLogLevels[args.level]
33
+ const prettify = prettyFactory()
34
+
35
+ const logsStream = client.getRuntimeLiveLogsStream(runtime.pid)
36
+
37
+ logsStream.on('data', (data) => {
38
+ const logs = data.toString().split('\n').filter(Boolean)
39
+
40
+ for (let log of logs) {
41
+ try {
42
+ const parsedLog = JSON.parse(log)
43
+ if (parsedLog.level < logLevelNumber) continue
44
+ if (args.service && parsedLog.name !== args.service) continue
45
+ if (args.pretty !== 'false') {
46
+ log = prettify(parsedLog)
47
+ } else {
48
+ log += '\n'
49
+ }
50
+ process.stdout.write(log)
51
+ } catch (err) {
52
+ console.error('Failed to parse log message: ', log, err)
53
+ }
54
+ }
55
+ })
35
56
 
36
57
  process.on('SIGINT', () => {
37
58
  logsStream.destroy()
@@ -3,13 +3,13 @@
3
3
  const { tmpdir, platform, EOL } = require('node:os')
4
4
  const { join } = require('node:path')
5
5
  const { exec, spawn } = require('node:child_process')
6
- const { readdir } = require('node:fs/promises')
6
+ const { readdir, unlink, access } = require('node:fs/promises')
7
7
  const { Readable } = require('node:stream')
8
8
  const { Client } = require('undici')
9
9
  const WebSocket = require('ws')
10
10
  const errors = require('./errors.js')
11
11
 
12
- const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'pids')
12
+ const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
13
13
  const PLATFORMATIC_PIPE_PREFIX = '\\\\.\\pipe\\platformatic-'
14
14
 
15
15
  class RuntimeApiClient {
@@ -44,16 +44,25 @@ class RuntimeApiClient {
44
44
  })
45
45
  )
46
46
 
47
- return getMetadataRequests
48
- .filter(result => result.status === 'fulfilled')
49
- .map(result => result.value)
47
+ const runtimes = []
48
+ for (let i = 0; i < runtimePIDs.length; i++) {
49
+ const runtimePID = runtimePIDs[i]
50
+ const metadataRequest = getMetadataRequests[i]
51
+
52
+ if (metadataRequest.status === 'rejected') {
53
+ await this.#removeRuntimeSocket(runtimePID).catch(() => {})
54
+ } else {
55
+ runtimes.push(metadataRequest.value)
56
+ }
57
+ }
58
+ return runtimes
50
59
  }
51
60
 
52
61
  async getRuntimeMetadata (pid) {
53
62
  const client = this.#getUndiciClient(pid)
54
63
 
55
64
  const { statusCode, body } = await client.request({
56
- path: '/api/metadata',
65
+ path: '/api/v1/metadata',
57
66
  method: 'GET'
58
67
  })
59
68
 
@@ -70,7 +79,7 @@ class RuntimeApiClient {
70
79
  const client = this.#getUndiciClient(pid)
71
80
 
72
81
  const { statusCode, body } = await client.request({
73
- path: '/api/services',
82
+ path: '/api/v1/services',
74
83
  method: 'GET'
75
84
  })
76
85
 
@@ -87,7 +96,7 @@ class RuntimeApiClient {
87
96
  const client = this.#getUndiciClient(pid)
88
97
 
89
98
  const { statusCode, body } = await client.request({
90
- path: `/api/services/${serviceId}/config`,
99
+ path: `/api/v1/services/${serviceId}/config`,
91
100
  method: 'GET'
92
101
  })
93
102
 
@@ -104,7 +113,7 @@ class RuntimeApiClient {
104
113
  const client = this.#getUndiciClient(pid)
105
114
 
106
115
  const { statusCode, body } = await client.request({
107
- path: '/api/config',
116
+ path: '/api/v1/config',
108
117
  method: 'GET'
109
118
  })
110
119
 
@@ -121,7 +130,7 @@ class RuntimeApiClient {
121
130
  const client = this.#getUndiciClient(pid)
122
131
 
123
132
  const { statusCode, body } = await client.request({
124
- path: '/api/env',
133
+ path: '/api/v1/env',
125
134
  method: 'GET'
126
135
  })
127
136
 
@@ -148,7 +157,7 @@ class RuntimeApiClient {
148
157
  const client = this.#getUndiciClient(pid)
149
158
 
150
159
  const { statusCode, body } = await client.request({
151
- path: '/api/reload',
160
+ path: '/api/v1/reload',
152
161
  method: 'POST'
153
162
  })
154
163
 
@@ -162,7 +171,7 @@ class RuntimeApiClient {
162
171
  const client = this.#getUndiciClient(pid)
163
172
 
164
173
  const { statusCode, body } = await client.request({
165
- path: '/api/stop',
174
+ path: '/api/v1/stop',
166
175
  method: 'POST'
167
176
  })
168
177
 
@@ -172,15 +181,22 @@ class RuntimeApiClient {
172
181
  }
173
182
  }
174
183
 
175
- getRuntimeLogsStream (pid, options) {
184
+ getRuntimeLiveLogsStream (pid) {
176
185
  const socketPath = this.#getSocketPathFromPid(pid)
177
- let query = ''
178
- if (options.level || options.pretty || options.serviceId) {
179
- query = '?' + new URLSearchParams(options).toString()
180
- }
181
186
 
182
187
  const protocol = platform() === 'win32' ? 'ws+unix:' : 'ws+unix://'
183
- const webSocketUrl = protocol + socketPath + ':/api/logs' + query
188
+ const webSocketUrl = protocol + socketPath + ':/api/v1/logs/live'
189
+ const webSocketStream = new WebSocketStream(webSocketUrl)
190
+ this.#webSockets.add(webSocketStream.ws)
191
+
192
+ return webSocketStream
193
+ }
194
+
195
+ getRuntimeHistoryLogsStream (pid) {
196
+ const socketPath = this.#getSocketPathFromPid(pid)
197
+
198
+ const protocol = platform() === 'win32' ? 'ws+unix:' : 'ws+unix://'
199
+ const webSocketUrl = protocol + socketPath + ':/api/v1/logs/history'
184
200
  const webSocketStream = new WebSocketStream(webSocketUrl)
185
201
  this.#webSockets.add(webSocketStream.ws)
186
202
 
@@ -191,7 +207,7 @@ class RuntimeApiClient {
191
207
  const client = this.#getUndiciClient(pid)
192
208
 
193
209
  const response = await client.request({
194
- path: `/api/services/${serviceId}/proxy` + options.url,
210
+ path: `/api/v1/services/${serviceId}/proxy` + options.url,
195
211
  method: options.method,
196
212
  headers: options.headers,
197
213
  query: options.query,
@@ -227,15 +243,19 @@ class RuntimeApiClient {
227
243
  if (platform() === 'win32') {
228
244
  return PLATFORMATIC_PIPE_PREFIX + pid
229
245
  }
230
- return join(PLATFORMATIC_TMP_DIR, `${pid}.sock`)
246
+ return join(PLATFORMATIC_TMP_DIR, pid.toString(), 'socket')
231
247
  }
232
248
 
233
249
  async #getUnixRuntimePIDs () {
234
- const socketNames = await readdir(PLATFORMATIC_TMP_DIR)
250
+ try {
251
+ await access(PLATFORMATIC_TMP_DIR)
252
+ } catch {
253
+ return []
254
+ }
255
+ const runtimeDirs = await readdir(PLATFORMATIC_TMP_DIR)
235
256
  const runtimePIDs = []
236
- for (const socketName of socketNames) {
237
- const runtimePID = socketName.replace('.sock', '')
238
- runtimePIDs.push(parseInt(runtimePID))
257
+ for (const runtimeDirName of runtimeDirs) {
258
+ runtimePIDs.push(parseInt(runtimeDirName))
239
259
  }
240
260
  return runtimePIDs
241
261
  }
@@ -268,6 +288,13 @@ class RuntimeApiClient {
268
288
  )
269
289
  })
270
290
  }
291
+
292
+ async #removeRuntimeSocket (pid) {
293
+ if (platform() !== 'win32') {
294
+ const socketPath = this.#getSocketPathFromPid(pid)
295
+ await unlink(socketPath)
296
+ }
297
+ }
271
298
  }
272
299
 
273
300
  class WebSocketStream extends Readable {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/control",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
4
4
  "description": "Platformatic Control",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -24,14 +24,14 @@
24
24
  "split2": "^4.2.0",
25
25
  "standard": "^17.1.0",
26
26
  "tsd": "^0.30.4",
27
- "@platformatic/runtime": "1.24.0"
27
+ "@platformatic/runtime": "1.26.0"
28
28
  },
29
29
  "dependencies": {
30
30
  "@fastify/error": "^3.4.1",
31
31
  "commist": "^3.2.0",
32
+ "help-me": "^5.0.0",
32
33
  "pino": "^8.17.2",
33
34
  "pino-pretty": "^10.3.1",
34
- "help-me": "^5.0.0",
35
35
  "table": "^6.8.1",
36
36
  "undici": "^6.6.0",
37
37
  "ws": "^8.16.0"