@logbrew/sdk 0.1.38 → 0.1.39

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.
Files changed (90) hide show
  1. package/automatic-delivery.cjs +271 -0
  2. package/batch-sender.cjs +164 -0
  3. package/bounded-number.cjs +34 -0
  4. package/browser-breadcrumbs.cjs +346 -136
  5. package/capture-support.cjs +100 -0
  6. package/client-options.cjs +292 -0
  7. package/client-report.cjs +232 -0
  8. package/client.cjs +447 -0
  9. package/console-arguments.cjs +151 -0
  10. package/console-capture.cjs +194 -0
  11. package/core.cjs +30 -2810
  12. package/delivery-health.cjs +133 -0
  13. package/error-causes.cjs +130 -0
  14. package/error-details.cjs +124 -0
  15. package/error-issue.cjs +187 -0
  16. package/event-clone.cjs +55 -0
  17. package/event-metadata.cjs +40 -0
  18. package/event-pipeline.cjs +325 -0
  19. package/event-queue.cjs +306 -0
  20. package/event-store.cjs +237 -0
  21. package/event-validation.cjs +159 -0
  22. package/examples/agent-timeline.cjs +57 -46
  23. package/examples/agent-timeline.mjs +41 -35
  24. package/examples/index.mjs +122 -94
  25. package/examples/readme-example.cjs +36 -27
  26. package/examples/readme-example.mjs +21 -17
  27. package/examples/real-user-smoke.cjs +41 -32
  28. package/examples/real-user-smoke.mjs +26 -22
  29. package/exception-chain.cjs +205 -0
  30. package/index.d.ts +144 -1076
  31. package/index.js +1 -1
  32. package/issue-exception.cjs +13 -8
  33. package/metric-validation.cjs +95 -0
  34. package/network-milestone.cjs +155 -0
  35. package/null-value.cjs +4 -0
  36. package/opentelemetry-bridge.cjs +330 -0
  37. package/opentelemetry-context.cjs +245 -0
  38. package/opentelemetry-exceptions.cjs +69 -0
  39. package/opentelemetry-keys.cjs +183 -0
  40. package/opentelemetry-span.cjs +286 -0
  41. package/opentelemetry-summary.cjs +213 -0
  42. package/opentelemetry-time.cjs +131 -0
  43. package/opentelemetry.cjs +99 -1068
  44. package/package.json +55 -1
  45. package/pino-destination.cjs +108 -0
  46. package/pino-record.cjs +237 -0
  47. package/product-action.cjs +163 -0
  48. package/provided-breadcrumbs.cjs +117 -0
  49. package/random-hex.cjs +29 -0
  50. package/react-native.js +1 -1
  51. package/recording-transport.cjs +68 -0
  52. package/release-artifacts-build.cjs +263 -84
  53. package/release-artifacts-common.js +347 -66
  54. package/release-artifacts-debug-id.js +178 -0
  55. package/release-artifacts-inspect.js +213 -0
  56. package/release-artifacts-issue-event.js +99 -0
  57. package/release-artifacts-manifest-artifact.js +107 -0
  58. package/release-artifacts-manifest.js +77 -0
  59. package/release-artifacts-mappings.js +265 -0
  60. package/release-artifacts-prepare.js +297 -0
  61. package/release-artifacts-source-context.js +210 -0
  62. package/release-artifacts-symbolication.js +272 -421
  63. package/release-artifacts-upload-files.js +122 -0
  64. package/release-artifacts-upload-request.js +246 -0
  65. package/release-artifacts-upload.js +155 -300
  66. package/release-artifacts-urls.js +49 -0
  67. package/release-artifacts.js +189 -662
  68. package/retry-delay.cjs +32 -0
  69. package/span-validation.cjs +191 -0
  70. package/stored-events.cjs +243 -0
  71. package/types/client.d.ts +67 -0
  72. package/types/delivery.d.ts +125 -0
  73. package/types/issues.d.ts +249 -0
  74. package/types/logging.d.ts +175 -0
  75. package/types/opentelemetry.d.ts +205 -0
  76. package/types/product.d.ts +128 -0
  77. package/types/recording-transport.d.ts +15 -0
  78. package/types/sdk-error.d.ts +9 -0
  79. package/types/telemetry.d.ts +171 -0
  80. package/types/trace-context.d.ts +110 -0
  81. package/types/transport-error.d.ts +10 -0
  82. package/utf8.cjs +37 -0
  83. package/validation.cjs +49 -1
  84. package/vite-release-artifacts-config.cjs +120 -0
  85. package/vite-release-artifacts.cjs +199 -99
  86. package/vite-release-artifacts.d.cts +65 -40
  87. package/vite-release-artifacts.d.ts +65 -40
  88. package/vite-release-artifacts.js +2 -2
  89. package/winston-info.cjs +205 -0
  90. package/winston.cjs +189 -289
