@glasstrace/sdk 0.12.1 → 0.12.3

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.
@@ -473,6 +473,7 @@ export {
473
473
  isAnonymousMode,
474
474
  maybeShowMcpNudge,
475
475
  installConsoleCapture,
476
+ sdkLog,
476
477
  discoverSourceMapFiles,
477
478
  collectSourceMaps,
478
479
  computeBuildHash,
@@ -484,4 +485,4 @@ export {
484
485
  uploadSourceMapsPresigned,
485
486
  uploadSourceMapsAuto
486
487
  };
487
- //# sourceMappingURL=chunk-MSMOH6IH.js.map
488
+ //# sourceMappingURL=chunk-J576N5MN.js.map
package/dist/index.cjs CHANGED
@@ -16951,6 +16951,60 @@ async function getOrCreateAnonKey(projectRoot) {
16951
16951
 
16952
16952
  // src/init-client.ts
16953
16953
  init_dist();
16954
+
16955
+ // src/health-collector.ts
16956
+ var tracesExported = 0;
16957
+ var tracesDropped = 0;
16958
+ var initFailures = 0;
16959
+ var lastConfigSyncAt = null;
16960
+ function recordSpansExported(count) {
16961
+ if (!Number.isFinite(count) || count < 0 || !Number.isInteger(count)) return;
16962
+ tracesExported += count;
16963
+ }
16964
+ function recordSpansDropped(count) {
16965
+ if (!Number.isFinite(count) || count < 0 || !Number.isInteger(count)) return;
16966
+ tracesDropped += count;
16967
+ }
16968
+ function recordInitFailure() {
16969
+ try {
16970
+ initFailures += 1;
16971
+ } catch {
16972
+ }
16973
+ }
16974
+ function recordConfigSync(timestamp) {
16975
+ try {
16976
+ lastConfigSyncAt = timestamp;
16977
+ } catch {
16978
+ }
16979
+ }
16980
+ function collectHealthReport(sdkVersion) {
16981
+ try {
16982
+ const now = Date.now();
16983
+ const configAge = lastConfigSyncAt !== null ? Math.max(0, now - lastConfigSyncAt) : 0;
16984
+ return {
16985
+ tracesExportedSinceLastInit: tracesExported,
16986
+ tracesDropped,
16987
+ initFailures,
16988
+ configAge: Math.round(configAge),
16989
+ sdkVersion
16990
+ };
16991
+ } catch {
16992
+ return null;
16993
+ }
16994
+ }
16995
+ function acknowledgeHealthReport(report) {
16996
+ const exp = Math.max(0, report.tracesExportedSinceLastInit);
16997
+ const expVal = tracesExported - exp;
16998
+ tracesExported = Number.isFinite(expVal) ? Math.max(0, expVal) : tracesExported;
16999
+ const drop = Math.max(0, report.tracesDropped);
17000
+ const dropVal = tracesDropped - drop;
17001
+ tracesDropped = Number.isFinite(dropVal) ? Math.max(0, dropVal) : tracesDropped;
17002
+ const fail = Math.max(0, report.initFailures);
17003
+ const failVal = initFailures - fail;
17004
+ initFailures = Number.isFinite(failVal) ? Math.max(0, failVal) : initFailures;
17005
+ }
17006
+
17007
+ // src/init-client.ts
16954
17008
  var GLASSTRACE_DIR2 = ".glasstrace";
16955
17009
  var CONFIG_FILE = "config";
16956
17010
  var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
@@ -16999,6 +17053,7 @@ function loadCachedConfig(projectRoot) {
16999
17053
  }
17000
17054
  const result = SdkInitResponseSchema.safeParse(cached2.response);
17001
17055
  if (result.success) {
17056
+ recordConfigSync(cached2.cachedAt);
17002
17057
  return result.data;
17003
17058
  }
17004
17059
  console.warn("[glasstrace] Cached config failed validation. Using defaults.");
@@ -17149,7 +17204,7 @@ async function writeClaimedKey(newApiKey, projectRoot) {
17149
17204
  } catch {
17150
17205
  }
17151
17206
  }
17152
- async function performInit(config2, anonKey, sdkVersion) {
17207
+ async function performInit(config2, anonKey, sdkVersion, healthReport) {
17153
17208
  if (rateLimitBackoff) {
17154
17209
  rateLimitBackoff = false;
17155
17210
  return null;
@@ -17168,12 +17223,16 @@ async function performInit(config2, anonKey, sdkVersion) {
17168
17223
  anonKey,
17169
17224
  sdkVersion,
17170
17225
  void 0,
17171
- void 0,
17226
+ healthReport ?? void 0,
17172
17227
  void 0,
17173
17228
  controller.signal
17174
17229
  );
17175
17230
  clearTimeout(timeoutId);
17176
17231
  currentConfig = result;
17232
+ recordConfigSync(Date.now());
17233
+ if (healthReport) {
17234
+ acknowledgeHealthReport(healthReport);
17235
+ }
17177
17236
  await saveCachedConfig(result);
17178
17237
  if (result.claimResult) {
17179
17238
  try {
@@ -17185,6 +17244,7 @@ async function performInit(config2, anonKey, sdkVersion) {
17185
17244
  return null;
17186
17245
  } catch (err) {
17187
17246
  clearTimeout(timeoutId);
17247
+ recordInitFailure();
17188
17248
  if (err instanceof DOMException && err.name === "AbortError") {
17189
17249
  console.warn("[glasstrace] ingestion_unreachable: Init request timed out.");
17190
17250
  return null;
@@ -17219,6 +17279,7 @@ async function performInit(config2, anonKey, sdkVersion) {
17219
17279
  return null;
17220
17280
  }
17221
17281
  } catch (err) {
17282
+ recordInitFailure();
17222
17283
  console.warn(
17223
17284
  `[glasstrace] Unexpected init error: ${err instanceof Error ? err.message : String(err)}`
17224
17285
  );
@@ -17273,6 +17334,7 @@ var GlasstraceSpanProcessor = class {
17273
17334
  // src/enriching-exporter.ts
17274
17335
  init_esm();
17275
17336
  init_dist();
17337
+ init_console_capture();
17276
17338
  var ATTR = GLASSTRACE_ATTRIBUTE_NAMES;
17277
17339
  var API_KEY_PENDING = "pending";
17278
17340
  var MAX_PENDING_SPANS = 1024;
@@ -17305,8 +17367,15 @@ var GlasstraceExporter = class {
17305
17367
  const enrichedSpans = spans.map((span) => this.enrichSpan(span));
17306
17368
  const exporter = this.ensureDelegate();
17307
17369
  if (exporter) {
17308
- exporter.export(enrichedSpans, resultCallback);
17370
+ exporter.export(enrichedSpans, (result) => {
17371
+ if (result.code !== 0) {
17372
+ sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
17373
+ }
17374
+ resultCallback(result);
17375
+ });
17376
+ recordSpansExported(enrichedSpans.length);
17309
17377
  } else {
17378
+ recordSpansDropped(enrichedSpans.length);
17310
17379
  resultCallback({ code: 0 });
17311
17380
  }
17312
17381
  }
@@ -17325,6 +17394,7 @@ var GlasstraceExporter = class {
17325
17394
  console.warn(
17326
17395
  `[glasstrace] Shutdown with ${this.pendingSpanCount} buffered spans \u2014 API key never resolved, spans lost.`
17327
17396
  );
17397
+ recordSpansDropped(this.pendingSpanCount);
17328
17398
  for (const batch of this.pendingBatches) {
17329
17399
  batch.resultCallback({ code: 0 });
17330
17400
  }
@@ -17335,7 +17405,14 @@ var GlasstraceExporter = class {
17335
17405
  return this.delegate.shutdown();
17336
17406
  }
17337
17407
  }
17408
+ /**
17409
+ * Flushes any pending buffered spans (if the API key has resolved) and
17410
+ * delegates to the underlying exporter's forceFlush to drain its queue.
17411
+ */
17338
17412
  forceFlush() {
17413
+ if (this.getApiKey() !== API_KEY_PENDING && this.pendingBatches.length > 0) {
17414
+ this.flushPending();
17415
+ }
17339
17416
  if (this.delegate?.forceFlush) {
17340
17417
  return this.delegate.forceFlush();
17341
17418
  }
@@ -17467,6 +17544,7 @@ var GlasstraceExporter = class {
17467
17544
  while (this.pendingSpanCount > MAX_PENDING_SPANS && this.pendingBatches.length > 1) {
17468
17545
  const evicted = this.pendingBatches.shift();
17469
17546
  this.pendingSpanCount -= evicted.spans.length;
17547
+ recordSpansDropped(evicted.spans.length);
17470
17548
  evicted.resultCallback({ code: 0 });
17471
17549
  if (!this.overflowLogged) {
17472
17550
  this.overflowLogged = true;
@@ -17488,9 +17566,12 @@ var GlasstraceExporter = class {
17488
17566
  if (this.pendingBatches.length === 0) return;
17489
17567
  const exporter = this.ensureDelegate();
17490
17568
  if (!exporter) {
17569
+ let discardedCount = 0;
17491
17570
  for (const batch of this.pendingBatches) {
17571
+ discardedCount += batch.spans.length;
17492
17572
  batch.resultCallback({ code: 0 });
17493
17573
  }
17574
+ recordSpansDropped(discardedCount);
17494
17575
  this.pendingBatches = [];
17495
17576
  this.pendingSpanCount = 0;
17496
17577
  return;
@@ -17500,7 +17581,13 @@ var GlasstraceExporter = class {
17500
17581
  this.pendingSpanCount = 0;
17501
17582
  for (const batch of batches) {
17502
17583
  const enriched = batch.spans.map((span) => this.enrichSpan(span));
17503
- exporter.export(enriched, batch.resultCallback);
17584
+ exporter.export(enriched, (result) => {
17585
+ if (result.code !== 0) {
17586
+ sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
17587
+ }
17588
+ batch.resultCallback(result);
17589
+ });
17590
+ recordSpansExported(enriched.length);
17504
17591
  }
17505
17592
  }
17506
17593
  };
@@ -20240,6 +20327,7 @@ var BasicTracerProvider = class {
20240
20327
 
20241
20328
  // src/otel-config.ts
20242
20329
  init_esm();
20330
+ init_console_capture();
20243
20331
  var _resolvedApiKey = API_KEY_PENDING;
20244
20332
  var _activeExporter = null;
20245
20333
  var _shutdownHandler = null;
@@ -20323,7 +20411,21 @@ async function configureOtel(config2, sessionManager) {
20323
20411
  _activeExporter = null;
20324
20412
  return;
20325
20413
  }
20326
- const processor = new BatchSpanProcessor(glasstraceExporter);
20414
+ if (config2.verbose) {
20415
+ diag2.setLogger(
20416
+ {
20417
+ verbose: (msg) => sdkLog("info", `[otel] ${msg}`),
20418
+ debug: (msg) => sdkLog("info", `[otel] ${msg}`),
20419
+ info: (msg) => sdkLog("info", `[otel] ${msg}`),
20420
+ warn: (msg) => sdkLog("warn", `[otel] ${msg}`),
20421
+ error: (msg) => sdkLog("error", `[otel] ${msg}`)
20422
+ },
20423
+ DiagLogLevel.WARN
20424
+ );
20425
+ }
20426
+ const processor = new BatchSpanProcessor(glasstraceExporter, {
20427
+ scheduledDelayMillis: 1e3
20428
+ });
20327
20429
  const provider = new BasicTracerProvider({
20328
20430
  spanProcessors: [processor]
20329
20431
  });
@@ -20485,7 +20587,8 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
20485
20587
  if (config2.verbose) {
20486
20588
  console.info("[glasstrace] Background init firing.");
20487
20589
  }
20488
- const initResult = await performInit(config2, anonKeyForInit, "0.12.1");
20590
+ const healthReport = collectHealthReport("0.12.3");
20591
+ const initResult = await performInit(config2, anonKeyForInit, "0.12.3", healthReport);
20489
20592
  if (generation !== registrationGeneration) return;
20490
20593
  if (initResult?.claimResult) {
20491
20594
  setResolvedApiKey(initResult.claimResult.newApiKey);