@sebspark/otel 0.1.0 → 0.2.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 +144 -81
- package/dist/index.d.mts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +651 -85
- package/dist/index.mjs +656 -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,75 @@ 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
|
+
async function initialize() {
|
|
624
|
+
try {
|
|
625
|
+
const serviceName = process.env.OTEL_SERVICE_NAME ?? "unknown-service";
|
|
626
|
+
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
627
|
+
const resource = await getResource();
|
|
628
|
+
import_api3.context.setGlobalContextManager(
|
|
629
|
+
new import_context_async_hooks.AsyncLocalStorageContextManager().enable()
|
|
630
|
+
);
|
|
631
|
+
const logProvider = getLogProvider(resource, otlpEndpoint);
|
|
632
|
+
import_api_logs.logs.setGlobalLoggerProvider(logProvider);
|
|
633
|
+
const spanProcessor = getSpanProcessor(otlpEndpoint);
|
|
634
|
+
const metricReader = getMetricReader(otlpEndpoint);
|
|
635
|
+
const sdk = new import_sdk_node.NodeSDK({
|
|
636
|
+
spanProcessor,
|
|
637
|
+
metricReader,
|
|
638
|
+
instrumentations: [(0, import_auto_instrumentations_node.getNodeAutoInstrumentations)()],
|
|
639
|
+
resource
|
|
640
|
+
});
|
|
641
|
+
await sdk.start();
|
|
642
|
+
console.log(`[otel] Telemetry initialized for "${serviceName}"`);
|
|
643
|
+
process.on("SIGTERM", async () => {
|
|
644
|
+
console.log("[otel] Shutting down...");
|
|
645
|
+
await Promise.all([sdk.shutdown(), logProvider.shutdown()]);
|
|
646
|
+
console.log("[otel] Shutdown complete.");
|
|
647
|
+
process.exit(0);
|
|
648
|
+
});
|
|
649
|
+
} catch (err) {
|
|
650
|
+
console.error("[otel] Startup error:", err);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
128
654
|
// src/logger.ts
|
|
655
|
+
var import_api4 = require("@opentelemetry/api");
|
|
656
|
+
var import_api_logs2 = require("@opentelemetry/api-logs");
|
|
129
657
|
function getLogger(serviceOverride, extraAttrs = {}) {
|
|
130
|
-
const {
|
|
131
|
-
const logger = import_api_logs2.logs.getLogger(
|
|
658
|
+
const { systemName, systemVersion, resourceAttributes } = detectTelemetryContext(serviceOverride);
|
|
659
|
+
const logger = import_api_logs2.logs.getLogger(systemName, systemVersion);
|
|
132
660
|
const defaultAttrs = {
|
|
133
661
|
...resourceAttributes,
|
|
134
662
|
...extraAttrs
|
|
135
663
|
};
|
|
136
664
|
function emit(severityText, body, attrs = {}) {
|
|
137
|
-
const span =
|
|
665
|
+
const span = import_api4.trace.getSpan(import_api4.context.active());
|
|
138
666
|
const spanContext = span?.spanContext();
|
|
139
667
|
logger.emit({
|
|
140
668
|
severityText,
|
|
669
|
+
severityNumber: LOG_SEVERITY_MAP[severityText],
|
|
141
670
|
body,
|
|
142
671
|
attributes: {
|
|
143
672
|
...defaultAttrs,
|
|
@@ -165,30 +694,40 @@ function getLogger(serviceOverride, extraAttrs = {}) {
|
|
|
165
694
|
}
|
|
166
695
|
|
|
167
696
|
// src/metrics.ts
|
|
168
|
-
var
|
|
169
|
-
function getMeter(
|
|
170
|
-
const {
|
|
171
|
-
|
|
697
|
+
var import_api5 = require("@opentelemetry/api");
|
|
698
|
+
function getMeter(componentNameOverride) {
|
|
699
|
+
const { componentName, systemName, systemVersion } = detectTelemetryContext(
|
|
700
|
+
componentNameOverride
|
|
701
|
+
);
|
|
702
|
+
return import_api5.metrics.getMeter(componentName ?? systemName, systemVersion);
|
|
172
703
|
}
|
|
173
704
|
|
|
174
705
|
// src/tracer.ts
|
|
175
|
-
var
|
|
176
|
-
function getTracer(
|
|
177
|
-
const {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
706
|
+
var import_api6 = require("@opentelemetry/api");
|
|
707
|
+
function getTracer(componentNameOverride) {
|
|
708
|
+
const { componentName, systemName, systemVersion } = detectTelemetryContext(
|
|
709
|
+
componentNameOverride
|
|
710
|
+
);
|
|
711
|
+
const tracer = import_api6.trace.getTracer(
|
|
712
|
+
componentName ?? systemName,
|
|
713
|
+
systemVersion
|
|
714
|
+
);
|
|
715
|
+
const withTrace = async (name, spanOptionsSpanOrFunc, spanOrFunc, func) => {
|
|
716
|
+
const { options, parent, fn } = extractArgs(
|
|
717
|
+
spanOptionsSpanOrFunc,
|
|
718
|
+
spanOrFunc,
|
|
719
|
+
func
|
|
720
|
+
);
|
|
721
|
+
const parentContext = parent ? import_api6.trace.setSpan(import_api6.context.active(), parent) : import_api6.context.active();
|
|
722
|
+
const span = tracer.startSpan(name, options, parentContext);
|
|
723
|
+
return await import_api6.context.with(import_api6.trace.setSpan(parentContext, span), async () => {
|
|
185
724
|
try {
|
|
186
725
|
const result = await fn(span);
|
|
187
|
-
span.setStatus({ code:
|
|
726
|
+
span.setStatus({ code: import_api6.SpanStatusCode.OK });
|
|
188
727
|
return result;
|
|
189
728
|
} catch (err) {
|
|
190
729
|
const error = err;
|
|
191
|
-
span.setStatus({ code:
|
|
730
|
+
span.setStatus({ code: import_api6.SpanStatusCode.ERROR, message: error.message });
|
|
192
731
|
span.recordException?.(error);
|
|
193
732
|
throw err;
|
|
194
733
|
} finally {
|
|
@@ -196,19 +735,22 @@ function getTracer(serviceOverride) {
|
|
|
196
735
|
}
|
|
197
736
|
});
|
|
198
737
|
};
|
|
199
|
-
const withTraceSync = (name,
|
|
200
|
-
const options
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
738
|
+
const withTraceSync = (name, spanOptionsSpanOrFunc, spanOrFunc, func) => {
|
|
739
|
+
const { options, parent, fn } = extractArgs(
|
|
740
|
+
spanOptionsSpanOrFunc,
|
|
741
|
+
spanOrFunc,
|
|
742
|
+
func
|
|
743
|
+
);
|
|
744
|
+
const parentContext = parent ? import_api6.trace.setSpan(import_api6.context.active(), parent) : import_api6.context.active();
|
|
745
|
+
const span = tracer.startSpan(name, options, parentContext);
|
|
746
|
+
return import_api6.context.with(import_api6.trace.setSpan(parentContext, span), () => {
|
|
205
747
|
try {
|
|
206
748
|
const result = fn(span);
|
|
207
|
-
span.setStatus({ code:
|
|
749
|
+
span.setStatus({ code: import_api6.SpanStatusCode.OK });
|
|
208
750
|
return result;
|
|
209
751
|
} catch (err) {
|
|
210
752
|
const error = err;
|
|
211
|
-
span.setStatus({ code:
|
|
753
|
+
span.setStatus({ code: import_api6.SpanStatusCode.ERROR, message: error.message });
|
|
212
754
|
span.recordException?.(error);
|
|
213
755
|
throw err;
|
|
214
756
|
} finally {
|
|
@@ -220,6 +762,30 @@ function getTracer(serviceOverride) {
|
|
|
220
762
|
tracer.withTraceSync = withTraceSync;
|
|
221
763
|
return tracer;
|
|
222
764
|
}
|
|
765
|
+
function extractArgs(spanOptionsSpanOrFunc, spanOrFunc, func) {
|
|
766
|
+
let options = {};
|
|
767
|
+
let parent;
|
|
768
|
+
let fn;
|
|
769
|
+
if (typeof spanOptionsSpanOrFunc === "function") {
|
|
770
|
+
fn = spanOptionsSpanOrFunc;
|
|
771
|
+
} else if (typeof spanOrFunc === "function") {
|
|
772
|
+
const spanOrSpanOptions = spanOptionsSpanOrFunc;
|
|
773
|
+
if ("startTime" in spanOrSpanOptions || "attributes" in spanOrSpanOptions || "kind" in spanOrSpanOptions) {
|
|
774
|
+
options = spanOrSpanOptions;
|
|
775
|
+
} else {
|
|
776
|
+
parent = spanOrSpanOptions;
|
|
777
|
+
}
|
|
778
|
+
fn = spanOrFunc;
|
|
779
|
+
} else {
|
|
780
|
+
options = spanOptionsSpanOrFunc;
|
|
781
|
+
parent = spanOrFunc;
|
|
782
|
+
fn = func;
|
|
783
|
+
}
|
|
784
|
+
return { options, parent, fn };
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/index.ts
|
|
788
|
+
initialize();
|
|
223
789
|
// Annotate the CommonJS export names for ESM import in node:
|
|
224
790
|
0 && (module.exports = {
|
|
225
791
|
getLogger,
|