@comprehend/telemetry-node 0.2.0 → 0.2.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/.claude/settings.local.json +2 -2
- package/.idea/telemetry-node.iml +1 -0
- package/README.md +39 -0
- package/dist/ComprehendDevSpanProcessor.d.ts +1 -0
- package/dist/ComprehendDevSpanProcessor.js +9 -0
- package/dist/sql-analyzer.js +11 -2
- package/dist/sql-analyzer.test.js +12 -0
- package/package.json +1 -1
- package/src/ComprehendDevSpanProcessor.ts +9 -0
package/.idea/telemetry-node.iml
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
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" />
|
|
6
7
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
8
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
9
|
</content>
|
package/README.md
CHANGED
|
@@ -119,6 +119,45 @@ 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
|
+
### Kubernetes / container resources
|
|
123
|
+
|
|
124
|
+
When running in containers or Kubernetes, the `NodeSDK` default resource detectors (`env`, `process`, `host`) do not include container or pod attributes. To populate `container.id`, `k8s.pod.name`, `k8s.namespace.name`, and similar, add the container resource detector explicitly via `resourceDetectors`:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm install @opentelemetry/resource-detector-container
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
132
|
+
import { envDetector, hostDetector, processDetector } from '@opentelemetry/resources';
|
|
133
|
+
import { containerDetector } from '@opentelemetry/resource-detector-container';
|
|
134
|
+
|
|
135
|
+
const sdk = new NodeSDK({
|
|
136
|
+
// ...
|
|
137
|
+
resourceDetectors: [envDetector, processDetector, hostDetector, containerDetector],
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Note that passing `resourceDetectors` replaces the defaults, so make sure to include the built-in detectors alongside `containerDetector`. The container detector reads `container.id` from `/proc/self/cgroup`. For other k8s attributes (pod name, namespace, node), use the Kubernetes Downward API to inject them as `OTEL_RESOURCE_ATTRIBUTES`:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
env:
|
|
145
|
+
- name: OTEL_RESOURCE_ATTRIBUTES
|
|
146
|
+
value: k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(POD_NAMESPACE),k8s.node.name=$(NODE_NAME)
|
|
147
|
+
- name: POD_NAME
|
|
148
|
+
valueFrom:
|
|
149
|
+
fieldRef:
|
|
150
|
+
fieldPath: metadata.name
|
|
151
|
+
- name: POD_NAMESPACE
|
|
152
|
+
valueFrom:
|
|
153
|
+
fieldRef:
|
|
154
|
+
fieldPath: metadata.namespace
|
|
155
|
+
- name: NODE_NAME
|
|
156
|
+
valueFrom:
|
|
157
|
+
fieldRef:
|
|
158
|
+
fieldPath: spec.nodeName
|
|
159
|
+
```
|
|
160
|
+
|
|
122
161
|
## Configuration
|
|
123
162
|
|
|
124
163
|
Set your comprehend.dev SDK token as an environment variable:
|
|
@@ -14,6 +14,7 @@ export declare class ComprehendDevSpanProcessor implements SpanProcessor {
|
|
|
14
14
|
updateCustomMetrics(specs: CustomMetricSpecification[]): void;
|
|
15
15
|
onStart(span: Span, parentContext: Context): void;
|
|
16
16
|
onEnd(span: ReadableSpan): void;
|
|
17
|
+
private processSpan;
|
|
17
18
|
private discoverService;
|
|
18
19
|
private processHTTPRoute;
|
|
19
20
|
private processDatabaseOperation;
|
|
@@ -25,6 +25,15 @@ class ComprehendDevSpanProcessor {
|
|
|
25
25
|
onStart(span, parentContext) {
|
|
26
26
|
}
|
|
27
27
|
onEnd(span) {
|
|
28
|
+
const process = () => this.processSpan(span);
|
|
29
|
+
if (span.resource.asyncAttributesPending) {
|
|
30
|
+
span.resource.waitForAsyncAttributes?.().then(process);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
process();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
processSpan(span) {
|
|
28
37
|
const currentService = this.discoverService(span);
|
|
29
38
|
if (!currentService)
|
|
30
39
|
return;
|
package/dist/sql-analyzer.js
CHANGED
|
@@ -23,6 +23,7 @@ function analyzeSQL(sql) {
|
|
|
23
23
|
let skippingValues = false;
|
|
24
24
|
let lookingForCommaOrEnd = false;
|
|
25
25
|
let valuesDepth = 0;
|
|
26
|
+
let skippedWhitespace = [];
|
|
26
27
|
for (let token of tokenizeSQL(sql)) {
|
|
27
28
|
switch (token.type) {
|
|
28
29
|
case "whitespace":
|
|
@@ -136,23 +137,31 @@ function analyzeSQL(sql) {
|
|
|
136
137
|
switch (token.type) {
|
|
137
138
|
case "comment":
|
|
138
139
|
case "whitespace":
|
|
139
|
-
//
|
|
140
|
+
// Collect whitespace/comments while looking for comma or end
|
|
141
|
+
skippedWhitespace.push(token);
|
|
140
142
|
break;
|
|
141
143
|
case "punct":
|
|
142
144
|
if (token.value === ",") {
|
|
143
|
-
// More tuples coming, continue skipping
|
|
145
|
+
// More tuples coming, clear skipped whitespace and continue skipping
|
|
146
|
+
skippedWhitespace = [];
|
|
144
147
|
lookingForCommaOrEnd = false;
|
|
145
148
|
skippingValues = true;
|
|
146
149
|
}
|
|
147
150
|
else {
|
|
148
151
|
// Not a comma, so VALUES clause is done
|
|
152
|
+
// Add back the skipped whitespace, then the current token
|
|
153
|
+
presentableTokens.push(...skippedWhitespace);
|
|
149
154
|
presentableTokens.push(token);
|
|
155
|
+
skippedWhitespace = [];
|
|
150
156
|
lookingForCommaOrEnd = false;
|
|
151
157
|
}
|
|
152
158
|
break;
|
|
153
159
|
default:
|
|
154
160
|
// VALUES clause is done, resume normal processing
|
|
161
|
+
// Add back the skipped whitespace, then the current token
|
|
162
|
+
presentableTokens.push(...skippedWhitespace);
|
|
155
163
|
presentableTokens.push(token);
|
|
164
|
+
skippedWhitespace = [];
|
|
156
165
|
lookingForCommaOrEnd = false;
|
|
157
166
|
break;
|
|
158
167
|
}
|
|
@@ -482,4 +482,16 @@ 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
|
+
});
|
|
485
497
|
});
|
package/package.json
CHANGED
|
@@ -93,6 +93,15 @@ export class ComprehendDevSpanProcessor implements SpanProcessor {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
onEnd(span: ReadableSpan): void {
|
|
96
|
+
const process = () => this.processSpan(span);
|
|
97
|
+
if (span.resource.asyncAttributesPending) {
|
|
98
|
+
span.resource.waitForAsyncAttributes?.().then(process);
|
|
99
|
+
} else {
|
|
100
|
+
process();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private processSpan(span: ReadableSpan): void {
|
|
96
105
|
const currentService = this.discoverService(span);
|
|
97
106
|
if (!currentService)
|
|
98
107
|
return;
|