@platformatic/telemetry 0.37.0 → 0.37.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/platformatic-trace-provider.js +5 -1
- package/lib/schema.js +32 -22
- package/lib/telemetry.js +30 -19
- package/package.json +1 -1
- package/test/client.test.js +13 -7
- package/test/telemetry.test.js +76 -14
|
@@ -32,7 +32,11 @@ class PlatformaticTracerProvider {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
addSpanProcessor (spanProcessor) {
|
|
35
|
-
|
|
35
|
+
if (Array.isArray(spanProcessor)) {
|
|
36
|
+
this._registeredSpanProcessors.push(...spanProcessor)
|
|
37
|
+
} else {
|
|
38
|
+
this._registeredSpanProcessors.push(spanProcessor)
|
|
39
|
+
}
|
|
36
40
|
this.activeSpanProcessor = new MultiSpanProcessor(
|
|
37
41
|
this._registeredSpanProcessors
|
|
38
42
|
)
|
package/lib/schema.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const ExporterSchema = {
|
|
4
|
+
type: 'object',
|
|
5
|
+
properties: {
|
|
6
|
+
type: {
|
|
7
|
+
type: 'string',
|
|
8
|
+
enum: ['console', 'otlp', 'zipkin', 'memory'],
|
|
9
|
+
default: 'console'
|
|
10
|
+
},
|
|
11
|
+
options: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
description: 'Options for the exporter. These are passed directly to the exporter.',
|
|
14
|
+
properties: {
|
|
15
|
+
url: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'The URL to send the traces to. Not used for console or memory exporters.'
|
|
18
|
+
},
|
|
19
|
+
headers: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
description: 'Headers to send to the exporter. Not used for console or memory exporters.'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
additionalProperties: false
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
3
29
|
const TelemetrySchema = {
|
|
4
30
|
$id: '/OpenTelemetry',
|
|
5
31
|
type: 'object',
|
|
@@ -20,29 +46,13 @@ const TelemetrySchema = {
|
|
|
20
46
|
}
|
|
21
47
|
},
|
|
22
48
|
exporter: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
enum: ['console', 'otlp', 'zipkin', 'memory'],
|
|
28
|
-
default: 'console'
|
|
29
|
-
},
|
|
30
|
-
options: {
|
|
31
|
-
type: 'object',
|
|
32
|
-
description: 'Options for the exporter. These are passed directly to the exporter.',
|
|
33
|
-
properties: {
|
|
34
|
-
url: {
|
|
35
|
-
type: 'string',
|
|
36
|
-
description: 'The URL to send the traces to. Not used for console or memory exporters.'
|
|
37
|
-
},
|
|
38
|
-
headers: {
|
|
39
|
-
type: 'object',
|
|
40
|
-
description: 'Headers to send to the exporter. Not used for console or memory exporters.'
|
|
41
|
-
}
|
|
42
|
-
}
|
|
49
|
+
anyOf: [
|
|
50
|
+
{
|
|
51
|
+
type: 'array',
|
|
52
|
+
items: ExporterSchema
|
|
43
53
|
},
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
ExporterSchema
|
|
55
|
+
]
|
|
46
56
|
}
|
|
47
57
|
},
|
|
48
58
|
required: ['serviceName'],
|
package/lib/telemetry.js
CHANGED
|
@@ -81,7 +81,11 @@ const setupProvider = (app, opts) => {
|
|
|
81
81
|
app.log.warn('No exporter configured, defaulting to console.')
|
|
82
82
|
exporter = { type: 'console' }
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
const exporters = Array.isArray(exporter) ? exporter : [exporter]
|
|
86
|
+
|
|
84
87
|
app.log.info(`Setting up telemetry for service: ${serviceName}${version ? ' version: ' + version : ''} with exporter of type ${exporter.type}`)
|
|
88
|
+
|
|
85
89
|
const provider = new PlatformaticTracerProvider({
|
|
86
90
|
resource: new Resource({
|
|
87
91
|
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
@@ -89,33 +93,40 @@ const setupProvider = (app, opts) => {
|
|
|
89
93
|
})
|
|
90
94
|
})
|
|
91
95
|
|
|
96
|
+
const exporterObjs = []
|
|
97
|
+
const spanProcessors = []
|
|
98
|
+
for (const exporter of exporters) {
|
|
92
99
|
// Exporter config:
|
|
93
100
|
// https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_exporter_zipkin.ExporterConfig.html
|
|
94
|
-
|
|
101
|
+
const exporterOptions = { ...exporter.options, serviceName }
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
let exporterObj
|
|
104
|
+
if (exporter.type === 'console') {
|
|
105
|
+
exporterObj = new ConsoleSpanExporter(exporterOptions)
|
|
106
|
+
} else if (exporter.type === 'otlp') {
|
|
100
107
|
// We require here because this require (and only the require!) creates some issue with c8 on some mjs tests on other modules. Since we need an assignemet here, we don't use a switch.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto')
|
|
109
|
+
exporterObj = new OTLPTraceExporter(exporterOptions)
|
|
110
|
+
} else if (exporter.type === 'zipkin') {
|
|
111
|
+
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin')
|
|
112
|
+
exporterObj = new ZipkinExporter(exporterOptions)
|
|
113
|
+
} else if (exporter.type === 'memory') {
|
|
114
|
+
exporterObj = new InMemorySpanExporter()
|
|
115
|
+
} else {
|
|
116
|
+
app.log.warn(`Unknown exporter type: ${exporter.type}, defaulting to console.`)
|
|
117
|
+
exporterObj = new ConsoleSpanExporter(exporterOptions)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// We use a SimpleSpanProcessor for the console/memory exporters and a BatchSpanProcessor for the others.
|
|
121
|
+
const spanProcessor = ['memory', 'console'].includes(exporter.type) ? new SimpleSpanProcessor(exporterObj) : new BatchSpanProcessor(exporterObj)
|
|
122
|
+
spanProcessors.push(spanProcessor)
|
|
123
|
+
exporterObjs.push(exporterObj)
|
|
111
124
|
}
|
|
112
125
|
|
|
113
|
-
|
|
114
|
-
const spanProcessor = ['memory', 'console'].includes(exporter.type) ? new SimpleSpanProcessor(exporterObj) : new BatchSpanProcessor(exporterObj)
|
|
115
|
-
provider.addSpanProcessor(spanProcessor)
|
|
126
|
+
provider.addSpanProcessor(spanProcessors)
|
|
116
127
|
const tracer = provider.getTracer(moduleName, moduleVersion)
|
|
117
128
|
const propagator = provider.getPropagator()
|
|
118
|
-
return { tracer,
|
|
129
|
+
return { tracer, exporters: exporterObjs, propagator, provider }
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
async function setupTelemetry (app, opts) {
|
package/package.json
CHANGED
package/test/client.test.js
CHANGED
|
@@ -14,10 +14,12 @@ async function setupApp (pluginOpts, routeHandler, teardown) {
|
|
|
14
14
|
app.ready()
|
|
15
15
|
teardown(async () => {
|
|
16
16
|
await app.close()
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
exporter.
|
|
20
|
-
|
|
17
|
+
const { exporters } = app.openTelemetry
|
|
18
|
+
exporters.forEach(exporter => {
|
|
19
|
+
if (exporter.constructor.name === 'InMemorySpanExporter') {
|
|
20
|
+
exporter.reset()
|
|
21
|
+
}
|
|
22
|
+
})
|
|
21
23
|
})
|
|
22
24
|
return app
|
|
23
25
|
}
|
|
@@ -119,7 +121,8 @@ test('should trace a client request', async ({ equal, same, teardown }) => {
|
|
|
119
121
|
const response = await app.inject(args)
|
|
120
122
|
endSpanClient(span, response)
|
|
121
123
|
|
|
122
|
-
const {
|
|
124
|
+
const { exporters } = app.openTelemetry
|
|
125
|
+
const exporter = exporters[0]
|
|
123
126
|
const finishedSpans = exporter.getFinishedSpans()
|
|
124
127
|
equal(finishedSpans.length, 2)
|
|
125
128
|
// We have two one for the client and one for the server
|
|
@@ -173,7 +176,9 @@ test('should trace a client request failing', async ({ equal, same, teardown })
|
|
|
173
176
|
const response = await app.inject(args)
|
|
174
177
|
endSpanClient(span, response)
|
|
175
178
|
|
|
176
|
-
const {
|
|
179
|
+
const { exporters } = app.openTelemetry
|
|
180
|
+
const exporter = exporters[0]
|
|
181
|
+
|
|
177
182
|
const finishedSpans = exporter.getFinishedSpans()
|
|
178
183
|
equal(finishedSpans.length, 2)
|
|
179
184
|
// We have two one for the client and one for the server
|
|
@@ -218,7 +223,8 @@ test('should trace a client request failing (no HTTP error)', async ({ equal, sa
|
|
|
218
223
|
endSpanClient(span)
|
|
219
224
|
}
|
|
220
225
|
|
|
221
|
-
const {
|
|
226
|
+
const { exporters } = app.openTelemetry
|
|
227
|
+
const exporter = exporters[0]
|
|
222
228
|
const finishedSpans = exporter.getFinishedSpans()
|
|
223
229
|
equal(finishedSpans.length, 1)
|
|
224
230
|
|
package/test/telemetry.test.js
CHANGED
|
@@ -13,10 +13,12 @@ async function setupApp (pluginOpts, routeHandler, teardown) {
|
|
|
13
13
|
app.ready()
|
|
14
14
|
teardown(async () => {
|
|
15
15
|
await app.close()
|
|
16
|
-
const {
|
|
17
|
-
|
|
18
|
-
exporter.
|
|
19
|
-
|
|
16
|
+
const { exporters } = app.openTelemetry
|
|
17
|
+
exporters.forEach(exporter => {
|
|
18
|
+
if (exporter.constructor.name === 'InMemorySpanExporter') {
|
|
19
|
+
exporter.reset()
|
|
20
|
+
}
|
|
21
|
+
})
|
|
20
22
|
})
|
|
21
23
|
return app
|
|
22
24
|
}
|
|
@@ -43,7 +45,8 @@ test('should trace a request not failing', async ({ equal, same, teardown }) =>
|
|
|
43
45
|
}, handler, teardown)
|
|
44
46
|
|
|
45
47
|
await app.inject(injectArgs)
|
|
46
|
-
const {
|
|
48
|
+
const { exporters } = app.openTelemetry
|
|
49
|
+
const exporter = exporters[0]
|
|
47
50
|
const finishedSpans = exporter.getFinishedSpans()
|
|
48
51
|
equal(finishedSpans.length, 1)
|
|
49
52
|
const span = finishedSpans[0]
|
|
@@ -82,7 +85,8 @@ test('should not put query in `url.path', async ({ equal, same, teardown }) => {
|
|
|
82
85
|
}, handler, teardown)
|
|
83
86
|
|
|
84
87
|
await app.inject(injectArgs)
|
|
85
|
-
const {
|
|
88
|
+
const { exporters } = app.openTelemetry
|
|
89
|
+
const exporter = exporters[0]
|
|
86
90
|
const finishedSpans = exporter.getFinishedSpans()
|
|
87
91
|
equal(finishedSpans.length, 1)
|
|
88
92
|
const span = finishedSpans[0]
|
|
@@ -114,7 +118,8 @@ test('request should add attribute to a span', async ({ equal, same, teardown })
|
|
|
114
118
|
}, handler, teardown)
|
|
115
119
|
|
|
116
120
|
await app.inject(injectArgs)
|
|
117
|
-
const {
|
|
121
|
+
const { exporters } = app.openTelemetry
|
|
122
|
+
const exporter = exporters[0]
|
|
118
123
|
const finishedSpans = exporter.getFinishedSpans()
|
|
119
124
|
equal(finishedSpans.length, 1)
|
|
120
125
|
const span = finishedSpans[0]
|
|
@@ -164,7 +169,8 @@ test('should trace a request that fails', async ({ equal, same, teardown }) => {
|
|
|
164
169
|
}, handler, teardown)
|
|
165
170
|
|
|
166
171
|
await app.inject(injectArgs)
|
|
167
|
-
const {
|
|
172
|
+
const { exporters } = app.openTelemetry
|
|
173
|
+
const exporter = exporters[0]
|
|
168
174
|
const finishedSpans = exporter.getFinishedSpans()
|
|
169
175
|
equal(finishedSpans.length, 1)
|
|
170
176
|
const span = finishedSpans[0]
|
|
@@ -189,7 +195,8 @@ test('if no exporter is configured, should default to console', async ({ equal,
|
|
|
189
195
|
}, handler, teardown)
|
|
190
196
|
|
|
191
197
|
await app.inject(injectArgs)
|
|
192
|
-
const {
|
|
198
|
+
const { exporters } = app.openTelemetry
|
|
199
|
+
const exporter = exporters[0]
|
|
193
200
|
same(exporter.constructor.name, 'ConsoleSpanExporter')
|
|
194
201
|
})
|
|
195
202
|
|
|
@@ -208,7 +215,8 @@ test('should configure OTLP correctly', async ({ equal, same, teardown }) => {
|
|
|
208
215
|
}
|
|
209
216
|
}, handler, teardown)
|
|
210
217
|
|
|
211
|
-
const {
|
|
218
|
+
const { exporters } = app.openTelemetry
|
|
219
|
+
const exporter = exporters[0]
|
|
212
220
|
same(exporter.constructor.name, 'OTLPTraceExporter')
|
|
213
221
|
same(exporter.url, 'http://localhost:4317')
|
|
214
222
|
})
|
|
@@ -228,7 +236,8 @@ test('should configure Zipkin correctly', async ({ equal, same, teardown }) => {
|
|
|
228
236
|
}
|
|
229
237
|
}, handler, teardown)
|
|
230
238
|
|
|
231
|
-
const {
|
|
239
|
+
const { exporters } = app.openTelemetry
|
|
240
|
+
const exporter = exporters[0]
|
|
232
241
|
same(exporter.constructor.name, 'ZipkinExporter')
|
|
233
242
|
same(exporter._urlStr, 'http://localhost:9876')
|
|
234
243
|
})
|
|
@@ -246,7 +255,8 @@ test('wrong exporter is configured, should default to console', async ({ equal,
|
|
|
246
255
|
}, handler, teardown)
|
|
247
256
|
|
|
248
257
|
await app.inject(injectArgs)
|
|
249
|
-
const {
|
|
258
|
+
const { exporters } = app.openTelemetry
|
|
259
|
+
const exporter = exporters[0]
|
|
250
260
|
same(exporter.constructor.name, 'ConsoleSpanExporter')
|
|
251
261
|
})
|
|
252
262
|
|
|
@@ -275,7 +285,8 @@ test('should not trace if the operation is skipped', async ({ equal, same, teard
|
|
|
275
285
|
}
|
|
276
286
|
|
|
277
287
|
await app.inject(injectArgs)
|
|
278
|
-
const {
|
|
288
|
+
const { exporters } = app.openTelemetry
|
|
289
|
+
const exporter = exporters[0]
|
|
279
290
|
const finishedSpans = exporter.getFinishedSpans()
|
|
280
291
|
equal(finishedSpans.length, 0)
|
|
281
292
|
})
|
|
@@ -302,7 +313,8 @@ test('should not put the URL param in path', async ({ equal, same, teardown }) =
|
|
|
302
313
|
}, handler, teardown)
|
|
303
314
|
|
|
304
315
|
await app.inject(injectArgs)
|
|
305
|
-
const {
|
|
316
|
+
const { exporters } = app.openTelemetry
|
|
317
|
+
const exporter = exporters[0]
|
|
306
318
|
const finishedSpans = exporter.getFinishedSpans()
|
|
307
319
|
equal(finishedSpans.length, 1)
|
|
308
320
|
const span = finishedSpans[0]
|
|
@@ -318,3 +330,53 @@ test('should not put the URL param in path', async ({ equal, same, teardown }) =
|
|
|
318
330
|
same(resource.attributes['service.name'], 'test-service')
|
|
319
331
|
same(resource.attributes['service.version'], '1.0.0')
|
|
320
332
|
})
|
|
333
|
+
|
|
334
|
+
test('should configure an exporter as an array', async ({ equal, same, teardown }) => {
|
|
335
|
+
const handler = async (request, reply) => {
|
|
336
|
+
return {}
|
|
337
|
+
}
|
|
338
|
+
const app = await setupApp({
|
|
339
|
+
serviceName: 'test-service',
|
|
340
|
+
version: '1.0.0',
|
|
341
|
+
exporter: [{
|
|
342
|
+
type: 'otlp',
|
|
343
|
+
options: {
|
|
344
|
+
url: 'http://localhost:4317'
|
|
345
|
+
}
|
|
346
|
+
}]
|
|
347
|
+
}, handler, teardown)
|
|
348
|
+
const { exporters } = app.openTelemetry
|
|
349
|
+
const exporter = exporters[0]
|
|
350
|
+
same(exporter.constructor.name, 'OTLPTraceExporter')
|
|
351
|
+
same(exporter.url, 'http://localhost:4317')
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
test('should use multiple exporters and sent traces to all the exporters', async ({ equal, same, teardown }) => {
|
|
355
|
+
const handler = async (request, reply) => {
|
|
356
|
+
return {}
|
|
357
|
+
}
|
|
358
|
+
const app = await setupApp({
|
|
359
|
+
serviceName: 'test-service',
|
|
360
|
+
version: '1.0.0',
|
|
361
|
+
exporter: [{
|
|
362
|
+
type: 'memory'
|
|
363
|
+
}, {
|
|
364
|
+
type: 'memory'
|
|
365
|
+
}]
|
|
366
|
+
}, handler, teardown)
|
|
367
|
+
const { exporters } = app.openTelemetry
|
|
368
|
+
|
|
369
|
+
await app.inject(injectArgs)
|
|
370
|
+
|
|
371
|
+
const finishedSpans0 = exporters[0].getFinishedSpans()
|
|
372
|
+
equal(finishedSpans0.length, 1)
|
|
373
|
+
const span0 = finishedSpans0[0]
|
|
374
|
+
equal(span0.name, 'GET /test')
|
|
375
|
+
equal(span0.status.code, SpanStatusCode.OK)
|
|
376
|
+
|
|
377
|
+
const finishedSpans1 = exporters[1].getFinishedSpans()
|
|
378
|
+
equal(finishedSpans1.length, 1)
|
|
379
|
+
const span1 = finishedSpans1[0]
|
|
380
|
+
equal(span1.name, 'GET /test')
|
|
381
|
+
equal(span1.status.code, SpanStatusCode.OK)
|
|
382
|
+
})
|