@davidilie/telemetry-node 0.3.0 → 0.5.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.
- package/README.md +13 -4
- package/dist/index.cjs +66 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.mts +12 -2
- package/dist/index.mjs +66 -2
- package/dist/register.cjs +2 -1
- package/dist/register.mjs +2 -1
- package/dist/{sampler-B1PsF--M.mjs → sampler-80dS1PU-.mjs} +32 -16
- package/dist/{sampler-D5mi13Eh.cjs → sampler-vVC2xXgy.cjs} +32 -16
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -55,6 +55,16 @@ otherwise silently use a stopped provider. Do not register this package beside a
|
|
|
55
55
|
The optional `sdk` property accepts `NodeSDKConfiguration` fields other than
|
|
56
56
|
`resource` and `serviceName`. Use it for maintained instrumentations, readers,
|
|
57
57
|
processors, or exporters when environment-based defaults are insufficient.
|
|
58
|
+
Resource detectors default to an empty list, excluding automatic host/process
|
|
59
|
+
metadata and `OTEL_RESOURCE_ATTRIBUTES` / `OTEL_SERVICE_NAME` overrides. Set
|
|
60
|
+
`sdk.resourceDetectors` only for reviewed detectors: their output bypasses core
|
|
61
|
+
redaction and may override the configured resource identity.
|
|
62
|
+
|
|
63
|
+
Wrap a custom trace exporter in the exported `RedactingSpanExporter` to apply
|
|
64
|
+
core privacy bounds to completed automatic spans as well. It redacts copies
|
|
65
|
+
of span names, attributes, events, links, and status messages, leaving the
|
|
66
|
+
original spans and trace context intact. Resource and instrumentation-scope
|
|
67
|
+
metadata remain trusted configuration; configure those separately.
|
|
58
68
|
|
|
59
69
|
## Export configuration
|
|
60
70
|
|
|
@@ -62,7 +72,6 @@ The standalone SDK follows standard OpenTelemetry environment variables. For
|
|
|
62
72
|
Alloy's private OTLP/HTTP receiver:
|
|
63
73
|
|
|
64
74
|
```sh
|
|
65
|
-
OTEL_SERVICE_NAME=email-worker
|
|
66
75
|
OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy.observability.svc.cluster.local:4318
|
|
67
76
|
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
|
68
77
|
```
|
|
@@ -175,7 +184,7 @@ core applies context, consent, sampling, and shutdown checks. Use it when events
|
|
|
175
184
|
The callback receives a safe span handle, including `traceContext()`.
|
|
176
185
|
`client.traceWebhook({ destination: "discord.sales" }, send)` tracks one
|
|
177
186
|
outgoing POST under the active parent. See the
|
|
178
|
-
[webhook guide](https://github.com/
|
|
187
|
+
[webhook guide](https://github.com/davidapps-org/davidapps-telemetry/blob/main/docs/webhooks.md)
|
|
179
188
|
for privacy, delivery outcomes, metrics, and Grafana Discord notifications.
|
|
180
189
|
|
|
181
190
|
Both methods accept `{ kind: "internal" | "server" | "client" | "producer" |
|
|
@@ -231,5 +240,5 @@ context; `startSpan` does not.
|
|
|
231
240
|
- `onError(error, context)` receives contained hook/adapter/span/lifecycle
|
|
232
241
|
failures. Its own failure is ignored; never recurse into the same client.
|
|
233
242
|
|
|
234
|
-
See the [core API](https://www.npmjs.com/package/@davidilie/telemetry-core), [privacy rules](https://github.com/
|
|
235
|
-
[troubleshooting guide](https://github.com/
|
|
243
|
+
See the [core API](https://www.npmjs.com/package/@davidilie/telemetry-core), [privacy rules](https://github.com/davidapps-org/davidapps-telemetry/blob/main/docs/privacy.md), and
|
|
244
|
+
[troubleshooting guide](https://github.com/davidapps-org/davidapps-telemetry/blob/main/docs/troubleshooting.md).
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,74 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_sampler = require("./sampler-
|
|
2
|
+
const require_sampler = require("./sampler-vVC2xXgy.cjs");
|
|
3
|
+
let _davidilie_telemetry_core = require("@davidilie/telemetry-core");
|
|
4
|
+
//#region src/exporter.ts
|
|
5
|
+
function redactSpan(span) {
|
|
6
|
+
const name = span.name.replace(/https?:\/\/[^\s<>"']+/gi, (value) => {
|
|
7
|
+
try {
|
|
8
|
+
return new URL(value).origin;
|
|
9
|
+
} catch {
|
|
10
|
+
return "[REDACTED_URL]";
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
name: (0, _davidilie_telemetry_core.redactText)(name, 256),
|
|
15
|
+
kind: span.kind,
|
|
16
|
+
spanContext: () => span.spanContext(),
|
|
17
|
+
...span.parentSpanContext ? { parentSpanContext: span.parentSpanContext } : {},
|
|
18
|
+
startTime: span.startTime,
|
|
19
|
+
endTime: span.endTime,
|
|
20
|
+
duration: span.duration,
|
|
21
|
+
ended: span.ended,
|
|
22
|
+
resource: span.resource,
|
|
23
|
+
instrumentationScope: span.instrumentationScope,
|
|
24
|
+
droppedAttributesCount: span.droppedAttributesCount,
|
|
25
|
+
droppedEventsCount: span.droppedEventsCount,
|
|
26
|
+
droppedLinksCount: span.droppedLinksCount,
|
|
27
|
+
attributes: require_sampler.toOtelAttributes((0, _davidilie_telemetry_core.sanitizeAttributes)(span.attributes)),
|
|
28
|
+
status: {
|
|
29
|
+
code: span.status.code,
|
|
30
|
+
...span.status.message ? { message: (0, _davidilie_telemetry_core.redactText)(span.status.message) } : {}
|
|
31
|
+
},
|
|
32
|
+
events: span.events.map((event) => ({
|
|
33
|
+
...event,
|
|
34
|
+
name: (0, _davidilie_telemetry_core.redactText)(event.name, 256),
|
|
35
|
+
attributes: require_sampler.toOtelAttributes((0, _davidilie_telemetry_core.sanitizeAttributes)(event.attributes))
|
|
36
|
+
})),
|
|
37
|
+
links: span.links.map((link) => ({
|
|
38
|
+
...link,
|
|
39
|
+
attributes: require_sampler.toOtelAttributes((0, _davidilie_telemetry_core.sanitizeAttributes)(link.attributes))
|
|
40
|
+
}))
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Apply core privacy bounds to completed spans without mutating another processor's data. */
|
|
44
|
+
var RedactingSpanExporter = class {
|
|
45
|
+
delegate;
|
|
46
|
+
constructor(delegate) {
|
|
47
|
+
this.delegate = delegate;
|
|
48
|
+
}
|
|
49
|
+
export(spans, done) {
|
|
50
|
+
let sanitized;
|
|
51
|
+
try {
|
|
52
|
+
sanitized = spans.map(redactSpan);
|
|
53
|
+
} catch {
|
|
54
|
+
done({ code: 1 });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.delegate.export(sanitized, done);
|
|
58
|
+
}
|
|
59
|
+
shutdown() {
|
|
60
|
+
return this.delegate.shutdown();
|
|
61
|
+
}
|
|
62
|
+
forceFlush() {
|
|
63
|
+
return this.delegate.forceFlush?.() ?? Promise.resolve();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
3
67
|
exports.DynamicTelemetrySampler = require_sampler.DynamicTelemetrySampler;
|
|
4
68
|
exports.NodeTelemetryClient = require_sampler.NodeTelemetryClient;
|
|
5
69
|
exports.OpenTelemetryAdapter = require_sampler.OpenTelemetryAdapter;
|
|
6
70
|
exports.OpenTelemetrySpan = require_sampler.OpenTelemetrySpan;
|
|
71
|
+
exports.RedactingSpanExporter = RedactingSpanExporter;
|
|
7
72
|
exports.createNodeTelemetry = require_sampler.createNodeTelemetry;
|
|
8
73
|
exports.createOpenTelemetryAdapter = require_sampler.createOpenTelemetryAdapter;
|
|
9
74
|
exports.currentTraceContext = require_sampler.currentTraceContext;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as OpenTelemetryAdapter, c as createOpenTelemetryAdapter, d as startSpan, f as withSpan, i as MeasurementMode, l as currentTraceContext, n as NodeTelemetryConfig, o as OpenTelemetryAdapterConfig, r as createNodeTelemetry, s as OpenTelemetrySpan, t as NodeTelemetryClient, u as recordException } from "./client-DYrSJX10.cjs";
|
|
2
2
|
import { AttributeValue, Attributes, Context, Link, SpanKind } from "@opentelemetry/api";
|
|
3
3
|
import { AttributeValue as AttributeValue$1, Attributes as Attributes$1, TelemetryResource } from "@davidilie/telemetry-core";
|
|
4
|
-
import { Sampler, SamplingResult } from "@opentelemetry/sdk-trace-base";
|
|
4
|
+
import { ReadableSpan, Sampler, SamplingResult, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
5
5
|
//#region src/attributes.d.ts
|
|
6
6
|
declare function toOtelAttributeValue(value: AttributeValue$1): AttributeValue;
|
|
7
7
|
declare function toOtelAttributes(attributes?: Attributes$1): Attributes;
|
|
@@ -31,4 +31,14 @@ declare class DynamicTelemetrySampler implements Sampler {
|
|
|
31
31
|
toString(): string;
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
34
|
-
|
|
34
|
+
//#region src/exporter.d.ts
|
|
35
|
+
/** Apply core privacy bounds to completed spans without mutating another processor's data. */
|
|
36
|
+
declare class RedactingSpanExporter implements SpanExporter {
|
|
37
|
+
private readonly delegate;
|
|
38
|
+
constructor(delegate: SpanExporter);
|
|
39
|
+
export(spans: ReadableSpan[], done: Parameters<SpanExporter["export"]>[1]): void;
|
|
40
|
+
shutdown(): Promise<void>;
|
|
41
|
+
forceFlush(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { DynamicTelemetrySampler, type MeasurementMode, NodeTelemetryClient, type NodeTelemetryConfig, OpenTelemetryAdapter, type OpenTelemetryAdapterConfig, OpenTelemetrySpan, RedactingSpanExporter, type TelemetrySamplingState, createNodeTelemetry, createOpenTelemetryAdapter, currentTraceContext, recordException, startSpan, telemetryResourceAttributes, toOtelAttributeValue, toOtelAttributes, withSpan };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as OpenTelemetryAdapter, c as createOpenTelemetryAdapter, d as startSpan, f as withSpan, i as MeasurementMode, l as currentTraceContext, n as NodeTelemetryConfig, o as OpenTelemetryAdapterConfig, r as createNodeTelemetry, s as OpenTelemetrySpan, t as NodeTelemetryClient, u as recordException } from "./client-DYrSJX10.mjs";
|
|
2
2
|
import { AttributeValue, Attributes, Context, Link, SpanKind } from "@opentelemetry/api";
|
|
3
3
|
import { AttributeValue as AttributeValue$1, Attributes as Attributes$1, TelemetryResource } from "@davidilie/telemetry-core";
|
|
4
|
-
import { Sampler, SamplingResult } from "@opentelemetry/sdk-trace-base";
|
|
4
|
+
import { ReadableSpan, Sampler, SamplingResult, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
5
5
|
//#region src/attributes.d.ts
|
|
6
6
|
declare function toOtelAttributeValue(value: AttributeValue$1): AttributeValue;
|
|
7
7
|
declare function toOtelAttributes(attributes?: Attributes$1): Attributes;
|
|
@@ -31,4 +31,14 @@ declare class DynamicTelemetrySampler implements Sampler {
|
|
|
31
31
|
toString(): string;
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
34
|
-
|
|
34
|
+
//#region src/exporter.d.ts
|
|
35
|
+
/** Apply core privacy bounds to completed spans without mutating another processor's data. */
|
|
36
|
+
declare class RedactingSpanExporter implements SpanExporter {
|
|
37
|
+
private readonly delegate;
|
|
38
|
+
constructor(delegate: SpanExporter);
|
|
39
|
+
export(spans: ReadableSpan[], done: Parameters<SpanExporter["export"]>[1]): void;
|
|
40
|
+
shutdown(): Promise<void>;
|
|
41
|
+
forceFlush(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { DynamicTelemetrySampler, type MeasurementMode, NodeTelemetryClient, type NodeTelemetryConfig, OpenTelemetryAdapter, type OpenTelemetryAdapterConfig, OpenTelemetrySpan, RedactingSpanExporter, type TelemetrySamplingState, createNodeTelemetry, createOpenTelemetryAdapter, currentTraceContext, recordException, startSpan, telemetryResourceAttributes, toOtelAttributeValue, toOtelAttributes, withSpan };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,66 @@
|
|
|
1
|
-
import { a as OpenTelemetrySpan, c as recordException, d as telemetryResourceAttributes, f as toOtelAttributeValue, i as OpenTelemetryAdapter, l as startSpan, n as NodeTelemetryClient, o as createOpenTelemetryAdapter, p as toOtelAttributes, r as createNodeTelemetry, s as currentTraceContext, t as DynamicTelemetrySampler, u as withSpan } from "./sampler-
|
|
2
|
-
|
|
1
|
+
import { a as OpenTelemetrySpan, c as recordException, d as telemetryResourceAttributes, f as toOtelAttributeValue, i as OpenTelemetryAdapter, l as startSpan, n as NodeTelemetryClient, o as createOpenTelemetryAdapter, p as toOtelAttributes, r as createNodeTelemetry, s as currentTraceContext, t as DynamicTelemetrySampler, u as withSpan } from "./sampler-80dS1PU-.mjs";
|
|
2
|
+
import { redactText, sanitizeAttributes } from "@davidilie/telemetry-core";
|
|
3
|
+
//#region src/exporter.ts
|
|
4
|
+
function redactSpan(span) {
|
|
5
|
+
const name = span.name.replace(/https?:\/\/[^\s<>"']+/gi, (value) => {
|
|
6
|
+
try {
|
|
7
|
+
return new URL(value).origin;
|
|
8
|
+
} catch {
|
|
9
|
+
return "[REDACTED_URL]";
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
name: redactText(name, 256),
|
|
14
|
+
kind: span.kind,
|
|
15
|
+
spanContext: () => span.spanContext(),
|
|
16
|
+
...span.parentSpanContext ? { parentSpanContext: span.parentSpanContext } : {},
|
|
17
|
+
startTime: span.startTime,
|
|
18
|
+
endTime: span.endTime,
|
|
19
|
+
duration: span.duration,
|
|
20
|
+
ended: span.ended,
|
|
21
|
+
resource: span.resource,
|
|
22
|
+
instrumentationScope: span.instrumentationScope,
|
|
23
|
+
droppedAttributesCount: span.droppedAttributesCount,
|
|
24
|
+
droppedEventsCount: span.droppedEventsCount,
|
|
25
|
+
droppedLinksCount: span.droppedLinksCount,
|
|
26
|
+
attributes: toOtelAttributes(sanitizeAttributes(span.attributes)),
|
|
27
|
+
status: {
|
|
28
|
+
code: span.status.code,
|
|
29
|
+
...span.status.message ? { message: redactText(span.status.message) } : {}
|
|
30
|
+
},
|
|
31
|
+
events: span.events.map((event) => ({
|
|
32
|
+
...event,
|
|
33
|
+
name: redactText(event.name, 256),
|
|
34
|
+
attributes: toOtelAttributes(sanitizeAttributes(event.attributes))
|
|
35
|
+
})),
|
|
36
|
+
links: span.links.map((link) => ({
|
|
37
|
+
...link,
|
|
38
|
+
attributes: toOtelAttributes(sanitizeAttributes(link.attributes))
|
|
39
|
+
}))
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Apply core privacy bounds to completed spans without mutating another processor's data. */
|
|
43
|
+
var RedactingSpanExporter = class {
|
|
44
|
+
delegate;
|
|
45
|
+
constructor(delegate) {
|
|
46
|
+
this.delegate = delegate;
|
|
47
|
+
}
|
|
48
|
+
export(spans, done) {
|
|
49
|
+
let sanitized;
|
|
50
|
+
try {
|
|
51
|
+
sanitized = spans.map(redactSpan);
|
|
52
|
+
} catch {
|
|
53
|
+
done({ code: 1 });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.delegate.export(sanitized, done);
|
|
57
|
+
}
|
|
58
|
+
shutdown() {
|
|
59
|
+
return this.delegate.shutdown();
|
|
60
|
+
}
|
|
61
|
+
forceFlush() {
|
|
62
|
+
return this.delegate.forceFlush?.() ?? Promise.resolve();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { DynamicTelemetrySampler, NodeTelemetryClient, OpenTelemetryAdapter, OpenTelemetrySpan, RedactingSpanExporter, createNodeTelemetry, createOpenTelemetryAdapter, currentTraceContext, recordException, startSpan, telemetryResourceAttributes, toOtelAttributeValue, toOtelAttributes, withSpan };
|
package/dist/register.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_sampler = require("./sampler-
|
|
2
|
+
const require_sampler = require("./sampler-vVC2xXgy.cjs");
|
|
3
3
|
let _davidilie_telemetry_core = require("@davidilie/telemetry-core");
|
|
4
4
|
let _opentelemetry_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
5
5
|
let _opentelemetry_resources = require("@opentelemetry/resources");
|
|
@@ -24,6 +24,7 @@ function registerNodeTelemetry(config) {
|
|
|
24
24
|
});
|
|
25
25
|
const sdk = new _opentelemetry_sdk_node.NodeSDK({
|
|
26
26
|
...config.sdk,
|
|
27
|
+
resourceDetectors: config.sdk?.resourceDetectors ?? [],
|
|
27
28
|
sampler,
|
|
28
29
|
resource: (0, _opentelemetry_resources.resourceFromAttributes)(require_sampler.telemetryResourceAttributes(resource)),
|
|
29
30
|
serviceName: resource.serviceName
|
package/dist/register.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as telemetryResourceAttributes, n as NodeTelemetryClient, t as DynamicTelemetrySampler } from "./sampler-
|
|
1
|
+
import { d as telemetryResourceAttributes, n as NodeTelemetryClient, t as DynamicTelemetrySampler } from "./sampler-80dS1PU-.mjs";
|
|
2
2
|
import { sanitizeResource } from "@davidilie/telemetry-core";
|
|
3
3
|
import { ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
|
|
4
4
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
@@ -23,6 +23,7 @@ function registerNodeTelemetry(config) {
|
|
|
23
23
|
});
|
|
24
24
|
const sdk = new NodeSDK({
|
|
25
25
|
...config.sdk,
|
|
26
|
+
resourceDetectors: config.sdk?.resourceDetectors ?? [],
|
|
26
27
|
sampler,
|
|
27
28
|
resource: resourceFromAttributes(telemetryResourceAttributes(resource)),
|
|
28
29
|
serviceName: resource.serviceName
|
|
@@ -62,13 +62,17 @@ const severityNumbers = {
|
|
|
62
62
|
error: SeverityNumber.ERROR
|
|
63
63
|
};
|
|
64
64
|
function exceptionForOtel(error) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
try {
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
const safe = new Error(redactText(error.message), { ...error.cause !== void 0 ? { cause: redactText(String(error.cause)) } : {} });
|
|
68
|
+
safe.name = redactText(error.name, 256);
|
|
69
|
+
if (error.stack) safe.stack = redactText(error.stack, 16384);
|
|
70
|
+
return safe;
|
|
71
|
+
}
|
|
72
|
+
return redactText(typeof error === "string" ? error : String(error));
|
|
73
|
+
} catch {
|
|
74
|
+
return "Unserializable exception";
|
|
70
75
|
}
|
|
71
|
-
return redactText(typeof error === "string" ? error : String(error));
|
|
72
76
|
}
|
|
73
77
|
function signalAttributes(signal) {
|
|
74
78
|
return {
|
|
@@ -136,6 +140,7 @@ var OpenTelemetryAdapter = class {
|
|
|
136
140
|
#logSampleRate;
|
|
137
141
|
#shutdown;
|
|
138
142
|
#histograms = /* @__PURE__ */ new Map();
|
|
143
|
+
#meterProvider;
|
|
139
144
|
constructor(config) {
|
|
140
145
|
this.#resource = sanitizeResource(config.resource);
|
|
141
146
|
this.#instrumentationName = config.instrumentationName ?? this.#resource.serviceName;
|
|
@@ -225,11 +230,16 @@ var OpenTelemetryAdapter = class {
|
|
|
225
230
|
}).end(new Date(signal.timestamp));
|
|
226
231
|
if (this.#measurementMode === "spans") return;
|
|
227
232
|
if (!VALID_INSTRUMENT_NAME.test(signal.name)) return;
|
|
233
|
+
const meterProvider = metrics.getMeterProvider();
|
|
234
|
+
if (meterProvider !== this.#meterProvider) {
|
|
235
|
+
this.#histograms.clear();
|
|
236
|
+
this.#meterProvider = meterProvider;
|
|
237
|
+
}
|
|
228
238
|
const key = `${signal.name}\u0000${signal.unit ?? ""}`;
|
|
229
239
|
let histogram = this.#histograms.get(key);
|
|
230
240
|
if (!histogram) {
|
|
231
241
|
if (this.#histograms.size >= this.#maxMetricInstruments) return;
|
|
232
|
-
histogram =
|
|
242
|
+
histogram = meterProvider.getMeter(this.#instrumentationName, this.#instrumentationVersion).createHistogram(signal.name, { ...signal.unit ? { unit: signal.unit } : {} });
|
|
233
243
|
this.#histograms.set(key, histogram);
|
|
234
244
|
}
|
|
235
245
|
const metricAttributes = Object.fromEntries(Object.entries(signal.attributes).filter(([name]) => this.#metricAttributeAllowlist.has(name)));
|
|
@@ -340,15 +350,21 @@ async function withSpan(name, operation, attributes = {}, options = {}) {
|
|
|
340
350
|
});
|
|
341
351
|
}
|
|
342
352
|
function recordException(error, attributes = {}) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
353
|
+
let ownedSpan;
|
|
354
|
+
try {
|
|
355
|
+
const span = trace.getActiveSpan() ?? (ownedSpan = trace.getTracer("@davidilie/telemetry-node").startSpan("exception"));
|
|
356
|
+
const exception = exceptionForOtel(error);
|
|
357
|
+
span.setAttributes(toOtelAttributes(sanitizeAttributes(attributes)));
|
|
358
|
+
span.recordException(exception);
|
|
359
|
+
span.setStatus({
|
|
360
|
+
code: SpanStatusCode.ERROR,
|
|
361
|
+
message: typeof exception === "string" ? exception : exception.message
|
|
362
|
+
});
|
|
363
|
+
} catch {} finally {
|
|
364
|
+
try {
|
|
365
|
+
ownedSpan?.end();
|
|
366
|
+
} catch {}
|
|
367
|
+
}
|
|
352
368
|
}
|
|
353
369
|
//#endregion
|
|
354
370
|
//#region src/client.ts
|
|
@@ -62,13 +62,17 @@ const severityNumbers = {
|
|
|
62
62
|
error: _opentelemetry_api_logs.SeverityNumber.ERROR
|
|
63
63
|
};
|
|
64
64
|
function exceptionForOtel(error) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
try {
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
const safe = new Error((0, _davidilie_telemetry_core.redactText)(error.message), { ...error.cause !== void 0 ? { cause: (0, _davidilie_telemetry_core.redactText)(String(error.cause)) } : {} });
|
|
68
|
+
safe.name = (0, _davidilie_telemetry_core.redactText)(error.name, 256);
|
|
69
|
+
if (error.stack) safe.stack = (0, _davidilie_telemetry_core.redactText)(error.stack, 16384);
|
|
70
|
+
return safe;
|
|
71
|
+
}
|
|
72
|
+
return (0, _davidilie_telemetry_core.redactText)(typeof error === "string" ? error : String(error));
|
|
73
|
+
} catch {
|
|
74
|
+
return "Unserializable exception";
|
|
70
75
|
}
|
|
71
|
-
return (0, _davidilie_telemetry_core.redactText)(typeof error === "string" ? error : String(error));
|
|
72
76
|
}
|
|
73
77
|
function signalAttributes(signal) {
|
|
74
78
|
return {
|
|
@@ -136,6 +140,7 @@ var OpenTelemetryAdapter = class {
|
|
|
136
140
|
#logSampleRate;
|
|
137
141
|
#shutdown;
|
|
138
142
|
#histograms = /* @__PURE__ */ new Map();
|
|
143
|
+
#meterProvider;
|
|
139
144
|
constructor(config) {
|
|
140
145
|
this.#resource = (0, _davidilie_telemetry_core.sanitizeResource)(config.resource);
|
|
141
146
|
this.#instrumentationName = config.instrumentationName ?? this.#resource.serviceName;
|
|
@@ -225,11 +230,16 @@ var OpenTelemetryAdapter = class {
|
|
|
225
230
|
}).end(new Date(signal.timestamp));
|
|
226
231
|
if (this.#measurementMode === "spans") return;
|
|
227
232
|
if (!VALID_INSTRUMENT_NAME.test(signal.name)) return;
|
|
233
|
+
const meterProvider = _opentelemetry_api.metrics.getMeterProvider();
|
|
234
|
+
if (meterProvider !== this.#meterProvider) {
|
|
235
|
+
this.#histograms.clear();
|
|
236
|
+
this.#meterProvider = meterProvider;
|
|
237
|
+
}
|
|
228
238
|
const key = `${signal.name}\u0000${signal.unit ?? ""}`;
|
|
229
239
|
let histogram = this.#histograms.get(key);
|
|
230
240
|
if (!histogram) {
|
|
231
241
|
if (this.#histograms.size >= this.#maxMetricInstruments) return;
|
|
232
|
-
histogram =
|
|
242
|
+
histogram = meterProvider.getMeter(this.#instrumentationName, this.#instrumentationVersion).createHistogram(signal.name, { ...signal.unit ? { unit: signal.unit } : {} });
|
|
233
243
|
this.#histograms.set(key, histogram);
|
|
234
244
|
}
|
|
235
245
|
const metricAttributes = Object.fromEntries(Object.entries(signal.attributes).filter(([name]) => this.#metricAttributeAllowlist.has(name)));
|
|
@@ -340,15 +350,21 @@ async function withSpan(name, operation, attributes = {}, options = {}) {
|
|
|
340
350
|
});
|
|
341
351
|
}
|
|
342
352
|
function recordException(error, attributes = {}) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
353
|
+
let ownedSpan;
|
|
354
|
+
try {
|
|
355
|
+
const span = _opentelemetry_api.trace.getActiveSpan() ?? (ownedSpan = _opentelemetry_api.trace.getTracer("@davidilie/telemetry-node").startSpan("exception"));
|
|
356
|
+
const exception = exceptionForOtel(error);
|
|
357
|
+
span.setAttributes(toOtelAttributes((0, _davidilie_telemetry_core.sanitizeAttributes)(attributes)));
|
|
358
|
+
span.recordException(exception);
|
|
359
|
+
span.setStatus({
|
|
360
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
361
|
+
message: typeof exception === "string" ? exception : exception.message
|
|
362
|
+
});
|
|
363
|
+
} catch {} finally {
|
|
364
|
+
try {
|
|
365
|
+
ownedSpan?.end();
|
|
366
|
+
} catch {}
|
|
367
|
+
}
|
|
352
368
|
}
|
|
353
369
|
//#endregion
|
|
354
370
|
//#region src/client.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davidilie/telemetry-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "OpenTelemetry-backed telemetry for Node.js services.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analytics",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/
|
|
16
|
+
"url": "git+https://github.com/davidapps-org/davidapps-telemetry.git",
|
|
17
17
|
"directory": "packages/node"
|
|
18
18
|
},
|
|
19
|
-
"homepage": "https://github.com/
|
|
19
|
+
"homepage": "https://github.com/davidapps-org/davidapps-telemetry#readme",
|
|
20
20
|
"bugs": {
|
|
21
|
-
"url": "https://github.com/
|
|
21
|
+
"url": "https://github.com/davidapps-org/davidapps-telemetry/issues"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=20"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"clean": "rm -rf dist coverage"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@davidilie/telemetry-core": "0.
|
|
68
|
+
"@davidilie/telemetry-core": "0.5.0",
|
|
69
69
|
"@opentelemetry/api-logs": "0.221.0",
|
|
70
70
|
"@opentelemetry/resources": "2.10.0",
|
|
71
71
|
"@opentelemetry/sdk-node": "0.221.0",
|
|
@@ -80,6 +80,6 @@
|
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public",
|
|
82
82
|
"registry": "https://registry.npmjs.org/",
|
|
83
|
-
"provenance":
|
|
83
|
+
"provenance": false
|
|
84
84
|
}
|
|
85
85
|
}
|