@comprehend/telemetry-node 0.2.2 → 0.2.4

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.
@@ -2,8 +2,8 @@
2
2
  "permissions": {
3
3
  "allow": [
4
4
  "Bash(npm test:*)",
5
- "Bash(pnpm run test:*)"
5
+ "Bash(grep:*)"
6
6
  ],
7
7
  "deny": []
8
8
  }
9
- }
9
+ }
@@ -3,7 +3,6 @@
3
3
  <component name="NewModuleRootManager">
4
4
  <content url="file://$MODULE_DIR$">
5
5
  <excludeFolder url="file://$MODULE_DIR$/.tmp" />
6
- <excludeFolder url="file://$MODULE_DIR$/dist" />
7
6
  <excludeFolder url="file://$MODULE_DIR$/temp" />
8
7
  <excludeFolder url="file://$MODULE_DIR$/tmp" />
9
8
  </content>
package/README.md CHANGED
@@ -119,16 +119,30 @@ hostMetrics.start();
119
119
 
120
120
  This collects `process.cpu.time`, `process.cpu.utilization`, and `process.memory.usage`. The `metricGroups` option limits collection to process-level metrics only, avoiding the overhead of system-wide CPU, memory, and network data gathering.
121
121
 
122
+ ### Service instance identity
123
+
124
+ Include `serviceInstanceIdDetector` in your `resourceDetectors` to give each running process a unique identity that changes on every restart. This lets comprehend.dev distinguish between different instances of the same service and track restarts over time:
125
+
126
+ ```typescript
127
+ import { envDetector, hostDetector, processDetector, serviceInstanceIdDetector } from '@opentelemetry/resources';
128
+
129
+ const sdk = new NodeSDK({
130
+ // ...
131
+ resourceDetectors: [envDetector, processDetector, hostDetector, serviceInstanceIdDetector],
132
+ });
133
+ ```
134
+
135
+ `serviceInstanceIdDetector` generates a random UUID (`service.instance.id`) at process startup. Note that specifying `resourceDetectors` explicitly replaces the `NodeSDK` defaults, so include the built-in detectors (`envDetector`, `processDetector`, `hostDetector`) alongside it.
136
+
122
137
  ### Kubernetes / container resources
123
138
 
124
- When running in containers or Kubernetes, the `NodeSDK` default resource detectors (`env`, `process`, `host`) do not include container or pod attributes. Configure `resourceDetectors` explicitly to add them — note that doing so replaces the defaults, so include the built-in detectors too:
139
+ When running in containers or Kubernetes, add `containerDetector` to your `resourceDetectors`:
125
140
 
126
141
  ```bash
127
142
  npm install @opentelemetry/resource-detector-container
128
143
  ```
129
144
 
130
145
  ```typescript
131
- import { NodeSDK } from '@opentelemetry/sdk-node';
132
146
  import { envDetector, hostDetector, processDetector, serviceInstanceIdDetector } from '@opentelemetry/resources';
133
147
  import { containerDetector } from '@opentelemetry/resource-detector-container';
134
148
 
@@ -138,8 +152,6 @@ const sdk = new NodeSDK({
138
152
  });
139
153
  ```
140
154
 
141
- **`serviceInstanceIdDetector`** generates a random UUID (`service.instance.id`) at process startup. This gives each deployment instance a unique identity that changes on every restart, which is useful for distinguishing runs in environments where `container.id` is not available (see below).
142
-
143
155
  **`containerDetector`** reads `container.id` from `/proc/self/cgroup`. This works reliably with Docker-based runtimes but not on modern containerd-based Kubernetes clusters (which is the default since k8s 1.24), where the cgroup format does not expose the container ID in the expected location. Add it anyway — it will populate `container.id` when available and is a no-op otherwise.
144
156
 
145
157
  For k8s identity attributes that cannot be read from the host (pod name, namespace, node), use the Kubernetes Downward API to inject them as `OTEL_RESOURCE_ATTRIBUTES`:
