@glasstrace/sdk 0.12.3 → 0.12.5

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/index.cjs CHANGED
@@ -17036,6 +17036,7 @@ function loadFsSyncOrNull() {
17036
17036
  var currentConfig = null;
17037
17037
  var configCacheChecked = false;
17038
17038
  var rateLimitBackoff = false;
17039
+ var lastInitSucceeded = false;
17039
17040
  function loadCachedConfig(projectRoot) {
17040
17041
  const modules = loadFsSyncOrNull();
17041
17042
  if (!modules) return null;
@@ -17205,6 +17206,7 @@ async function writeClaimedKey(newApiKey, projectRoot) {
17205
17206
  }
17206
17207
  }
17207
17208
  async function performInit(config2, anonKey, sdkVersion, healthReport) {
17209
+ lastInitSucceeded = false;
17208
17210
  if (rateLimitBackoff) {
17209
17211
  rateLimitBackoff = false;
17210
17212
  return null;
@@ -17233,6 +17235,7 @@ async function performInit(config2, anonKey, sdkVersion, healthReport) {
17233
17235
  if (healthReport) {
17234
17236
  acknowledgeHealthReport(healthReport);
17235
17237
  }
17238
+ lastInitSucceeded = true;
17236
17239
  await saveCachedConfig(result);
17237
17240
  if (result.claimResult) {
17238
17241
  try {
@@ -17279,7 +17282,6 @@ async function performInit(config2, anonKey, sdkVersion, healthReport) {
17279
17282
  return null;
17280
17283
  }
17281
17284
  } catch (err) {
17282
- recordInitFailure();
17283
17285
  console.warn(
17284
17286
  `[glasstrace] Unexpected init error: ${err instanceof Error ? err.message : String(err)}`
17285
17287
  );
@@ -17309,6 +17311,16 @@ function getClaimResult() {
17309
17311
  function _setCurrentConfig(config2) {
17310
17312
  currentConfig = config2;
17311
17313
  }
17314
+ function consumeRateLimitFlag() {
17315
+ if (rateLimitBackoff) {
17316
+ rateLimitBackoff = false;
17317
+ return true;
17318
+ }
17319
+ return false;
17320
+ }
17321
+ function didLastInitSucceed() {
17322
+ return lastInitSucceeded;
17323
+ }
17312
17324
 
17313
17325
  // src/span-processor.ts
17314
17326
  var GlasstraceSpanProcessor = class {
@@ -17345,6 +17357,7 @@ var GlasstraceExporter = class {
17345
17357
  environment;
17346
17358
  endpointUrl;
17347
17359
  createDelegateFn;
17360
+ verbose;
17348
17361
  delegate = null;
17349
17362
  delegateKey = null;
17350
17363
  pendingBatches = [];
@@ -17357,6 +17370,7 @@ var GlasstraceExporter = class {
17357
17370
  this.environment = options.environment;
17358
17371
  this.endpointUrl = options.endpointUrl;
17359
17372
  this.createDelegateFn = options.createDelegate;
17373
+ this.verbose = options.verbose ?? false;
17360
17374
  }
17361
17375
  export(spans, resultCallback) {
17362
17376
  const currentKey = this.getApiKey();
@@ -17365,11 +17379,16 @@ var GlasstraceExporter = class {
17365
17379
  return;
17366
17380
  }
17367
17381
  const enrichedSpans = spans.map((span) => this.enrichSpan(span));
17382
+ if (this.verbose) {
17383
+ sdkLog("info", `[glasstrace:diag] Export batch: ${enrichedSpans.length} spans`);
17384
+ }
17368
17385
  const exporter = this.ensureDelegate();
17369
17386
  if (exporter) {
17370
17387
  exporter.export(enrichedSpans, (result) => {
17371
17388
  if (result.code !== 0) {
17372
17389
  sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
17390
+ } else if (this.verbose) {
17391
+ sdkLog("info", `[glasstrace:diag] Export success: ${enrichedSpans.length} spans delivered`);
17373
17392
  }
17374
17393
  resultCallback(result);
17375
17394
  });
@@ -17462,6 +17481,21 @@ var GlasstraceExporter = class {
17462
17481
  if (statusCode !== void 0) {
17463
17482
  extra[ATTR.HTTP_STATUS_CODE] = statusCode;
17464
17483
  }
17484
+ if (method && span.status?.code === SpanStatusCode.ERROR) {
17485
+ if (statusCode === void 0 || statusCode === 0 || statusCode === 200) {
17486
+ const httpErrorType = attrs["error.type"];
17487
+ if (typeof httpErrorType === "string") {
17488
+ const parsed = parseInt(httpErrorType, 10);
17489
+ if (!isNaN(parsed) && parsed >= 400 && parsed <= 599) {
17490
+ extra[ATTR.HTTP_STATUS_CODE] = parsed;
17491
+ } else {
17492
+ extra[ATTR.HTTP_STATUS_CODE] = 500;
17493
+ }
17494
+ } else {
17495
+ extra[ATTR.HTTP_STATUS_CODE] = 500;
17496
+ }
17497
+ }
17498
+ }
17465
17499
  if (span.startTime && span.endTime) {
17466
17500
  const [startSec, startNano] = span.startTime;
17467
17501
  const [endSec, endNano] = span.endTime;
@@ -17541,11 +17575,17 @@ var GlasstraceExporter = class {
17541
17575
  bufferSpans(spans, resultCallback) {
17542
17576
  this.pendingBatches.push({ spans, resultCallback });
17543
17577
  this.pendingSpanCount += spans.length;
17578
+ if (this.verbose) {
17579
+ sdkLog("info", `[glasstrace:diag] Buffering ${spans.length} spans (key pending, total: ${this.pendingSpanCount})`);
17580
+ }
17544
17581
  while (this.pendingSpanCount > MAX_PENDING_SPANS && this.pendingBatches.length > 1) {
17545
17582
  const evicted = this.pendingBatches.shift();
17546
17583
  this.pendingSpanCount -= evicted.spans.length;
17547
17584
  recordSpansDropped(evicted.spans.length);
17548
17585
  evicted.resultCallback({ code: 0 });
17586
+ if (this.verbose) {
17587
+ sdkLog("info", `[glasstrace:diag] Buffer overflow: evicted ${evicted.spans.length} spans (total pending: ${this.pendingSpanCount})`);
17588
+ }
17549
17589
  if (!this.overflowLogged) {
17550
17590
  this.overflowLogged = true;
17551
17591
  console.warn(
@@ -17584,6 +17624,8 @@ var GlasstraceExporter = class {
17584
17624
  exporter.export(enriched, (result) => {
17585
17625
  if (result.code !== 0) {
17586
17626
  sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
17627
+ } else if (this.verbose) {
17628
+ sdkLog("info", `[glasstrace:diag] Flush export success: ${enriched.length} spans delivered`);
17587
17629
  }
17588
17630
  batch.resultCallback(result);
17589
17631
  });
@@ -20383,7 +20425,8 @@ async function configureOtel(config2, sessionManager) {
20383
20425
  getConfig: () => getActiveConfig(),
20384
20426
  environment: config2.environment,
20385
20427
  endpointUrl: exporterUrl,
20386
- createDelegate: createOtlpExporter
20428
+ createDelegate: createOtlpExporter,
20429
+ verbose: config2.verbose
20387
20430
  });
20388
20431
  _activeExporter = glasstraceExporter;
20389
20432
  const vercelOtel = await tryImport("@vercel/otel");
@@ -20426,6 +20469,9 @@ async function configureOtel(config2, sessionManager) {
20426
20469
  const processor = new BatchSpanProcessor(glasstraceExporter, {
20427
20470
  scheduledDelayMillis: 1e3
20428
20471
  });
20472
+ if (config2.verbose) {
20473
+ sdkLog("info", "[glasstrace:diag] BatchSpanProcessor configured: scheduledDelayMillis=1000");
20474
+ }
20429
20475
  const provider = new BasicTracerProvider({
20430
20476
  spanProcessors: [processor]
20431
20477
  });
@@ -20435,6 +20481,112 @@ async function configureOtel(config2, sessionManager) {
20435
20481
 
20436
20482
  // src/register.ts
20437
20483
  init_console_capture();
20484
+
20485
+ // src/heartbeat.ts
20486
+ init_console_capture();
20487
+ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
20488
+ var BACKOFF_BASE_MS = HEARTBEAT_INTERVAL_MS;
20489
+ var BACKOFF_MAX_MS = 30 * 60 * 1e3;
20490
+ var BACKOFF_JITTER = 0.2;
20491
+ var heartbeatTimer = null;
20492
+ var heartbeatGeneration = 0;
20493
+ var backoffAttempts = 0;
20494
+ var backoffUntil = 0;
20495
+ var tickInProgress = false;
20496
+ var _shutdownHandler2 = null;
20497
+ function startHeartbeat(config2, anonKey, sdkVersion, generation, onClaimTransition) {
20498
+ if (heartbeatTimer !== null) return;
20499
+ heartbeatGeneration = generation;
20500
+ heartbeatTimer = setInterval(() => {
20501
+ void heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTransition);
20502
+ }, HEARTBEAT_INTERVAL_MS);
20503
+ heartbeatTimer.unref();
20504
+ registerShutdownHandlers(config2, anonKey, sdkVersion);
20505
+ if (config2.verbose) {
20506
+ sdkLog("info", "[glasstrace] Heartbeat started (5-minute interval).");
20507
+ }
20508
+ }
20509
+ function stopHeartbeat() {
20510
+ if (heartbeatTimer !== null) {
20511
+ clearInterval(heartbeatTimer);
20512
+ heartbeatTimer = null;
20513
+ }
20514
+ removeShutdownHandlers();
20515
+ }
20516
+ async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTransition) {
20517
+ if (tickInProgress) return;
20518
+ tickInProgress = true;
20519
+ try {
20520
+ if (generation !== heartbeatGeneration) {
20521
+ stopHeartbeat();
20522
+ return;
20523
+ }
20524
+ if (Date.now() < backoffUntil) {
20525
+ if (config2.verbose) {
20526
+ sdkLog("info", "[glasstrace] Heartbeat skipped (rate-limit backoff).");
20527
+ }
20528
+ return;
20529
+ }
20530
+ const healthReport = collectHealthReport(sdkVersion);
20531
+ const initResult = await performInit(config2, anonKey, sdkVersion, healthReport);
20532
+ if (generation !== heartbeatGeneration) return;
20533
+ if (initResult === null && consumeRateLimitFlag()) {
20534
+ backoffAttempts++;
20535
+ const delay = Math.min(
20536
+ BACKOFF_BASE_MS * Math.pow(2, backoffAttempts - 1),
20537
+ BACKOFF_MAX_MS
20538
+ );
20539
+ const jitter = delay * BACKOFF_JITTER * (Math.random() * 2 - 1);
20540
+ backoffUntil = Date.now() + delay + jitter;
20541
+ if (config2.verbose) {
20542
+ sdkLog("info", `[glasstrace] Heartbeat backing off for ${Math.round((delay + jitter) / 1e3)}s.`);
20543
+ }
20544
+ } else {
20545
+ backoffAttempts = 0;
20546
+ backoffUntil = 0;
20547
+ }
20548
+ if (initResult?.claimResult) {
20549
+ onClaimTransition(initResult.claimResult.newApiKey);
20550
+ }
20551
+ if (config2.verbose) {
20552
+ sdkLog("info", "[glasstrace] Heartbeat completed.");
20553
+ }
20554
+ } finally {
20555
+ tickInProgress = false;
20556
+ }
20557
+ }
20558
+ function registerShutdownHandlers(config2, anonKey, sdkVersion) {
20559
+ if (typeof process === "undefined" || typeof process.once !== "function") {
20560
+ return;
20561
+ }
20562
+ let shutdownFired = false;
20563
+ const handler = (signal) => {
20564
+ if (shutdownFired) return;
20565
+ shutdownFired = true;
20566
+ if (heartbeatTimer !== null) {
20567
+ clearInterval(heartbeatTimer);
20568
+ heartbeatTimer = null;
20569
+ }
20570
+ const healthReport = collectHealthReport(sdkVersion);
20571
+ void performInit(config2, anonKey, sdkVersion, healthReport).catch(() => {
20572
+ }).finally(() => {
20573
+ removeShutdownHandlers();
20574
+ process.kill(process.pid, signal);
20575
+ });
20576
+ };
20577
+ _shutdownHandler2 = handler;
20578
+ process.once("SIGTERM", _shutdownHandler2);
20579
+ process.once("SIGINT", _shutdownHandler2);
20580
+ }
20581
+ function removeShutdownHandlers() {
20582
+ if (_shutdownHandler2 && typeof process !== "undefined") {
20583
+ process.removeListener("SIGTERM", _shutdownHandler2);
20584
+ process.removeListener("SIGINT", _shutdownHandler2);
20585
+ _shutdownHandler2 = null;
20586
+ }
20587
+ }
20588
+
20589
+ // src/register.ts
20438
20590
  var consoleCaptureInstalled = false;
20439
20591
  var discoveryHandler = null;
20440
20592
  var isRegistered = false;
@@ -20587,14 +20739,20 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
20587
20739
  if (config2.verbose) {
20588
20740
  console.info("[glasstrace] Background init firing.");
20589
20741
  }
20590
- const healthReport = collectHealthReport("0.12.3");
20591
- const initResult = await performInit(config2, anonKeyForInit, "0.12.3", healthReport);
20742
+ const healthReport = collectHealthReport("0.12.5");
20743
+ const initResult = await performInit(config2, anonKeyForInit, "0.12.5", healthReport);
20592
20744
  if (generation !== registrationGeneration) return;
20593
20745
  if (initResult?.claimResult) {
20594
20746
  setResolvedApiKey(initResult.claimResult.newApiKey);
20595
20747
  notifyApiKeyResolved();
20596
20748
  }
20597
20749
  maybeInstallConsoleCapture();
20750
+ if (didLastInitSucceed()) {
20751
+ startHeartbeat(config2, anonKeyForInit, "0.12.5", generation, (newApiKey) => {
20752
+ setResolvedApiKey(newApiKey);
20753
+ notifyApiKeyResolved();
20754
+ });
20755
+ }
20598
20756
  }
20599
20757
  function getDiscoveryHandler() {
20600
20758
  return discoveryHandler;