@davidilie/telemetry-node 0.1.3 → 0.3.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 +18 -7
- package/THIRD_PARTY_NOTICES.md +2 -1
- package/UPSTREAM.yml +5 -0
- package/dist/{client-BzoFCwmQ.d.cts → client-DYrSJX10.d.cts} +3 -2
- package/dist/{client-BzoFCwmQ.d.mts → client-DYrSJX10.d.mts} +3 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/register.cjs +9 -4
- package/dist/register.d.cts +1 -1
- package/dist/register.d.mts +1 -1
- package/dist/register.mjs +9 -4
- package/dist/{sampler-ND4YGh_7.mjs → sampler-B1PsF--M.mjs} +41 -39
- package/dist/{sampler-CGBWEZod.cjs → sampler-D5mi13Eh.cjs} +40 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,8 +47,10 @@ process.once("SIGTERM", async () => {
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
`registerNodeTelemetry` installs one `NodeSDK` per JavaScript global. Repeated
|
|
50
|
-
calls return the first client and ignore later configuration
|
|
51
|
-
|
|
50
|
+
calls return the first client and ignore later configuration. Registration is
|
|
51
|
+
terminal after shutdown: restart the process to register again. OTel retains
|
|
52
|
+
global provider references after shutdown, so another registration would
|
|
53
|
+
otherwise silently use a stopped provider. Do not register this package beside another provider.
|
|
52
54
|
|
|
53
55
|
The optional `sdk` property accepts `NodeSDKConfiguration` fields other than
|
|
54
56
|
`resource` and `serviceName`. Use it for maintained instrumentations, readers,
|
|
@@ -165,10 +167,17 @@ collected into the same backend. Disable one route deliberately.
|
|
|
165
167
|
|
|
166
168
|
## Spans and context
|
|
167
169
|
|
|
168
|
-
The
|
|
169
|
-
is active during the operation
|
|
170
|
+
The Node adapter supplies active-span execution to the shared core `withSpan`.
|
|
171
|
+
The span is active during the operation, including asynchronous work, while
|
|
172
|
+
core applies context, consent, sampling, and shutdown checks. Use it when events/logs need the same trace.
|
|
170
173
|
`client.startSpan()` creates a span but does not make it active.
|
|
171
174
|
|
|
175
|
+
The callback receives a safe span handle, including `traceContext()`.
|
|
176
|
+
`client.traceWebhook({ destination: "discord.sales" }, send)` tracks one
|
|
177
|
+
outgoing POST under the active parent. See the
|
|
178
|
+
[webhook guide](https://github.com/DavidIlie/davidapps-telemetry/blob/main/docs/webhooks.md)
|
|
179
|
+
for privacy, delivery outcomes, metrics, and Grafana Discord notifications.
|
|
180
|
+
|
|
172
181
|
Both methods accept `{ kind: "internal" | "server" | "client" | "producer" |
|
|
173
182
|
"consumer" }` after attributes. Manual span handles also expose
|
|
174
183
|
`setStatus("unset" | "ok" | "error", message?)`.
|
|
@@ -211,9 +220,11 @@ context; `startSpan` does not.
|
|
|
211
220
|
apply; set one layer to `1` if multiplied rates are not intentional.
|
|
212
221
|
- State changes affect new work and do not erase an already exported or queued
|
|
213
222
|
provider batch.
|
|
214
|
-
- `flush()` drains core work,
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
- `flush()` drains core work, then calls the registered trace, log, and metric
|
|
224
|
+
providers' public `forceFlush()` methods when available. This flushes all
|
|
225
|
+
pending work on those providers, not only this client's records. Failures
|
|
226
|
+
are reported through `onError` without rejecting the client promise.
|
|
227
|
+
`shutdown()` on the `/register` client shuts down the `NodeSDK` and its processors.
|
|
217
228
|
- Only a client returned by `/register` owns and shuts down the installed
|
|
218
229
|
`NodeSDK`. A client created from the root entrypoint does not own the
|
|
219
230
|
framework provider.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -7,5 +7,6 @@ DavidApps Telemetry depends on permissively licensed open-source projects and us
|
|
|
7
7
|
- Vercel Analytics and Vercel OTEL — MIT License — Copyright Vercel, Inc.
|
|
8
8
|
- PostHog JavaScript SDKs — Apache-2.0/MIT — API reference only; no source is vendored.
|
|
9
9
|
- Honeycomb OpenTelemetry React Native — MIT License — Copyright Honeycomb.io.
|
|
10
|
+
- Stripe Node.js SDK — MIT License — Copyright Ask Bjørn Hansen and Stripe, Inc.; API reference only.
|
|
10
11
|
|
|
11
|
-
Reference license texts are retained in `licenses/` so future adaptations cannot accidentally lose their attribution obligations. The current source tree does not vendor code from PostHog, Vercel, Honeycomb, or the OpenTelemetry demo.
|
|
12
|
+
Reference license texts are retained in `licenses/` so future adaptations cannot accidentally lose their attribution obligations. The current source tree does not vendor code from PostHog, Vercel, Honeycomb, Stripe, or the OpenTelemetry demo.
|
package/UPSTREAM.yml
CHANGED
|
@@ -54,3 +54,8 @@ sources:
|
|
|
54
54
|
license: MIT
|
|
55
55
|
commit: 07d144459177a58a347e18be762cf8f04e63cb1d
|
|
56
56
|
use: api-reference
|
|
57
|
+
- name: stripe-node
|
|
58
|
+
repository: https://github.com/stripe/stripe-node
|
|
59
|
+
license: MIT
|
|
60
|
+
commit: 8adba1d35fdb76ba4104e5d7ae1ac999649c7061
|
|
61
|
+
use: api-reference
|
|
@@ -9,6 +9,7 @@ declare class OpenTelemetrySpan implements TelemetrySpan {
|
|
|
9
9
|
recordException(error: unknown, attributes?: Attributes$1): void;
|
|
10
10
|
setStatus(status: TelemetrySpanStatus, message?: string): this;
|
|
11
11
|
end(): void;
|
|
12
|
+
traceContext(): TraceContext | undefined;
|
|
12
13
|
}
|
|
13
14
|
interface OpenTelemetryAdapterConfig {
|
|
14
15
|
resource: TelemetryResource;
|
|
@@ -31,7 +32,8 @@ declare class OpenTelemetryAdapter implements TelemetryAdapter {
|
|
|
31
32
|
send(signal: TelemetrySignal): void;
|
|
32
33
|
startSpan(name: string, attributes?: Attributes$1, options?: TelemetrySpanOptions): TelemetrySpan;
|
|
33
34
|
currentTraceContext(): TraceContext | undefined;
|
|
34
|
-
withActiveSpan<T>(name: string, operation: () => T | Promise<T>, attributes?: Attributes$1, options?: TelemetrySpanOptions): Promise<T>;
|
|
35
|
+
withActiveSpan<T>(name: string, operation: (span: TelemetrySpan) => T | Promise<T>, attributes?: Attributes$1, options?: TelemetrySpanOptions): Promise<T>;
|
|
36
|
+
flush(): Promise<void>;
|
|
35
37
|
shutdown(): Promise<void>;
|
|
36
38
|
}
|
|
37
39
|
declare function createOpenTelemetryAdapter(config: OpenTelemetryAdapterConfig): OpenTelemetryAdapter;
|
|
@@ -78,7 +80,6 @@ declare class NodeTelemetryClient extends TelemetryClient {
|
|
|
78
80
|
constructor(config: NodeTelemetryInternalConfig);
|
|
79
81
|
setEnabled(enabled: boolean): void;
|
|
80
82
|
setConsent(consent: "granted" | "denied" | "pending"): void;
|
|
81
|
-
withSpan<T>(name: string, operation: () => T | Promise<T>, attributes?: Attributes$1, options?: TelemetrySpanOptions): Promise<T>;
|
|
82
83
|
}
|
|
83
84
|
declare function createNodeTelemetry(config: NodeTelemetryConfig): NodeTelemetryClient;
|
|
84
85
|
//#endregion
|
|
@@ -9,6 +9,7 @@ declare class OpenTelemetrySpan implements TelemetrySpan {
|
|
|
9
9
|
recordException(error: unknown, attributes?: Attributes$1): void;
|
|
10
10
|
setStatus(status: TelemetrySpanStatus, message?: string): this;
|
|
11
11
|
end(): void;
|
|
12
|
+
traceContext(): TraceContext | undefined;
|
|
12
13
|
}
|
|
13
14
|
interface OpenTelemetryAdapterConfig {
|
|
14
15
|
resource: TelemetryResource;
|
|
@@ -31,7 +32,8 @@ declare class OpenTelemetryAdapter implements TelemetryAdapter {
|
|
|
31
32
|
send(signal: TelemetrySignal): void;
|
|
32
33
|
startSpan(name: string, attributes?: Attributes$1, options?: TelemetrySpanOptions): TelemetrySpan;
|
|
33
34
|
currentTraceContext(): TraceContext | undefined;
|
|
34
|
-
withActiveSpan<T>(name: string, operation: () => T | Promise<T>, attributes?: Attributes$1, options?: TelemetrySpanOptions): Promise<T>;
|
|
35
|
+
withActiveSpan<T>(name: string, operation: (span: TelemetrySpan) => T | Promise<T>, attributes?: Attributes$1, options?: TelemetrySpanOptions): Promise<T>;
|
|
36
|
+
flush(): Promise<void>;
|
|
35
37
|
shutdown(): Promise<void>;
|
|
36
38
|
}
|
|
37
39
|
declare function createOpenTelemetryAdapter(config: OpenTelemetryAdapterConfig): OpenTelemetryAdapter;
|
|
@@ -78,7 +80,6 @@ declare class NodeTelemetryClient extends TelemetryClient {
|
|
|
78
80
|
constructor(config: NodeTelemetryInternalConfig);
|
|
79
81
|
setEnabled(enabled: boolean): void;
|
|
80
82
|
setConsent(consent: "granted" | "denied" | "pending"): void;
|
|
81
|
-
withSpan<T>(name: string, operation: () => T | Promise<T>, attributes?: Attributes$1, options?: TelemetrySpanOptions): Promise<T>;
|
|
82
83
|
}
|
|
83
84
|
declare function createNodeTelemetry(config: NodeTelemetryConfig): NodeTelemetryClient;
|
|
84
85
|
//#endregion
|
package/dist/index.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-D5mi13Eh.cjs");
|
|
3
3
|
exports.DynamicTelemetrySampler = require_sampler.DynamicTelemetrySampler;
|
|
4
4
|
exports.NodeTelemetryClient = require_sampler.NodeTelemetryClient;
|
|
5
5
|
exports.OpenTelemetryAdapter = require_sampler.OpenTelemetryAdapter;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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
4
|
import { Sampler, SamplingResult } from "@opentelemetry/sdk-trace-base";
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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
4
|
import { Sampler, SamplingResult } from "@opentelemetry/sdk-trace-base";
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,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-
|
|
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-B1PsF--M.mjs";
|
|
2
2
|
export { DynamicTelemetrySampler, NodeTelemetryClient, OpenTelemetryAdapter, OpenTelemetrySpan, 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-D5mi13Eh.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");
|
|
@@ -13,7 +13,10 @@ function traceSampleRate(config) {
|
|
|
13
13
|
function registerNodeTelemetry(config) {
|
|
14
14
|
const sharedGlobal = globalThis;
|
|
15
15
|
const existing = sharedGlobal[REGISTRATION];
|
|
16
|
-
if (existing)
|
|
16
|
+
if (existing) {
|
|
17
|
+
if (existing.stopped) throw new Error("Node telemetry registration is terminal after shutdown; restart the process to register again");
|
|
18
|
+
return existing.client;
|
|
19
|
+
}
|
|
17
20
|
const resource = (0, _davidilie_telemetry_core.sanitizeResource)(config.resource);
|
|
18
21
|
const sampler = new require_sampler.DynamicTelemetrySampler(config.sdk?.sampler ?? new _opentelemetry_sdk_trace_base.ParentBasedSampler({ root: new _opentelemetry_sdk_trace_base.TraceIdRatioBasedSampler(traceSampleRate(config)) }), {
|
|
19
22
|
enabled: config.enabled ?? true,
|
|
@@ -37,13 +40,15 @@ function registerNodeTelemetry(config) {
|
|
|
37
40
|
},
|
|
38
41
|
measurementMode: config.measurementMode ?? (config.sdk?.metricReaders?.length ? "metrics" : "spans"),
|
|
39
42
|
shutdown: async () => {
|
|
43
|
+
const registration = sharedGlobal[REGISTRATION];
|
|
44
|
+
if (registration?.client === client) registration.stopped = true;
|
|
40
45
|
await sdk.shutdown();
|
|
41
|
-
if (sharedGlobal[REGISTRATION]?.client === client) delete sharedGlobal[REGISTRATION];
|
|
42
46
|
}
|
|
43
47
|
});
|
|
44
48
|
sharedGlobal[REGISTRATION] = {
|
|
45
49
|
client,
|
|
46
|
-
sdk
|
|
50
|
+
sdk,
|
|
51
|
+
stopped: false
|
|
47
52
|
};
|
|
48
53
|
return client;
|
|
49
54
|
}
|
package/dist/register.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as NodeTelemetryConfig, t as NodeTelemetryClient } from "./client-
|
|
1
|
+
import { n as NodeTelemetryConfig, t as NodeTelemetryClient } from "./client-DYrSJX10.cjs";
|
|
2
2
|
import { NodeSDKConfiguration, NodeSDKConfiguration as NodeSDKConfiguration$1 } from "@opentelemetry/sdk-node";
|
|
3
3
|
//#region src/register.d.ts
|
|
4
4
|
interface RegisterNodeTelemetryConfig extends NodeTelemetryConfig {
|
package/dist/register.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as NodeTelemetryConfig, t as NodeTelemetryClient } from "./client-
|
|
1
|
+
import { n as NodeTelemetryConfig, t as NodeTelemetryClient } from "./client-DYrSJX10.mjs";
|
|
2
2
|
import { NodeSDKConfiguration, NodeSDKConfiguration as NodeSDKConfiguration$1 } from "@opentelemetry/sdk-node";
|
|
3
3
|
//#region src/register.d.ts
|
|
4
4
|
interface RegisterNodeTelemetryConfig extends NodeTelemetryConfig {
|
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-B1PsF--M.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";
|
|
@@ -12,7 +12,10 @@ function traceSampleRate(config) {
|
|
|
12
12
|
function registerNodeTelemetry(config) {
|
|
13
13
|
const sharedGlobal = globalThis;
|
|
14
14
|
const existing = sharedGlobal[REGISTRATION];
|
|
15
|
-
if (existing)
|
|
15
|
+
if (existing) {
|
|
16
|
+
if (existing.stopped) throw new Error("Node telemetry registration is terminal after shutdown; restart the process to register again");
|
|
17
|
+
return existing.client;
|
|
18
|
+
}
|
|
16
19
|
const resource = sanitizeResource(config.resource);
|
|
17
20
|
const sampler = new DynamicTelemetrySampler(config.sdk?.sampler ?? new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(traceSampleRate(config)) }), {
|
|
18
21
|
enabled: config.enabled ?? true,
|
|
@@ -36,13 +39,15 @@ function registerNodeTelemetry(config) {
|
|
|
36
39
|
},
|
|
37
40
|
measurementMode: config.measurementMode ?? (config.sdk?.metricReaders?.length ? "metrics" : "spans"),
|
|
38
41
|
shutdown: async () => {
|
|
42
|
+
const registration = sharedGlobal[REGISTRATION];
|
|
43
|
+
if (registration?.client === client) registration.stopped = true;
|
|
39
44
|
await sdk.shutdown();
|
|
40
|
-
if (sharedGlobal[REGISTRATION]?.client === client) delete sharedGlobal[REGISTRATION];
|
|
41
45
|
}
|
|
42
46
|
});
|
|
43
47
|
sharedGlobal[REGISTRATION] = {
|
|
44
48
|
client,
|
|
45
|
-
sdk
|
|
49
|
+
sdk,
|
|
50
|
+
stopped: false
|
|
46
51
|
};
|
|
47
52
|
return client;
|
|
48
53
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SpanKind, SpanStatusCode, context, metrics, trace } from "@opentelemetry/api";
|
|
1
|
+
import { SpanKind, SpanStatusCode, context, isSpanContextValid, metrics, trace } from "@opentelemetry/api";
|
|
2
2
|
import { SeverityNumber, logs } from "@opentelemetry/api-logs";
|
|
3
3
|
import { TelemetryClient, redactText, sanitizeAttributes, sanitizeResource, sanitizeSignal } from "@davidilie/telemetry-core";
|
|
4
4
|
import { SamplingDecision } from "@opentelemetry/sdk-trace-base";
|
|
@@ -38,6 +38,22 @@ function telemetryResourceAttributes(resource) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
//#endregion
|
|
41
|
+
//#region src/flush.ts
|
|
42
|
+
/** Flush registered SDK providers using their public lifecycle methods. */
|
|
43
|
+
async function flushOpenTelemetryProviders() {
|
|
44
|
+
let tracerProvider = trace.getTracerProvider();
|
|
45
|
+
if ("getDelegate" in tracerProvider && typeof tracerProvider.getDelegate === "function") tracerProvider = tracerProvider.getDelegate();
|
|
46
|
+
const providers = [
|
|
47
|
+
tracerProvider,
|
|
48
|
+
logs.getLoggerProvider(),
|
|
49
|
+
metrics.getMeterProvider()
|
|
50
|
+
];
|
|
51
|
+
const errors = (await Promise.allSettled(providers.map(async (provider) => {
|
|
52
|
+
if ("forceFlush" in provider && typeof provider.forceFlush === "function") await provider.forceFlush();
|
|
53
|
+
}))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
54
|
+
if (errors.length > 0) throw new AggregateError(errors, "OpenTelemetry provider flush failed");
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
41
57
|
//#region src/adapter.ts
|
|
42
58
|
const severityNumbers = {
|
|
43
59
|
debug: SeverityNumber.DEBUG,
|
|
@@ -100,6 +116,14 @@ var OpenTelemetrySpan = class {
|
|
|
100
116
|
end() {
|
|
101
117
|
this.#span.end();
|
|
102
118
|
}
|
|
119
|
+
traceContext() {
|
|
120
|
+
const value = this.#span.spanContext();
|
|
121
|
+
return isSpanContextValid(value) ? {
|
|
122
|
+
traceId: value.traceId,
|
|
123
|
+
spanId: value.spanId,
|
|
124
|
+
traceFlags: value.traceFlags
|
|
125
|
+
} : void 0;
|
|
126
|
+
}
|
|
103
127
|
};
|
|
104
128
|
var OpenTelemetryAdapter = class {
|
|
105
129
|
#instrumentationName;
|
|
@@ -129,7 +153,7 @@ var OpenTelemetryAdapter = class {
|
|
|
129
153
|
switch (signal.type) {
|
|
130
154
|
case "event": {
|
|
131
155
|
const activeSpan = trace.getActiveSpan();
|
|
132
|
-
if (activeSpan) {
|
|
156
|
+
if (activeSpan?.isRecording()) {
|
|
133
157
|
activeSpan.addEvent(signal.name, attributes, new Date(signal.timestamp));
|
|
134
158
|
return;
|
|
135
159
|
}
|
|
@@ -144,22 +168,23 @@ var OpenTelemetryAdapter = class {
|
|
|
144
168
|
}
|
|
145
169
|
case "exception": {
|
|
146
170
|
const activeSpan = trace.getActiveSpan();
|
|
147
|
-
const
|
|
171
|
+
const recordingSpan = activeSpan?.isRecording() ? activeSpan : void 0;
|
|
172
|
+
const span = recordingSpan ?? this.#tracer().startSpan("exception", {
|
|
148
173
|
kind: SpanKind.INTERNAL,
|
|
149
174
|
attributes,
|
|
150
175
|
startTime: new Date(signal.timestamp)
|
|
151
176
|
});
|
|
152
|
-
span.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
message: signal.exception.message,
|
|
156
|
-
...signal.exception.stack ? {
|
|
157
|
-
});
|
|
177
|
+
span.addEvent("exception", {
|
|
178
|
+
...attributes,
|
|
179
|
+
"exception.type": signal.exception.name,
|
|
180
|
+
"exception.message": signal.exception.message,
|
|
181
|
+
...signal.exception.stack ? { "exception.stacktrace": signal.exception.stack } : {}
|
|
182
|
+
}, new Date(signal.timestamp));
|
|
158
183
|
span.setStatus({
|
|
159
184
|
code: SpanStatusCode.ERROR,
|
|
160
185
|
message: signal.exception.message
|
|
161
186
|
});
|
|
162
|
-
if (!
|
|
187
|
+
if (!recordingSpan) span.end(new Date(signal.timestamp));
|
|
163
188
|
return;
|
|
164
189
|
}
|
|
165
190
|
case "log":
|
|
@@ -239,7 +264,7 @@ var OpenTelemetryAdapter = class {
|
|
|
239
264
|
}, async (span) => {
|
|
240
265
|
operationRan = true;
|
|
241
266
|
try {
|
|
242
|
-
operationResult = await operation();
|
|
267
|
+
operationResult = await operation(new OpenTelemetrySpan(span));
|
|
243
268
|
operationSucceeded = true;
|
|
244
269
|
} catch (error) {
|
|
245
270
|
operationError = error;
|
|
@@ -261,7 +286,10 @@ var OpenTelemetryAdapter = class {
|
|
|
261
286
|
}
|
|
262
287
|
if (operationSucceeded) return operationResult;
|
|
263
288
|
if (operationRan) throw operationError;
|
|
264
|
-
|
|
289
|
+
throw new Error("OpenTelemetry tracer did not invoke the span operation");
|
|
290
|
+
}
|
|
291
|
+
flush() {
|
|
292
|
+
return flushOpenTelemetryProviders();
|
|
265
293
|
}
|
|
266
294
|
async shutdown() {
|
|
267
295
|
await this.#shutdown?.();
|
|
@@ -275,7 +303,7 @@ function createOpenTelemetryAdapter(config) {
|
|
|
275
303
|
}
|
|
276
304
|
function currentTraceContext() {
|
|
277
305
|
const spanContext = trace.getSpan(context.active())?.spanContext();
|
|
278
|
-
if (!spanContext) return void 0;
|
|
306
|
+
if (!spanContext || !isSpanContextValid(spanContext)) return void 0;
|
|
279
307
|
return {
|
|
280
308
|
traceId: spanContext.traceId,
|
|
281
309
|
spanId: spanContext.spanId,
|
|
@@ -325,9 +353,6 @@ function recordException(error, attributes = {}) {
|
|
|
325
353
|
//#endregion
|
|
326
354
|
//#region src/client.ts
|
|
327
355
|
var NodeTelemetryClient = class extends TelemetryClient {
|
|
328
|
-
#adapter;
|
|
329
|
-
#sampleRate;
|
|
330
|
-
#onError;
|
|
331
356
|
#onCollectionStateChange;
|
|
332
357
|
#enabled;
|
|
333
358
|
#consent;
|
|
@@ -353,9 +378,6 @@ var NodeTelemetryClient = class extends TelemetryClient {
|
|
|
353
378
|
...config.debug !== void 0 ? { debug: config.debug } : {},
|
|
354
379
|
...config.onError ? { onError: config.onError } : {}
|
|
355
380
|
});
|
|
356
|
-
this.#adapter = adapter;
|
|
357
|
-
this.#sampleRate = config.providerManagedSampling ? 1 : Number.isFinite(config.sampleRate) ? Math.min(1, Math.max(0, config.sampleRate ?? 1)) : 1;
|
|
358
|
-
this.#onError = config.onError;
|
|
359
381
|
this.#onCollectionStateChange = config.onCollectionStateChange;
|
|
360
382
|
this.#enabled = config.enabled ?? true;
|
|
361
383
|
this.#consent = config.consent ?? "granted";
|
|
@@ -376,26 +398,6 @@ var NodeTelemetryClient = class extends TelemetryClient {
|
|
|
376
398
|
consent: this.#consent
|
|
377
399
|
});
|
|
378
400
|
}
|
|
379
|
-
async withSpan(name, operation, attributes = {}, options = {}) {
|
|
380
|
-
if (!this.#enabled || this.#consent !== "granted" || Math.random() >= this.#sampleRate) return operation();
|
|
381
|
-
let operationStarted = false;
|
|
382
|
-
try {
|
|
383
|
-
return await this.#adapter.withActiveSpan(redactText(name, 256), () => {
|
|
384
|
-
operationStarted = true;
|
|
385
|
-
return operation();
|
|
386
|
-
}, sanitizeAttributes(attributes), options);
|
|
387
|
-
} catch (error) {
|
|
388
|
-
if (operationStarted) throw error;
|
|
389
|
-
this.#reportAdapterError(error, { operation: "startSpan" });
|
|
390
|
-
return operation();
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
#reportAdapterError(error, context) {
|
|
394
|
-
if (!this.#onError) return;
|
|
395
|
-
try {
|
|
396
|
-
Promise.resolve(this.#onError(error, context)).catch(() => void 0);
|
|
397
|
-
} catch {}
|
|
398
|
-
}
|
|
399
401
|
};
|
|
400
402
|
function createNodeTelemetry(config) {
|
|
401
403
|
return new NodeTelemetryClient(config);
|
|
@@ -38,6 +38,22 @@ function telemetryResourceAttributes(resource) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
//#endregion
|
|
41
|
+
//#region src/flush.ts
|
|
42
|
+
/** Flush registered SDK providers using their public lifecycle methods. */
|
|
43
|
+
async function flushOpenTelemetryProviders() {
|
|
44
|
+
let tracerProvider = _opentelemetry_api.trace.getTracerProvider();
|
|
45
|
+
if ("getDelegate" in tracerProvider && typeof tracerProvider.getDelegate === "function") tracerProvider = tracerProvider.getDelegate();
|
|
46
|
+
const providers = [
|
|
47
|
+
tracerProvider,
|
|
48
|
+
_opentelemetry_api_logs.logs.getLoggerProvider(),
|
|
49
|
+
_opentelemetry_api.metrics.getMeterProvider()
|
|
50
|
+
];
|
|
51
|
+
const errors = (await Promise.allSettled(providers.map(async (provider) => {
|
|
52
|
+
if ("forceFlush" in provider && typeof provider.forceFlush === "function") await provider.forceFlush();
|
|
53
|
+
}))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
54
|
+
if (errors.length > 0) throw new AggregateError(errors, "OpenTelemetry provider flush failed");
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
41
57
|
//#region src/adapter.ts
|
|
42
58
|
const severityNumbers = {
|
|
43
59
|
debug: _opentelemetry_api_logs.SeverityNumber.DEBUG,
|
|
@@ -100,6 +116,14 @@ var OpenTelemetrySpan = class {
|
|
|
100
116
|
end() {
|
|
101
117
|
this.#span.end();
|
|
102
118
|
}
|
|
119
|
+
traceContext() {
|
|
120
|
+
const value = this.#span.spanContext();
|
|
121
|
+
return (0, _opentelemetry_api.isSpanContextValid)(value) ? {
|
|
122
|
+
traceId: value.traceId,
|
|
123
|
+
spanId: value.spanId,
|
|
124
|
+
traceFlags: value.traceFlags
|
|
125
|
+
} : void 0;
|
|
126
|
+
}
|
|
103
127
|
};
|
|
104
128
|
var OpenTelemetryAdapter = class {
|
|
105
129
|
#instrumentationName;
|
|
@@ -129,7 +153,7 @@ var OpenTelemetryAdapter = class {
|
|
|
129
153
|
switch (signal.type) {
|
|
130
154
|
case "event": {
|
|
131
155
|
const activeSpan = _opentelemetry_api.trace.getActiveSpan();
|
|
132
|
-
if (activeSpan) {
|
|
156
|
+
if (activeSpan?.isRecording()) {
|
|
133
157
|
activeSpan.addEvent(signal.name, attributes, new Date(signal.timestamp));
|
|
134
158
|
return;
|
|
135
159
|
}
|
|
@@ -144,22 +168,23 @@ var OpenTelemetryAdapter = class {
|
|
|
144
168
|
}
|
|
145
169
|
case "exception": {
|
|
146
170
|
const activeSpan = _opentelemetry_api.trace.getActiveSpan();
|
|
147
|
-
const
|
|
171
|
+
const recordingSpan = activeSpan?.isRecording() ? activeSpan : void 0;
|
|
172
|
+
const span = recordingSpan ?? this.#tracer().startSpan("exception", {
|
|
148
173
|
kind: _opentelemetry_api.SpanKind.INTERNAL,
|
|
149
174
|
attributes,
|
|
150
175
|
startTime: new Date(signal.timestamp)
|
|
151
176
|
});
|
|
152
|
-
span.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
message: signal.exception.message,
|
|
156
|
-
...signal.exception.stack ? {
|
|
157
|
-
});
|
|
177
|
+
span.addEvent("exception", {
|
|
178
|
+
...attributes,
|
|
179
|
+
"exception.type": signal.exception.name,
|
|
180
|
+
"exception.message": signal.exception.message,
|
|
181
|
+
...signal.exception.stack ? { "exception.stacktrace": signal.exception.stack } : {}
|
|
182
|
+
}, new Date(signal.timestamp));
|
|
158
183
|
span.setStatus({
|
|
159
184
|
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
160
185
|
message: signal.exception.message
|
|
161
186
|
});
|
|
162
|
-
if (!
|
|
187
|
+
if (!recordingSpan) span.end(new Date(signal.timestamp));
|
|
163
188
|
return;
|
|
164
189
|
}
|
|
165
190
|
case "log":
|
|
@@ -239,7 +264,7 @@ var OpenTelemetryAdapter = class {
|
|
|
239
264
|
}, async (span) => {
|
|
240
265
|
operationRan = true;
|
|
241
266
|
try {
|
|
242
|
-
operationResult = await operation();
|
|
267
|
+
operationResult = await operation(new OpenTelemetrySpan(span));
|
|
243
268
|
operationSucceeded = true;
|
|
244
269
|
} catch (error) {
|
|
245
270
|
operationError = error;
|
|
@@ -261,7 +286,10 @@ var OpenTelemetryAdapter = class {
|
|
|
261
286
|
}
|
|
262
287
|
if (operationSucceeded) return operationResult;
|
|
263
288
|
if (operationRan) throw operationError;
|
|
264
|
-
|
|
289
|
+
throw new Error("OpenTelemetry tracer did not invoke the span operation");
|
|
290
|
+
}
|
|
291
|
+
flush() {
|
|
292
|
+
return flushOpenTelemetryProviders();
|
|
265
293
|
}
|
|
266
294
|
async shutdown() {
|
|
267
295
|
await this.#shutdown?.();
|
|
@@ -275,7 +303,7 @@ function createOpenTelemetryAdapter(config) {
|
|
|
275
303
|
}
|
|
276
304
|
function currentTraceContext() {
|
|
277
305
|
const spanContext = _opentelemetry_api.trace.getSpan(_opentelemetry_api.context.active())?.spanContext();
|
|
278
|
-
if (!spanContext) return void 0;
|
|
306
|
+
if (!spanContext || !(0, _opentelemetry_api.isSpanContextValid)(spanContext)) return void 0;
|
|
279
307
|
return {
|
|
280
308
|
traceId: spanContext.traceId,
|
|
281
309
|
spanId: spanContext.spanId,
|
|
@@ -325,9 +353,6 @@ function recordException(error, attributes = {}) {
|
|
|
325
353
|
//#endregion
|
|
326
354
|
//#region src/client.ts
|
|
327
355
|
var NodeTelemetryClient = class extends _davidilie_telemetry_core.TelemetryClient {
|
|
328
|
-
#adapter;
|
|
329
|
-
#sampleRate;
|
|
330
|
-
#onError;
|
|
331
356
|
#onCollectionStateChange;
|
|
332
357
|
#enabled;
|
|
333
358
|
#consent;
|
|
@@ -353,9 +378,6 @@ var NodeTelemetryClient = class extends _davidilie_telemetry_core.TelemetryClien
|
|
|
353
378
|
...config.debug !== void 0 ? { debug: config.debug } : {},
|
|
354
379
|
...config.onError ? { onError: config.onError } : {}
|
|
355
380
|
});
|
|
356
|
-
this.#adapter = adapter;
|
|
357
|
-
this.#sampleRate = config.providerManagedSampling ? 1 : Number.isFinite(config.sampleRate) ? Math.min(1, Math.max(0, config.sampleRate ?? 1)) : 1;
|
|
358
|
-
this.#onError = config.onError;
|
|
359
381
|
this.#onCollectionStateChange = config.onCollectionStateChange;
|
|
360
382
|
this.#enabled = config.enabled ?? true;
|
|
361
383
|
this.#consent = config.consent ?? "granted";
|
|
@@ -376,26 +398,6 @@ var NodeTelemetryClient = class extends _davidilie_telemetry_core.TelemetryClien
|
|
|
376
398
|
consent: this.#consent
|
|
377
399
|
});
|
|
378
400
|
}
|
|
379
|
-
async withSpan(name, operation, attributes = {}, options = {}) {
|
|
380
|
-
if (!this.#enabled || this.#consent !== "granted" || Math.random() >= this.#sampleRate) return operation();
|
|
381
|
-
let operationStarted = false;
|
|
382
|
-
try {
|
|
383
|
-
return await this.#adapter.withActiveSpan((0, _davidilie_telemetry_core.redactText)(name, 256), () => {
|
|
384
|
-
operationStarted = true;
|
|
385
|
-
return operation();
|
|
386
|
-
}, (0, _davidilie_telemetry_core.sanitizeAttributes)(attributes), options);
|
|
387
|
-
} catch (error) {
|
|
388
|
-
if (operationStarted) throw error;
|
|
389
|
-
this.#reportAdapterError(error, { operation: "startSpan" });
|
|
390
|
-
return operation();
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
#reportAdapterError(error, context) {
|
|
394
|
-
if (!this.#onError) return;
|
|
395
|
-
try {
|
|
396
|
-
Promise.resolve(this.#onError(error, context)).catch(() => void 0);
|
|
397
|
-
} catch {}
|
|
398
|
-
}
|
|
399
401
|
};
|
|
400
402
|
function createNodeTelemetry(config) {
|
|
401
403
|
return new NodeTelemetryClient(config);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davidilie/telemetry-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "OpenTelemetry-backed telemetry for Node.js services.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analytics",
|
|
@@ -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.3.0",
|
|
69
69
|
"@opentelemetry/api-logs": "0.221.0",
|
|
70
70
|
"@opentelemetry/resources": "2.10.0",
|
|
71
71
|
"@opentelemetry/sdk-node": "0.221.0",
|