@glasstrace/sdk 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/dist/index.cjs +253 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -5
- package/dist/index.d.ts +37 -5
- package/dist/index.js +252 -99
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -68,17 +68,18 @@ function isAnonymousMode(config) {
|
|
|
68
68
|
import { createHash } from "crypto";
|
|
69
69
|
import { SessionIdSchema } from "@glasstrace/protocol";
|
|
70
70
|
var FOUR_HOURS_MS = 4 * 60 * 60 * 1e3;
|
|
71
|
+
var cachedGlasstraceEnv = process.env.GLASSTRACE_ENV;
|
|
72
|
+
var cachedPort = process.env.PORT ?? "3000";
|
|
71
73
|
function deriveSessionId(apiKey, origin, date, windowIndex) {
|
|
72
74
|
const input = JSON.stringify([apiKey, origin, date, windowIndex]);
|
|
73
75
|
const hash = createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
74
76
|
return SessionIdSchema.parse(hash);
|
|
75
77
|
}
|
|
76
78
|
function getOrigin() {
|
|
77
|
-
if (
|
|
78
|
-
return
|
|
79
|
+
if (cachedGlasstraceEnv) {
|
|
80
|
+
return cachedGlasstraceEnv;
|
|
79
81
|
}
|
|
80
|
-
|
|
81
|
-
return `localhost:${port}`;
|
|
82
|
+
return `localhost:${cachedPort}`;
|
|
82
83
|
}
|
|
83
84
|
function getDateString() {
|
|
84
85
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -132,6 +133,7 @@ var SessionManager = class {
|
|
|
132
133
|
};
|
|
133
134
|
|
|
134
135
|
// src/fetch-classifier.ts
|
|
136
|
+
var cachedPort2 = process.env.PORT ?? "3000";
|
|
135
137
|
function classifyFetchTarget(url) {
|
|
136
138
|
let parsed;
|
|
137
139
|
try {
|
|
@@ -146,8 +148,7 @@ function classifyFetchTarget(url) {
|
|
|
146
148
|
if (hostname === "stripe.com" || hostname.endsWith(".stripe.com")) {
|
|
147
149
|
return "stripe";
|
|
148
150
|
}
|
|
149
|
-
const
|
|
150
|
-
const internalOrigin = `localhost:${port}`;
|
|
151
|
+
const internalOrigin = `localhost:${cachedPort2}`;
|
|
151
152
|
const parsedPort = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
152
153
|
const urlOrigin = `${hostname}:${parsedPort}`;
|
|
153
154
|
if (urlOrigin === internalOrigin) {
|
|
@@ -296,6 +297,10 @@ async function sendInitRequest(config, anonKey, sdkVersion, importGraph, healthR
|
|
|
296
297
|
signal
|
|
297
298
|
});
|
|
298
299
|
if (!response.ok) {
|
|
300
|
+
try {
|
|
301
|
+
await response.text();
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
299
304
|
const error = new Error(`Init request failed with status ${response.status}`);
|
|
300
305
|
error.status = response.status;
|
|
301
306
|
throw error;
|
|
@@ -418,6 +423,7 @@ var GlasstraceExporter = class {
|
|
|
418
423
|
endpointUrl;
|
|
419
424
|
createDelegateFn;
|
|
420
425
|
delegate = null;
|
|
426
|
+
delegateKey = null;
|
|
421
427
|
pendingBatches = [];
|
|
422
428
|
pendingSpanCount = 0;
|
|
423
429
|
overflowLogged = false;
|
|
@@ -430,12 +436,12 @@ var GlasstraceExporter = class {
|
|
|
430
436
|
this.createDelegateFn = options.createDelegate;
|
|
431
437
|
}
|
|
432
438
|
export(spans, resultCallback) {
|
|
433
|
-
const enrichedSpans = spans.map((span) => this.enrichSpan(span));
|
|
434
439
|
const currentKey = this.getApiKey();
|
|
435
440
|
if (currentKey === API_KEY_PENDING) {
|
|
436
|
-
this.bufferSpans(
|
|
441
|
+
this.bufferSpans(spans, resultCallback);
|
|
437
442
|
return;
|
|
438
443
|
}
|
|
444
|
+
const enrichedSpans = spans.map((span) => this.enrichSpan(span));
|
|
439
445
|
const exporter = this.ensureDelegate();
|
|
440
446
|
if (exporter) {
|
|
441
447
|
exporter.export(enrichedSpans, resultCallback);
|
|
@@ -477,58 +483,43 @@ var GlasstraceExporter = class {
|
|
|
477
483
|
/**
|
|
478
484
|
* Enriches a ReadableSpan with all glasstrace.* attributes.
|
|
479
485
|
* Returns a new ReadableSpan wrapper; the original span is not mutated.
|
|
480
|
-
*
|
|
481
|
-
*
|
|
486
|
+
*
|
|
487
|
+
* External function calls (getSessionId, deriveErrorCategory,
|
|
488
|
+
* deriveOrmProvider, classifyFetchTarget) are individually guarded so a
|
|
489
|
+
* failure in one does not prevent the remaining attributes from being set.
|
|
490
|
+
* On total failure, returns the original span unchanged.
|
|
482
491
|
*/
|
|
483
492
|
enrichSpan(span) {
|
|
484
|
-
const attrs = span.attributes ?? {};
|
|
485
|
-
const name = span.name ?? "";
|
|
486
|
-
const extra = {};
|
|
487
493
|
try {
|
|
494
|
+
const attrs = span.attributes ?? {};
|
|
495
|
+
const name = span.name ?? "";
|
|
496
|
+
const extra = {};
|
|
488
497
|
extra[ATTR.TRACE_TYPE] = "server";
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
} catch {
|
|
495
|
-
}
|
|
496
|
-
try {
|
|
498
|
+
try {
|
|
499
|
+
const sessionId = this.sessionManager.getSessionId(this.getApiKey());
|
|
500
|
+
extra[ATTR.SESSION_ID] = sessionId;
|
|
501
|
+
} catch {
|
|
502
|
+
}
|
|
497
503
|
const env = this.environment ?? process.env.GLASSTRACE_ENV;
|
|
498
504
|
if (env) {
|
|
499
505
|
extra[ATTR.ENVIRONMENT] = env;
|
|
500
506
|
}
|
|
501
|
-
} catch {
|
|
502
|
-
}
|
|
503
|
-
try {
|
|
504
507
|
const existingCid = attrs["glasstrace.correlation.id"];
|
|
505
508
|
if (typeof existingCid === "string") {
|
|
506
509
|
extra[ATTR.CORRELATION_ID] = existingCid;
|
|
507
510
|
}
|
|
508
|
-
} catch {
|
|
509
|
-
}
|
|
510
|
-
try {
|
|
511
511
|
const route = attrs["http.route"] ?? name;
|
|
512
512
|
if (route) {
|
|
513
513
|
extra[ATTR.ROUTE] = route;
|
|
514
514
|
}
|
|
515
|
-
} catch {
|
|
516
|
-
}
|
|
517
|
-
try {
|
|
518
515
|
const method = attrs["http.method"] ?? attrs["http.request.method"];
|
|
519
516
|
if (method) {
|
|
520
517
|
extra[ATTR.HTTP_METHOD] = method;
|
|
521
518
|
}
|
|
522
|
-
} catch {
|
|
523
|
-
}
|
|
524
|
-
try {
|
|
525
519
|
const statusCode = attrs["http.status_code"] ?? attrs["http.response.status_code"];
|
|
526
520
|
if (statusCode !== void 0) {
|
|
527
521
|
extra[ATTR.HTTP_STATUS_CODE] = statusCode;
|
|
528
522
|
}
|
|
529
|
-
} catch {
|
|
530
|
-
}
|
|
531
|
-
try {
|
|
532
523
|
if (span.startTime && span.endTime) {
|
|
533
524
|
const [startSec, startNano] = span.startTime;
|
|
534
525
|
const [endSec, endNano] = span.endTime;
|
|
@@ -537,72 +528,78 @@ var GlasstraceExporter = class {
|
|
|
537
528
|
extra[ATTR.HTTP_DURATION_MS] = durationMs;
|
|
538
529
|
}
|
|
539
530
|
}
|
|
540
|
-
} catch {
|
|
541
|
-
}
|
|
542
|
-
try {
|
|
543
531
|
const errorMessage = attrs["exception.message"];
|
|
544
532
|
if (errorMessage) {
|
|
545
533
|
extra[ATTR.ERROR_MESSAGE] = errorMessage;
|
|
546
534
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
535
|
+
try {
|
|
536
|
+
const errorType = attrs["exception.type"];
|
|
537
|
+
if (errorType) {
|
|
538
|
+
extra[ATTR.ERROR_CODE] = errorType;
|
|
539
|
+
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
|
|
540
|
+
}
|
|
541
|
+
} catch {
|
|
554
542
|
}
|
|
555
|
-
} catch {
|
|
556
|
-
}
|
|
557
|
-
try {
|
|
558
543
|
const errorField = attrs["error.field"];
|
|
559
544
|
if (errorField) {
|
|
560
545
|
extra[ATTR.ERROR_FIELD] = errorField;
|
|
561
546
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
extra[ATTR.ORM_OPERATION] = operation;
|
|
547
|
+
try {
|
|
548
|
+
const spanAny = span;
|
|
549
|
+
const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
|
|
550
|
+
const ormProvider = deriveOrmProvider(instrumentationName);
|
|
551
|
+
if (ormProvider) {
|
|
552
|
+
extra[ATTR.ORM_PROVIDER] = ormProvider;
|
|
553
|
+
const model = attrs["db.sql.table"] ?? attrs["db.prisma.model"];
|
|
554
|
+
if (model) {
|
|
555
|
+
extra[ATTR.ORM_MODEL] = model;
|
|
556
|
+
}
|
|
557
|
+
const operation = attrs["db.operation"];
|
|
558
|
+
if (operation) {
|
|
559
|
+
extra[ATTR.ORM_OPERATION] = operation;
|
|
560
|
+
}
|
|
577
561
|
}
|
|
562
|
+
} catch {
|
|
578
563
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
564
|
+
try {
|
|
565
|
+
const url = attrs["http.url"] ?? attrs["url.full"];
|
|
566
|
+
if (url && span.kind === SpanKind.CLIENT) {
|
|
567
|
+
extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
|
|
568
|
+
}
|
|
569
|
+
} catch {
|
|
585
570
|
}
|
|
571
|
+
return createEnrichedSpan(span, extra);
|
|
586
572
|
} catch {
|
|
573
|
+
return span;
|
|
587
574
|
}
|
|
588
|
-
return createEnrichedSpan(span, extra);
|
|
589
575
|
}
|
|
590
576
|
/**
|
|
591
577
|
* Lazily creates the delegate OTLP exporter once the API key is resolved.
|
|
578
|
+
* Recreates the delegate if the key has changed (e.g., after key rotation)
|
|
579
|
+
* so the Authorization header stays current.
|
|
592
580
|
*/
|
|
593
581
|
ensureDelegate() {
|
|
594
582
|
if (!this.createDelegateFn) return null;
|
|
595
|
-
if (this.delegate) return this.delegate;
|
|
596
583
|
const currentKey = this.getApiKey();
|
|
597
584
|
if (currentKey === API_KEY_PENDING) return null;
|
|
585
|
+
if (this.delegate && this.delegateKey === currentKey) {
|
|
586
|
+
return this.delegate;
|
|
587
|
+
}
|
|
588
|
+
if (this.delegate) {
|
|
589
|
+
void this.delegate.shutdown?.().catch(() => {
|
|
590
|
+
});
|
|
591
|
+
}
|
|
598
592
|
this.delegate = this.createDelegateFn(this.endpointUrl, {
|
|
599
593
|
Authorization: `Bearer ${currentKey}`
|
|
600
594
|
});
|
|
595
|
+
this.delegateKey = currentKey;
|
|
601
596
|
return this.delegate;
|
|
602
597
|
}
|
|
603
598
|
/**
|
|
604
|
-
* Buffers
|
|
599
|
+
* Buffers raw (unenriched) spans while the API key is pending.
|
|
605
600
|
* Evicts oldest batches if the buffer exceeds MAX_PENDING_SPANS.
|
|
601
|
+
* Re-checks the key after buffering to close the race window where
|
|
602
|
+
* the key resolves between the caller's check and this buffer call.
|
|
606
603
|
*/
|
|
607
604
|
bufferSpans(spans, resultCallback) {
|
|
608
605
|
this.pendingBatches.push({ spans, resultCallback });
|
|
@@ -618,10 +615,14 @@ var GlasstraceExporter = class {
|
|
|
618
615
|
);
|
|
619
616
|
}
|
|
620
617
|
}
|
|
618
|
+
if (this.getApiKey() !== API_KEY_PENDING) {
|
|
619
|
+
this.flushPending();
|
|
620
|
+
}
|
|
621
621
|
}
|
|
622
622
|
/**
|
|
623
623
|
* Flushes all buffered spans through the delegate exporter.
|
|
624
|
-
*
|
|
624
|
+
* Enriches spans at flush time (not buffer time) so that session IDs
|
|
625
|
+
* are computed with the resolved API key instead of the "pending" sentinel.
|
|
625
626
|
*/
|
|
626
627
|
flushPending() {
|
|
627
628
|
if (this.pendingBatches.length === 0) return;
|
|
@@ -638,7 +639,8 @@ var GlasstraceExporter = class {
|
|
|
638
639
|
this.pendingBatches = [];
|
|
639
640
|
this.pendingSpanCount = 0;
|
|
640
641
|
for (const batch of batches) {
|
|
641
|
-
|
|
642
|
+
const enriched = batch.spans.map((span) => this.enrichSpan(span));
|
|
643
|
+
exporter.export(enriched, batch.resultCallback);
|
|
642
644
|
}
|
|
643
645
|
}
|
|
644
646
|
};
|
|
@@ -762,6 +764,7 @@ function createDiscoveryHandler(getAnonKey, getSessionId) {
|
|
|
762
764
|
// src/otel-config.ts
|
|
763
765
|
var _resolvedApiKey = API_KEY_PENDING;
|
|
764
766
|
var _activeExporter = null;
|
|
767
|
+
var _shutdownHandler = null;
|
|
765
768
|
function setResolvedApiKey(key) {
|
|
766
769
|
_resolvedApiKey = key;
|
|
767
770
|
}
|
|
@@ -778,6 +781,33 @@ async function tryImport(moduleId) {
|
|
|
778
781
|
return null;
|
|
779
782
|
}
|
|
780
783
|
}
|
|
784
|
+
function registerShutdownHooks(provider) {
|
|
785
|
+
if (typeof process === "undefined" || typeof process.once !== "function") {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (_shutdownHandler) {
|
|
789
|
+
process.removeListener("SIGTERM", _shutdownHandler);
|
|
790
|
+
process.removeListener("SIGINT", _shutdownHandler);
|
|
791
|
+
}
|
|
792
|
+
let shutdownCalled = false;
|
|
793
|
+
const shutdown = (signal) => {
|
|
794
|
+
if (shutdownCalled) return;
|
|
795
|
+
shutdownCalled = true;
|
|
796
|
+
void provider.shutdown().catch((err) => {
|
|
797
|
+
console.warn(
|
|
798
|
+
`[glasstrace] Error during OTel shutdown: ${err instanceof Error ? err.message : String(err)}`
|
|
799
|
+
);
|
|
800
|
+
}).finally(() => {
|
|
801
|
+
process.removeListener("SIGTERM", _shutdownHandler);
|
|
802
|
+
process.removeListener("SIGINT", _shutdownHandler);
|
|
803
|
+
process.kill(process.pid, signal);
|
|
804
|
+
});
|
|
805
|
+
};
|
|
806
|
+
const handler = (signal) => shutdown(signal);
|
|
807
|
+
_shutdownHandler = handler;
|
|
808
|
+
process.once("SIGTERM", handler);
|
|
809
|
+
process.once("SIGINT", handler);
|
|
810
|
+
}
|
|
781
811
|
async function configureOtel(config, sessionManager) {
|
|
782
812
|
const exporterUrl = `${config.endpoint}/v1/traces`;
|
|
783
813
|
let createOtlpExporter = null;
|
|
@@ -818,7 +848,16 @@ async function configureOtel(config, sessionManager) {
|
|
|
818
848
|
}
|
|
819
849
|
try {
|
|
820
850
|
const otelSdk = await import("@opentelemetry/sdk-trace-base");
|
|
821
|
-
const
|
|
851
|
+
const otelApi3 = await import("@opentelemetry/api");
|
|
852
|
+
const existingProvider = otelApi3.trace.getTracerProvider();
|
|
853
|
+
const probeTracer = existingProvider.getTracer("glasstrace-probe");
|
|
854
|
+
if (probeTracer.constructor.name !== "ProxyTracer") {
|
|
855
|
+
console.warn(
|
|
856
|
+
"[glasstrace] An existing OpenTelemetry TracerProvider is already registered. Glasstrace will not overwrite it. To use Glasstrace alongside another tracing tool, add GlasstraceExporter as an additional span processor on your existing provider."
|
|
857
|
+
);
|
|
858
|
+
_activeExporter = null;
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
822
861
|
if (!createOtlpExporter) {
|
|
823
862
|
const consoleExporter = new otelSdk.ConsoleSpanExporter();
|
|
824
863
|
const consoleGlasstraceExporter = new GlasstraceExporter({
|
|
@@ -837,20 +876,16 @@ async function configureOtel(config, sessionManager) {
|
|
|
837
876
|
const provider2 = new otelSdk.BasicTracerProvider({
|
|
838
877
|
spanProcessors: [processor2]
|
|
839
878
|
});
|
|
840
|
-
|
|
879
|
+
otelApi3.trace.setGlobalTracerProvider(provider2);
|
|
880
|
+
registerShutdownHooks(provider2);
|
|
841
881
|
return;
|
|
842
882
|
}
|
|
843
|
-
const processor = new otelSdk.
|
|
883
|
+
const processor = new otelSdk.BatchSpanProcessor(glasstraceExporter);
|
|
844
884
|
const provider = new otelSdk.BasicTracerProvider({
|
|
845
885
|
spanProcessors: [processor]
|
|
846
886
|
});
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
console.warn(
|
|
850
|
-
"[glasstrace] An existing OpenTelemetry TracerProvider was detected and will be replaced. If you use another tracing tool, configure Glasstrace as an additional exporter instead."
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
|
-
otelApi.trace.setGlobalTracerProvider(provider);
|
|
887
|
+
otelApi3.trace.setGlobalTracerProvider(provider);
|
|
888
|
+
registerShutdownHooks(provider);
|
|
854
889
|
} catch {
|
|
855
890
|
console.warn(
|
|
856
891
|
"[glasstrace] Neither @vercel/otel nor @opentelemetry/sdk-trace-base available. Tracing disabled."
|
|
@@ -858,7 +893,108 @@ async function configureOtel(config, sessionManager) {
|
|
|
858
893
|
}
|
|
859
894
|
}
|
|
860
895
|
|
|
896
|
+
// src/console-capture.ts
|
|
897
|
+
var isGlasstraceLog = false;
|
|
898
|
+
var originalError = null;
|
|
899
|
+
var originalWarn = null;
|
|
900
|
+
var installed = false;
|
|
901
|
+
var otelApi = null;
|
|
902
|
+
function formatArgs(args) {
|
|
903
|
+
return args.map((arg) => {
|
|
904
|
+
if (typeof arg === "string") return arg;
|
|
905
|
+
if (arg instanceof Error) return arg.stack ?? arg.message;
|
|
906
|
+
try {
|
|
907
|
+
return JSON.stringify(arg);
|
|
908
|
+
} catch {
|
|
909
|
+
return String(arg);
|
|
910
|
+
}
|
|
911
|
+
}).join(" ");
|
|
912
|
+
}
|
|
913
|
+
function isSdkMessage(args) {
|
|
914
|
+
return typeof args[0] === "string" && args[0].startsWith("[glasstrace]");
|
|
915
|
+
}
|
|
916
|
+
async function installConsoleCapture() {
|
|
917
|
+
if (installed) return;
|
|
918
|
+
try {
|
|
919
|
+
otelApi = await import("@opentelemetry/api");
|
|
920
|
+
} catch {
|
|
921
|
+
otelApi = null;
|
|
922
|
+
}
|
|
923
|
+
originalError = console.error;
|
|
924
|
+
originalWarn = console.warn;
|
|
925
|
+
installed = true;
|
|
926
|
+
console.error = (...args) => {
|
|
927
|
+
originalError.apply(console, args);
|
|
928
|
+
if (isGlasstraceLog || isSdkMessage(args)) return;
|
|
929
|
+
if (otelApi) {
|
|
930
|
+
const span = otelApi.trace.getSpan(otelApi.context.active());
|
|
931
|
+
if (span) {
|
|
932
|
+
span.addEvent("console.error", {
|
|
933
|
+
"console.message": formatArgs(args)
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
console.warn = (...args) => {
|
|
939
|
+
originalWarn.apply(console, args);
|
|
940
|
+
if (isGlasstraceLog || isSdkMessage(args)) return;
|
|
941
|
+
if (otelApi) {
|
|
942
|
+
const span = otelApi.trace.getSpan(otelApi.context.active());
|
|
943
|
+
if (span) {
|
|
944
|
+
span.addEvent("console.warn", {
|
|
945
|
+
"console.message": formatArgs(args)
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/capture-error.ts
|
|
953
|
+
var otelApi2 = null;
|
|
954
|
+
var otelLoadAttempted = false;
|
|
955
|
+
var otelLoadPromise = null;
|
|
956
|
+
async function _preloadOtelApi() {
|
|
957
|
+
if (otelLoadAttempted) return;
|
|
958
|
+
otelLoadAttempted = true;
|
|
959
|
+
try {
|
|
960
|
+
otelApi2 = await import("@opentelemetry/api");
|
|
961
|
+
} catch {
|
|
962
|
+
otelApi2 = null;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
function captureError(error) {
|
|
966
|
+
if (otelApi2) {
|
|
967
|
+
recordError(otelApi2, error);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
if (!otelLoadAttempted) {
|
|
971
|
+
otelLoadPromise ??= _preloadOtelApi();
|
|
972
|
+
}
|
|
973
|
+
if (otelLoadPromise) {
|
|
974
|
+
void otelLoadPromise.then(() => {
|
|
975
|
+
if (otelApi2) {
|
|
976
|
+
recordError(otelApi2, error);
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function recordError(api, error) {
|
|
982
|
+
try {
|
|
983
|
+
const span = api.trace.getSpan(api.context.active());
|
|
984
|
+
if (!span) return;
|
|
985
|
+
const attributes = {
|
|
986
|
+
"error.message": String(error)
|
|
987
|
+
};
|
|
988
|
+
if (error instanceof Error) {
|
|
989
|
+
attributes["error.type"] = error.constructor.name;
|
|
990
|
+
}
|
|
991
|
+
span.addEvent("glasstrace.error", attributes);
|
|
992
|
+
} catch {
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
861
996
|
// src/register.ts
|
|
997
|
+
var consoleCaptureInstalled = false;
|
|
862
998
|
var discoveryHandler = null;
|
|
863
999
|
var isRegistered = false;
|
|
864
1000
|
var registrationGeneration = 0;
|
|
@@ -869,7 +1005,7 @@ function registerGlasstrace(options) {
|
|
|
869
1005
|
}
|
|
870
1006
|
const config = resolveConfig(options);
|
|
871
1007
|
if (config.verbose) {
|
|
872
|
-
console.info("[glasstrace]
|
|
1008
|
+
console.info("[glasstrace] Config resolved.");
|
|
873
1009
|
}
|
|
874
1010
|
if (isProductionDisabled(config)) {
|
|
875
1011
|
console.warn(
|
|
@@ -878,7 +1014,7 @@ function registerGlasstrace(options) {
|
|
|
878
1014
|
return;
|
|
879
1015
|
}
|
|
880
1016
|
if (config.verbose) {
|
|
881
|
-
console.info("[glasstrace]
|
|
1017
|
+
console.info("[glasstrace] Not production-disabled.");
|
|
882
1018
|
}
|
|
883
1019
|
const anonymous = isAnonymousMode(config);
|
|
884
1020
|
let effectiveKey = config.apiKey;
|
|
@@ -887,7 +1023,7 @@ function registerGlasstrace(options) {
|
|
|
887
1023
|
}
|
|
888
1024
|
if (config.verbose) {
|
|
889
1025
|
console.info(
|
|
890
|
-
`[glasstrace]
|
|
1026
|
+
`[glasstrace] Auth mode = ${anonymous ? "anonymous" : "dev-key"}.`
|
|
891
1027
|
);
|
|
892
1028
|
}
|
|
893
1029
|
const cachedInitResponse = loadCachedConfig();
|
|
@@ -896,19 +1032,21 @@ function registerGlasstrace(options) {
|
|
|
896
1032
|
}
|
|
897
1033
|
if (config.verbose) {
|
|
898
1034
|
console.info(
|
|
899
|
-
`[glasstrace]
|
|
1035
|
+
`[glasstrace] Cached config ${cachedInitResponse ? "loaded and applied" : "not found"}.`
|
|
900
1036
|
);
|
|
901
1037
|
}
|
|
902
1038
|
const sessionManager = new SessionManager();
|
|
903
1039
|
if (config.verbose) {
|
|
904
|
-
console.info("[glasstrace]
|
|
1040
|
+
console.info("[glasstrace] SessionManager created.");
|
|
905
1041
|
}
|
|
906
1042
|
isRegistered = true;
|
|
907
1043
|
const currentGeneration = registrationGeneration;
|
|
908
1044
|
void configureOtel(config, sessionManager).then(
|
|
909
1045
|
() => {
|
|
1046
|
+
void _preloadOtelApi();
|
|
1047
|
+
maybeInstallConsoleCapture();
|
|
910
1048
|
if (config.verbose) {
|
|
911
|
-
console.info("[glasstrace]
|
|
1049
|
+
console.info("[glasstrace] OTel configured.");
|
|
912
1050
|
}
|
|
913
1051
|
},
|
|
914
1052
|
(err) => {
|
|
@@ -926,7 +1064,7 @@ function registerGlasstrace(options) {
|
|
|
926
1064
|
() => sessionManager.getSessionId(getResolvedApiKey())
|
|
927
1065
|
);
|
|
928
1066
|
if (config.verbose) {
|
|
929
|
-
console.info("[glasstrace]
|
|
1067
|
+
console.info("[glasstrace] Discovery endpoint registered (key pending).");
|
|
930
1068
|
}
|
|
931
1069
|
void (async () => {
|
|
932
1070
|
try {
|
|
@@ -942,9 +1080,10 @@ function registerGlasstrace(options) {
|
|
|
942
1080
|
() => sessionManager.getSessionId(getResolvedApiKey())
|
|
943
1081
|
);
|
|
944
1082
|
if (config.verbose) {
|
|
945
|
-
console.info("[glasstrace]
|
|
1083
|
+
console.info("[glasstrace] Background init firing.");
|
|
946
1084
|
}
|
|
947
|
-
await performInit(config, anonKey, "0.
|
|
1085
|
+
await performInit(config, anonKey, "0.2.0");
|
|
1086
|
+
maybeInstallConsoleCapture();
|
|
948
1087
|
} catch (err) {
|
|
949
1088
|
console.warn(
|
|
950
1089
|
`[glasstrace] Background init failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -961,9 +1100,10 @@ function registerGlasstrace(options) {
|
|
|
961
1100
|
effectiveKey = anonKey;
|
|
962
1101
|
if (currentGeneration !== registrationGeneration) return;
|
|
963
1102
|
if (config.verbose) {
|
|
964
|
-
console.info("[glasstrace]
|
|
1103
|
+
console.info("[glasstrace] Background init firing.");
|
|
965
1104
|
}
|
|
966
|
-
await performInit(config, anonKey, "0.
|
|
1105
|
+
await performInit(config, anonKey, "0.2.0");
|
|
1106
|
+
maybeInstallConsoleCapture();
|
|
967
1107
|
} catch (err) {
|
|
968
1108
|
console.warn(
|
|
969
1109
|
`[glasstrace] Background init failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -982,9 +1122,10 @@ function registerGlasstrace(options) {
|
|
|
982
1122
|
}
|
|
983
1123
|
if (currentGeneration !== registrationGeneration) return;
|
|
984
1124
|
if (config.verbose) {
|
|
985
|
-
console.info("[glasstrace]
|
|
1125
|
+
console.info("[glasstrace] Background init firing.");
|
|
986
1126
|
}
|
|
987
|
-
await performInit(config, anonKeyForInit, "0.
|
|
1127
|
+
await performInit(config, anonKeyForInit, "0.2.0");
|
|
1128
|
+
maybeInstallConsoleCapture();
|
|
988
1129
|
} catch (err) {
|
|
989
1130
|
console.warn(
|
|
990
1131
|
`[glasstrace] Background init failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -993,7 +1134,7 @@ function registerGlasstrace(options) {
|
|
|
993
1134
|
})();
|
|
994
1135
|
}
|
|
995
1136
|
if (config.coverageMapEnabled && config.verbose) {
|
|
996
|
-
console.info("[glasstrace]
|
|
1137
|
+
console.info("[glasstrace] Import graph building skipped.");
|
|
997
1138
|
}
|
|
998
1139
|
} catch (err) {
|
|
999
1140
|
console.warn(
|
|
@@ -1004,6 +1145,13 @@ function registerGlasstrace(options) {
|
|
|
1004
1145
|
function getDiscoveryHandler() {
|
|
1005
1146
|
return discoveryHandler;
|
|
1006
1147
|
}
|
|
1148
|
+
function maybeInstallConsoleCapture() {
|
|
1149
|
+
if (consoleCaptureInstalled) return;
|
|
1150
|
+
if (getActiveConfig().consoleErrors) {
|
|
1151
|
+
consoleCaptureInstalled = true;
|
|
1152
|
+
void installConsoleCapture();
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1007
1155
|
function isDiscoveryEnabled(config) {
|
|
1008
1156
|
if (process.env.GLASSTRACE_DISCOVERY_ENABLED === "true") return true;
|
|
1009
1157
|
if (process.env.GLASSTRACE_DISCOVERY_ENABLED === "false") return false;
|
|
@@ -1088,6 +1236,10 @@ async function uploadSourceMaps(apiKey, endpoint, buildHash, maps) {
|
|
|
1088
1236
|
body: JSON.stringify(body)
|
|
1089
1237
|
});
|
|
1090
1238
|
if (!response.ok) {
|
|
1239
|
+
try {
|
|
1240
|
+
await response.text();
|
|
1241
|
+
} catch {
|
|
1242
|
+
}
|
|
1091
1243
|
throw new Error(
|
|
1092
1244
|
`Source map upload failed: ${String(response.status)} ${response.statusText}`
|
|
1093
1245
|
);
|
|
@@ -1163,6 +1315,7 @@ export {
|
|
|
1163
1315
|
SdkError,
|
|
1164
1316
|
SessionManager,
|
|
1165
1317
|
buildImportGraph,
|
|
1318
|
+
captureError,
|
|
1166
1319
|
classifyFetchTarget,
|
|
1167
1320
|
collectSourceMaps,
|
|
1168
1321
|
computeBuildHash,
|