@blaxel/telemetry 0.2.25 → 0.2.26
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/dist/auth_refresh_exporters.d.ts +44 -0
- package/dist/auth_refresh_exporters.js +200 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -6
- package/dist/json_logger.js +90 -6
- package/dist/telemetry.d.ts +2 -13
- package/dist/telemetry.js +34 -52
- package/package.json +2 -2
- package/dist/legacy_logger.d.ts +0 -8
- package/dist/legacy_logger.js +0 -58
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
2
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
3
|
+
import { ResourceMetrics } from "@opentelemetry/sdk-metrics";
|
|
4
|
+
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-node";
|
|
5
|
+
/**
|
|
6
|
+
* SpanExporter that refreshes authentication before each export.
|
|
7
|
+
* This is necessary for long-running containers where tokens may expire.
|
|
8
|
+
*/
|
|
9
|
+
export declare class AuthRefreshingSpanExporter implements SpanExporter {
|
|
10
|
+
private createExporter;
|
|
11
|
+
constructor(createExporter: () => SpanExporter);
|
|
12
|
+
private currentExporter;
|
|
13
|
+
export(spans: ReadableSpan[], resultCallback: (result: {
|
|
14
|
+
code: number;
|
|
15
|
+
error?: Error;
|
|
16
|
+
}) => void): void;
|
|
17
|
+
private doExport;
|
|
18
|
+
shutdown(): Promise<void>;
|
|
19
|
+
forceFlush(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* MetricExporter that refreshes authentication before each export.
|
|
23
|
+
* This is necessary for long-running containers where tokens may expire.
|
|
24
|
+
*/
|
|
25
|
+
export declare class AuthRefreshingMetricExporter {
|
|
26
|
+
private createExporter;
|
|
27
|
+
constructor(createExporter: () => OTLPMetricExporter);
|
|
28
|
+
private currentExporter;
|
|
29
|
+
export(metrics: ResourceMetrics, resultCallback: (result: {
|
|
30
|
+
code: number;
|
|
31
|
+
error?: Error;
|
|
32
|
+
}) => void): void;
|
|
33
|
+
private doExport;
|
|
34
|
+
shutdown(): Promise<void>;
|
|
35
|
+
forceFlush(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates an OTLP Trace Exporter with the current auth headers
|
|
39
|
+
*/
|
|
40
|
+
export declare function createTraceExporter(): OTLPTraceExporter;
|
|
41
|
+
/**
|
|
42
|
+
* Creates an OTLP Metric Exporter with the current auth headers
|
|
43
|
+
*/
|
|
44
|
+
export declare function createMetricExporter(): OTLPMetricExporter;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthRefreshingMetricExporter = exports.AuthRefreshingSpanExporter = void 0;
|
|
4
|
+
exports.createTraceExporter = createTraceExporter;
|
|
5
|
+
exports.createMetricExporter = createMetricExporter;
|
|
6
|
+
const core_1 = require("@blaxel/core");
|
|
7
|
+
const exporter_metrics_otlp_http_1 = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
8
|
+
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
|
|
9
|
+
/**
|
|
10
|
+
* SpanExporter that refreshes authentication before each export.
|
|
11
|
+
* This is necessary for long-running containers where tokens may expire.
|
|
12
|
+
*/
|
|
13
|
+
class AuthRefreshingSpanExporter {
|
|
14
|
+
createExporter;
|
|
15
|
+
constructor(createExporter) {
|
|
16
|
+
this.createExporter = createExporter;
|
|
17
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Initialized");
|
|
18
|
+
}
|
|
19
|
+
currentExporter = null;
|
|
20
|
+
export(spans, resultCallback) {
|
|
21
|
+
core_1.logger.debug(`[AuthRefreshingSpanExporter] Exporting ${spans.length} spans`);
|
|
22
|
+
// Execute async operations but return void as required by interface
|
|
23
|
+
this.doExport(spans, resultCallback).catch(error => {
|
|
24
|
+
core_1.logger.error("[AuthRefreshingSpanExporter] Fatal error in export:", error);
|
|
25
|
+
resultCallback({ code: 1, error: error });
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async doExport(spans, resultCallback) {
|
|
29
|
+
try {
|
|
30
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Starting authentication refresh");
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
// Always refresh auth before export
|
|
33
|
+
await (0, core_1.authenticate)();
|
|
34
|
+
const authTime = Date.now() - startTime;
|
|
35
|
+
core_1.logger.debug(`[AuthRefreshingSpanExporter] Authentication completed in ${authTime}ms`);
|
|
36
|
+
// Log current auth status
|
|
37
|
+
if (core_1.settings.authorization) {
|
|
38
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Authorization token is present");
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
core_1.logger.warn("[AuthRefreshingSpanExporter] No authorization token after authentication!");
|
|
42
|
+
}
|
|
43
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Creating new exporter");
|
|
44
|
+
// Create new exporter with fresh headers
|
|
45
|
+
this.currentExporter = this.createExporter();
|
|
46
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] New exporter created with fresh auth headers");
|
|
47
|
+
// Export using the fresh exporter
|
|
48
|
+
if (this.currentExporter && this.currentExporter.export) {
|
|
49
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Calling export on fresh exporter");
|
|
50
|
+
this.currentExporter.export(spans, resultCallback);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const error = new Error('Exporter not initialized');
|
|
54
|
+
core_1.logger.error("[AuthRefreshingSpanExporter] Exporter not properly initialized", error);
|
|
55
|
+
resultCallback({ code: 1, error });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
core_1.logger.error("[AuthRefreshingSpanExporter] Error during authentication or export:", error);
|
|
60
|
+
core_1.logger.error("[AuthRefreshingSpanExporter] Error details:", {
|
|
61
|
+
message: error.message,
|
|
62
|
+
stack: error.stack
|
|
63
|
+
});
|
|
64
|
+
resultCallback({ code: 1, error: error });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async shutdown() {
|
|
68
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Shutting down");
|
|
69
|
+
if (this.currentExporter) {
|
|
70
|
+
return this.currentExporter.shutdown();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async forceFlush() {
|
|
74
|
+
core_1.logger.debug("[AuthRefreshingSpanExporter] Force flushing");
|
|
75
|
+
if (this.currentExporter && this.currentExporter.forceFlush) {
|
|
76
|
+
return this.currentExporter.forceFlush();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.AuthRefreshingSpanExporter = AuthRefreshingSpanExporter;
|
|
81
|
+
/**
|
|
82
|
+
* MetricExporter that refreshes authentication before each export.
|
|
83
|
+
* This is necessary for long-running containers where tokens may expire.
|
|
84
|
+
*/
|
|
85
|
+
class AuthRefreshingMetricExporter {
|
|
86
|
+
createExporter;
|
|
87
|
+
constructor(createExporter) {
|
|
88
|
+
this.createExporter = createExporter;
|
|
89
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Initialized");
|
|
90
|
+
}
|
|
91
|
+
currentExporter = null;
|
|
92
|
+
export(metrics, resultCallback) {
|
|
93
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Exporting metrics");
|
|
94
|
+
// Execute async operations but return void
|
|
95
|
+
this.doExport(metrics, resultCallback).catch(error => {
|
|
96
|
+
core_1.logger.error("[AuthRefreshingMetricExporter] Fatal error in export:", error);
|
|
97
|
+
resultCallback({ code: 1, error: error });
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async doExport(metrics, resultCallback) {
|
|
101
|
+
try {
|
|
102
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Starting authentication refresh");
|
|
103
|
+
const startTime = Date.now();
|
|
104
|
+
// Always refresh auth before export
|
|
105
|
+
await (0, core_1.authenticate)();
|
|
106
|
+
const authTime = Date.now() - startTime;
|
|
107
|
+
core_1.logger.debug(`[AuthRefreshingMetricExporter] Authentication completed in ${authTime}ms`);
|
|
108
|
+
// Log current auth status
|
|
109
|
+
if (core_1.settings.authorization) {
|
|
110
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Authorization token is present");
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
core_1.logger.warn("[AuthRefreshingMetricExporter] No authorization token after authentication!");
|
|
114
|
+
}
|
|
115
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Creating new exporter");
|
|
116
|
+
// Create new exporter with fresh headers
|
|
117
|
+
this.currentExporter = this.createExporter();
|
|
118
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] New exporter created with fresh auth headers");
|
|
119
|
+
// Export using the fresh exporter
|
|
120
|
+
if (this.currentExporter && this.currentExporter.export) {
|
|
121
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Calling export on fresh exporter");
|
|
122
|
+
this.currentExporter.export(metrics, resultCallback);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const error = new Error('Exporter not initialized');
|
|
126
|
+
core_1.logger.error("[AuthRefreshingMetricExporter] Exporter not properly initialized", error);
|
|
127
|
+
resultCallback({ code: 1, error });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
core_1.logger.error("[AuthRefreshingMetricExporter] Error during authentication or export:", error);
|
|
132
|
+
core_1.logger.error("[AuthRefreshingMetricExporter] Error details:", {
|
|
133
|
+
message: error.message,
|
|
134
|
+
stack: error.stack
|
|
135
|
+
});
|
|
136
|
+
resultCallback({ code: 1, error: error });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async shutdown() {
|
|
140
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Shutting down");
|
|
141
|
+
if (this.currentExporter) {
|
|
142
|
+
return this.currentExporter.shutdown();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async forceFlush() {
|
|
146
|
+
core_1.logger.debug("[AuthRefreshingMetricExporter] Force flushing");
|
|
147
|
+
if (this.currentExporter && this.currentExporter.forceFlush) {
|
|
148
|
+
return this.currentExporter.forceFlush();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.AuthRefreshingMetricExporter = AuthRefreshingMetricExporter;
|
|
153
|
+
/**
|
|
154
|
+
* Creates an OTLP Trace Exporter with the current auth headers
|
|
155
|
+
*/
|
|
156
|
+
function createTraceExporter() {
|
|
157
|
+
const headers = {};
|
|
158
|
+
if (core_1.settings.authorization) {
|
|
159
|
+
headers["x-blaxel-authorization"] = core_1.settings.authorization;
|
|
160
|
+
core_1.logger.debug("[createTraceExporter] Added authorization header");
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
core_1.logger.warn("[createTraceExporter] No authorization available");
|
|
164
|
+
}
|
|
165
|
+
if (core_1.settings.workspace) {
|
|
166
|
+
headers["x-blaxel-workspace"] = core_1.settings.workspace;
|
|
167
|
+
core_1.logger.debug(`[createTraceExporter] Added workspace header: ${core_1.settings.workspace}`);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
core_1.logger.warn("[createTraceExporter] No workspace available");
|
|
171
|
+
}
|
|
172
|
+
core_1.logger.debug("[createTraceExporter] Creating OTLPTraceExporter with headers:", Object.keys(headers));
|
|
173
|
+
return new exporter_trace_otlp_http_1.OTLPTraceExporter({
|
|
174
|
+
headers,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Creates an OTLP Metric Exporter with the current auth headers
|
|
179
|
+
*/
|
|
180
|
+
function createMetricExporter() {
|
|
181
|
+
const headers = {};
|
|
182
|
+
if (core_1.settings.authorization) {
|
|
183
|
+
headers["x-blaxel-authorization"] = core_1.settings.authorization;
|
|
184
|
+
core_1.logger.debug("[createMetricExporter] Added authorization header");
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
core_1.logger.warn("[createMetricExporter] No authorization available");
|
|
188
|
+
}
|
|
189
|
+
if (core_1.settings.workspace) {
|
|
190
|
+
headers["x-blaxel-workspace"] = core_1.settings.workspace;
|
|
191
|
+
core_1.logger.debug(`[createMetricExporter] Added workspace header: ${core_1.settings.workspace}`);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
core_1.logger.warn("[createMetricExporter] No workspace available");
|
|
195
|
+
}
|
|
196
|
+
core_1.logger.debug("[createMetricExporter] Creating OTLPMetricExporter with headers:", Object.keys(headers));
|
|
197
|
+
return new exporter_metrics_otlp_http_1.OTLPMetricExporter({
|
|
198
|
+
headers,
|
|
199
|
+
});
|
|
200
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.blaxelTelemetry = void 0;
|
|
3
|
+
exports.blaxelTelemetry = exports.setJsonLogger = exports.createTraceExporter = exports.createMetricExporter = exports.AuthRefreshingSpanExporter = exports.AuthRefreshingMetricExporter = void 0;
|
|
4
4
|
const core_1 = require("@blaxel/core");
|
|
5
5
|
const json_logger_1 = require("./json_logger");
|
|
6
|
-
const legacy_logger_1 = require("./legacy_logger");
|
|
7
6
|
const telemetry_1 = require("./telemetry");
|
|
8
7
|
Object.defineProperty(exports, "blaxelTelemetry", { enumerable: true, get: function () { return telemetry_1.blaxelTelemetry; } });
|
|
9
8
|
telemetry_1.blaxelTelemetry.initialize();
|
|
10
|
-
if (core_1.settings.loggerType === "
|
|
11
|
-
(0, legacy_logger_1.setLegacyLogger)();
|
|
12
|
-
}
|
|
13
|
-
else if (core_1.settings.loggerType === "json") {
|
|
9
|
+
if (core_1.settings.loggerType === "json") {
|
|
14
10
|
(0, json_logger_1.setJsonLogger)();
|
|
15
11
|
}
|
|
12
|
+
var auth_refresh_exporters_1 = require("./auth_refresh_exporters");
|
|
13
|
+
Object.defineProperty(exports, "AuthRefreshingMetricExporter", { enumerable: true, get: function () { return auth_refresh_exporters_1.AuthRefreshingMetricExporter; } });
|
|
14
|
+
Object.defineProperty(exports, "AuthRefreshingSpanExporter", { enumerable: true, get: function () { return auth_refresh_exporters_1.AuthRefreshingSpanExporter; } });
|
|
15
|
+
Object.defineProperty(exports, "createMetricExporter", { enumerable: true, get: function () { return auth_refresh_exporters_1.createMetricExporter; } });
|
|
16
|
+
Object.defineProperty(exports, "createTraceExporter", { enumerable: true, get: function () { return auth_refresh_exporters_1.createTraceExporter; } });
|
|
17
|
+
var json_logger_2 = require("./json_logger");
|
|
18
|
+
Object.defineProperty(exports, "setJsonLogger", { enumerable: true, get: function () { return json_logger_2.setJsonLogger; } });
|
package/dist/json_logger.js
CHANGED
|
@@ -43,16 +43,83 @@ const taskIndex = core_1.env.BL_TASK_KEY || 'TASK_INDEX';
|
|
|
43
43
|
const taskPrefix = core_1.env.BL_TASK_PREFIX || '';
|
|
44
44
|
const executionKey = core_1.env.BL_EXECUTION_KEY || 'BL_EXECUTION_ID';
|
|
45
45
|
const executionPrefix = core_1.env.BL_EXECUTION_PREFIX || '';
|
|
46
|
+
// Validate environment variables to prevent issues
|
|
47
|
+
function validateEnvVar(value, defaultValue, varName) {
|
|
48
|
+
if (!value || value.trim() === '') {
|
|
49
|
+
exports.originalLogger.warn(`Warning: ${varName} environment variable is empty, using default: ${defaultValue}`);
|
|
50
|
+
return defaultValue;
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
const validatedLabelsName = validateEnvVar(labelsName, 'labels', 'BL_LOGGER_LABELS');
|
|
55
|
+
// Enhanced error serialization to capture all properties
|
|
56
|
+
function serializeError(error) {
|
|
57
|
+
const serialized = {
|
|
58
|
+
message: error.message,
|
|
59
|
+
name: error.name,
|
|
60
|
+
stack: error.stack
|
|
61
|
+
};
|
|
62
|
+
// Capture any additional properties on the error object
|
|
63
|
+
for (const key of Object.keys(error)) {
|
|
64
|
+
if (!(key in serialized)) {
|
|
65
|
+
try {
|
|
66
|
+
const value = error[key];
|
|
67
|
+
// Avoid circular references by limiting depth
|
|
68
|
+
serialized[key] = typeof value === 'object' ? (0, core_1.stringify)(value, 2) : value;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
serialized[key] = '[Unserializable]';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return serialized;
|
|
76
|
+
}
|
|
77
|
+
// Enhanced stringify function with better error handling
|
|
78
|
+
function enhancedStringify(obj, maxDepth = 2) {
|
|
79
|
+
if (obj instanceof Error) {
|
|
80
|
+
return JSON.stringify(serializeError(obj));
|
|
81
|
+
}
|
|
82
|
+
// Handle circular references by using a simple set to track seen objects
|
|
83
|
+
const seen = new WeakSet();
|
|
84
|
+
const stringifyWithCircularCheck = (value, depth = 0) => {
|
|
85
|
+
if (value === null || value === undefined)
|
|
86
|
+
return value;
|
|
87
|
+
if (typeof value !== 'object')
|
|
88
|
+
return value;
|
|
89
|
+
if (seen.has(value)) {
|
|
90
|
+
return '[Circular Reference]';
|
|
91
|
+
}
|
|
92
|
+
if (depth >= maxDepth) {
|
|
93
|
+
return Array.isArray(value) ? '[Array]' : '[Object]';
|
|
94
|
+
}
|
|
95
|
+
seen.add(value);
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value.map(item => stringifyWithCircularCheck(item, depth + 1));
|
|
98
|
+
}
|
|
99
|
+
const result = {};
|
|
100
|
+
for (const [key, val] of Object.entries(value)) {
|
|
101
|
+
result[key] = stringifyWithCircularCheck(val, depth + 1);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
};
|
|
105
|
+
try {
|
|
106
|
+
const processed = stringifyWithCircularCheck(obj);
|
|
107
|
+
return JSON.stringify(processed);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return (0, core_1.stringify)(obj, maxDepth);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
46
113
|
// Format a log message with appropriate color and prefix
|
|
47
114
|
function formatLogMessage(severity, message, args) {
|
|
48
|
-
const messageStr = typeof message === "string" ? message : (
|
|
49
|
-
const argsStr = args.map(arg => typeof arg === "string" ? arg : (
|
|
115
|
+
const messageStr = typeof message === "string" ? message : enhancedStringify(message, 2);
|
|
116
|
+
const argsStr = args.map(arg => typeof arg === "string" ? arg : enhancedStringify(arg, 2)).join(" ");
|
|
50
117
|
const msg = `${messageStr}${argsStr ? " " + argsStr : ""}`;
|
|
51
118
|
const logEntry = {
|
|
52
119
|
message: msg,
|
|
53
120
|
severity
|
|
54
121
|
};
|
|
55
|
-
logEntry[
|
|
122
|
+
logEntry[validatedLabelsName] = {};
|
|
56
123
|
const currentSpan = api_1.trace.getActiveSpan();
|
|
57
124
|
if (currentSpan) {
|
|
58
125
|
const { traceId, spanId } = currentSpan.spanContext();
|
|
@@ -61,11 +128,28 @@ function formatLogMessage(severity, message, args) {
|
|
|
61
128
|
}
|
|
62
129
|
const taskId = core_1.env[taskIndex] || null;
|
|
63
130
|
if (taskId) {
|
|
64
|
-
logEntry[
|
|
131
|
+
logEntry[validatedLabelsName]['blaxel-task'] = `${taskPrefix}${taskId}`;
|
|
65
132
|
}
|
|
66
133
|
const executionId = core_1.env[executionKey] || null;
|
|
67
134
|
if (executionId) {
|
|
68
|
-
logEntry[
|
|
135
|
+
logEntry[validatedLabelsName]['blaxel-execution'] = `${executionPrefix}${executionId.split('-').pop()}`;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return JSON.stringify(logEntry);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// Fallback for serialization errors
|
|
142
|
+
const fallbackEntry = {
|
|
143
|
+
message: `JSON serialization failed: ${msg}`,
|
|
144
|
+
severity,
|
|
145
|
+
error: error instanceof Error ? error.message : String(error)
|
|
146
|
+
};
|
|
147
|
+
try {
|
|
148
|
+
return JSON.stringify(fallbackEntry);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Last resort fallback
|
|
152
|
+
return `{"message":"${severity}: ${msg.replace(/"/g, '\\"')}","severity":"${severity}","error":"Failed to serialize log entry"}`;
|
|
153
|
+
}
|
|
69
154
|
}
|
|
70
|
-
return JSON.stringify(logEntry);
|
|
71
155
|
}
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { Span } from "@opentelemetry/api";
|
|
2
|
-
import { Logger } from "@opentelemetry/api-logs";
|
|
3
|
-
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
4
|
-
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
5
|
-
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
6
2
|
import { RawResourceAttribute, Resource } from "@opentelemetry/resources";
|
|
7
3
|
import { SpanProcessor } from "@opentelemetry/sdk-trace-node";
|
|
8
4
|
export declare class BlaxelResource implements Resource {
|
|
@@ -28,8 +24,6 @@ export type TelemetryOptions = {
|
|
|
28
24
|
declare class TelemetryManager {
|
|
29
25
|
private nodeTracerProvider;
|
|
30
26
|
private meterProvider;
|
|
31
|
-
private loggerProvider;
|
|
32
|
-
private otelLogger;
|
|
33
27
|
private initialized;
|
|
34
28
|
private configured;
|
|
35
29
|
constructor();
|
|
@@ -40,7 +34,6 @@ declare class TelemetryManager {
|
|
|
40
34
|
get authHeaders(): Record<string, string>;
|
|
41
35
|
sleep(ms: number): Promise<unknown>;
|
|
42
36
|
flush(): Promise<void>;
|
|
43
|
-
getLogger(): Promise<Logger>;
|
|
44
37
|
setupSignalHandler(): void;
|
|
45
38
|
/**
|
|
46
39
|
* Get resource attributes for OpenTelemetry.
|
|
@@ -49,15 +42,11 @@ declare class TelemetryManager {
|
|
|
49
42
|
/**
|
|
50
43
|
* Initialize and return the OTLP Metric Exporter.
|
|
51
44
|
*/
|
|
52
|
-
getMetricExporter(): OTLPMetricExporter;
|
|
45
|
+
getMetricExporter(): import("@opentelemetry/exporter-metrics-otlp-http").OTLPMetricExporter;
|
|
53
46
|
/**
|
|
54
47
|
* Initialize and return the OTLP Trace Exporter.
|
|
55
48
|
*/
|
|
56
|
-
getTraceExporter(): OTLPTraceExporter;
|
|
57
|
-
/**
|
|
58
|
-
* Initialize and return the OTLP Log Exporter.
|
|
59
|
-
*/
|
|
60
|
-
getLogExporter(): OTLPLogExporter;
|
|
49
|
+
getTraceExporter(): import("@opentelemetry/exporter-trace-otlp-http").OTLPTraceExporter;
|
|
61
50
|
instrumentApp(): void;
|
|
62
51
|
setExporters(): void;
|
|
63
52
|
shutdownApp(): Promise<void>;
|
package/dist/telemetry.js
CHANGED
|
@@ -3,16 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.blaxelTelemetry = exports.DefaultAttributesSpanProcessor = exports.BlaxelResource = void 0;
|
|
4
4
|
const core_1 = require("@blaxel/core");
|
|
5
5
|
const api_1 = require("@opentelemetry/api");
|
|
6
|
-
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
7
|
-
const exporter_logs_otlp_http_1 = require("@opentelemetry/exporter-logs-otlp-http");
|
|
8
|
-
const exporter_metrics_otlp_http_1 = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
9
|
-
const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
|
|
10
6
|
const instrumentation_1 = require("@opentelemetry/instrumentation");
|
|
11
7
|
const instrumentation_http_1 = require("@opentelemetry/instrumentation-http");
|
|
12
8
|
const resources_1 = require("@opentelemetry/resources");
|
|
13
|
-
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
|
|
14
9
|
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
|
15
10
|
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
|
|
11
|
+
const auth_refresh_exporters_1 = require("./auth_refresh_exporters");
|
|
16
12
|
const telemetry_provider_1 = require("./telemetry_provider");
|
|
17
13
|
class BlaxelResource {
|
|
18
14
|
attributes;
|
|
@@ -54,6 +50,9 @@ class DefaultAttributesSpanProcessor {
|
|
|
54
50
|
}
|
|
55
51
|
exports.DefaultAttributesSpanProcessor = DefaultAttributesSpanProcessor;
|
|
56
52
|
class HasBeenProcessedSpanProcessor extends sdk_trace_node_1.BatchSpanProcessor {
|
|
53
|
+
constructor(exporter, config) {
|
|
54
|
+
super(exporter, config);
|
|
55
|
+
}
|
|
57
56
|
onEnd(span) {
|
|
58
57
|
super.onEnd(span);
|
|
59
58
|
}
|
|
@@ -61,38 +60,39 @@ class HasBeenProcessedSpanProcessor extends sdk_trace_node_1.BatchSpanProcessor
|
|
|
61
60
|
class TelemetryManager {
|
|
62
61
|
nodeTracerProvider;
|
|
63
62
|
meterProvider;
|
|
64
|
-
loggerProvider;
|
|
65
|
-
otelLogger;
|
|
66
63
|
initialized;
|
|
67
64
|
configured;
|
|
68
65
|
constructor() {
|
|
69
66
|
this.nodeTracerProvider = null;
|
|
70
67
|
this.meterProvider = null;
|
|
71
|
-
this.loggerProvider = null;
|
|
72
|
-
this.otelLogger = null;
|
|
73
68
|
this.initialized = false;
|
|
74
69
|
this.configured = false;
|
|
75
70
|
}
|
|
76
71
|
// This method need to stay sync to avoid non booted instrumentations
|
|
77
72
|
initialize() {
|
|
78
73
|
if (!this.enabled || this.initialized) {
|
|
74
|
+
core_1.logger.debug(`[TelemetryManager] Initialize skipped - enabled: ${this.enabled}, initialized: ${this.initialized}`);
|
|
79
75
|
return;
|
|
80
76
|
}
|
|
77
|
+
core_1.logger.debug("[TelemetryManager] Starting telemetry initialization");
|
|
81
78
|
this.instrumentApp();
|
|
82
79
|
this.setupSignalHandler();
|
|
83
80
|
this.initialized = true;
|
|
81
|
+
core_1.logger.debug("[TelemetryManager] Telemetry initialized, setting configuration async");
|
|
84
82
|
this.setConfiguration().catch((error) => {
|
|
85
|
-
core_1.logger.error("Error setting configuration:", error);
|
|
83
|
+
core_1.logger.error("[TelemetryManager] Error setting configuration:", error);
|
|
86
84
|
});
|
|
87
85
|
}
|
|
88
86
|
async setConfiguration() {
|
|
89
87
|
if (!this.enabled || this.configured) {
|
|
88
|
+
core_1.logger.debug(`[TelemetryManager] SetConfiguration skipped - enabled: ${this.enabled}, configured: ${this.configured}`);
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
91
|
+
core_1.logger.debug("[TelemetryManager] Starting authentication for telemetry configuration");
|
|
92
92
|
await (0, core_1.authenticate)();
|
|
93
|
+
core_1.logger.debug("[TelemetryManager] Authentication completed, setting up exporters");
|
|
93
94
|
this.setExporters();
|
|
94
|
-
|
|
95
|
-
core_1.logger.debug("Telemetry ready");
|
|
95
|
+
core_1.logger.debug("[TelemetryManager] Telemetry configuration complete");
|
|
96
96
|
this.configured = true;
|
|
97
97
|
}
|
|
98
98
|
get tracer() {
|
|
@@ -121,16 +121,6 @@ class TelemetryManager {
|
|
|
121
121
|
if (this.meterProvider) {
|
|
122
122
|
await this.meterProvider.shutdown();
|
|
123
123
|
}
|
|
124
|
-
if (this.loggerProvider) {
|
|
125
|
-
await this.loggerProvider.shutdown();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async getLogger() {
|
|
129
|
-
if (!this.otelLogger) {
|
|
130
|
-
await this.sleep(100);
|
|
131
|
-
return this.getLogger();
|
|
132
|
-
}
|
|
133
|
-
return this.otelLogger;
|
|
134
124
|
}
|
|
135
125
|
setupSignalHandler() {
|
|
136
126
|
const signals = ["SIGINT", "SIGTERM", "uncaughtException", "exit"];
|
|
@@ -171,25 +161,13 @@ class TelemetryManager {
|
|
|
171
161
|
* Initialize and return the OTLP Metric Exporter.
|
|
172
162
|
*/
|
|
173
163
|
getMetricExporter() {
|
|
174
|
-
return
|
|
175
|
-
headers: this.authHeaders,
|
|
176
|
-
});
|
|
164
|
+
return (0, auth_refresh_exporters_1.createMetricExporter)();
|
|
177
165
|
}
|
|
178
166
|
/**
|
|
179
167
|
* Initialize and return the OTLP Trace Exporter.
|
|
180
168
|
*/
|
|
181
169
|
getTraceExporter() {
|
|
182
|
-
return
|
|
183
|
-
headers: this.authHeaders,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Initialize and return the OTLP Log Exporter.
|
|
188
|
-
*/
|
|
189
|
-
getLogExporter() {
|
|
190
|
-
return new exporter_logs_otlp_http_1.OTLPLogExporter({
|
|
191
|
-
headers: this.authHeaders,
|
|
192
|
-
});
|
|
170
|
+
return (0, auth_refresh_exporters_1.createTraceExporter)();
|
|
193
171
|
}
|
|
194
172
|
instrumentApp() {
|
|
195
173
|
core_1.telemetryRegistry.registerProvider(new telemetry_provider_1.OtelTelemetryProvider());
|
|
@@ -202,13 +180,18 @@ class TelemetryManager {
|
|
|
202
180
|
}
|
|
203
181
|
setExporters() {
|
|
204
182
|
const resource = new BlaxelResource(this.resourceAttributes);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
183
|
+
core_1.logger.debug("[TelemetryManager] Setting up exporters with authentication refresh");
|
|
184
|
+
// Configure batch processor options with 1-second delay
|
|
185
|
+
const batchProcessorOptions = {
|
|
186
|
+
scheduledDelayMillis: 1000, // Export every 1 second
|
|
187
|
+
exportTimeoutMillis: 5000, // Timeout for export
|
|
188
|
+
maxExportBatchSize: 512, // Max batch size
|
|
189
|
+
maxQueueSize: 2048 // Max queue size
|
|
190
|
+
};
|
|
191
|
+
core_1.logger.debug("[TelemetryManager] Batch processor options:", batchProcessorOptions);
|
|
192
|
+
// Create auth-refreshing trace exporter
|
|
193
|
+
const traceExporter = new auth_refresh_exporters_1.AuthRefreshingSpanExporter(() => this.getTraceExporter());
|
|
194
|
+
core_1.logger.debug("[TelemetryManager] Created AuthRefreshingSpanExporter");
|
|
212
195
|
this.nodeTracerProvider = new sdk_trace_node_1.NodeTracerProvider({
|
|
213
196
|
resource,
|
|
214
197
|
sampler: new sdk_trace_node_1.AlwaysOnSampler(),
|
|
@@ -218,22 +201,26 @@ class TelemetryManager {
|
|
|
218
201
|
"workload.type": core_1.settings.type ? core_1.settings.type + "s" : "",
|
|
219
202
|
workspace: core_1.settings.workspace || "",
|
|
220
203
|
}),
|
|
221
|
-
new sdk_trace_node_1.BatchSpanProcessor(traceExporter),
|
|
222
|
-
new HasBeenProcessedSpanProcessor(traceExporter),
|
|
204
|
+
new sdk_trace_node_1.BatchSpanProcessor(traceExporter, batchProcessorOptions),
|
|
205
|
+
new HasBeenProcessedSpanProcessor(traceExporter, batchProcessorOptions),
|
|
223
206
|
],
|
|
224
207
|
});
|
|
225
208
|
this.nodeTracerProvider.register();
|
|
226
|
-
|
|
209
|
+
core_1.logger.debug("[TelemetryManager] Trace provider registered");
|
|
210
|
+
// Create auth-refreshing metric exporter
|
|
211
|
+
const metricExporter = new auth_refresh_exporters_1.AuthRefreshingMetricExporter(() => this.getMetricExporter());
|
|
212
|
+
core_1.logger.debug("[TelemetryManager] Created AuthRefreshingMetricExporter");
|
|
227
213
|
this.meterProvider = new sdk_metrics_1.MeterProvider({
|
|
228
214
|
resource,
|
|
229
215
|
readers: [
|
|
230
216
|
new sdk_metrics_1.PeriodicExportingMetricReader({
|
|
231
217
|
exporter: metricExporter,
|
|
232
|
-
exportIntervalMillis: 60000
|
|
218
|
+
exportIntervalMillis: 1000, // Changed from 60000 to 1000 (1 second)
|
|
233
219
|
}),
|
|
234
220
|
],
|
|
235
221
|
});
|
|
236
222
|
api_1.metrics.setGlobalMeterProvider(this.meterProvider);
|
|
223
|
+
core_1.logger.debug("[TelemetryManager] Metric provider configured with 1-second export interval");
|
|
237
224
|
}
|
|
238
225
|
async shutdownApp() {
|
|
239
226
|
try {
|
|
@@ -253,11 +240,6 @@ class TelemetryManager {
|
|
|
253
240
|
.shutdown()
|
|
254
241
|
.catch((error) => core_1.logger.debug("Error shutting down meter provider:", error)));
|
|
255
242
|
}
|
|
256
|
-
if (this.loggerProvider) {
|
|
257
|
-
shutdownPromises.push(this.loggerProvider
|
|
258
|
-
.shutdown()
|
|
259
|
-
.catch((error) => core_1.logger.debug("Error shutting down logger provider:", error)));
|
|
260
|
-
}
|
|
261
243
|
// Wait for all providers to shutdown with a timeout
|
|
262
244
|
await Promise.race([
|
|
263
245
|
Promise.all(shutdownPromises),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blaxel/telemetry",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"description": "Blaxel SDK for TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Blaxel, INC (https://blaxel.ai)",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
72
72
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
73
73
|
"ai": "^4.3.13",
|
|
74
|
-
"@blaxel/core": "0.2.
|
|
74
|
+
"@blaxel/core": "0.2.26"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@eslint/js": "^9.26.0",
|
package/dist/legacy_logger.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare function setLegacyLogger(): void;
|
|
2
|
-
export declare const originalLogger: {
|
|
3
|
-
info: (message?: any, ...optionalParams: any[]) => void;
|
|
4
|
-
error: (message?: any, ...optionalParams: any[]) => void;
|
|
5
|
-
warn: (message?: any, ...optionalParams: any[]) => void;
|
|
6
|
-
debug: (message?: any, ...optionalParams: any[]) => void;
|
|
7
|
-
log: (message?: any, ...optionalParams: any[]) => void;
|
|
8
|
-
};
|
package/dist/legacy_logger.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.originalLogger = void 0;
|
|
4
|
-
exports.setLegacyLogger = setLegacyLogger;
|
|
5
|
-
/* eslint-disable no-console */
|
|
6
|
-
const core_1 = require("@blaxel/core");
|
|
7
|
-
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
8
|
-
const telemetry_1 = require("./telemetry");
|
|
9
|
-
function setLegacyLogger() {
|
|
10
|
-
console.debug = (message, ...args) => {
|
|
11
|
-
const msg = formatLogMessage(message, args);
|
|
12
|
-
exports.originalLogger.log(msg);
|
|
13
|
-
emitLogSync(api_logs_1.SeverityNumber.DEBUG, msg);
|
|
14
|
-
};
|
|
15
|
-
console.log = (message, ...args) => {
|
|
16
|
-
const msg = formatLogMessage(message, args);
|
|
17
|
-
exports.originalLogger.log(msg);
|
|
18
|
-
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
19
|
-
};
|
|
20
|
-
console.info = (message, ...args) => {
|
|
21
|
-
const msg = formatLogMessage(message, args);
|
|
22
|
-
exports.originalLogger.log(msg);
|
|
23
|
-
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
24
|
-
};
|
|
25
|
-
console.error = (message, ...args) => {
|
|
26
|
-
const msg = formatLogMessage(message, args);
|
|
27
|
-
exports.originalLogger.log(msg);
|
|
28
|
-
emitLogSync(api_logs_1.SeverityNumber.ERROR, msg);
|
|
29
|
-
};
|
|
30
|
-
console.warn = (message, ...args) => {
|
|
31
|
-
const msg = formatLogMessage(message, args);
|
|
32
|
-
exports.originalLogger.log(msg);
|
|
33
|
-
emitLogSync(api_logs_1.SeverityNumber.WARN, msg);
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
exports.originalLogger = {
|
|
37
|
-
info: console.info,
|
|
38
|
-
error: console.error,
|
|
39
|
-
warn: console.warn,
|
|
40
|
-
debug: console.debug,
|
|
41
|
-
log: console.log,
|
|
42
|
-
};
|
|
43
|
-
// Format a log message with appropriate color and prefix
|
|
44
|
-
function formatLogMessage(message, args) {
|
|
45
|
-
const messageStr = typeof message === "string" ? message : (0, core_1.stringify)(message, 2);
|
|
46
|
-
const argsStr = args.map(arg => typeof arg === "string" ? arg : (0, core_1.stringify)(arg, 2)).join(" ");
|
|
47
|
-
return `${messageStr}${argsStr ? " " + argsStr : ""}`;
|
|
48
|
-
}
|
|
49
|
-
async function emitLog(severityNumber, message) {
|
|
50
|
-
const loggerInstance = await telemetry_1.blaxelTelemetry.getLogger();
|
|
51
|
-
loggerInstance.emit({
|
|
52
|
-
severityNumber: severityNumber,
|
|
53
|
-
body: message,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
function emitLogSync(severityNumber, message) {
|
|
57
|
-
emitLog(severityNumber, message).catch(() => { });
|
|
58
|
-
}
|