@@ -0,0 +1,286 @@
1
+ const { NULL_VALUE } = require("./null-value.cjs");
2
+ const {
3
+ normalizeOpenTelemetrySpanContextIds,
4
+ normalizeSpanId
5
+ } = require("./opentelemetry-context.cjs");
6
+ const { openTelemetryExceptionEventSummary } = require("./opentelemetry-exceptions.cjs");
7
+ const {
8
+ openTelemetrySelectedMetadata,
9
+ resolveOpenTelemetryReadableSpanOptions
10
+ } = require("./opentelemetry-keys.cjs");
11
+ const { durationMsFromOpenTelemetryReadableSpan, timestampFromOpenTelemetryTime } = require("./opentelemetry-time.cjs");
12
+ const { isList, isRecord, optionalProperty, stringOrUndefined } = require("./validation.cjs");
13
+
14
+ /**
15
+ * @import { Metadata, SpanAttributes, SpanEventSummary, SpanLinkSummary } from "./index.cjs"
16
+ * @import { ReadableSpanOptions, ResolvedSpanOptions } from "./opentelemetry-keys.cjs"
17
+ * @typedef {Readonly<Record<string, unknown>>} ReadableSpan
18
+ * @typedef {{ traceId: string, spanId: string, parentSpanId?: string, sampled: boolean }} ReadableSpanContext
19
+ */
20
+
21
+ const MAX_SPAN_EVENTS = 8;
22
+ const MAX_SPAN_LINKS = 8;
23
+ const FIRST_ITEM = 0;
24
+ const EMPTY = 0;
25
+ const OTEL_STATUS_CODE_ERROR = 2;
26
+ const OTEL_STATUS_CODE_OK = 1;
27
+ const SPAN_KIND_INTERNAL = 0;
28
+ const SPAN_KIND_SERVER = 1;
29
+ const SPAN_KIND_CLIENT = 2;
30
+ const SPAN_KIND_PRODUCER = 3;
31
+ const SPAN_KIND_CONSUMER = 4;
32
+ /** @type {ReadonlyMap<unknown, string>} */
33
+ const OTEL_SPAN_KIND_NAMES = new Map([
34
+ [SPAN_KIND_INTERNAL, "internal"],
35
+ [SPAN_KIND_SERVER, "server"],
36
+ [SPAN_KIND_CLIENT, "client"],
37
+ [SPAN_KIND_PRODUCER, "producer"],
38
+ [SPAN_KIND_CONSUMER, "consumer"]
39
+ ]);
40
+
41
+ /**
42
+ * @param {ReadableSpan} span
43
+ * @returns {unknown}
44
+ */
45
+ function readOpenTelemetrySpanContext(span) {
46
+ const { spanContext } = span;
47
+ if (typeof spanContext !== "function") {
48
+ return undefined;
49
+ }
50
+ try {
51
+ return Reflect.apply(spanContext, span, []);
52
+ } catch {
53
+ return undefined;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * @param {ReadableSpan} span
59
+ * @returns {ReadableSpanContext | undefined}
60
+ */
61
+ function normalizeOpenTelemetryReadableSpanContext(span) {
62
+ const context = normalizeOpenTelemetrySpanContextIds(readOpenTelemetrySpanContext(span));
63
+ if (context === undefined) {
64
+ return undefined;
65
+ }
66
+ const parentContext = normalizeOpenTelemetrySpanContextIds(span["parentSpanContext"]);
67
+ const parentSpanId = parentContext?.traceId === context.traceId
68
+ ? parentContext.spanId
69
+ : normalizeSpanId(span["parentSpanId"]);
70
+ return {
71
+ traceId: context.traceId,
72
+
73
+ spanId: context.spanId,
74
+ ...(parentSpanId === undefined ? {} : { parentSpanId }),
75
+ sampled: context.sampled
76
+ };
77
+ }
78
+
79
+ /**
80
+ * @param {unknown} kind
81
+ * @returns {string | undefined}
82
+ */
83
+ function openTelemetrySpanKindName(kind) {
84
+ if (typeof kind === "number") {
85
+ return OTEL_SPAN_KIND_NAMES.get(kind);
86
+ }
87
+ if (typeof kind === "string" && kind.trim() !== "") {
88
+ return kind.toLowerCase();
89
+ }
90
+ return undefined;
91
+ }
92
+
93
+ /**
94
+ * @param {string} key
95
+ * @param {unknown} value
96
+ * @returns {Metadata}
97
+ */
98
+ function positiveOpenTelemetryCount(key, value) {
99
+ return typeof value === "number" && Number.isSafeInteger(value) && value > EMPTY ? { [key]: value } : {};
100
+ }
101
+
102
+ /**
103
+ * @param {string} key
104
+ * @param {string | undefined} value
105
+ * @returns {Metadata}
106
+ */
107
+ function definedEntry(key, value) {
108
+ return value === undefined ? {} : { [key]: value };
109
+ }
110
+
111
+ /**
112
+ * @param {ReadableSpan} span
113
+ * @param {ResolvedSpanOptions} options
114
+ * @returns {Metadata}
115
+ */
116
+ function openTelemetryReadableSpanMetadata(span, options) {
117
+ const resource = openTelemetrySelectedMetadata(optionalProperty(span["resource"], "attributes"), options.resourceAttributeKeys);
118
+ const kind = openTelemetrySpanKindName(span["kind"]);
119
+ const scopeName = stringOrUndefined(optionalProperty(span["instrumentationScope"], "name"));
120
+ const scopeVersion = stringOrUndefined(optionalProperty(span["instrumentationScope"], "version"));
121
+ return {
122
+ source: "opentelemetry.readable_span",
123
+ ...options.metadata,
124
+ ...resource,
125
+ ...definedEntry("otel.kind", kind),
126
+ ...definedEntry("otel.scope.name", scopeName),
127
+ ...definedEntry("otel.scope.version", scopeVersion),
128
+ ...positiveOpenTelemetryCount("otel.dropped_attributes_count", span["droppedAttributesCount"]),
129
+ ...positiveOpenTelemetryCount("otel.dropped_events_count", span["droppedEventsCount"]),
130
+ ...positiveOpenTelemetryCount("otel.dropped_links_count", span["droppedLinksCount"]),
131
+ ...openTelemetrySelectedMetadata(span["attributes"], options.attributeKeys)
132
+ };
133
+ }
134
+
135
+ /**
136
+ * @param {Readonly<Record<string, unknown>>} event
137
+ * @param {ResolvedSpanOptions} options
138
+ * @returns {SpanEventSummary}
139
+ */
140
+ function openTelemetryEventSummary(event, options) {
141
+ const eventName = event["name"];
142
+ const name = typeof eventName === "string" && eventName.trim() !== "" ? eventName : "opentelemetry.event";
143
+ const timestamp = timestampFromOpenTelemetryTime(event["time"] ?? event["timestamp"]);
144
+ const metadata = openTelemetrySelectedMetadata(event["attributes"], options.eventAttributeKeys);
145
+ return {
146
+ name,
147
+ ...(timestamp === undefined ? {} : { timestamp }),
148
+ ...(Object.keys(metadata).length > EMPTY ? { metadata } : {})
149
+ };
150
+ }
151
+
152
+ /**
153
+ * @param {unknown} events
154
+ * @param {ResolvedSpanOptions} options
155
+ * @returns {SpanEventSummary[] | undefined}
156
+ */
157
+ function openTelemetryReadableSpanEvents(events, options) {
158
+ if (!isList(events) || events.length === EMPTY) {
159
+ return undefined;
160
+ }
161
+ /** @type {SpanEventSummary[]} */
162
+ const summaries = [];
163
+ for (const event of events.slice(FIRST_ITEM, MAX_SPAN_EVENTS)) {
164
+ if (isRecord(event)) {
165
+ summaries.push(openTelemetryEventSummary(event, options));
166
+ }
167
+ }
168
+ return summaries.length > EMPTY ? summaries : undefined;
169
+ }
170
+
171
+ /**
172
+ * @param {unknown} link
173
+ * @param {ResolvedSpanOptions} options
174
+ * @returns {SpanLinkSummary | undefined}
175
+ */
176
+ function openTelemetryLinkSummary(link, options) {
177
+ const context = normalizeOpenTelemetrySpanContextIds(
178
+ optionalProperty(link, "context") ?? optionalProperty(link, "spanContext")
179
+ );
180
+ if (context === undefined) {
181
+ return undefined;
182
+ }
183
+ const metadata = openTelemetrySelectedMetadata(optionalProperty(link, "attributes"), options.linkAttributeKeys);
184
+ return {
185
+ traceId: context.traceId,
186
+
187
+ spanId: context.spanId,
188
+
189
+ sampled: context.sampled,
190
+ ...(Object.keys(metadata).length > EMPTY ? { metadata } : {})
191
+ };
192
+ }
193
+
194
+ /**
195
+ * @param {unknown} links
196
+ * @param {ResolvedSpanOptions} options
197
+ * @returns {SpanLinkSummary[] | undefined}
198
+ */
199
+ function openTelemetryReadableSpanLinks(links, options) {
200
+ if (!isList(links) || links.length === EMPTY) {
201
+ return undefined;
202
+ }
203
+ /** @type {SpanLinkSummary[]} */
204
+ const summaries = [];
205
+ for (const link of links.slice(FIRST_ITEM, MAX_SPAN_LINKS)) {
206
+ const summary = openTelemetryLinkSummary(link, options);
207
+ if (summary !== undefined) {
208
+ summaries.push(summary);
209
+ }
210
+ }
211
+ return summaries.length > EMPTY ? summaries : undefined;
212
+ }
213
+
214
+ /**
215
+ * @param {ReadableSpan} span
216
+ * @returns {string}
217
+ */
218
+ function openTelemetrySpanName(span) {
219
+ const { name } = span;
220
+ return typeof name === "string" && name.trim() !== "" ? name : "opentelemetry.span";
221
+ }
222
+
223
+ /**
224
+ * @param {unknown} status
225
+ * @param {number} escapedExceptions
226
+ * @returns {"ok" | "error"}
227
+ */
228
+ function openTelemetrySpanStatus(status, escapedExceptions) {
229
+ const code = optionalProperty(status, "code");
230
+ if (code === OTEL_STATUS_CODE_ERROR || code === "ERROR") {
231
+ return "error";
232
+ }
233
+ if (code === OTEL_STATUS_CODE_OK || code === "OK") {
234
+ return "ok";
235
+ }
236
+ if (escapedExceptions > EMPTY) {
237
+ return "error";
238
+ }
239
+ return "ok";
240
+ }
241
+
242
+ /**
243
+ * @param {ReadableSpan} span
244
+ * @param {ResolvedSpanOptions} options
245
+ * @returns {SpanAttributes | undefined}
246
+ */
247
+ function readableSpanAttributes(span, options) {
248
+ const context = normalizeOpenTelemetryReadableSpanContext(span);
249
+ if (context === undefined || (!options.captureUnsampled && !context.sampled)) {
250
+ return undefined;
251
+ }
252
+ const metadata = openTelemetryReadableSpanMetadata(span, options);
253
+ const events = options.includeSpanEvents ? openTelemetryReadableSpanEvents(span["events"], options) : undefined;
254
+ const links = options.includeSpanLinks ? openTelemetryReadableSpanLinks(span["links"], options) : undefined;
255
+ const exceptionSummary = openTelemetryExceptionEventSummary(span["events"]);
256
+ const durationMs = durationMsFromOpenTelemetryReadableSpan(span);
257
+ const resolvedMetadata = { ...metadata, ...exceptionSummary?.metadata() };
258
+ return {
259
+ name: openTelemetrySpanName(span),
260
+
261
+ traceId: context.traceId,
262
+
263
+ spanId: context.spanId,
264
+ ...(context.parentSpanId === undefined ? {} : { parentSpanId: context.parentSpanId }),
265
+ status: openTelemetrySpanStatus(span["status"], exceptionSummary?.escapedCount ?? EMPTY),
266
+ ...(durationMs === undefined ? {} : { durationMs }),
267
+ ...(events === undefined ? {} : { events }),
268
+ ...(links === undefined ? {} : { links }),
269
+ ...(Object.keys(resolvedMetadata).length > EMPTY ? { metadata: resolvedMetadata } : {})
270
+ };
271
+ }
272
+
273
+ /**
274
+ * @param {unknown} span
275
+ * @param {ReadableSpanOptions} [options]
276
+ * @returns {SpanAttributes | null}
277
+ */
278
+ function spanAttributesFromOpenTelemetryReadableSpan(span, options = {}) {
279
+ const resolvedOptions = resolveOpenTelemetryReadableSpanOptions(options);
280
+ if (!isRecord(span)) {
281
+ return NULL_VALUE;
282
+ }
283
+ return readableSpanAttributes(span, resolvedOptions) ?? NULL_VALUE;
284
+ }
285
+
286
+ module.exports = { readableSpanAttributes, spanAttributesFromOpenTelemetryReadableSpan };
@@ -0,0 +1,213 @@
1
+ const { compactMetadata, isMetadataValue } = require("./log-context.cjs");
2
+ const { defaultSpanIdFactory } = require("./opentelemetry-context.cjs");
3
+ const { safeOpenTelemetryExceptionType } = require("./opentelemetry-exceptions.cjs");
4
+ const { TRACE_SUMMARY_METADATA_KEYS } = require("./opentelemetry-keys.cjs");
5
+ const {
6
+ endMsFromOpenTelemetryReadableSpan,
7
+ fallbackTimestampValue,
8
+ openTelemetryTimeMs,
9
+ timestampFromOpenTelemetryTime
10
+ } = require("./opentelemetry-time.cjs");
11
+ const { isTruthy } = require("./validation.cjs");
12
+
13
+ /**
14
+ * @import { Metadata, MetadataValue, SpanAttributes } from "./index.cjs"
15
+ * @import { DeepReadonly } from "./validation.cjs"
16
+ * @typedef {DeepReadonly<SpanAttributes>} ReadonlySpanAttributes
17
+ */
18
+
19
+ const NONE = 0;
20
+
21
+ /**
22
+ * @param {Readonly<Metadata> | undefined} metadata
23
+ * @param {string} key
24
+ * @returns {number}
25
+ */
26
+ function positiveSafeInteger(metadata, key) {
27
+ const value = metadata?.[key];
28
+ return typeof value === "number" && Number.isSafeInteger(value) && value > NONE ? value : NONE;
29
+ }
30
+
31
+ class OpenTelemetryTraceSummary {
32
+ spanCount = NONE;
33
+ errorSpanCount = NONE;
34
+ /** @type {Metadata} */
35
+ metadata = {};
36
+ rootSeen = false;
37
+ /** @type {number | undefined} */
38
+ firstStartMs = undefined;
39
+ /** @type {number | undefined} */
40
+ lastEndMs = undefined;
41
+ /** @type {string | undefined} */
42
+ rootSpanId = undefined;
43
+ /** @type {string | undefined} */
44
+ rootName = undefined;
45
+ /** @type {MetadataValue | undefined} */
46
+ rootKind = undefined;
47
+ /** @type {number | undefined} */
48
+ rootStartMs = undefined;
49
+ /** @type {number | undefined} */
50
+ rootDurationMs = undefined;
51
+ exceptionEventCount = NONE;
52
+ exceptionEscapedCount = NONE;
53
+ /** @type {Set<string>} */
54
+ exceptionTypes = new Set();
55
+
56
+ /**
57
+ * @param {string} traceId
58
+ */
59
+ constructor(traceId) {
60
+ this.traceId = traceId;
61
+ }
62
+
63
+ /**
64
+ * @param {ReadonlySpanAttributes} attributes
65
+ * @param {Readonly<Record<string, unknown>>} span
66
+ * @returns {void}
67
+ */
68
+ record(attributes, span) {
69
+ this.spanCount += 1;
70
+ if (attributes.status === "error") {
71
+ this.errorSpanCount += 1;
72
+ }
73
+ this.recordExceptions(attributes.metadata);
74
+ const startMs = openTelemetryTimeMs(span["startTime"]);
75
+ this.recordWindow(startMs, endMsFromOpenTelemetryReadableSpan(span, startMs, attributes.durationMs));
76
+ this.copyMetadata(attributes.metadata, false);
77
+ const isRootSpan = attributes.parentSpanId === undefined;
78
+ if (isRootSpan || this.rootSpanId === undefined) {
79
+ this.recordRoot(attributes, isRootSpan, startMs);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @param {number | undefined} startMs
85
+ * @param {number | undefined} endMs
86
+ * @returns {void}
87
+ */
88
+ recordWindow(startMs, endMs) {
89
+ if (startMs !== undefined && (this.firstStartMs === undefined || startMs < this.firstStartMs)) {
90
+ this.firstStartMs = startMs;
91
+ }
92
+ if (endMs !== undefined && (this.lastEndMs === undefined || endMs > this.lastEndMs)) {
93
+ this.lastEndMs = endMs;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * @param {ReadonlySpanAttributes} attributes
99
+ * @param {boolean} isRootSpan
100
+ * @param {number | undefined} startMs
101
+ * @returns {void}
102
+ */
103
+ recordRoot(attributes, isRootSpan, startMs) {
104
+ this.rootSpanId = attributes.spanId;
105
+ this.rootName = attributes.name;
106
+ this.rootKind = attributes.metadata?.["otel.kind"];
107
+ this.rootSeen = isRootSpan;
108
+ if (startMs !== undefined) {
109
+ this.rootStartMs = startMs;
110
+ }
111
+ if (attributes.durationMs !== undefined) {
112
+ this.rootDurationMs = attributes.durationMs;
113
+ }
114
+ this.copyMetadata(attributes.metadata, true);
115
+ }
116
+
117
+ /**
118
+ * @param {Readonly<Metadata> | undefined} metadata
119
+ * @returns {void}
120
+ */
121
+ recordExceptions(metadata) {
122
+ this.exceptionEventCount += positiveSafeInteger(metadata, "otel.exception_event_count");
123
+ this.exceptionEscapedCount += positiveSafeInteger(metadata, "otel.exception_escaped_count");
124
+ const types = metadata?.["otel.exception_types"];
125
+ if (typeof types === "string" && types.trim() !== "") {
126
+ for (const type of types.split(",")) {
127
+ const safeType = safeOpenTelemetryExceptionType(type);
128
+ if (safeType !== undefined) {
129
+ this.exceptionTypes.add(safeType);
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * @param {Readonly<Metadata> | undefined} metadata
137
+ * @param {boolean} overwrite
138
+ * @returns {void}
139
+ */
140
+ copyMetadata(metadata, overwrite) {
141
+ if (metadata === undefined) {
142
+ return;
143
+ }
144
+ for (const [key, value] of Object.entries(metadata)) {
145
+ if (
146
+ TRACE_SUMMARY_METADATA_KEYS.has(key)
147
+ && (overwrite || this.metadata[key] === undefined)
148
+ && isMetadataValue(value)
149
+ ) {
150
+ this.metadata[key] = value;
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * @returns {number | undefined}
157
+ */
158
+ durationMs() {
159
+ if (this.firstStartMs !== undefined && this.lastEndMs !== undefined && this.lastEndMs >= this.firstStartMs) {
160
+ return this.lastEndMs - this.firstStartMs;
161
+ }
162
+ return this.rootDurationMs;
163
+ }
164
+
165
+ /**
166
+ * @param {(() => unknown) | undefined} fallbackTimestamp
167
+ * @returns {unknown}
168
+ */
169
+ timestamp(fallbackTimestamp) {
170
+ return timestampFromOpenTelemetryTime(this.rootStartMs)
171
+ ?? timestampFromOpenTelemetryTime(this.firstStartMs)
172
+ ?? fallbackTimestampValue(fallbackTimestamp);
173
+ }
174
+
175
+ /**
176
+ * @returns {Metadata}
177
+ */
178
+ summaryMetadata() {
179
+ return compactMetadata({
180
+ source: "opentelemetry.trace_summary",
181
+ ...this.metadata,
182
+ "otel.trace.span_count": this.spanCount,
183
+ ...(this.errorSpanCount > NONE ? { "otel.trace.error_span_count": this.errorSpanCount } : {}),
184
+ ...(this.exceptionEventCount > NONE ? { "otel.trace.exception_event_count": this.exceptionEventCount } : {}),
185
+ ...(this.exceptionEscapedCount > NONE ? { "otel.trace.exception_escaped_count": this.exceptionEscapedCount } : {}),
186
+ ...(this.exceptionTypes.size > NONE ? { "otel.trace.exception_types": [...this.exceptionTypes].join(",") } : {}),
187
+ ...(isTruthy(this.rootSpanId) ? { "otel.trace.root_span_id": this.rootSpanId } : {}),
188
+ ...(isTruthy(this.rootName) ? { "otel.trace.root_name": this.rootName } : {}),
189
+ ...(isTruthy(this.rootKind) ? { "otel.trace.root_kind": this.rootKind } : {}),
190
+ "otel.trace.summary_kind": this.rootSeen ? "rooted" : "flush_batch"
191
+ });
192
+ }
193
+
194
+ /**
195
+ * @returns {SpanAttributes}
196
+ */
197
+ attributes() {
198
+ const metadata = this.summaryMetadata();
199
+ const durationMs = this.durationMs();
200
+ return {
201
+ name: isTruthy(this.rootName) ? `opentelemetry.trace:${String(this.rootName)}` : "opentelemetry.trace",
202
+
203
+ traceId: this.traceId,
204
+
205
+ spanId: defaultSpanIdFactory(),
206
+ status: this.errorSpanCount > NONE ? "error" : "ok",
207
+ ...(durationMs === undefined ? {} : { durationMs }),
208
+ ...(Object.keys(metadata).length > NONE ? { metadata } : {})
209
+ };
210
+ }
211
+ }
212
+
213
+ module.exports = { OpenTelemetryTraceSummary };
@@ -0,0 +1,131 @@
1
+ const { isList } = require("./validation.cjs");
2
+
3
+ /**
4
+ * @typedef {Readonly<Record<string, unknown>>} ReadableSpan
5
+ */
6
+
7
+ const HR_TIME_PARTS = 2;
8
+ const MS_PER_SECOND = 1000;
9
+ const NANOS_PER_MS = 1_000_000;
10
+ const MIN_HR_TIME_PART = 0;
11
+
12
+ /**
13
+ * @param {unknown} value
14
+ * @returns {value is number}
15
+ */
16
+ function isFiniteNumber(value) {
17
+ return typeof value === "number" && Number.isFinite(value);
18
+ }
19
+
20
+ /**
21
+ * @param {unknown} value
22
+ * @returns {number | undefined}
23
+ */
24
+ function openTelemetryTimeMs(value) {
25
+ if (isList(value) && value.length >= HR_TIME_PARTS) {
26
+ const [seconds, nanos] = value;
27
+ if (isFiniteNumber(seconds) && isFiniteNumber(nanos)) {
28
+ return seconds * MS_PER_SECOND + nanos / NANOS_PER_MS;
29
+ }
30
+ }
31
+ if (isFiniteNumber(value)) {
32
+ return value;
33
+ }
34
+ if (value instanceof Date && !Number.isNaN(value.valueOf())) {
35
+ return value.valueOf();
36
+ }
37
+ return undefined;
38
+ }
39
+
40
+ /**
41
+ * @param {unknown} value
42
+ * @returns {number | undefined}
43
+ */
44
+ function openTelemetryDurationMs(value) {
45
+ if (!isList(value) || value.length < HR_TIME_PARTS) {
46
+ return undefined;
47
+ }
48
+ const [seconds, nanos] = value;
49
+ if (
50
+ !isFiniteNumber(seconds)
51
+ || !isFiniteNumber(nanos)
52
+ || seconds < MIN_HR_TIME_PART
53
+ || nanos < MIN_HR_TIME_PART
54
+ ) {
55
+ return undefined;
56
+ }
57
+ return seconds * MS_PER_SECOND + nanos / NANOS_PER_MS;
58
+ }
59
+
60
+ /**
61
+ * @param {unknown} value
62
+ * @returns {string | undefined}
63
+ */
64
+ function timestampFromOpenTelemetryTime(value) {
65
+ const milliseconds = openTelemetryTimeMs(value);
66
+ if (milliseconds === undefined) {
67
+ return undefined;
68
+ }
69
+ const date = new Date(milliseconds);
70
+ return Number.isNaN(date.valueOf()) ? undefined : date.toISOString();
71
+ }
72
+
73
+ /**
74
+ * @param {(() => unknown) | undefined} fallbackTimestamp
75
+ * @returns {unknown}
76
+ */
77
+ function fallbackTimestampValue(fallbackTimestamp) {
78
+ return fallbackTimestamp === undefined ? new Date().toISOString() : fallbackTimestamp();
79
+ }
80
+
81
+ /**
82
+ * @param {ReadableSpan} span
83
+ * @returns {number | undefined}
84
+ */
85
+ function durationMsFromOpenTelemetryReadableSpan(span) {
86
+ const durationMs = openTelemetryDurationMs(span["duration"]);
87
+ if (durationMs !== undefined) {
88
+ return durationMs;
89
+ }
90
+ const startMs = openTelemetryTimeMs(span["startTime"]);
91
+ const endMs = openTelemetryTimeMs(span["endTime"]);
92
+ if (startMs !== undefined && endMs !== undefined && endMs >= startMs) {
93
+ return endMs - startMs;
94
+ }
95
+ return undefined;
96
+ }
97
+
98
+ /**
99
+ * @param {ReadableSpan} span
100
+ * @param {number | undefined} startMs
101
+ * @param {number | undefined} durationMs
102
+ * @returns {number | undefined}
103
+ */
104
+ function endMsFromOpenTelemetryReadableSpan(span, startMs, durationMs) {
105
+ const endMs = openTelemetryTimeMs(span["endTime"]);
106
+ if (endMs !== undefined) {
107
+ return endMs;
108
+ }
109
+ if (startMs !== undefined && durationMs !== undefined) {
110
+ return startMs + durationMs;
111
+ }
112
+ return undefined;
113
+ }
114
+
115
+ /**
116
+ * @param {ReadableSpan} span
117
+ * @param {(() => unknown) | undefined} fallbackTimestamp
118
+ * @returns {unknown}
119
+ */
120
+ function timestampFromOpenTelemetryReadableSpan(span, fallbackTimestamp) {
121
+ return timestampFromOpenTelemetryTime(span["startTime"]) ?? fallbackTimestampValue(fallbackTimestamp);
122
+ }
123
+
124
+ module.exports = {
125
+ durationMsFromOpenTelemetryReadableSpan,
126
+ endMsFromOpenTelemetryReadableSpan,
127
+ fallbackTimestampValue,
128
+ openTelemetryTimeMs,
129
+ timestampFromOpenTelemetryReadableSpan,
130
+ timestampFromOpenTelemetryTime
131
+ };