@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 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 heapSizeAndUsed from '@platformatic/prom-client/lib/metrics/heapSizeAndUsed.js'
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
- method,
179
- status_code: statusCode,
180
- dispatcher_stats_url: dispatcherStatsUrl,
181
- error_type: errorType
182
- }, duration)
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: (error) => {
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.59.0",
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
+ })