@sebspark/otel 0.1.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/README.md +144 -81
- package/dist/index.d.mts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +656 -85
- package/dist/index.mjs +661 -89
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -27,72 +37,543 @@ __export(index_exports, {
|
|
|
27
37
|
module.exports = __toCommonJS(index_exports);
|
|
28
38
|
|
|
29
39
|
// src/otel.ts
|
|
30
|
-
var
|
|
40
|
+
var import_api3 = require("@opentelemetry/api");
|
|
31
41
|
var import_api_logs = require("@opentelemetry/api-logs");
|
|
32
42
|
var import_auto_instrumentations_node = require("@opentelemetry/auto-instrumentations-node");
|
|
43
|
+
var import_context_async_hooks = require("@opentelemetry/context-async-hooks");
|
|
44
|
+
var import_sdk_node = require("@opentelemetry/sdk-node");
|
|
45
|
+
|
|
46
|
+
// src/providers.ts
|
|
33
47
|
var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
|
34
48
|
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
35
49
|
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
36
|
-
var import_resources = require("@opentelemetry/resources");
|
|
37
50
|
var import_sdk_logs = require("@opentelemetry/sdk-logs");
|
|
38
51
|
var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
39
|
-
var
|
|
52
|
+
var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
|
|
53
|
+
|
|
54
|
+
// src/loggers/console-log-pretty-exporter.ts
|
|
55
|
+
var import_core = require("@opentelemetry/core");
|
|
56
|
+
|
|
57
|
+
// src/consts.ts
|
|
58
|
+
var LOG_SEVERITY_MAP = {
|
|
59
|
+
TRACE: 1,
|
|
60
|
+
DEBUG: 5,
|
|
61
|
+
INFO: 9,
|
|
62
|
+
NOTICE: 10,
|
|
63
|
+
WARNING: 13,
|
|
64
|
+
WARN: 13,
|
|
65
|
+
ERROR: 17,
|
|
66
|
+
FATAL: 21,
|
|
67
|
+
CRITICAL: 21,
|
|
68
|
+
ALERT: 22,
|
|
69
|
+
EMERGENCY: 23
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/loggers/formatters/shared.ts
|
|
40
73
|
var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
var import_fast_safe_stringify = __toESM(require("fast-safe-stringify"));
|
|
75
|
+
|
|
76
|
+
// src/loggers/formatters/style.ts
|
|
77
|
+
var import_kleur = __toESM(require("kleur"));
|
|
78
|
+
var colors = {
|
|
79
|
+
gray: import_kleur.default.gray,
|
|
80
|
+
dim: import_kleur.default.dim,
|
|
81
|
+
cyan: import_kleur.default.cyan,
|
|
82
|
+
white: import_kleur.default.white,
|
|
83
|
+
green: import_kleur.default.green,
|
|
84
|
+
yellow: import_kleur.default.yellow,
|
|
85
|
+
red: import_kleur.default.red,
|
|
86
|
+
magenta: import_kleur.default.magenta
|
|
87
|
+
};
|
|
88
|
+
var levelColorMap = {
|
|
89
|
+
DEBUG: import_kleur.default.magenta,
|
|
90
|
+
INFO: import_kleur.default.green,
|
|
91
|
+
WARN: import_kleur.default.yellow,
|
|
92
|
+
ERROR: import_kleur.default.red,
|
|
93
|
+
FATAL: import_kleur.default.red
|
|
94
|
+
};
|
|
95
|
+
var levelIconMap = {
|
|
96
|
+
DEBUG: "\u{1F41B}",
|
|
97
|
+
INFO: "\u2139\uFE0F ",
|
|
98
|
+
WARN: "\u26A0\uFE0F ",
|
|
99
|
+
ERROR: "\u274C",
|
|
100
|
+
FATAL: "\u{1F480}"
|
|
101
|
+
};
|
|
102
|
+
var statusLabelMap = {
|
|
103
|
+
0: "UNSET",
|
|
104
|
+
1: "OK",
|
|
105
|
+
2: "ERROR"
|
|
106
|
+
};
|
|
107
|
+
var statusColorMap = {
|
|
108
|
+
0: colors.gray,
|
|
109
|
+
// UNSET
|
|
110
|
+
1: colors.green,
|
|
111
|
+
// OK
|
|
112
|
+
2: colors.red
|
|
113
|
+
// ERROR
|
|
114
|
+
};
|
|
115
|
+
var kindColorMap = {
|
|
116
|
+
0: colors.white,
|
|
117
|
+
// INTERNAL
|
|
118
|
+
1: colors.cyan,
|
|
119
|
+
// SERVER
|
|
120
|
+
2: colors.yellow,
|
|
121
|
+
// CLIENT
|
|
122
|
+
3: colors.magenta,
|
|
123
|
+
// PRODUCER
|
|
124
|
+
4: colors.green
|
|
125
|
+
// CONSUMER
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/loggers/formatters/shared.ts
|
|
129
|
+
function formatTimestamp(time) {
|
|
130
|
+
const date = new Date(hrTimeToMillis(time));
|
|
131
|
+
return colors.dim(date.toISOString().slice(11, 23));
|
|
132
|
+
}
|
|
133
|
+
function formatService(resource) {
|
|
134
|
+
const name = resource.attributes[import_semantic_conventions.ATTR_SERVICE_NAME] ?? "unknown-service";
|
|
135
|
+
const version = resource.attributes[import_semantic_conventions.ATTR_SERVICE_VERSION] ?? "1.0.0";
|
|
136
|
+
return colors.gray(`[${name}@${version}]`);
|
|
137
|
+
}
|
|
138
|
+
function formatLevel(record) {
|
|
139
|
+
const text = (record.severityText ?? "INFO").toUpperCase();
|
|
140
|
+
const colorFn = levelColorMap[text] ?? colors.white;
|
|
141
|
+
const icon = levelIconMap[text] ?? "\u2022";
|
|
142
|
+
return `${icon} ${colorFn(text.padEnd(5))}`;
|
|
143
|
+
}
|
|
144
|
+
function formatScope(resource, instrumentationScope) {
|
|
145
|
+
const component = resource.attributes["component.name"];
|
|
146
|
+
const { name, version } = instrumentationScope;
|
|
147
|
+
const scopeLabel = component || (name && name !== "unknown" ? name : void 0);
|
|
148
|
+
if (!scopeLabel) return "";
|
|
149
|
+
const versionLabel = version ? `@${version}` : "";
|
|
150
|
+
return colors.cyan(`${scopeLabel}${versionLabel} `);
|
|
151
|
+
}
|
|
152
|
+
function formatMessage(record) {
|
|
153
|
+
return typeof record.body === "string" ? record.body : (0, import_fast_safe_stringify.default)(record.body);
|
|
154
|
+
}
|
|
155
|
+
function formatAttributes(attrs) {
|
|
156
|
+
const keys = Object.keys(attrs).filter(
|
|
157
|
+
(k) => !k.startsWith("service.") && !k.startsWith("serviceContext.")
|
|
158
|
+
);
|
|
159
|
+
if (keys.length === 0) return "";
|
|
160
|
+
const formatted = keys.map((k) => {
|
|
161
|
+
const val = attrs[k];
|
|
162
|
+
return `${k}=${typeof val === "object" ? (0, import_fast_safe_stringify.default)(val) : val}`;
|
|
163
|
+
});
|
|
164
|
+
return ` ${colors.gray(formatted.join(" "))}`;
|
|
165
|
+
}
|
|
166
|
+
function hrTimeToMillis(hrTime) {
|
|
167
|
+
return hrTime[0] * 1e3 + Math.floor(hrTime[1] / 1e6);
|
|
168
|
+
}
|
|
169
|
+
function calculateDuration(span) {
|
|
170
|
+
const start = hrTimeToMillis(span.startTime);
|
|
171
|
+
const end = hrTimeToMillis(span.endTime);
|
|
172
|
+
return Math.max(0, Math.round(end - start));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/loggers/formatters/log-record.ts
|
|
176
|
+
function formatLogRecord(record) {
|
|
177
|
+
const timestamp = formatTimestamp(record.hrTime);
|
|
178
|
+
const service = formatService(record.resource);
|
|
179
|
+
const level = formatLevel(record);
|
|
180
|
+
const scope = formatScope(record.resource, record.instrumentationScope);
|
|
181
|
+
const message = formatMessage(record);
|
|
182
|
+
const attrs = formatAttributes(record.attributes);
|
|
183
|
+
return `${service} ${timestamp} ${level} ${scope}${message}${attrs}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/loggers/formatters/metrics.ts
|
|
187
|
+
function formatMetrics(resourceMetrics) {
|
|
188
|
+
const { resource, scopeMetrics } = resourceMetrics;
|
|
189
|
+
return scopeMetrics.map((scopeMetric) => formatScopeMetric(scopeMetric, resource)).join("\n");
|
|
190
|
+
}
|
|
191
|
+
function formatScopeMetric(scopeMetric, resource) {
|
|
192
|
+
return scopeMetric.metrics.map(
|
|
193
|
+
(metric) => formatMetricData(metric, resource, scopeMetric.scope)
|
|
194
|
+
).join("\n");
|
|
195
|
+
}
|
|
196
|
+
function formatMetricData(metric, resource, scope) {
|
|
197
|
+
const scopeStr = formatScope(resource, scope);
|
|
198
|
+
const serviceStr = formatService(resource);
|
|
199
|
+
const lines = [];
|
|
200
|
+
for (const dp of metric.dataPoints) {
|
|
201
|
+
const ts = formatTimestamp(dp.startTime);
|
|
202
|
+
const value = extractMetricValue(dp);
|
|
203
|
+
const attrs = formatAttributes(dp.attributes ?? {});
|
|
204
|
+
lines.push(
|
|
205
|
+
`${serviceStr} ${ts} \u{1F4CA} ${scopeStr}${colors.white(metric.descriptor.name)} ${colors.dim(value)}${attrs}`
|
|
206
|
+
);
|
|
83
207
|
}
|
|
208
|
+
return lines.join("\n");
|
|
209
|
+
}
|
|
210
|
+
function extractMetricValue(dp) {
|
|
211
|
+
const value = dp.value;
|
|
212
|
+
if (typeof value === "number") {
|
|
213
|
+
return value.toString();
|
|
214
|
+
}
|
|
215
|
+
if (isHistogramLike(value)) {
|
|
216
|
+
return value.sum.toString();
|
|
217
|
+
}
|
|
218
|
+
return "[complex]";
|
|
219
|
+
}
|
|
220
|
+
function isHistogramLike(val) {
|
|
221
|
+
return typeof val === "object" && val !== null && "sum" in val && typeof val.sum === "number";
|
|
84
222
|
}
|
|
85
|
-
initialize();
|
|
86
223
|
|
|
87
|
-
// src/
|
|
224
|
+
// src/loggers/formatters/span.ts
|
|
225
|
+
var import_api = require("@opentelemetry/api");
|
|
226
|
+
function formatSpans(spans) {
|
|
227
|
+
const rootSpan = spans[0];
|
|
228
|
+
const rootStart = hrTimeToMillis(rootSpan.startTime);
|
|
229
|
+
const rootEnd = hrTimeToMillis(rootSpan.endTime);
|
|
230
|
+
const totalDuration = rootEnd - rootStart;
|
|
231
|
+
const service = formatService(rootSpan.resource);
|
|
232
|
+
const timestamp = formatTimestamp(rootSpan.startTime);
|
|
233
|
+
const lines = [`${service} ${timestamp}`];
|
|
234
|
+
for (const span of spans) {
|
|
235
|
+
const offset = hrTimeToMillis(span.startTime) - rootStart;
|
|
236
|
+
const depth = computeDepth(span, spans);
|
|
237
|
+
lines.push(
|
|
238
|
+
formatSpan(span, {
|
|
239
|
+
offsetMs: offset,
|
|
240
|
+
totalDurationMs: totalDuration,
|
|
241
|
+
depth
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return lines.join("\n");
|
|
246
|
+
}
|
|
247
|
+
function formatSpan(span, opts) {
|
|
248
|
+
const label = formatLabel(span, opts.depth);
|
|
249
|
+
const bar = buildBar(span, opts?.offsetMs, opts?.totalDurationMs);
|
|
250
|
+
const barColor = span.status.code === import_api.SpanStatusCode.OK ? colors.green : span.status.code === import_api.SpanStatusCode.ERROR ? colors.red : colors.gray;
|
|
251
|
+
const desc = formatDescription(span);
|
|
252
|
+
const status = formatStatus(span);
|
|
253
|
+
const duration = formatDuration(span, opts?.offsetMs);
|
|
254
|
+
return `${label} ${barColor(bar)} ${desc} ${status} (${duration})`;
|
|
255
|
+
}
|
|
256
|
+
var LABEL_WIDTH = 25;
|
|
257
|
+
function formatLabel(span, depth) {
|
|
258
|
+
const indent = " ".repeat(depth);
|
|
259
|
+
const label = `${indent}\u2514\u2500 ${span.name}`;
|
|
260
|
+
return label.padEnd(LABEL_WIDTH);
|
|
261
|
+
}
|
|
262
|
+
var BAR_MIN_WIDTH = 1;
|
|
263
|
+
var BAR_MAX_WIDTH = 20;
|
|
264
|
+
function buildBar(span, offsetMs, totalDurationMs) {
|
|
265
|
+
const duration = calculateDuration(span);
|
|
266
|
+
if (typeof offsetMs !== "number" || typeof totalDurationMs !== "number" || totalDurationMs === 0) {
|
|
267
|
+
const capped = Math.min(duration, 1e3);
|
|
268
|
+
const barLength = Math.max(
|
|
269
|
+
BAR_MIN_WIDTH,
|
|
270
|
+
Math.round(capped / 1e3 * BAR_MAX_WIDTH)
|
|
271
|
+
);
|
|
272
|
+
return "\u2588".repeat(barLength).padEnd(BAR_MAX_WIDTH + 2);
|
|
273
|
+
}
|
|
274
|
+
const offsetRatio = Math.max(0, Math.min(offsetMs / totalDurationMs, 1));
|
|
275
|
+
const durationRatio = Math.max(0, Math.min(duration / totalDurationMs, 1));
|
|
276
|
+
const offsetChars = Math.floor(offsetRatio * BAR_MAX_WIDTH);
|
|
277
|
+
const barChars = Math.max(
|
|
278
|
+
BAR_MIN_WIDTH,
|
|
279
|
+
Math.round(durationRatio * BAR_MAX_WIDTH)
|
|
280
|
+
);
|
|
281
|
+
const empty = " ".repeat(offsetChars);
|
|
282
|
+
const bar = "\u2588".repeat(barChars);
|
|
283
|
+
return (empty + bar).padEnd(BAR_MAX_WIDTH + 2);
|
|
284
|
+
}
|
|
285
|
+
var DESCRIPTION_MAX_WIDTH = 20;
|
|
286
|
+
function formatDescription(span) {
|
|
287
|
+
const keyPriority = [
|
|
288
|
+
// HTTP
|
|
289
|
+
["http.method", "http.target"],
|
|
290
|
+
// → GET /users/123
|
|
291
|
+
["http.route"],
|
|
292
|
+
// → /users/:id
|
|
293
|
+
["http.url"],
|
|
294
|
+
// → https://...
|
|
295
|
+
// GraphQL
|
|
296
|
+
["graphql.operation.name"],
|
|
297
|
+
// → getUsers
|
|
298
|
+
["graphql.operation.type"],
|
|
299
|
+
// → query
|
|
300
|
+
["graphql.document"],
|
|
301
|
+
// → full query text (maybe too long)
|
|
302
|
+
// WebSocket
|
|
303
|
+
["ws.event"],
|
|
304
|
+
// → connection, message, disconnect
|
|
305
|
+
["ws.message_type"],
|
|
306
|
+
// → ping/pong/text/binary
|
|
307
|
+
["ws.url"],
|
|
308
|
+
// → wss://...
|
|
309
|
+
// Redis
|
|
310
|
+
["db.system", "db.statement"],
|
|
311
|
+
// → redis, "SET foo bar"
|
|
312
|
+
["db.operation"],
|
|
313
|
+
// → GET, SET
|
|
314
|
+
// Spanner
|
|
315
|
+
["db.statement"],
|
|
316
|
+
// → SELECT * FROM...
|
|
317
|
+
["db.operation"],
|
|
318
|
+
// → SELECT, INSERT
|
|
319
|
+
["db.name"],
|
|
320
|
+
// → projects/.../instances/.../databases/...
|
|
321
|
+
// OpenSearch
|
|
322
|
+
["db.operation"],
|
|
323
|
+
// → search, index, bulk
|
|
324
|
+
["db.statement"],
|
|
325
|
+
// → { query DSL... }
|
|
326
|
+
// Pub/Sub (GCP)
|
|
327
|
+
["messaging.operation"],
|
|
328
|
+
// → publish, receive
|
|
329
|
+
["messaging.destination"],
|
|
330
|
+
// → topic-a
|
|
331
|
+
["messaging.gcp_pubsub.topic"],
|
|
332
|
+
// → projects/x/topics/y
|
|
333
|
+
// General FaaS
|
|
334
|
+
["faas.invoked_name"],
|
|
335
|
+
// → myFunction
|
|
336
|
+
["faas.trigger"],
|
|
337
|
+
// → http, pubsub, etc.
|
|
338
|
+
// Custom or fallback
|
|
339
|
+
["otel.description"]
|
|
340
|
+
];
|
|
341
|
+
for (const keys of keyPriority) {
|
|
342
|
+
const parts = keys.map((k) => span.attributes[k]).filter((v) => v !== void 0 && v !== null).map((v) => String(v));
|
|
343
|
+
if (parts.length > 0) {
|
|
344
|
+
return truncate(parts.join(" "), DESCRIPTION_MAX_WIDTH - 1).padEnd(
|
|
345
|
+
DESCRIPTION_MAX_WIDTH
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return "".padEnd(DESCRIPTION_MAX_WIDTH);
|
|
350
|
+
}
|
|
351
|
+
function formatStatus(span) {
|
|
352
|
+
const code = span.status.code;
|
|
353
|
+
const label = statusLabelMap[code] ?? "UNSET";
|
|
354
|
+
const colorFn = statusColorMap[code] ?? colors.gray;
|
|
355
|
+
return colorFn(label).padEnd(6);
|
|
356
|
+
}
|
|
357
|
+
function formatDuration(span, offsetMs) {
|
|
358
|
+
const duration = calculateDuration(span);
|
|
359
|
+
const format = (ms) => ms >= 1e3 ? `${(ms / 1e3).toFixed(2)} s` : `${ms} ms`;
|
|
360
|
+
return `${format(offsetMs || 0)}\u2013${format(duration)}`;
|
|
361
|
+
}
|
|
362
|
+
function truncate(input, maxLength) {
|
|
363
|
+
const str = String(input ?? "");
|
|
364
|
+
return str.length > maxLength ? `${str.slice(0, maxLength - 1)}\u2026` : str;
|
|
365
|
+
}
|
|
366
|
+
function computeDepth(span, allSpans) {
|
|
367
|
+
let depth = 0;
|
|
368
|
+
let currentParentId = span.parentSpanContext?.spanId;
|
|
369
|
+
while (currentParentId) {
|
|
370
|
+
const parentSpan = allSpans.find(
|
|
371
|
+
(s) => s.spanContext().spanId === currentParentId
|
|
372
|
+
);
|
|
373
|
+
if (!parentSpan) break;
|
|
374
|
+
depth += 1;
|
|
375
|
+
currentParentId = parentSpan.parentSpanContext?.spanId;
|
|
376
|
+
}
|
|
377
|
+
return depth;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/loggers/console-log-pretty-exporter.ts
|
|
381
|
+
var ConsoleLogPrettyExporter = class {
|
|
382
|
+
logThreshold;
|
|
383
|
+
constructor() {
|
|
384
|
+
const defaultLogLevel = "INFO";
|
|
385
|
+
const env = process.env.LOG_LEVEL?.toUpperCase() ?? defaultLogLevel;
|
|
386
|
+
this.logThreshold = LOG_SEVERITY_MAP[env] ?? LOG_SEVERITY_MAP[defaultLogLevel];
|
|
387
|
+
}
|
|
388
|
+
export(logs3, resultCallback) {
|
|
389
|
+
this._sendLogRecords(logs3, resultCallback);
|
|
390
|
+
}
|
|
391
|
+
shutdown() {
|
|
392
|
+
return Promise.resolve();
|
|
393
|
+
}
|
|
394
|
+
forceFlush() {
|
|
395
|
+
return Promise.resolve();
|
|
396
|
+
}
|
|
397
|
+
_sendLogRecords(logRecords, done) {
|
|
398
|
+
for (const record of logRecords) {
|
|
399
|
+
if ((record.severityNumber ?? 0) >= this.logThreshold) {
|
|
400
|
+
console.log(formatLogRecord(record));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
done?.({ code: import_core.ExportResultCode.SUCCESS });
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// src/loggers/console-span-pretty-exporter.ts
|
|
88
408
|
var import_api2 = require("@opentelemetry/api");
|
|
89
|
-
var
|
|
409
|
+
var import_core2 = require("@opentelemetry/core");
|
|
410
|
+
var ConsoleSpanPrettyExporter = class {
|
|
411
|
+
allowedStatuses;
|
|
412
|
+
constructor() {
|
|
413
|
+
const env = process.env.SPAN_LEVEL?.toUpperCase();
|
|
414
|
+
if (!env) {
|
|
415
|
+
this.allowedStatuses = /* @__PURE__ */ new Set([
|
|
416
|
+
import_api2.SpanStatusCode.UNSET,
|
|
417
|
+
import_api2.SpanStatusCode.OK,
|
|
418
|
+
import_api2.SpanStatusCode.ERROR
|
|
419
|
+
]);
|
|
420
|
+
} else {
|
|
421
|
+
const map = {
|
|
422
|
+
UNSET: import_api2.SpanStatusCode.UNSET,
|
|
423
|
+
OK: import_api2.SpanStatusCode.OK,
|
|
424
|
+
ERROR: import_api2.SpanStatusCode.ERROR
|
|
425
|
+
};
|
|
426
|
+
this.allowedStatuses = new Set(
|
|
427
|
+
env.split(",").map((s) => s.trim()).map((s) => map[s]).filter((v) => typeof v === "number")
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
shouldExport(spans) {
|
|
432
|
+
if (this.allowedStatuses.size === 3) {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
return spans.some((span) => this.allowedStatuses.has(span.status.code));
|
|
436
|
+
}
|
|
437
|
+
export(spans, resultCallback) {
|
|
438
|
+
if (this.shouldExport(spans)) {
|
|
439
|
+
console.log(formatSpans(spans));
|
|
440
|
+
}
|
|
441
|
+
resultCallback({ code: import_core2.ExportResultCode.SUCCESS });
|
|
442
|
+
}
|
|
443
|
+
shutdown() {
|
|
444
|
+
return Promise.resolve();
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// src/loggers/console-metric-pretty-exporter.ts
|
|
449
|
+
var import_core3 = require("@opentelemetry/core");
|
|
450
|
+
var ConsoleMetricPrettyExporter = class {
|
|
451
|
+
patterns;
|
|
452
|
+
constructor() {
|
|
453
|
+
const raw = process.env.METRIC_FILTER ?? "";
|
|
454
|
+
const entries = raw.split(",").map((e) => e.trim()).filter(Boolean);
|
|
455
|
+
this.patterns = entries.map(globToRegex);
|
|
456
|
+
}
|
|
457
|
+
filterMetrics(resourceMetrics) {
|
|
458
|
+
if (this.patterns.length === 0) return void 0;
|
|
459
|
+
const filteredScopes = resourceMetrics.scopeMetrics.map((scopeMetric) => {
|
|
460
|
+
const filteredMetrics = scopeMetric.metrics.filter(
|
|
461
|
+
(metric) => this.patterns.some((pattern) => pattern.test(metric.descriptor.name))
|
|
462
|
+
);
|
|
463
|
+
if (filteredMetrics.length === 0) return void 0;
|
|
464
|
+
return {
|
|
465
|
+
...scopeMetric,
|
|
466
|
+
metrics: filteredMetrics
|
|
467
|
+
};
|
|
468
|
+
}).filter((s) => s !== void 0);
|
|
469
|
+
if (filteredScopes.length === 0) return void 0;
|
|
470
|
+
return {
|
|
471
|
+
...resourceMetrics,
|
|
472
|
+
scopeMetrics: filteredScopes
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
export(metrics2, resultCallback) {
|
|
476
|
+
const filtered = this.filterMetrics(metrics2);
|
|
477
|
+
if (filtered) {
|
|
478
|
+
console.log(formatMetrics(filtered));
|
|
479
|
+
}
|
|
480
|
+
resultCallback({ code: import_core3.ExportResultCode.SUCCESS });
|
|
481
|
+
}
|
|
482
|
+
shutdown() {
|
|
483
|
+
return Promise.resolve();
|
|
484
|
+
}
|
|
485
|
+
forceFlush() {
|
|
486
|
+
return Promise.resolve();
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
function globToRegex(glob) {
|
|
490
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
491
|
+
const regex = `^${escaped.replace(/\*/g, ".*")}$`;
|
|
492
|
+
return new RegExp(regex);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/loggers/tree-span-processor.ts
|
|
496
|
+
var TreeSpanProcessor = class {
|
|
497
|
+
exporter;
|
|
498
|
+
orphans = /* @__PURE__ */ new Map();
|
|
499
|
+
constructor(exporter) {
|
|
500
|
+
this.exporter = exporter;
|
|
501
|
+
}
|
|
502
|
+
onStart() {
|
|
503
|
+
}
|
|
504
|
+
onEnd(span) {
|
|
505
|
+
const parentId = span.parentSpanContext?.spanId;
|
|
506
|
+
if (parentId) {
|
|
507
|
+
const siblings = this.orphans.get(parentId) || [];
|
|
508
|
+
this.orphans.set(parentId, [...siblings, span]);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const children = this.getChildrenRecursively(span);
|
|
512
|
+
const sorted = [span, ...children].sort((s1, s2) => {
|
|
513
|
+
const [sec1, nano1] = s1.startTime;
|
|
514
|
+
const [sec2, nano2] = s2.startTime;
|
|
515
|
+
if (sec1 !== sec2) return sec1 - sec2;
|
|
516
|
+
return nano1 - nano2;
|
|
517
|
+
});
|
|
518
|
+
this.exporter.export(sorted, () => {
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
getChildrenRecursively(span) {
|
|
522
|
+
const spanId = span.spanContext().spanId;
|
|
523
|
+
const children = this.orphans.get(spanId) || [];
|
|
524
|
+
this.orphans.delete(spanId);
|
|
525
|
+
const result = [...children];
|
|
526
|
+
for (const child of children) {
|
|
527
|
+
result.push(...this.getChildrenRecursively(child));
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
shutdown() {
|
|
532
|
+
return this.exporter.shutdown();
|
|
533
|
+
}
|
|
534
|
+
async forceFlush() {
|
|
535
|
+
await this.exporter.forceFlush?.();
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/providers.ts
|
|
540
|
+
var getLogProvider = (resource, otlpEndpoint) => {
|
|
541
|
+
const exporter = otlpEndpoint ? new import_exporter_logs_otlp_http.OTLPLogExporter({ url: `${otlpEndpoint}/v1/logs` }) : new ConsoleLogPrettyExporter();
|
|
542
|
+
const processor = otlpEndpoint ? new import_sdk_logs.BatchLogRecordProcessor(exporter) : new import_sdk_logs.SimpleLogRecordProcessor(exporter);
|
|
543
|
+
const provider = new import_sdk_logs.LoggerProvider({
|
|
544
|
+
resource,
|
|
545
|
+
processors: [processor]
|
|
546
|
+
});
|
|
547
|
+
return provider;
|
|
548
|
+
};
|
|
549
|
+
var getSpanProcessor = (otlpEndpoint) => {
|
|
550
|
+
const exporter = otlpEndpoint ? new import_exporter_trace_otlp_http.OTLPTraceExporter({
|
|
551
|
+
url: `${otlpEndpoint}/v1/traces`
|
|
552
|
+
}) : new ConsoleSpanPrettyExporter();
|
|
553
|
+
const processor = otlpEndpoint ? new import_sdk_trace_node.BatchSpanProcessor(exporter) : new TreeSpanProcessor(exporter);
|
|
554
|
+
return processor;
|
|
555
|
+
};
|
|
556
|
+
var getMetricReader = (otlpEndpoint) => {
|
|
557
|
+
const metricExporter = otlpEndpoint ? new import_exporter_metrics_otlp_http.OTLPMetricExporter({
|
|
558
|
+
url: `${otlpEndpoint}/v1/metrics`
|
|
559
|
+
}) : new ConsoleMetricPrettyExporter();
|
|
560
|
+
const metricReader = new import_sdk_metrics.PeriodicExportingMetricReader({
|
|
561
|
+
exporter: metricExporter
|
|
562
|
+
});
|
|
563
|
+
return metricReader;
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// src/resource.ts
|
|
567
|
+
var import_resources = require("@opentelemetry/resources");
|
|
90
568
|
|
|
91
569
|
// src/otel-context.ts
|
|
92
|
-
|
|
570
|
+
var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
|
|
571
|
+
function detectTelemetryContext(componentNameOverride) {
|
|
93
572
|
const {
|
|
94
573
|
OTEL_SERVICE_NAME,
|
|
574
|
+
// e.g. "UserSystem"
|
|
95
575
|
OTEL_SERVICE_VERSION,
|
|
576
|
+
// e.g. "1.2.3"
|
|
96
577
|
K_SERVICE,
|
|
97
578
|
K_REVISION,
|
|
98
579
|
K_CONFIGURATION,
|
|
@@ -102,13 +583,14 @@ function detectTelemetryContext(serviceOverride) {
|
|
|
102
583
|
GCP_PROJECT,
|
|
103
584
|
CLOUD_PROVIDER
|
|
104
585
|
} = process.env;
|
|
105
|
-
const
|
|
106
|
-
const
|
|
586
|
+
const systemName = OTEL_SERVICE_NAME || "unknown-service";
|
|
587
|
+
const systemVersion = OTEL_SERVICE_VERSION || "1.0.0";
|
|
588
|
+
const componentName = componentNameOverride || void 0;
|
|
107
589
|
const resourceAttributes = {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"serviceContext.service":
|
|
111
|
-
"serviceContext.version":
|
|
590
|
+
[import_semantic_conventions2.ATTR_SERVICE_NAME]: systemName,
|
|
591
|
+
[import_semantic_conventions2.ATTR_SERVICE_VERSION]: systemVersion,
|
|
592
|
+
"serviceContext.service": systemName,
|
|
593
|
+
"serviceContext.version": systemVersion,
|
|
112
594
|
...K_SERVICE && { "cloud.run.service": K_SERVICE },
|
|
113
595
|
...K_REVISION && { "cloud.run.revision": K_REVISION },
|
|
114
596
|
...K_CONFIGURATION && { "cloud.run.configuration": K_CONFIGURATION },
|
|
@@ -116,28 +598,80 @@ function detectTelemetryContext(serviceOverride) {
|
|
|
116
598
|
...POD_NAMESPACE && { "k8s.namespace_name": POD_NAMESPACE },
|
|
117
599
|
...KUBERNETES_SERVICE_HOST && { "cloud.orchestrator": "kubernetes" },
|
|
118
600
|
...GCP_PROJECT && { "cloud.account.id": GCP_PROJECT },
|
|
119
|
-
...CLOUD_PROVIDER && { "cloud.provider": CLOUD_PROVIDER }
|
|
601
|
+
...CLOUD_PROVIDER && { "cloud.provider": CLOUD_PROVIDER },
|
|
602
|
+
...componentName && { "component.name": componentName }
|
|
120
603
|
};
|
|
121
604
|
return {
|
|
122
|
-
|
|
123
|
-
|
|
605
|
+
systemName,
|
|
606
|
+
systemVersion,
|
|
607
|
+
componentName,
|
|
124
608
|
resourceAttributes
|
|
125
609
|
};
|
|
126
610
|
}
|
|
127
611
|
|
|
612
|
+
// src/resource.ts
|
|
613
|
+
var getResource = async () => {
|
|
614
|
+
const baseRes = await (0, import_resources.detectResources)();
|
|
615
|
+
const { resourceAttributes } = detectTelemetryContext();
|
|
616
|
+
const customRes = (0, import_resources.resourceFromAttributes)(resourceAttributes);
|
|
617
|
+
const resource = baseRes.merge(customRes);
|
|
618
|
+
return resource;
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// src/otel.ts
|
|
622
|
+
import_api3.diag.setLogger(new import_api3.DiagConsoleLogger(), import_api3.DiagLogLevel.ERROR);
|
|
623
|
+
var isInitialized = false;
|
|
624
|
+
async function initialize() {
|
|
625
|
+
if (isInitialized) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
isInitialized = true;
|
|
629
|
+
try {
|
|
630
|
+
const serviceName = process.env.OTEL_SERVICE_NAME ?? "unknown-service";
|
|
631
|
+
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
632
|
+
const resource = await getResource();
|
|
633
|
+
const contextManager = new import_context_async_hooks.AsyncLocalStorageContextManager().enable();
|
|
634
|
+
import_api3.context.setGlobalContextManager(contextManager);
|
|
635
|
+
const logProvider = getLogProvider(resource, otlpEndpoint);
|
|
636
|
+
import_api_logs.logs.setGlobalLoggerProvider(logProvider);
|
|
637
|
+
const spanProcessor = getSpanProcessor(otlpEndpoint);
|
|
638
|
+
const metricReader = getMetricReader(otlpEndpoint);
|
|
639
|
+
const sdk = new import_sdk_node.NodeSDK({
|
|
640
|
+
contextManager,
|
|
641
|
+
spanProcessor,
|
|
642
|
+
metricReader,
|
|
643
|
+
instrumentations: [(0, import_auto_instrumentations_node.getNodeAutoInstrumentations)()],
|
|
644
|
+
resource
|
|
645
|
+
});
|
|
646
|
+
await sdk.start();
|
|
647
|
+
console.log(`[otel] Telemetry initialized for "${serviceName}"`);
|
|
648
|
+
process.on("SIGTERM", async () => {
|
|
649
|
+
console.log("[otel] Shutting down...");
|
|
650
|
+
await Promise.all([sdk.shutdown(), logProvider.shutdown()]);
|
|
651
|
+
console.log("[otel] Shutdown complete.");
|
|
652
|
+
process.exit(0);
|
|
653
|
+
});
|
|
654
|
+
} catch (err) {
|
|
655
|
+
console.error("[otel] Startup error:", err);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
128
659
|
// src/logger.ts
|
|
660
|
+
var import_api4 = require("@opentelemetry/api");
|
|
661
|
+
var import_api_logs2 = require("@opentelemetry/api-logs");
|
|
129
662
|
function getLogger(serviceOverride, extraAttrs = {}) {
|
|
130
|
-
const {
|
|
131
|
-
const logger = import_api_logs2.logs.getLogger(
|
|
663
|
+
const { systemName, systemVersion, resourceAttributes } = detectTelemetryContext(serviceOverride);
|
|
664
|
+
const logger = import_api_logs2.logs.getLogger(systemName, systemVersion);
|
|
132
665
|
const defaultAttrs = {
|
|
133
666
|
...resourceAttributes,
|
|
134
667
|
...extraAttrs
|
|
135
668
|
};
|
|
136
669
|
function emit(severityText, body, attrs = {}) {
|
|
137
|
-
const span =
|
|
670
|
+
const span = import_api4.trace.getSpan(import_api4.context.active());
|
|
138
671
|
const spanContext = span?.spanContext();
|
|
139
672
|
logger.emit({
|
|
140
673
|
severityText,
|
|
674
|
+
severityNumber: LOG_SEVERITY_MAP[severityText],
|
|
141
675
|
body,
|
|
142
676
|
attributes: {
|
|
143
677
|
...defaultAttrs,
|
|
@@ -165,30 +699,40 @@ function getLogger(serviceOverride, extraAttrs = {}) {
|
|
|
165
699
|
}
|
|
166
700
|
|
|
167
701
|
// src/metrics.ts
|
|
168
|
-
var
|
|
169
|
-
function getMeter(
|
|
170
|
-
const {
|
|
171
|
-
|
|
702
|
+
var import_api5 = require("@opentelemetry/api");
|
|
703
|
+
function getMeter(componentNameOverride) {
|
|
704
|
+
const { componentName, systemName, systemVersion } = detectTelemetryContext(
|
|
705
|
+
componentNameOverride
|
|
706
|
+
);
|
|
707
|
+
return import_api5.metrics.getMeter(componentName ?? systemName, systemVersion);
|
|
172
708
|
}
|
|
173
709
|
|
|
174
710
|
// src/tracer.ts
|
|
175
|
-
var
|
|
176
|
-
function getTracer(
|
|
177
|
-
const {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
711
|
+
var import_api6 = require("@opentelemetry/api");
|
|
712
|
+
function getTracer(componentNameOverride) {
|
|
713
|
+
const { componentName, systemName, systemVersion } = detectTelemetryContext(
|
|
714
|
+
componentNameOverride
|
|
715
|
+
);
|
|
716
|
+
const tracer = import_api6.trace.getTracer(
|
|
717
|
+
componentName ?? systemName,
|
|
718
|
+
systemVersion
|
|
719
|
+
);
|
|
720
|
+
const withTrace = async (name, spanOptionsSpanOrFunc, spanOrFunc, func) => {
|
|
721
|
+
const { options, parent, fn } = extractArgs(
|
|
722
|
+
spanOptionsSpanOrFunc,
|
|
723
|
+
spanOrFunc,
|
|
724
|
+
func
|
|
725
|
+
);
|
|
726
|
+
const parentContext = parent ? import_api6.trace.setSpan(import_api6.context.active(), parent) : import_api6.context.active();
|
|
727
|
+
const span = tracer.startSpan(name, options, parentContext);
|
|
728
|
+
return await import_api6.context.with(import_api6.trace.setSpan(parentContext, span), async () => {
|
|
185
729
|
try {
|
|
186
730
|
const result = await fn(span);
|
|
187
|
-
span.setStatus({ code:
|
|
731
|
+
span.setStatus({ code: import_api6.SpanStatusCode.OK });
|
|
188
732
|
return result;
|
|
189
733
|
} catch (err) {
|
|
190
734
|
const error = err;
|
|
191
|
-
span.setStatus({ code:
|
|
735
|
+
span.setStatus({ code: import_api6.SpanStatusCode.ERROR, message: error.message });
|
|
192
736
|
span.recordException?.(error);
|
|
193
737
|
throw err;
|
|
194
738
|
} finally {
|
|
@@ -196,19 +740,22 @@ function getTracer(serviceOverride) {
|
|
|
196
740
|
}
|
|
197
741
|
});
|
|
198
742
|
};
|
|
199
|
-
const withTraceSync = (name,
|
|
200
|
-
const options
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
743
|
+
const withTraceSync = (name, spanOptionsSpanOrFunc, spanOrFunc, func) => {
|
|
744
|
+
const { options, parent, fn } = extractArgs(
|
|
745
|
+
spanOptionsSpanOrFunc,
|
|
746
|
+
spanOrFunc,
|
|
747
|
+
func
|
|
748
|
+
);
|
|
749
|
+
const parentContext = parent ? import_api6.trace.setSpan(import_api6.context.active(), parent) : import_api6.context.active();
|
|
750
|
+
const span = tracer.startSpan(name, options, parentContext);
|
|
751
|
+
return import_api6.context.with(import_api6.trace.setSpan(parentContext, span), () => {
|
|
205
752
|
try {
|
|
206
753
|
const result = fn(span);
|
|
207
|
-
span.setStatus({ code:
|
|
754
|
+
span.setStatus({ code: import_api6.SpanStatusCode.OK });
|
|
208
755
|
return result;
|
|
209
756
|
} catch (err) {
|
|
210
757
|
const error = err;
|
|
211
|
-
span.setStatus({ code:
|
|
758
|
+
span.setStatus({ code: import_api6.SpanStatusCode.ERROR, message: error.message });
|
|
212
759
|
span.recordException?.(error);
|
|
213
760
|
throw err;
|
|
214
761
|
} finally {
|
|
@@ -220,6 +767,30 @@ function getTracer(serviceOverride) {
|
|
|
220
767
|
tracer.withTraceSync = withTraceSync;
|
|
221
768
|
return tracer;
|
|
222
769
|
}
|
|
770
|
+
function extractArgs(spanOptionsSpanOrFunc, spanOrFunc, func) {
|
|
771
|
+
let options = {};
|
|
772
|
+
let parent;
|
|
773
|
+
let fn;
|
|
774
|
+
if (typeof spanOptionsSpanOrFunc === "function") {
|
|
775
|
+
fn = spanOptionsSpanOrFunc;
|
|
776
|
+
} else if (typeof spanOrFunc === "function") {
|
|
777
|
+
const spanOrSpanOptions = spanOptionsSpanOrFunc;
|
|
778
|
+
if ("startTime" in spanOrSpanOptions || "attributes" in spanOrSpanOptions || "kind" in spanOrSpanOptions) {
|
|
779
|
+
options = spanOrSpanOptions;
|
|
780
|
+
} else {
|
|
781
|
+
parent = spanOrSpanOptions;
|
|
782
|
+
}
|
|
783
|
+
fn = spanOrFunc;
|
|
784
|
+
} else {
|
|
785
|
+
options = spanOptionsSpanOrFunc;
|
|
786
|
+
parent = spanOrFunc;
|
|
787
|
+
fn = func;
|
|
788
|
+
}
|
|
789
|
+
return { options, parent, fn };
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// src/index.ts
|
|
793
|
+
initialize();
|
|
223
794
|
// Annotate the CommonJS export names for ESM import in node:
|
|
224
795
|
0 && (module.exports = {
|
|
225
796
|
getLogger,
|