@blaxel/telemetry 0.2.33 → 0.2.35-preview.78

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.
@@ -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
@@ -1,2 +1,4 @@
1
1
  import { blaxelTelemetry } from "./telemetry";
2
+ export { AuthRefreshingMetricExporter, AuthRefreshingSpanExporter, createMetricExporter, createTraceExporter } from "./auth_refresh_exporters";
3
+ export { setJsonLogger } from "./json_logger";
2
4
  export { blaxelTelemetry };
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 === "http") {
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; } });
@@ -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 : (0, core_1.stringify)(message, 2);
49
- const argsStr = args.map(arg => typeof arg === "string" ? arg : (0, core_1.stringify)(arg, 2)).join(" ");
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[labelsName] = {};
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[labelsName]['blaxel-task'] = `${taskPrefix}${taskId}`;
131
+ logEntry[validatedLabelsName]['blaxel-task'] = `${taskPrefix}${taskId}`;
65
132
  }
66
133
  const executionId = core_1.env[executionKey] || null;
67
134
  if (executionId) {
68
- logEntry[labelsName]['blaxel-execution'] = `${executionPrefix}${executionId.split('-').pop()}`;
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
  }
@@ -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,10 +24,9 @@ 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;
29
+ private shuttingDown;
35
30
  constructor();
36
31
  initialize(): void;
37
32
  setConfiguration(): Promise<void>;
