@comprehend/telemetry-node 0.2.3 → 0.2.5
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/.claude/settings.local.json +2 -2
- package/.idea/telemetry-node.iml +0 -1
- package/README.md +16 -4
- package/dist/ComprehendDevSpanProcessor.js +1 -0
- package/dist/ComprehendDevSpanProcessor.test.js +1 -0
- package/dist/ComprehendMetricsExporter.d.ts +1 -1
- package/dist/ComprehendMetricsExporter.js +2 -8
- package/dist/ComprehendMetricsExporter.test.js +11 -19
- package/dist/sql-analyzer.js +2 -11
- package/dist/sql-analyzer.test.js +0 -12
- package/dist/wire-protocol.d.ts +1 -0
- package/package.json +1 -1
- package/src/ComprehendDevSpanProcessor.test.ts +1 -0
- package/src/ComprehendDevSpanProcessor.ts +1 -0
- package/src/ComprehendMetricsExporter.test.ts +11 -23
- package/src/ComprehendMetricsExporter.ts +2 -8
- package/src/wire-protocol.ts +1 -0
package/.idea/telemetry-node.iml
CHANGED
|
@@ -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,
|
|
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`:
|
|
@@ -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', () => {
|
|
@@ -12,7 +12,7 @@ export declare class ComprehendMetricsExporter implements PushMetricExporter {
|
|
|
12
12
|
private sendTimeSeriesData;
|
|
13
13
|
private sendCumulativeData;
|
|
14
14
|
private getServiceSubject;
|
|
15
|
-
selectAggregationTemporality(
|
|
15
|
+
selectAggregationTemporality(_instrumentType: InstrumentType): AggregationTemporality;
|
|
16
16
|
forceFlush(): Promise<void>;
|
|
17
17
|
shutdown(): Promise<void>;
|
|
18
18
|
}
|
|
@@ -141,14 +141,8 @@ class ComprehendMetricsExporter {
|
|
|
141
141
|
const idString = `service:${name}:${namespace ?? ''}:${environment ?? ''}`;
|
|
142
142
|
return hashIdString(idString);
|
|
143
143
|
}
|
|
144
|
-
selectAggregationTemporality(
|
|
145
|
-
|
|
146
|
-
case sdk_metrics_1.InstrumentType.COUNTER:
|
|
147
|
-
case sdk_metrics_1.InstrumentType.HISTOGRAM:
|
|
148
|
-
return sdk_metrics_1.AggregationTemporality.DELTA;
|
|
149
|
-
default:
|
|
150
|
-
return sdk_metrics_1.AggregationTemporality.CUMULATIVE;
|
|
151
|
-
}
|
|
144
|
+
selectAggregationTemporality(_instrumentType) {
|
|
145
|
+
return sdk_metrics_1.AggregationTemporality.CUMULATIVE;
|
|
152
146
|
}
|
|
153
147
|
async forceFlush() {
|
|
154
148
|
}
|
|
@@ -234,25 +234,17 @@ describe('ComprehendMetricsExporter', () => {
|
|
|
234
234
|
});
|
|
235
235
|
});
|
|
236
236
|
describe('Aggregation Temporality', () => {
|
|
237
|
-
it('should return
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
249
|
-
it('should return CUMULATIVE for OBSERVABLE_COUNTER', () => {
|
|
250
|
-
expect(exporter.selectAggregationTemporality(sdk_metrics_1.InstrumentType.OBSERVABLE_COUNTER))
|
|
251
|
-
.toBe(sdk_metrics_1.AggregationTemporality.CUMULATIVE);
|
|
252
|
-
});
|
|
253
|
-
it('should return CUMULATIVE for GAUGE', () => {
|
|
254
|
-
expect(exporter.selectAggregationTemporality(sdk_metrics_1.InstrumentType.GAUGE))
|
|
255
|
-
.toBe(sdk_metrics_1.AggregationTemporality.CUMULATIVE);
|
|
237
|
+
it('should return CUMULATIVE for all instrument types', () => {
|
|
238
|
+
for (const instrumentType of [
|
|
239
|
+
sdk_metrics_1.InstrumentType.COUNTER,
|
|
240
|
+
sdk_metrics_1.InstrumentType.HISTOGRAM,
|
|
241
|
+
sdk_metrics_1.InstrumentType.OBSERVABLE_GAUGE,
|
|
242
|
+
sdk_metrics_1.InstrumentType.OBSERVABLE_COUNTER,
|
|
243
|
+
sdk_metrics_1.InstrumentType.GAUGE,
|
|
244
|
+
]) {
|
|
245
|
+
expect(exporter.selectAggregationTemporality(instrumentType))
|
|
246
|
+
.toBe(sdk_metrics_1.AggregationTemporality.CUMULATIVE);
|
|
247
|
+
}
|
|
256
248
|
});
|
|
257
249
|
});
|
|
258
250
|
describe('Lifecycle', () => {
|
package/dist/sql-analyzer.js
CHANGED
|
@@ -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
|
-
//
|
|
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,
|
|
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
|
});
|
package/dist/wire-protocol.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -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
|
|
|
@@ -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 } : {}),
|
|
@@ -296,29 +296,17 @@ describe('ComprehendMetricsExporter', () => {
|
|
|
296
296
|
});
|
|
297
297
|
|
|
298
298
|
describe('Aggregation Temporality', () => {
|
|
299
|
-
it('should return
|
|
300
|
-
|
|
301
|
-
.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
expect(exporter.selectAggregationTemporality(InstrumentType.OBSERVABLE_GAUGE))
|
|
311
|
-
.toBe(AggregationTemporality.CUMULATIVE);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it('should return CUMULATIVE for OBSERVABLE_COUNTER', () => {
|
|
315
|
-
expect(exporter.selectAggregationTemporality(InstrumentType.OBSERVABLE_COUNTER))
|
|
316
|
-
.toBe(AggregationTemporality.CUMULATIVE);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it('should return CUMULATIVE for GAUGE', () => {
|
|
320
|
-
expect(exporter.selectAggregationTemporality(InstrumentType.GAUGE))
|
|
321
|
-
.toBe(AggregationTemporality.CUMULATIVE);
|
|
299
|
+
it('should return CUMULATIVE for all instrument types', () => {
|
|
300
|
+
for (const instrumentType of [
|
|
301
|
+
InstrumentType.COUNTER,
|
|
302
|
+
InstrumentType.HISTOGRAM,
|
|
303
|
+
InstrumentType.OBSERVABLE_GAUGE,
|
|
304
|
+
InstrumentType.OBSERVABLE_COUNTER,
|
|
305
|
+
InstrumentType.GAUGE,
|
|
306
|
+
]) {
|
|
307
|
+
expect(exporter.selectAggregationTemporality(instrumentType))
|
|
308
|
+
.toBe(AggregationTemporality.CUMULATIVE);
|
|
309
|
+
}
|
|
322
310
|
});
|
|
323
311
|
});
|
|
324
312
|
|
|
@@ -183,14 +183,8 @@ export class ComprehendMetricsExporter implements PushMetricExporter {
|
|
|
183
183
|
return hashIdString(idString);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
selectAggregationTemporality(
|
|
187
|
-
|
|
188
|
-
case InstrumentType.COUNTER:
|
|
189
|
-
case InstrumentType.HISTOGRAM:
|
|
190
|
-
return AggregationTemporality.DELTA;
|
|
191
|
-
default:
|
|
192
|
-
return AggregationTemporality.CUMULATIVE;
|
|
193
|
-
}
|
|
186
|
+
selectAggregationTemporality(_instrumentType: InstrumentType): AggregationTemporality {
|
|
187
|
+
return AggregationTemporality.CUMULATIVE;
|
|
194
188
|
}
|
|
195
189
|
|
|
196
190
|
async forceFlush(): Promise<void> {
|
package/src/wire-protocol.ts
CHANGED