@saidsef/tracing-node 3.11.1 → 3.12.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.
Files changed (2) hide show
  1. package/libs/index.mjs +129 -17
  2. package/package.json +1 -1
package/libs/index.mjs CHANGED
@@ -109,24 +109,61 @@ export function setupTracing(options = {}) {
109
109
  return req.url.startsWith('/metrics') || req.url.startsWith('/healthz');
110
110
  };
111
111
 
112
- // Hook to set peer service name for outgoing requests
113
- const applyCustomAttributesOnSpan = (span, request) => {
114
- const url = request?.url || request?.uri || '';
115
- const hostname = request?.hostname || request?.host || '';
112
+ // Helper function to extract hostname from various request formats
113
+ const extractHostname = (request) => {
114
+ // Try to get hostname from various possible locations
115
+ let hostname = request?.hostname || request?.host || '';
116
116
 
117
- // Detect Elasticsearch endpoints
118
- if (hostname.includes('elasticsearch') || url.includes('elasticsearch') ||
119
- hostname.includes(':9200') || url.includes(':9200')) {
120
- span.setAttribute('peer.service', 'elasticsearch');
121
- span.setAttribute('db.system', 'elasticsearch');
117
+ // If not found, try to parse from URL
118
+ if (!hostname && request?.url) {
119
+ try {
120
+ const urlObj = new URL(request.url);
121
+ hostname = urlObj.hostname;
122
+ } catch {
123
+ // If URL parsing fails, try to extract from request options
124
+ if (request?.options?.hostname) {
125
+ hostname = request.options.hostname;
126
+ } else if (request?.options?.host) {
127
+ hostname = request.options.host;
128
+ }
129
+ }
122
130
  }
123
131
 
124
- // Detect Redis endpoints
125
- if (hostname.includes('redis') || url.includes('redis') ||
126
- hostname.includes(':6379') || url.includes(':6379')) {
127
- span.setAttribute('peer.service', 'redis');
128
- span.setAttribute('db.system', 'redis');
132
+ return hostname;
133
+ };
134
+
135
+ // Helper function to determine peer service name from hostname/URL
136
+ const getPeerServiceName = (hostname, url = '') => {
137
+ const lowerHostname = hostname.toLowerCase();
138
+ const lowerUrl = url.toLowerCase();
139
+
140
+ // Check for known service patterns
141
+ if (lowerHostname.includes('elasticsearch') || lowerUrl.includes('elasticsearch') ||
142
+ lowerHostname.includes(':9200') || lowerUrl.includes(':9200')) {
143
+ return 'elasticsearch';
129
144
  }
145
+
146
+ if (lowerHostname.includes('redis') || lowerUrl.includes('redis') ||
147
+ lowerHostname.includes(':6379') || lowerUrl.includes(':6379')) {
148
+ return 'redis';
149
+ }
150
+
151
+ // Extract service name from hostname patterns like:
152
+ // - service-name.namespace.svc.cluster.local
153
+ // - service-name.namespace.svc
154
+ // - api.service-name.com
155
+ if (lowerHostname.includes('.svc')) {
156
+ const parts = lowerHostname.split('.');
157
+ return parts[0]; // Return the service name part
158
+ }
159
+
160
+ // For external domains, use the hostname without 'www'
161
+ if (lowerHostname.startsWith('www.')) {
162
+ return lowerHostname.substring(4);
163
+ }
164
+
165
+ // Return the full hostname as the service name
166
+ return hostname || 'unknown';
130
167
  };
131
168
 
132
169
  // Register instrumentations
@@ -134,8 +171,39 @@ export function setupTracing(options = {}) {
134
171
  new HttpInstrumentation({
135
172
  serverName: serviceName,
136
173
  ignoreIncomingRequestHook,
137
- applyCustomAttributesOnSpan,
174
+ requireParentforOutgoingSpans: false,
175
+ requireParentforIncomingSpans: false,
138
176
  requestHook: (span, request) => {
177
+ const spanKind = span.kind;
178
+ const isClientSpan = spanKind === 3; // SpanKind.CLIENT = 3
179
+
180
+ // For CLIENT spans (outgoing requests), add peer service attributes
181
+ if (isClientSpan) {
182
+ const hostname = extractHostname(request);
183
+ const url = request?.url || request?.uri || '';
184
+
185
+ if (hostname) {
186
+ const peerService = getPeerServiceName(hostname, url);
187
+
188
+ // Set attributes for service graph
189
+ span.setAttribute('peer.service', peerService);
190
+ span.setAttribute('net.peer.name', hostname);
191
+
192
+ // Add port if available
193
+ const port = request?.port || request?.options?.port;
194
+ if (port) {
195
+ span.setAttribute('net.peer.port', parseInt(port, 10));
196
+ }
197
+
198
+ // Add method for better span naming
199
+ const method = request?.method || 'GET';
200
+ span.setAttribute('http.method', method.toUpperCase());
201
+
202
+ // Update span name for clarity
203
+ span.updateName(`${method.toUpperCase()} ${peerService}`);
204
+ }
205
+ }
206
+
139
207
  // Enrich spans with additional HTTP request attributes
140
208
  if (request.headers) {
141
209
  const userAgent = request.headers['user-agent'];
@@ -240,11 +308,55 @@ export function setupTracing(options = {}) {
240
308
  },
241
309
  }),
242
310
  new IORedisInstrumentation({
243
- responseHook: (span) => {
311
+ requireParentSpan: false,
312
+ responseHook: (span, cmdName, cmdArgs, response) => {
313
+ // Set peer.service for service graph visualization
244
314
  span.setAttribute('peer.service', 'redis');
315
+ span.setAttribute('db.system', 'redis');
316
+
317
+ // Add command details for better observability
318
+ if (cmdName) {
319
+ span.setAttribute('db.operation', cmdName.toUpperCase());
320
+ }
321
+
322
+ // Log response size if available
323
+ if (response !== undefined && response !== null) {
324
+ const responseType = typeof response;
325
+ span.setAttribute('db.response.type', responseType);
326
+
327
+ if (Array.isArray(response)) {
328
+ span.setAttribute('db.response.count', response.length);
329
+ }
330
+ }
245
331
  },
246
- requestHook: (span) => {
332
+ requestHook: (span, cmdName, cmdArgs) => {
333
+ // Set peer.service for service graph visualization - CRITICAL for Tempo
247
334
  span.setAttribute('peer.service', 'redis');
335
+ span.setAttribute('db.system', 'redis');
336
+
337
+ // Add command details
338
+ if (cmdName) {
339
+ span.setAttribute('db.operation', cmdName.toUpperCase());
340
+ span.updateName(`redis.${cmdName.toUpperCase()}`);
341
+ }
342
+
343
+ // Add key information (first argument is usually the key)
344
+ if (cmdArgs && cmdArgs.length > 0) {
345
+ span.setAttribute('db.redis.key', String(cmdArgs[0]));
346
+
347
+ // For operations with multiple keys or complex args
348
+ if (cmdArgs.length > 1) {
349
+ span.setAttribute('db.redis.args_count', cmdArgs.length);
350
+ }
351
+ }
352
+ },
353
+ dbStatementSerializer: (cmdName, cmdArgs) => {
354
+ // Serialize command for better observability (limit arg length to avoid huge spans)
355
+ const args = cmdArgs.map(arg => {
356
+ const str = String(arg);
357
+ return str.length > 100 ? `${str.substring(0, 100)}...` : str;
358
+ });
359
+ return `${cmdName} ${args.join(' ')}`;
248
360
  },
249
361
  }),
250
362
  new ElasticsearchInstrumentation(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saidsef/tracing-node",
3
- "version": "3.11.1",
3
+ "version": "3.12.0",
4
4
  "description": "tracing NodeJS - Wrapper for OpenTelemetry instrumentation packages",
5
5
  "main": "libs/index.mjs",
6
6
  "scripts": {