@@ -40,7 +35,6 @@ declare class TelemetryManager {
40
35
  get authHeaders(): Record<string, string>;
41
36
  sleep(ms: number): Promise<unknown>;
42
37
  flush(): Promise<void>;
43
- getLogger(): Promise<Logger>;
44
38
  setupSignalHandler(): void;
45
39
  /**
46
40
  * Get resource attributes for OpenTelemetry.
@@ -49,15 +43,11 @@ declare class TelemetryManager {
49
43
  /**
50
44
  * Initialize and return the OTLP Metric Exporter.
51
45
  */
52
- getMetricExporter(): OTLPMetricExporter;
46
+ getMetricExporter(): import("@opentelemetry/exporter-metrics-otlp-http").OTLPMetricExporter;
53
47
  /**
54
48
  * Initialize and return the OTLP Trace Exporter.
55
49
  */
56
- getTraceExporter(): OTLPTraceExporter;
57
- /**
58
- * Initialize and return the OTLP Log Exporter.
59
- */
60
- getLogExporter(): OTLPLogExporter;
50
+ getTraceExporter(): import("@opentelemetry/exporter-trace-otlp-http").OTLPTraceExporter;
61
51
  instrumentApp(): void;
62
52
  setExporters(): void;
63
53
  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,41 @@ 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;
65
+ shuttingDown;
68
66
  constructor() {
69
67
  this.nodeTracerProvider = null;
70
68
  this.meterProvider = null;
71
- this.loggerProvider = null;
72
- this.otelLogger = null;
73
69
  this.initialized = false;
74
70
  this.configured = false;
71
+ this.shuttingDown = false;
75
72
  }
76
73
  // This method need to stay sync to avoid non booted instrumentations
77
74
  initialize() {
78
75
  if (!this.enabled || this.initialized) {
76
+ core_1.logger.debug(`[TelemetryManager] Initialize skipped - enabled: ${this.enabled}, initialized: ${this.initialized}`);
79
77
  return;
80
78
  }
79
+ core_1.logger.debug("[TelemetryManager] Starting telemetry initialization");
81
80
  this.instrumentApp();
82
81
  this.setupSignalHandler();
83
82
  this.initialized = true;
83
+ core_1.logger.debug("[TelemetryManager] Telemetry initialized, setting configuration async");
84
84
  this.setConfiguration().catch((error) => {
85
- core_1.logger.error("Error setting configuration:", error);
85
+ core_1.logger.error("[TelemetryManager] Error setting configuration:", error);
86
86
  });
87
87
  }
88
88
  async setConfiguration() {
89
89
  if (!this.enabled || this.configured) {
90
+ core_1.logger.debug(`[TelemetryManager] SetConfiguration skipped - enabled: ${this.enabled}, configured: ${this.configured}`);
90
91
  return;
91
92
  }
93
+ core_1.logger.debug("[TelemetryManager] Starting authentication for telemetry configuration");
92
94
  await (0, core_1.authenticate)();
95
+ core_1.logger.debug("[TelemetryManager] Authentication completed, setting up exporters");
93
96
  this.setExporters();
94
- this.otelLogger = api_logs_1.logs.getLogger("blaxel");
95
- core_1.logger.debug("Telemetry ready");
97
+ core_1.logger.debug("[TelemetryManager] Telemetry configuration complete");
96
98
  this.configured = true;
97
99
  }
98
100
  get tracer() {
@@ -121,27 +123,39 @@ class TelemetryManager {
121
123
  if (this.meterProvider) {
122
124
  await this.meterProvider.shutdown();
123
125
  }
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
126
  }
135
127
  setupSignalHandler() {
136
- const signals = ["SIGINT", "SIGTERM", "uncaughtException", "exit"];
137
- for (const signal of signals) {
138
- process.on(signal, () => {
139
- this.shutdownApp().catch((error) => {
140
- core_1.logger.debug("Fatal error during shutdown:", error);
141
- process.exit(0);
128
+ const forward = (sig) => {
129
+ if (this.shuttingDown)
130
+ return;
131
+ this.shuttingDown = true;
132
+ this.shutdownApp()
133
+ .catch((error) => {
134
+ core_1.logger.debug("Fatal error during shutdown:", error);
135
+ })
136
+ .finally(() => {
137
+ try {
138
+ // Re-send the original signal to let the default handler terminate the process with the correct code
139
+ process.kill(process.pid, sig);
140
+ }
141
+ catch {
142
+ process.exit(1);
143
+ }
144
+ });
145
+ };
146
+ process.once("SIGINT", () => forward("SIGINT"));
147
+ process.once("SIGTERM", () => forward("SIGTERM"));
148
+ process.once("uncaughtException", (err) => {
149
+ core_1.logger.error("Uncaught exception:", err);
150
+ this.shutdownApp().catch((error) => {
151
+ core_1.logger.debug("Fatal error during shutdown:", error);
152
+ }).finally(() => {
153
+ // Re-throw on next tick so Node's default handler sets exit code and prints stack
154
+ setImmediate(() => {
155
+ throw (err instanceof Error ? err : new Error(String(err)));
142
156
  });
143
157
  });
144
- }
158
+ });
145
159
  }
146
160
  /**
147
161
  * Get resource attributes for OpenTelemetry.
@@ -171,25 +185,13 @@ class TelemetryManager {
171
185
  * Initialize and return the OTLP Metric Exporter.
172
186
  */
173
187
  getMetricExporter() {
174
- return new exporter_metrics_otlp_http_1.OTLPMetricExporter({
175
- headers: this.authHeaders,
176
- });
188
+ return (0, auth_refresh_exporters_1.createMetricExporter)();
177
189
  }
178
190
  /**
179
191
  * Initialize and return the OTLP Trace Exporter.
180
192
  */
181
193
  getTraceExporter() {
182
- return new exporter_trace_otlp_http_1.OTLPTraceExporter({
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
- });
194
+ return (0, auth_refresh_exporters_1.createTraceExporter)();
193
195
  }
194
196
  instrumentApp() {
195
197
  core_1.telemetryRegistry.registerProvider(new telemetry_provider_1.OtelTelemetryProvider());
@@ -202,13 +204,18 @@ class TelemetryManager {
202
204
  }
203
205
  setExporters() {
204
206
  const resource = new BlaxelResource(this.resourceAttributes);
205
- const logExporter = this.getLogExporter();
206
- this.loggerProvider = new sdk_logs_1.LoggerProvider({
207
- resource,
208
- });
209
- this.loggerProvider.addLogRecordProcessor(new sdk_logs_1.BatchLogRecordProcessor(logExporter));
210
- api_logs_1.logs.setGlobalLoggerProvider(this.loggerProvider);
211
- const traceExporter = this.getTraceExporter();
207
+ core_1.logger.debug("[TelemetryManager] Setting up exporters with authentication refresh");
208
+ // Configure batch processor options with 1-second delay
209
+ const batchProcessorOptions = {
210
+ scheduledDelayMillis: 1000, // Export every 1 second
211
+ exportTimeoutMillis: 5000, // Timeout for export
212
+ maxExportBatchSize: 512, // Max batch size
213
+ maxQueueSize: 2048 // Max queue size
214
+ };
215
+ core_1.logger.debug("[TelemetryManager] Batch processor options:", batchProcessorOptions);
216
+ // Create auth-refreshing trace exporter
217
+ const traceExporter = new auth_refresh_exporters_1.AuthRefreshingSpanExporter(() => this.getTraceExporter());
218
+ core_1.logger.debug("[TelemetryManager] Created AuthRefreshingSpanExporter");
212
219
  this.nodeTracerProvider = new sdk_trace_node_1.NodeTracerProvider({
213
220
  resource,
214
221
  sampler: new sdk_trace_node_1.AlwaysOnSampler(),
@@ -218,22 +225,26 @@ class TelemetryManager {
218
225
  "workload.type": core_1.settings.type ? core_1.settings.type + "s" : "",
219
226
  workspace: core_1.settings.workspace || "",
220
227
  }),
221
- new sdk_trace_node_1.BatchSpanProcessor(traceExporter),
222
- new HasBeenProcessedSpanProcessor(traceExporter),
228
+ new sdk_trace_node_1.BatchSpanProcessor(traceExporter, batchProcessorOptions),
229
+ new HasBeenProcessedSpanProcessor(traceExporter, batchProcessorOptions),
223
230
  ],
224
231
  });
225
232
  this.nodeTracerProvider.register();
226
- const metricExporter = this.getMetricExporter();
233
+ core_1.logger.debug("[TelemetryManager] Trace provider registered");
234
+ // Create auth-refreshing metric exporter
235
+ const metricExporter = new auth_refresh_exporters_1.AuthRefreshingMetricExporter(() => this.getMetricExporter());
236
+ core_1.logger.debug("[TelemetryManager] Created AuthRefreshingMetricExporter");
227
237
  this.meterProvider = new sdk_metrics_1.MeterProvider({
228
238
  resource,
229
239
  readers: [
230
240
  new sdk_metrics_1.PeriodicExportingMetricReader({
231
241
  exporter: metricExporter,
232
- exportIntervalMillis: 60000,
242
+ exportIntervalMillis: 1000, // Changed from 60000 to 1000 (1 second)
233
243
  }),
234
244
  ],
235
245
  });
236
246
  api_1.metrics.setGlobalMeterProvider(this.meterProvider);
247
+ core_1.logger.debug("[TelemetryManager] Metric provider configured with 1-second export interval");
237
248
  }
238
249
  async shutdownApp() {
239
250
  try {
@@ -253,22 +264,15 @@ class TelemetryManager {
253
264
  .shutdown()
254
265
  .catch((error) => core_1.logger.debug("Error shutting down meter provider:", error)));
255
266
  }
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
267
  // Wait for all providers to shutdown with a timeout
262
268
  await Promise.race([
263
269
  Promise.all(shutdownPromises),
264
270
  new Promise((resolve) => setTimeout(resolve, 5000)), // 5 second timeout
265
271
  ]);
266
272
  core_1.logger.debug("Instrumentation shutdown complete");
267
- process.exit(0);
268
273
  }
269
274
  catch (error) {
270
275
  core_1.logger.error("Error during shutdown:", error);
271
- process.exit(1);
272
276
  }
273
277
  }
274
278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/telemetry",
3
- "version": "0.2.33",
3
+ "version": "0.2.35-preview.78",
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.33"
74
+ "@blaxel/core": "0.2.35-preview.78"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@eslint/js": "^9.26.0",
@@ -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
- };
@@ -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
- }