@platformatic/metrics 3.59.0 → 3.60.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/index.js +80 -23
- package/package.json +3 -2
- package/test/opentelemetry.test.js +56 -0
package/index.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
+
import { getApplicationId, getITC, getWorkerId } from '@platformatic/globals'
|
|
1
2
|
import collectHttpMetrics from '@platformatic/http-metrics'
|
|
3
|
+
import client from '@platformatic/prom-client'
|
|
2
4
|
import { subscribe, unsubscribe } from 'node:diagnostics_channel'
|
|
3
5
|
import os from 'node:os'
|
|
4
6
|
import { performance } from 'node:perf_hooks'
|
|
5
|
-
import client from '@platformatic/prom-client'
|
|
6
7
|
|
|
7
8
|
// Import individual metric collectors from prom-client
|
|
8
|
-
import processCpuTotal from '@platformatic/prom-client/lib/metrics/processCpuTotal.js'
|
|
9
|
-
import processStartTime from '@platformatic/prom-client/lib/metrics/processStartTime.js'
|
|
10
|
-
import osMemoryHeap from '@platformatic/prom-client/lib/metrics/osMemoryHeap.js'
|
|
11
|
-
import processOpenFileDescriptors from '@platformatic/prom-client/lib/metrics/processOpenFileDescriptors.js'
|
|
12
|
-
import processMaxFileDescriptors from '@platformatic/prom-client/lib/metrics/processMaxFileDescriptors.js'
|
|
13
9
|
import eventLoopLag from '@platformatic/prom-client/lib/metrics/eventLoopLag.js'
|
|
10
|
+
import gc from '@platformatic/prom-client/lib/metrics/gc.js'
|
|
11
|
+
import heapSizeAndUsed from '@platformatic/prom-client/lib/metrics/heapSizeAndUsed.js'
|
|
12
|
+
import heapSpacesSizeAndUsed from '@platformatic/prom-client/lib/metrics/heapSpacesSizeAndUsed.js'
|
|
13
|
+
import osMemoryHeap from '@platformatic/prom-client/lib/metrics/osMemoryHeap.js'
|
|
14
|
+
import processCpuTotal from '@platformatic/prom-client/lib/metrics/processCpuTotal.js'
|
|
14
15
|
import processHandles from '@platformatic/prom-client/lib/metrics/processHandles.js'
|
|
16
|
+
import processMaxFileDescriptors from '@platformatic/prom-client/lib/metrics/processMaxFileDescriptors.js'
|
|
17
|
+
import processOpenFileDescriptors from '@platformatic/prom-client/lib/metrics/processOpenFileDescriptors.js'
|
|
15
18
|
import processRequests from '@platformatic/prom-client/lib/metrics/processRequests.js'
|
|
16
19
|
import processResources from '@platformatic/prom-client/lib/metrics/processResources.js'
|
|
17
|
-
import
|
|
18
|
-
import heapSpacesSizeAndUsed from '@platformatic/prom-client/lib/metrics/heapSpacesSizeAndUsed.js'
|
|
20
|
+
import processStartTime from '@platformatic/prom-client/lib/metrics/processStartTime.js'
|
|
19
21
|
import version from '@platformatic/prom-client/lib/metrics/version.js'
|
|
20
|
-
import gc from '@platformatic/prom-client/lib/metrics/gc.js'
|
|
21
22
|
|
|
22
23
|
export * as client from '@platformatic/prom-client'
|
|
23
24
|
|
|
@@ -28,6 +29,65 @@ export const kMetricsGroups = Symbol('plt.metrics.MetricsGroups')
|
|
|
28
29
|
const kMetricsCleanups = Symbol('plt.metrics.MetricsCleanups')
|
|
29
30
|
const kHttpClientRequestStart = Symbol('plt.metrics.HttpClientRequestStart')
|
|
30
31
|
const kHttpClientRequestStatusCode = Symbol('plt.metrics.HttpClientRequestStatusCode')
|
|
32
|
+
export const openTelemetryITCMessage = 'plt.metrics.itc.notification'
|
|
33
|
+
|
|
34
|
+
export class OpenTelemetryExporter {
|
|
35
|
+
constructor (options = {}) {
|
|
36
|
+
this._shutdown = false
|
|
37
|
+
this.applicationId = options.applicationId
|
|
38
|
+
this.workerId = options.workerId
|
|
39
|
+
this.itc = options.itc
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export (resourceMetrics, resultCallback) {
|
|
43
|
+
if (this._shutdown) {
|
|
44
|
+
resultCallback({ code: 1 })
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const itc = this.itc ?? getITC({ throwOnMissing: false })
|
|
50
|
+
if (!itc) {
|
|
51
|
+
resultCallback({ code: 1, error: new Error('Platformatic ITC is not available') })
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const applicationId = this.applicationId ?? getApplicationId({ throwOnMissing: false })
|
|
56
|
+
const workerId = this.workerId ?? getWorkerId({ throwOnMissing: false })
|
|
57
|
+
|
|
58
|
+
itc.notify(openTelemetryITCMessage, this.#addPlatformaticAttributes(resourceMetrics, applicationId, workerId))
|
|
59
|
+
resultCallback({ code: 0 })
|
|
60
|
+
} catch (error) {
|
|
61
|
+
resultCallback({ code: 1, error })
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async forceFlush () {}
|
|
66
|
+
|
|
67
|
+
async shutdown () {
|
|
68
|
+
this._shutdown = true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#addPlatformaticAttributes (resourceMetrics, applicationId, workerId) {
|
|
72
|
+
return {
|
|
73
|
+
...resourceMetrics,
|
|
74
|
+
resource: {
|
|
75
|
+
...resourceMetrics.resource,
|
|
76
|
+
attributes: {
|
|
77
|
+
...this.#getResourceAttributes(resourceMetrics),
|
|
78
|
+
applicationId,
|
|
79
|
+
...(typeof workerId !== 'undefined' ? { workerId } : {})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#getResourceAttributes (resourceMetrics) {
|
|
86
|
+
const resource = resourceMetrics.resource
|
|
87
|
+
const attributes = resource?.attributes ?? {}
|
|
88
|
+
return { ...attributes }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
31
91
|
|
|
32
92
|
function getRegistrySet (registry, key) {
|
|
33
93
|
registry[key] ??= new Set()
|
|
@@ -174,12 +234,15 @@ export function collectHttpClientMetrics (registry) {
|
|
|
174
234
|
delete request[kHttpClientRequestStart]
|
|
175
235
|
delete request[kHttpClientRequestStatusCode]
|
|
176
236
|
|
|
177
|
-
requestDurationMetric.observe(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
237
|
+
requestDurationMetric.observe(
|
|
238
|
+
{
|
|
239
|
+
method,
|
|
240
|
+
status_code: statusCode,
|
|
241
|
+
dispatcher_stats_url: dispatcherStatsUrl,
|
|
242
|
+
error_type: errorType
|
|
243
|
+
},
|
|
244
|
+
duration
|
|
245
|
+
)
|
|
183
246
|
}
|
|
184
247
|
|
|
185
248
|
subscribe('undici:request:create', onRequestCreate)
|
|
@@ -500,13 +563,7 @@ export async function setupOtlpExporter (registry, otlpExporterConfig, applicati
|
|
|
500
563
|
// Dynamically import PromClientBridge to defer loading until after telemetry is initialized
|
|
501
564
|
const { PromClientBridge } = await import('@platformatic/promotel')
|
|
502
565
|
|
|
503
|
-
const {
|
|
504
|
-
endpoint,
|
|
505
|
-
headers,
|
|
506
|
-
interval = 60000,
|
|
507
|
-
serviceName = applicationId,
|
|
508
|
-
serviceVersion
|
|
509
|
-
} = otlpExporterConfig
|
|
566
|
+
const { endpoint, headers, interval = 60000, serviceName = applicationId, serviceVersion } = otlpExporterConfig
|
|
510
567
|
|
|
511
568
|
const otlpEndpointOptions = {
|
|
512
569
|
url: endpoint
|
|
@@ -529,7 +586,7 @@ export async function setupOtlpExporter (registry, otlpExporterConfig, applicati
|
|
|
529
586
|
otlpEndpoint: otlpEndpointOptions,
|
|
530
587
|
interval,
|
|
531
588
|
conversionOptions,
|
|
532
|
-
onError:
|
|
589
|
+
onError: error => {
|
|
533
590
|
// Log error but don't crash the application
|
|
534
591
|
console.error('OTLP metrics export error:', error)
|
|
535
592
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/metrics",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.60.0",
|
|
4
4
|
"description": "Platformatic Capability Metrics",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@platformatic/http-metrics": "^0.3.0",
|
|
19
19
|
"@platformatic/promotel": "^0.2.0",
|
|
20
|
-
"@platformatic/prom-client": "^1.0.0"
|
|
20
|
+
"@platformatic/prom-client": "^1.0.0",
|
|
21
|
+
"@platformatic/globals": "3.60.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"cleaner-spec-reporter": "^0.5.0",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { openTelemetryITCMessage, OpenTelemetryExporter } from '../index.js'
|
|
4
|
+
|
|
5
|
+
test('OpenTelemetryExporter relays resource metrics with Platformatic attributes', async () => {
|
|
6
|
+
let notification
|
|
7
|
+
const exporter = new OpenTelemetryExporter({
|
|
8
|
+
applicationId: 'test-app',
|
|
9
|
+
workerId: 7,
|
|
10
|
+
itc: {
|
|
11
|
+
notify (name, payload) {
|
|
12
|
+
notification = { name, payload }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const resourceMetrics = {
|
|
18
|
+
resource: {
|
|
19
|
+
attributes: {
|
|
20
|
+
'service.name': 'user-service'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
scopeMetrics: []
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result = await new Promise(resolve => {
|
|
27
|
+
exporter.export(resourceMetrics, resolve)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
assert.strictEqual(result.code, 0)
|
|
31
|
+
assert.strictEqual(notification.name, openTelemetryITCMessage)
|
|
32
|
+
assert.deepStrictEqual(notification.payload.resource.attributes, {
|
|
33
|
+
'service.name': 'user-service',
|
|
34
|
+
applicationId: 'test-app',
|
|
35
|
+
workerId: 7
|
|
36
|
+
})
|
|
37
|
+
assert.deepStrictEqual(resourceMetrics.resource.attributes, {
|
|
38
|
+
'service.name': 'user-service'
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('OpenTelemetryExporter fails after shutdown', async () => {
|
|
43
|
+
const exporter = new OpenTelemetryExporter({
|
|
44
|
+
itc: {
|
|
45
|
+
notify () {}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
await exporter.shutdown()
|
|
50
|
+
|
|
51
|
+
const result = await new Promise(resolve => {
|
|
52
|
+
exporter.export({ resource: { attributes: {} }, scopeMetrics: [] }, resolve)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
assert.strictEqual(result.code, 1)
|
|
56
|
+
})
|