@saidsef/tracing-node 3.10.0 → 3.11.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/README.md +12 -10
- package/libs/index.mjs +155 -6
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -12,19 +12,21 @@ Get telemetry for your app in less than 3 minutes!
|
|
|
12
12
|
Effortlessly supercharge your applications with world-class distributed tracing! This OpenTelemetry wrapper delivers seamless, lightning-fast observability, empowering developers to monitor, debug, and optimise microservices with ease. Designed for modern cloud-native environments, it's the smart choice for engineers who demand reliability, scalability, and actionable insights. Get started in minutes and unlock the full potential of your service architecture—no fuss, just results. This is to make instrumentation (more) idempotent.
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
| Feature | Description |
|
|
16
|
+
|---------|-------------|
|
|
17
|
+
| HTTP/HTTPS instrumentation | Automatic service detection |
|
|
18
|
+
| Express.js support | Framework instrumentation |
|
|
19
|
+
| Elasticsearch client | Database instrumentation |
|
|
20
|
+
| IORedis client | Cache instrumentation |
|
|
21
|
+
| AWS SDK | Cloud service instrumentation |
|
|
22
|
+
| Pino logger | Integration with trace/span IDs |
|
|
23
|
+
| DNS/FS instrumentation | Optional monitoring |
|
|
24
|
+
| Resource detection | Host, OS, process, container |
|
|
25
|
+
| W3C Trace Context | Standard propagation |
|
|
25
26
|
|
|
26
27
|
## Prerequisites
|
|
27
28
|
- NodeJS
|
|
29
|
+
- Observability
|
|
28
30
|
- ...
|
|
29
31
|
- Profit?
|
|
30
32
|
|
package/libs/index.mjs
CHANGED
|
@@ -132,14 +132,113 @@ export function setupTracing(options = {}) {
|
|
|
132
132
|
// Register instrumentations
|
|
133
133
|
const instrumentations = [
|
|
134
134
|
new HttpInstrumentation({
|
|
135
|
-
serverName: serviceName,
|
|
135
|
+
serverName: serviceName,
|
|
136
136
|
ignoreIncomingRequestHook,
|
|
137
137
|
applyCustomAttributesOnSpan,
|
|
138
|
+
requestHook: (span, request) => {
|
|
139
|
+
// Enrich spans with additional HTTP request attributes
|
|
140
|
+
if (request.headers) {
|
|
141
|
+
const userAgent = request.headers['user-agent'];
|
|
142
|
+
const contentType = request.headers['content-type'];
|
|
143
|
+
const contentLength = request.headers['content-length'];
|
|
144
|
+
|
|
145
|
+
if (userAgent) span.setAttribute('http.user_agent', userAgent);
|
|
146
|
+
if (contentType) span.setAttribute('http.request.content_type', contentType);
|
|
147
|
+
if (contentLength) span.setAttribute('http.request.content_length', parseInt(contentLength, 10));
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
responseHook: (span, response) => {
|
|
151
|
+
// Add response attributes for better observability
|
|
152
|
+
if (response.headers) {
|
|
153
|
+
const contentType = response.headers['content-type'];
|
|
154
|
+
const contentLength = response.headers['content-length'];
|
|
155
|
+
|
|
156
|
+
if (contentType) span.setAttribute('http.response.content_type', contentType);
|
|
157
|
+
if (contentLength) span.setAttribute('http.response.content_length', parseInt(contentLength, 10));
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
headersToSpanAttributes: {
|
|
161
|
+
server: {
|
|
162
|
+
requestHeaders: ['x-request-id', 'x-correlation-id', 'x-trace-id'],
|
|
163
|
+
responseHeaders: ['x-request-id', 'x-correlation-id'],
|
|
164
|
+
},
|
|
165
|
+
client: {
|
|
166
|
+
requestHeaders: ['x-request-id', 'x-correlation-id', 'x-trace-id'],
|
|
167
|
+
responseHeaders: ['x-request-id', 'x-correlation-id'],
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}),
|
|
171
|
+
new ExpressInstrumentation({
|
|
172
|
+
ignoreIncomingRequestHook,
|
|
173
|
+
requestHook: (span, request) => {
|
|
174
|
+
// Add Express-specific attributes
|
|
175
|
+
if (request.route?.path) {
|
|
176
|
+
span.setAttribute('express.route', request.route.path);
|
|
177
|
+
span.updateName(`${request.method} ${request.route.path}`);
|
|
178
|
+
}
|
|
179
|
+
if (request.params && Object.keys(request.params).length > 0) {
|
|
180
|
+
span.setAttribute('express.params', JSON.stringify(request.params));
|
|
181
|
+
}
|
|
182
|
+
if (request.query && Object.keys(request.query).length > 0) {
|
|
183
|
+
span.setAttribute('express.query', JSON.stringify(request.query));
|
|
184
|
+
}
|
|
185
|
+
// Add user context if available
|
|
186
|
+
if (request.user?.id) {
|
|
187
|
+
span.setAttribute('user.id', request.user.id);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
}),
|
|
191
|
+
new PinoInstrumentation({
|
|
192
|
+
logHook: (span, record) => {
|
|
193
|
+
// Inject trace context into log records
|
|
194
|
+
const spanContext = span.spanContext();
|
|
195
|
+
record['trace_id'] = spanContext.traceId;
|
|
196
|
+
record['span_id'] = spanContext.spanId;
|
|
197
|
+
record['trace_flags'] = `0${spanContext.traceFlags.toString(16)}`;
|
|
198
|
+
|
|
199
|
+
// Add service name for better log correlation
|
|
200
|
+
if (serviceName) {
|
|
201
|
+
record['service.name'] = serviceName;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
logSeverity: {
|
|
205
|
+
error: 'ERROR',
|
|
206
|
+
warn: 'WARN',
|
|
207
|
+
info: 'INFO',
|
|
208
|
+
debug: 'DEBUG',
|
|
209
|
+
trace: 'TRACE',
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
new ConnectInstrumentation({
|
|
213
|
+
ignoreIncomingRequestHook,
|
|
214
|
+
requestHook: (span, request) => {
|
|
215
|
+
// Add Connect middleware attributes
|
|
216
|
+
if (request.url) {
|
|
217
|
+
span.setAttribute('connect.url', request.url);
|
|
218
|
+
}
|
|
219
|
+
if (request.method) {
|
|
220
|
+
span.setAttribute('connect.method', request.method);
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
new AwsInstrumentation({
|
|
225
|
+
suppressInternalInstrumentation: false,
|
|
226
|
+
sqsExtractContextPropagationFromPayload: true,
|
|
227
|
+
preRequestHook: (span, request) => {
|
|
228
|
+
// Add peer.service attribute for better service map visualization
|
|
229
|
+
const serviceName = request.serviceName || request.service?.serviceIdentifier;
|
|
230
|
+
if (serviceName) {
|
|
231
|
+
span.setAttribute('peer.service', serviceName.toLowerCase());
|
|
232
|
+
span.setAttribute('aws.service', serviceName.toLowerCase());
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
responseHook: (span, response) => {
|
|
236
|
+
// Add additional attributes from response if available
|
|
237
|
+
if (response?.requestId) {
|
|
238
|
+
span.setAttribute('aws.request_id', response.requestId);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
138
241
|
}),
|
|
139
|
-
new ExpressInstrumentation({ ignoreIncomingRequestHook, }),
|
|
140
|
-
new PinoInstrumentation({logHook: (span, record) => {record['trace_id'] = span.spanContext().traceId;record['span_id'] = span.spanContext().spanId;},}),
|
|
141
|
-
new ConnectInstrumentation(),
|
|
142
|
-
new AwsInstrumentation({ sqsExtractContextPropagationFromPayload: true, }),
|
|
143
242
|
new IORedisInstrumentation({
|
|
144
243
|
responseHook: (span) => {
|
|
145
244
|
span.setAttribute('peer.service', 'redis');
|
|
@@ -160,7 +259,57 @@ export function setupTracing(options = {}) {
|
|
|
160
259
|
if (enableDnsInstrumentation) {
|
|
161
260
|
// Enable DNS instrumentation if specified
|
|
162
261
|
// This instrumentation is useful for tracing DNS operations.
|
|
163
|
-
instrumentations.push(new DnsInstrumentation(
|
|
262
|
+
instrumentations.push(new DnsInstrumentation({
|
|
263
|
+
ignoreHostnames: ['localhost', '127.0.0.1', '::1'],
|
|
264
|
+
requestHook: (span, request) => {
|
|
265
|
+
// Add DNS query details for better observability
|
|
266
|
+
if (request.hostname) {
|
|
267
|
+
span.setAttribute('dns.hostname', request.hostname);
|
|
268
|
+
span.updateName(`DNS ${request.hostname}`);
|
|
269
|
+
}
|
|
270
|
+
if (request.rrtype) {
|
|
271
|
+
span.setAttribute('dns.record_type', request.rrtype);
|
|
272
|
+
}
|
|
273
|
+
// Add additional context
|
|
274
|
+
span.setAttribute('peer.service', 'dns');
|
|
275
|
+
span.setAttribute('dns.query_count', 1);
|
|
276
|
+
},
|
|
277
|
+
responseHook: (span, response) => {
|
|
278
|
+
// Add DNS response details
|
|
279
|
+
if (Array.isArray(response)) {
|
|
280
|
+
span.setAttribute('dns.result_count', response.length);
|
|
281
|
+
// Log first few results for debugging (limit to avoid overwhelming spans)
|
|
282
|
+
const resultSample = response.slice(0, 3).map(r =>
|
|
283
|
+
typeof r === 'string' ? r : JSON.stringify(r)
|
|
284
|
+
);
|
|
285
|
+
if (resultSample.length > 0) {
|
|
286
|
+
span.setAttribute('dns.results', JSON.stringify(resultSample));
|
|
287
|
+
}
|
|
288
|
+
} else if (response) {
|
|
289
|
+
span.setAttribute('dns.result_count', 1);
|
|
290
|
+
span.setAttribute('dns.result', typeof response === 'string' ? response : JSON.stringify(response));
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
errorHook: (span, error) => {
|
|
294
|
+
// Enhanced error tracking for DNS failures
|
|
295
|
+
if (error) {
|
|
296
|
+
span.setAttribute('dns.error', true);
|
|
297
|
+
span.setAttribute('dns.error.code', error.code || 'UNKNOWN');
|
|
298
|
+
span.setAttribute('dns.error.message', error.message || 'DNS lookup failed');
|
|
299
|
+
|
|
300
|
+
// Categorize common DNS errors
|
|
301
|
+
if (error.code === 'ENOTFOUND') {
|
|
302
|
+
span.setAttribute('dns.error.type', 'NOT_FOUND');
|
|
303
|
+
} else if (error.code === 'ETIMEOUT') {
|
|
304
|
+
span.setAttribute('dns.error.type', 'TIMEOUT');
|
|
305
|
+
} else if (error.code === 'ECONNREFUSED') {
|
|
306
|
+
span.setAttribute('dns.error.type', 'CONNECTION_REFUSED');
|
|
307
|
+
} else {
|
|
308
|
+
span.setAttribute('dns.error.type', 'OTHER');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
}));
|
|
164
313
|
}
|
|
165
314
|
|
|
166
315
|
// Register instrumentations
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saidsef/tracing-node",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.1",
|
|
4
4
|
"description": "tracing NodeJS - Wrapper for OpenTelemetry instrumentation packages",
|
|
5
5
|
"main": "libs/index.mjs",
|
|
6
6
|
"scripts": {
|
|
@@ -32,16 +32,16 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@opentelemetry/api": "^1.9.0",
|
|
34
34
|
"@opentelemetry/context-async-hooks": "^2.0.1",
|
|
35
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
36
|
-
"@opentelemetry/instrumentation": "^0.
|
|
37
|
-
"@opentelemetry/instrumentation-aws-sdk": "^0.
|
|
38
|
-
"@opentelemetry/instrumentation-connect": "^0.
|
|
39
|
-
"@opentelemetry/instrumentation-dns": "^0.
|
|
40
|
-
"@opentelemetry/instrumentation-express": "^0.
|
|
41
|
-
"@opentelemetry/instrumentation-fs": "^0.
|
|
42
|
-
"@opentelemetry/instrumentation-http": "^0.
|
|
43
|
-
"@opentelemetry/instrumentation-ioredis": "^0.
|
|
44
|
-
"@opentelemetry/instrumentation-pino": "^0.
|
|
35
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
|
|
36
|
+
"@opentelemetry/instrumentation": "^0.208.0",
|
|
37
|
+
"@opentelemetry/instrumentation-aws-sdk": "^0.64.0",
|
|
38
|
+
"@opentelemetry/instrumentation-connect": "^0.52.0",
|
|
39
|
+
"@opentelemetry/instrumentation-dns": "^0.52.0",
|
|
40
|
+
"@opentelemetry/instrumentation-express": "^0.57.0",
|
|
41
|
+
"@opentelemetry/instrumentation-fs": "^0.28.0",
|
|
42
|
+
"@opentelemetry/instrumentation-http": "^0.208.0",
|
|
43
|
+
"@opentelemetry/instrumentation-ioredis": "^0.56.0",
|
|
44
|
+
"@opentelemetry/instrumentation-pino": "^0.55.0",
|
|
45
45
|
"@opentelemetry/resources": "^2.0.1",
|
|
46
46
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
47
47
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|