@@ -174,9 +174,9 @@ class ComprehendDevSpanProcessor {
174
174
  : {
175
175
  scrubbed: '',
176
176
  user: undefined,
177
- host: (attrs['net.peer.name'] ?? attrs['net.peer.ip']),
178
- port: attrs['net.peer.port']?.toString(),
179
- name: attrs['db.name'],
177
+ host: (attrs['server.address'] ?? attrs['net.peer.name'] ?? attrs['net.peer.ip']),
178
+ port: (attrs['server.port'] ?? attrs['net.peer.port'])?.toString(),
179
+ name: (attrs['db.namespace'] ?? attrs['db.name']),
180
180
  };
181
181
  const hash = hashIdString(`database:${system}:${parsed.host ?? ''}:${parsed.port ?? ''}:${parsed.name ?? ''}`);
182
182
  let observedDatabase = this.observedDatabases.find(db => db.hash === hash);
@@ -351,6 +351,7 @@ class ComprehendDevSpanProcessor {
351
351
  spanId,
352
352
  traceId,
353
353
  timestamp: span.startTime,
354
+ duration: span.duration,
354
355
  attributes: collectedAttrs,
355
356
  ...(errorMessage ? { errorMessage } : {}),
356
357
  ...(errorType ? { errorType } : {}),
@@ -304,6 +304,7 @@ describe('ComprehendDevSpanProcessor', () => {
304
304
  expect(customObs).toBeDefined();
305
305
  expect(customObs.subject).toBe('custom-server-obs');
306
306
  expect(customObs.id).toBe('custom-server-obs');
307
+ expect(customObs.duration).toBeDefined();
307
308
  expect(customObs.attributes).toBeDefined();
308
309
  });
309
310
  it('should match spans by attribute-present rule', () => {
@@ -23,7 +23,6 @@ function analyzeSQL(sql) {
23
23
  let skippingValues = false;
24
24
  let lookingForCommaOrEnd = false;
25
25
  let valuesDepth = 0;
26
- let skippedWhitespace = [];
27
26
  for (let token of tokenizeSQL(sql)) {
28
27
  switch (token.type) {
29
28
  case "whitespace":
@@ -137,31 +136,23 @@ function analyzeSQL(sql) {
137
136
  switch (token.type) {
138
137
  case "comment":
139
138
  case "whitespace":
140
- // Collect whitespace/comments while looking for comma or end
141
- skippedWhitespace.push(token);
139
+ // Skip whitespace/comments while looking for comma or end
142
140
  break;
143
141
  case "punct":
144
142
  if (token.value === ",") {
145
- // More tuples coming, clear skipped whitespace and continue skipping
146
- skippedWhitespace = [];
143
+ // More tuples coming, continue skipping
147
144
  lookingForCommaOrEnd = false;
148
145
  skippingValues = true;
149
146
  }
150
147
  else {
151
148
  // Not a comma, so VALUES clause is done
152
- // Add back the skipped whitespace, then the current token
153
- presentableTokens.push(...skippedWhitespace);
154
149
  presentableTokens.push(token);
155
- skippedWhitespace = [];
156
150
  lookingForCommaOrEnd = false;
157
151
  }
158
152
  break;
159
153
  default:
160
154
  // VALUES clause is done, resume normal processing
161
- // Add back the skipped whitespace, then the current token
162
- presentableTokens.push(...skippedWhitespace);
163
155
  presentableTokens.push(token);
164
- skippedWhitespace = [];
165
156
  lookingForCommaOrEnd = false;
166
157
  break;
167
158
  }
@@ -482,16 +482,4 @@ describe('SQL Analyzer - bulk INSERT VALUES cardinality reduction', () => {
482
482
  expect(result.presentableQuery).toEqual(`INSERT INTO comments (text, author) VALUES
483
483
  (...)`);
484
484
  });
485
- it('preserves whitespace before ON CONFLICT after VALUES clause', () => {
486
- const sql = `INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com') ON CONFLICT (email) DO NOTHING`;
487
- const result = (0, sql_analyzer_1.analyzeSQL)(sql);
488
- expect(result.tableOperations).toEqual({ users: ['INSERT'] });
489
- expect(result.presentableQuery).toEqual(`INSERT INTO users (name, email) VALUES (...) ON CONFLICT (email) DO NOTHING`);
490
- });
491
- it('preserves whitespace before ON CONFLICT with multiple VALUES tuples', () => {
492
- const sql = `INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'), ('Bob', 'bob@example.com') ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name`;
493
- const result = (0, sql_analyzer_1.analyzeSQL)(sql);
494
- expect(result.tableOperations).toEqual({ users: ['INSERT'] });
495
- expect(result.presentableQuery).toEqual(`INSERT INTO users (name, email) VALUES (...) ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name`);
496
- });
497
485
  });
@@ -120,6 +120,7 @@ export type AttributeType = string | number | boolean;
120
120
  export interface CustomObservation extends BaseObservation {
121
121
  type: "custom";
122
122
  id: string;
123
+ duration?: HrTime;
123
124
  attributes: {
124
125
  [key: string]: AttributeType;
125
126
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comprehend/telemetry-node",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Integration of comprehend.dev with OpenTelemetry in Node.js and similar environemnts.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -403,6 +403,7 @@ describe('ComprehendDevSpanProcessor', () => {
403
403
  expect(customObs).toBeDefined();
404
404
  expect(customObs.subject).toBe('custom-server-obs');
405
405
  expect(customObs.id).toBe('custom-server-obs');
406
+ expect(customObs.duration).toBeDefined();
406
407
  expect(customObs.attributes).toBeDefined();
407
408
  });
408
409
 
@@ -257,9 +257,9 @@ export class ComprehendDevSpanProcessor implements SpanProcessor {
257
257
  : {
258
258
  scrubbed: '',
259
259
  user: undefined,
260
- host: (attrs['net.peer.name'] ?? attrs['net.peer.ip']) as string | undefined,
261
- port: (attrs['net.peer.port'] as number | undefined)?.toString(),
262
- name: attrs['db.name'] as string | undefined,
260
+ host: (attrs['server.address'] ?? attrs['net.peer.name'] ?? attrs['net.peer.ip']) as string | undefined,
261
+ port: (attrs['server.port'] ?? attrs['net.peer.port'] as number | undefined)?.toString(),
262
+ name: (attrs['db.namespace'] ?? attrs['db.name']) as string | undefined,
263
263
  };
264
264
 
265
265
  const hash = hashIdString(`database:${system}:${parsed.host ?? ''}:${parsed.port ?? ''}:${parsed.name ?? ''}`);
@@ -448,6 +448,7 @@ export class ComprehendDevSpanProcessor implements SpanProcessor {
448
448
  spanId,
449
449
  traceId,
450
450
  timestamp: span.startTime,
451
+ duration: span.duration,
451
452
  attributes: collectedAttrs,
452
453
  ...(errorMessage ? { errorMessage } : {}),
453
454
  ...(errorType ? { errorType } : {}),
@@ -162,6 +162,7 @@ export type AttributeType = string | number | boolean;
162
162
  export interface CustomObservation extends BaseObservation {
163
163
  type: "custom";
164
164
  id: string;
165
+ duration?: HrTime;
165
166
  attributes: {
166
167
  [key: string]: AttributeType
167
168
  };