@use-tusk/drift-node-sdk 0.1.0 → 0.1.4
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/README.md +67 -14
- package/dist/index.cjs +385 -230
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +385 -231
- package/dist/index.js.map +1 -1
- package/hook.mjs +11 -0
- package/package.json +25 -9
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,8 @@ let semver = require("semver");
|
|
|
34
34
|
semver = __toESM(semver);
|
|
35
35
|
let require_in_the_middle = require("require-in-the-middle");
|
|
36
36
|
require_in_the_middle = __toESM(require_in_the_middle);
|
|
37
|
+
let import_in_the_middle = require("import-in-the-middle");
|
|
38
|
+
import_in_the_middle = __toESM(import_in_the_middle);
|
|
37
39
|
let __use_tusk_drift_schemas_google_protobuf_struct = require("@use-tusk/drift-schemas/google/protobuf/struct");
|
|
38
40
|
__use_tusk_drift_schemas_google_protobuf_struct = __toESM(__use_tusk_drift_schemas_google_protobuf_struct);
|
|
39
41
|
let __opentelemetry_api = require("@opentelemetry/api");
|
|
@@ -89,7 +91,7 @@ var TdInstrumentationAbstract = class {
|
|
|
89
91
|
|
|
90
92
|
//#endregion
|
|
91
93
|
//#region package.json
|
|
92
|
-
var version = "0.1.
|
|
94
|
+
var version = "0.1.4";
|
|
93
95
|
|
|
94
96
|
//#endregion
|
|
95
97
|
//#region src/version.ts
|
|
@@ -414,10 +416,11 @@ function sendUnpatchedDependencyAlert({ method, spanId, traceId, stackTrace }) {
|
|
|
414
416
|
|
|
415
417
|
//#endregion
|
|
416
418
|
//#region src/instrumentation/core/baseClasses/TdInstrumentationBase.ts
|
|
417
|
-
var TdInstrumentationBase = class extends TdInstrumentationAbstract {
|
|
419
|
+
var TdInstrumentationBase = class TdInstrumentationBase extends TdInstrumentationAbstract {
|
|
418
420
|
constructor(instrumentationName, config = {}) {
|
|
419
421
|
super(instrumentationName, config);
|
|
420
422
|
this._modules = [];
|
|
423
|
+
this._hooks = [];
|
|
421
424
|
this._enabled = false;
|
|
422
425
|
let modules = this.init();
|
|
423
426
|
if (modules && !Array.isArray(modules)) modules = [modules];
|
|
@@ -458,12 +461,35 @@ var TdInstrumentationBase = class extends TdInstrumentationAbstract {
|
|
|
458
461
|
const onRequire = (exports$1, name, baseDir) => {
|
|
459
462
|
return this._onRequire(module$1, exports$1, name, baseDir);
|
|
460
463
|
};
|
|
461
|
-
|
|
464
|
+
const hookFn = (exports$1, name, baseDir) => {
|
|
465
|
+
if (!baseDir && path.default.isAbsolute(name)) {
|
|
466
|
+
const parsedPath = path.default.parse(name);
|
|
467
|
+
name = parsedPath.name;
|
|
468
|
+
baseDir = parsedPath.dir;
|
|
469
|
+
}
|
|
470
|
+
return this._onRequire(module$1, exports$1, name, baseDir || void 0);
|
|
471
|
+
};
|
|
472
|
+
const cjsHook = new require_in_the_middle.Hook([module$1.name], { internals: true }, onRequire);
|
|
473
|
+
this._hooks.push(cjsHook);
|
|
474
|
+
const esmHook = new import_in_the_middle.Hook([module$1.name], { internals: false }, hookFn);
|
|
475
|
+
this._hooks.push(esmHook);
|
|
462
476
|
}
|
|
463
477
|
}
|
|
464
478
|
isEnabled() {
|
|
465
479
|
return this._enabled;
|
|
466
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Mark a module as patched.
|
|
483
|
+
*/
|
|
484
|
+
markModuleAsPatched(moduleExports) {
|
|
485
|
+
TdInstrumentationBase._patchedModules.add(moduleExports);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Check if a module has already been patched.
|
|
489
|
+
*/
|
|
490
|
+
isModulePatched(moduleExports) {
|
|
491
|
+
return TdInstrumentationBase._patchedModules.has(moduleExports);
|
|
492
|
+
}
|
|
467
493
|
_onRequire(module$1, exports$1, name, baseDir) {
|
|
468
494
|
if (!baseDir) {
|
|
469
495
|
if (typeof module$1.patch === "function") {
|
|
@@ -526,6 +552,7 @@ var TdInstrumentationBase = class extends TdInstrumentationAbstract {
|
|
|
526
552
|
}, exports$1);
|
|
527
553
|
}
|
|
528
554
|
};
|
|
555
|
+
TdInstrumentationBase._patchedModules = /* @__PURE__ */ new WeakSet();
|
|
529
556
|
|
|
530
557
|
//#endregion
|
|
531
558
|
//#region src/instrumentation/core/baseClasses/TdInstrumentationNodeModule.ts
|
|
@@ -1552,10 +1579,11 @@ async function findMockResponseAsync({ mockRequestData, tuskDrift, inputValueSch
|
|
|
1552
1579
|
|
|
1553
1580
|
//#endregion
|
|
1554
1581
|
//#region src/instrumentation/libraries/http/mocks/TdMockClientRequest.ts
|
|
1582
|
+
let ClientRequest;
|
|
1555
1583
|
/**
|
|
1556
1584
|
* Mock ClientRequest implementation for Tusk Drift HTTP replay
|
|
1557
1585
|
*/
|
|
1558
|
-
var TdMockClientRequest = class extends events.EventEmitter {
|
|
1586
|
+
var TdMockClientRequest = class TdMockClientRequest extends events.EventEmitter {
|
|
1559
1587
|
constructor(options, spanInfo, callback) {
|
|
1560
1588
|
super();
|
|
1561
1589
|
this.INSTRUMENTATION_NAME = "HttpInstrumentation";
|
|
@@ -1564,6 +1592,7 @@ var TdMockClientRequest = class extends events.EventEmitter {
|
|
|
1564
1592
|
this.playbackStarted = false;
|
|
1565
1593
|
this.readyToStartPlaybackOnSocketEvent = false;
|
|
1566
1594
|
this.headers = {};
|
|
1595
|
+
TdMockClientRequest._setupPrototype();
|
|
1567
1596
|
this.tuskDrift = TuskDriftCore.getInstance();
|
|
1568
1597
|
this.spanInfo = spanInfo;
|
|
1569
1598
|
if (!options || Object.keys(options).length === 0) throw new Error("Making a request with empty `options` is not supported in TdMockClientRequest");
|
|
@@ -1804,8 +1833,16 @@ var TdMockClientRequest = class extends events.EventEmitter {
|
|
|
1804
1833
|
this.socket.destroy();
|
|
1805
1834
|
return this;
|
|
1806
1835
|
}
|
|
1836
|
+
static _setupPrototype() {
|
|
1837
|
+
if (!ClientRequest) {
|
|
1838
|
+
ClientRequest = require("http").ClientRequest;
|
|
1839
|
+
Object.setPrototypeOf(TdMockClientRequest.prototype, ClientRequest.prototype);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
static initialize() {
|
|
1843
|
+
TdMockClientRequest._setupPrototype();
|
|
1844
|
+
}
|
|
1807
1845
|
};
|
|
1808
|
-
Object.setPrototypeOf(TdMockClientRequest.prototype, http.ClientRequest.prototype);
|
|
1809
1846
|
|
|
1810
1847
|
//#endregion
|
|
1811
1848
|
//#region src/instrumentation/libraries/http/HttpReplayHooks.ts
|
|
@@ -1930,6 +1967,7 @@ function wrap(target, propertyName, wrapper) {
|
|
|
1930
1967
|
wrapped._original = original;
|
|
1931
1968
|
wrapped._propertyName = propertyName;
|
|
1932
1969
|
target[propertyName] = wrapped;
|
|
1970
|
+
return wrapped;
|
|
1933
1971
|
}
|
|
1934
1972
|
|
|
1935
1973
|
//#endregion
|
|
@@ -2075,7 +2113,19 @@ var HttpTransformEngine = class {
|
|
|
2075
2113
|
const target = span[selector];
|
|
2076
2114
|
if (!target) return false;
|
|
2077
2115
|
try {
|
|
2078
|
-
|
|
2116
|
+
const body = target.body;
|
|
2117
|
+
if (!body) return false;
|
|
2118
|
+
let bodyObj;
|
|
2119
|
+
if (typeof body === "string") try {
|
|
2120
|
+
const decoded = Buffer.from(body, "base64").toString("utf8");
|
|
2121
|
+
bodyObj = JSON.parse(decoded);
|
|
2122
|
+
} catch (e) {
|
|
2123
|
+
return false;
|
|
2124
|
+
}
|
|
2125
|
+
else bodyObj = body;
|
|
2126
|
+
const nodes = jsonpath.default.apply(bodyObj, jsonPath, actionFunction);
|
|
2127
|
+
if (typeof body === "string" && nodes.length > 0) target.body = Buffer.from(JSON.stringify(bodyObj)).toString("base64");
|
|
2128
|
+
return nodes.length > 0;
|
|
2079
2129
|
} catch (error) {
|
|
2080
2130
|
return false;
|
|
2081
2131
|
}
|
|
@@ -2083,9 +2133,8 @@ var HttpTransformEngine = class {
|
|
|
2083
2133
|
}
|
|
2084
2134
|
compileHeaderAction(headerName, actionFunction, direction) {
|
|
2085
2135
|
const lowerHeader = headerName.toLowerCase();
|
|
2086
|
-
const selector = direction === "inbound" ? "inputValue" : "outputValue";
|
|
2087
2136
|
return (span) => {
|
|
2088
|
-
const target = span
|
|
2137
|
+
const target = span.inputValue;
|
|
2089
2138
|
if (!target?.headers) return false;
|
|
2090
2139
|
let applied = false;
|
|
2091
2140
|
for (const key of Object.keys(target.headers)) if (key.toLowerCase() === lowerHeader) {
|
|
@@ -2106,7 +2155,15 @@ var HttpTransformEngine = class {
|
|
|
2106
2155
|
return (span) => {
|
|
2107
2156
|
const target = span[selector];
|
|
2108
2157
|
if (!target || target.body === void 0) return false;
|
|
2109
|
-
|
|
2158
|
+
if (direction === "outbound") if (typeof target.body === "string") try {
|
|
2159
|
+
const decoded = Buffer.from(target.body, "base64").toString("utf8");
|
|
2160
|
+
const transformed = actionFunction(decoded);
|
|
2161
|
+
target.body = Buffer.from(transformed).toString("base64");
|
|
2162
|
+
} catch (error) {
|
|
2163
|
+
target.body = Buffer.from(actionFunction(target.body)).toString("base64");
|
|
2164
|
+
}
|
|
2165
|
+
else target.body = Buffer.from(actionFunction(JSON.stringify(target.body))).toString("base64");
|
|
2166
|
+
else target.body = actionFunction(typeof target.body === "string" ? target.body : JSON.stringify(target.body));
|
|
2110
2167
|
return true;
|
|
2111
2168
|
};
|
|
2112
2169
|
}
|
|
@@ -2212,18 +2269,25 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2212
2269
|
_patchHttpModule(httpModule, protocol) {
|
|
2213
2270
|
const protocolUpper = protocol.toUpperCase();
|
|
2214
2271
|
logger.debug(`[HttpInstrumentation] Patching ${protocolUpper} module in ${this.mode} mode`);
|
|
2215
|
-
if (httpModule
|
|
2272
|
+
if (this.isModulePatched(httpModule)) {
|
|
2216
2273
|
logger.debug(`[HttpInstrumentation] ${protocolUpper} module already patched, skipping`);
|
|
2217
2274
|
return httpModule;
|
|
2218
2275
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2276
|
+
if (httpModule[Symbol.toStringTag] === "Module") {
|
|
2277
|
+
if (httpModule.default) {
|
|
2278
|
+
this._wrap(httpModule.default, "request", this._getRequestPatchFn(protocol));
|
|
2279
|
+
this._wrap(httpModule.default, "get", this._getGetPatchFn(protocol));
|
|
2280
|
+
}
|
|
2281
|
+
} else {
|
|
2282
|
+
this._wrap(httpModule, "request", this._getRequestPatchFn(protocol));
|
|
2283
|
+
this._wrap(httpModule, "get", this._getGetPatchFn(protocol));
|
|
2284
|
+
}
|
|
2221
2285
|
const HttpServer = httpModule.Server;
|
|
2222
2286
|
if (HttpServer && HttpServer.prototype) {
|
|
2223
2287
|
this._wrap(HttpServer.prototype, "emit", this._getServerEmitPatchFn(protocol));
|
|
2224
2288
|
logger.debug(`[HttpInstrumentation] Wrapped Server.prototype.emit for ${protocolUpper}`);
|
|
2225
2289
|
}
|
|
2226
|
-
httpModule
|
|
2290
|
+
this.markModuleAsPatched(httpModule);
|
|
2227
2291
|
logger.debug(`[HttpInstrumentation] ${protocolUpper} module patching complete`);
|
|
2228
2292
|
return httpModule;
|
|
2229
2293
|
}
|
|
@@ -2568,7 +2632,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2568
2632
|
}
|
|
2569
2633
|
});
|
|
2570
2634
|
}
|
|
2571
|
-
_captureClientRequestBody(req, spanInfo, inputValue, schemaMerges) {
|
|
2635
|
+
_captureClientRequestBody(req, spanInfo, inputValue, schemaMerges, onBodyCaptured) {
|
|
2572
2636
|
const requestBodyChunks = [];
|
|
2573
2637
|
let requestBodyCaptured = false;
|
|
2574
2638
|
const originalWrite = req.write?.bind(req);
|
|
@@ -2590,6 +2654,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2590
2654
|
body: encodedBody,
|
|
2591
2655
|
bodySize: bodyBuffer.length
|
|
2592
2656
|
};
|
|
2657
|
+
if (onBodyCaptured) onBodyCaptured(updatedInputValue);
|
|
2593
2658
|
SpanUtils.addSpanAttributes(spanInfo.span, {
|
|
2594
2659
|
inputValue: updatedInputValue,
|
|
2595
2660
|
inputSchemaMerges: {
|
|
@@ -2611,7 +2676,10 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2611
2676
|
}
|
|
2612
2677
|
_handleOutboundRequestInSpan(originalRequest, args, spanInfo, inputValue, schemaMerges) {
|
|
2613
2678
|
const req = originalRequest.apply(this, args);
|
|
2614
|
-
|
|
2679
|
+
let completeInputValue = inputValue;
|
|
2680
|
+
this._captureClientRequestBody(req, spanInfo, inputValue, schemaMerges, (updatedInputValue) => {
|
|
2681
|
+
completeInputValue = updatedInputValue;
|
|
2682
|
+
});
|
|
2615
2683
|
req.on("response", (res) => {
|
|
2616
2684
|
logger.debug(`[HttpInstrumentation] HTTP response received: ${res.statusCode} (${SpanUtils.getTraceInfo()})`);
|
|
2617
2685
|
const outputValue = {
|
|
@@ -2651,7 +2719,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2651
2719
|
},
|
|
2652
2720
|
headers: { matchImportance: 0 }
|
|
2653
2721
|
},
|
|
2654
|
-
inputValue
|
|
2722
|
+
inputValue: completeInputValue
|
|
2655
2723
|
});
|
|
2656
2724
|
} catch (error) {
|
|
2657
2725
|
logger.error(`[HttpInstrumentation] Error processing response body:`, error);
|
|
@@ -2669,7 +2737,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2669
2737
|
},
|
|
2670
2738
|
headers: { matchImportance: 0 }
|
|
2671
2739
|
},
|
|
2672
|
-
inputValue
|
|
2740
|
+
inputValue: completeInputValue
|
|
2673
2741
|
});
|
|
2674
2742
|
} catch (error) {
|
|
2675
2743
|
logger.error(`[HttpInstrumentation] Error adding output attributes to span:`, error);
|
|
@@ -2912,7 +2980,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2912
2980
|
return fallback;
|
|
2913
2981
|
}
|
|
2914
2982
|
_wrap(target, propertyName, wrapper) {
|
|
2915
|
-
wrap(target, propertyName, wrapper);
|
|
2983
|
+
return wrap(target, propertyName, wrapper);
|
|
2916
2984
|
}
|
|
2917
2985
|
};
|
|
2918
2986
|
|
|
@@ -3307,7 +3375,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3307
3375
|
}
|
|
3308
3376
|
_patchPgModule(pgModule) {
|
|
3309
3377
|
logger.debug(`[PgInstrumentation] Patching PG module in ${this.mode} mode`);
|
|
3310
|
-
if (pgModule
|
|
3378
|
+
if (this.isModulePatched(pgModule)) {
|
|
3311
3379
|
logger.debug(`[PgInstrumentation] PG module already patched, skipping`);
|
|
3312
3380
|
return pgModule;
|
|
3313
3381
|
}
|
|
@@ -3319,7 +3387,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3319
3387
|
this._wrap(pgModule.Client.prototype, "connect", this._getConnectPatchFn("client"));
|
|
3320
3388
|
logger.debug(`[PgInstrumentation] Wrapped Client.prototype.connect`);
|
|
3321
3389
|
}
|
|
3322
|
-
pgModule
|
|
3390
|
+
this.markModuleAsPatched(pgModule);
|
|
3323
3391
|
logger.debug(`[PgInstrumentation] PG module patching complete`);
|
|
3324
3392
|
return pgModule;
|
|
3325
3393
|
}
|
|
@@ -3641,7 +3709,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3641
3709
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
3642
3710
|
}
|
|
3643
3711
|
_patchPgPoolModule(pgPoolModule) {
|
|
3644
|
-
if (pgPoolModule
|
|
3712
|
+
if (this.isModulePatched(pgPoolModule)) {
|
|
3645
3713
|
logger.debug(`[PgInstrumentation] PG Pool module already patched, skipping`);
|
|
3646
3714
|
return pgPoolModule;
|
|
3647
3715
|
}
|
|
@@ -3653,7 +3721,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3653
3721
|
this._wrap(pgPoolModule.prototype, "connect", this._getPoolConnectPatchFn());
|
|
3654
3722
|
logger.debug(`[PgInstrumentation] Wrapped Pool.prototype.connect`);
|
|
3655
3723
|
}
|
|
3656
|
-
pgPoolModule
|
|
3724
|
+
this.markModuleAsPatched(pgPoolModule);
|
|
3657
3725
|
logger.debug(`[PgInstrumentation] PG Pool module patching complete`);
|
|
3658
3726
|
return pgPoolModule;
|
|
3659
3727
|
}
|
|
@@ -3764,6 +3832,17 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3764
3832
|
}
|
|
3765
3833
|
};
|
|
3766
3834
|
|
|
3835
|
+
//#endregion
|
|
3836
|
+
//#region src/instrumentation/libraries/postgres/types.ts
|
|
3837
|
+
function isPostgresOutputValueType(value) {
|
|
3838
|
+
return value !== null && value !== void 0 && typeof value === "object" && "_tdOriginalFormat" in value && Object.values(PostgresReturnType).includes(value._tdOriginalFormat);
|
|
3839
|
+
}
|
|
3840
|
+
let PostgresReturnType = /* @__PURE__ */ function(PostgresReturnType$1) {
|
|
3841
|
+
PostgresReturnType$1["ARRAY"] = "array";
|
|
3842
|
+
PostgresReturnType$1["OBJECT"] = "object";
|
|
3843
|
+
return PostgresReturnType$1;
|
|
3844
|
+
}({});
|
|
3845
|
+
|
|
3767
3846
|
//#endregion
|
|
3768
3847
|
//#region src/instrumentation/libraries/postgres/Instrumentation.ts
|
|
3769
3848
|
var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
@@ -3782,25 +3861,41 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
3782
3861
|
}
|
|
3783
3862
|
_patchPostgresModule(postgresModule) {
|
|
3784
3863
|
logger.debug(`[PostgresInstrumentation] Patching Postgres module in ${this.mode} mode`);
|
|
3785
|
-
if (postgresModule
|
|
3864
|
+
if (this.isModulePatched(postgresModule)) {
|
|
3786
3865
|
logger.debug(`[PostgresInstrumentation] Postgres module already patched, skipping`);
|
|
3787
3866
|
return postgresModule;
|
|
3788
3867
|
}
|
|
3789
|
-
|
|
3868
|
+
const self$1 = this;
|
|
3869
|
+
if (postgresModule[Symbol.toStringTag] === "Module") {
|
|
3870
|
+
logger.debug(`[PostgresInstrumentation] Wrapping ESM default export`);
|
|
3871
|
+
this._wrap(postgresModule, "default", (originalFunction) => {
|
|
3872
|
+
return function(...args) {
|
|
3873
|
+
return self$1._handlePostgresConnection(originalFunction, args);
|
|
3874
|
+
};
|
|
3875
|
+
});
|
|
3876
|
+
} else {
|
|
3877
|
+
logger.debug(`[PostgresInstrumentation] Module is a function (CJS style)`);
|
|
3790
3878
|
const originalFunction = postgresModule;
|
|
3791
|
-
const self$1 = this;
|
|
3792
3879
|
const wrappedFunction = function(...args) {
|
|
3880
|
+
logger.debug(`[PostgresInstrumentation] Wrapped postgres() (CJS) called with args:`, args);
|
|
3793
3881
|
return self$1._handlePostgresConnection(originalFunction, args);
|
|
3794
3882
|
};
|
|
3795
3883
|
Object.setPrototypeOf(wrappedFunction, Object.getPrototypeOf(originalFunction));
|
|
3796
3884
|
Object.defineProperty(wrappedFunction, "name", { value: originalFunction.name });
|
|
3885
|
+
for (const key in originalFunction) if (originalFunction.hasOwnProperty(key)) wrappedFunction[key] = originalFunction[key];
|
|
3886
|
+
Object.getOwnPropertyNames(originalFunction).forEach((key) => {
|
|
3887
|
+
if (key !== "prototype" && key !== "length" && key !== "name") {
|
|
3888
|
+
const descriptor = Object.getOwnPropertyDescriptor(originalFunction, key);
|
|
3889
|
+
if (descriptor) Object.defineProperty(wrappedFunction, key, descriptor);
|
|
3890
|
+
}
|
|
3891
|
+
});
|
|
3797
3892
|
postgresModule = wrappedFunction;
|
|
3798
3893
|
}
|
|
3799
3894
|
if (postgresModule.sql && typeof postgresModule.sql === "function") {
|
|
3800
3895
|
this._wrap(postgresModule, "sql", this._getSqlPatchFn());
|
|
3801
3896
|
logger.debug(`[PostgresInstrumentation] Wrapped sql function`);
|
|
3802
3897
|
}
|
|
3803
|
-
postgresModule
|
|
3898
|
+
this.markModuleAsPatched(postgresModule);
|
|
3804
3899
|
logger.debug(`[PostgresInstrumentation] Postgres module patching complete`);
|
|
3805
3900
|
return postgresModule;
|
|
3806
3901
|
}
|
|
@@ -4262,8 +4357,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4262
4357
|
const isResultObject = processedResult && typeof processedResult === "object" && "rows" in processedResult;
|
|
4263
4358
|
const rows = isResultObject ? processedResult.rows || [] : processedResult || [];
|
|
4264
4359
|
return Object.assign(rows, {
|
|
4265
|
-
command: isResultObject ? processedResult.command :
|
|
4266
|
-
count: isResultObject ? processedResult.count :
|
|
4360
|
+
command: isResultObject ? processedResult.command : void 0,
|
|
4361
|
+
count: isResultObject ? processedResult.count : void 0
|
|
4267
4362
|
});
|
|
4268
4363
|
}
|
|
4269
4364
|
async handleReplayUnsafeQuery({ inputValue, spanInfo, submodule, name }) {
|
|
@@ -4314,8 +4409,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4314
4409
|
return Object.values(row);
|
|
4315
4410
|
});
|
|
4316
4411
|
return Object.assign(valueArrays, {
|
|
4317
|
-
command: isResultObject ? result.command :
|
|
4318
|
-
count: isResultObject ? result.count :
|
|
4412
|
+
command: isResultObject ? result.command : void 0,
|
|
4413
|
+
count: isResultObject ? result.count : void 0
|
|
4319
4414
|
});
|
|
4320
4415
|
});
|
|
4321
4416
|
} });
|
|
@@ -4325,61 +4420,40 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4325
4420
|
* based on common PostgreSQL data patterns.
|
|
4326
4421
|
*/
|
|
4327
4422
|
convertPostgresTypes(result) {
|
|
4328
|
-
if (!result)
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
const dateObj = new Date(value);
|
|
4340
|
-
if (!isNaN(dateObj.getTime())) convertedRow[fieldName] = dateObj;
|
|
4341
|
-
}
|
|
4342
|
-
}
|
|
4343
|
-
});
|
|
4344
|
-
return convertedRow;
|
|
4345
|
-
});
|
|
4346
|
-
return convertedResult;
|
|
4423
|
+
if (!isPostgresOutputValueType(result)) {
|
|
4424
|
+
logger.error(`[PostgresInstrumentation] output value is not of type PostgresOutputValueType: ${JSON.stringify(result)}`);
|
|
4425
|
+
return;
|
|
4426
|
+
}
|
|
4427
|
+
if (!result) return;
|
|
4428
|
+
const { _tdOriginalFormat: originalFormat,...convertedResult } = result;
|
|
4429
|
+
if (originalFormat === PostgresReturnType.OBJECT) return convertedResult;
|
|
4430
|
+
else if (originalFormat === PostgresReturnType.ARRAY) return convertedResult.rows || [];
|
|
4431
|
+
else {
|
|
4432
|
+
logger.error(`[PostgresInstrumentation] Invalid result format: ${JSON.stringify(result)}`);
|
|
4433
|
+
return;
|
|
4347
4434
|
}
|
|
4348
|
-
if (Array.isArray(result)) return result.map((row) => {
|
|
4349
|
-
if (typeof row !== "object" || row === null) return row;
|
|
4350
|
-
const convertedRow = { ...row };
|
|
4351
|
-
Object.keys(row).forEach((fieldName) => {
|
|
4352
|
-
const value = row[fieldName];
|
|
4353
|
-
if (value === null || value === void 0) return;
|
|
4354
|
-
if (typeof value === "string") {
|
|
4355
|
-
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
|
|
4356
|
-
const dateObj = new Date(value);
|
|
4357
|
-
if (!isNaN(dateObj.getTime())) convertedRow[fieldName] = dateObj;
|
|
4358
|
-
}
|
|
4359
|
-
}
|
|
4360
|
-
});
|
|
4361
|
-
return convertedRow;
|
|
4362
|
-
});
|
|
4363
|
-
return result;
|
|
4364
4435
|
}
|
|
4365
4436
|
_addOutputAttributesToSpan(spanInfo, result) {
|
|
4366
4437
|
if (!result) return;
|
|
4367
4438
|
let outputValue;
|
|
4368
|
-
if (Array.isArray(result))
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
}
|
|
4439
|
+
if (Array.isArray(result)) {
|
|
4440
|
+
logger.debug(`[PostgresInstrumentation] Adding output attributes to span for array result: ${JSON.stringify(result)}`);
|
|
4441
|
+
outputValue = {
|
|
4442
|
+
_tdOriginalFormat: PostgresReturnType.ARRAY,
|
|
4443
|
+
rows: result
|
|
4444
|
+
};
|
|
4445
|
+
} else if (typeof result === "object") {
|
|
4446
|
+
logger.debug(`[PostgresInstrumentation] Adding output attributes to span for object result: ${JSON.stringify(result)}`);
|
|
4447
|
+
outputValue = {
|
|
4448
|
+
_tdOriginalFormat: PostgresReturnType.OBJECT,
|
|
4449
|
+
count: result.count,
|
|
4450
|
+
rows: result.rows,
|
|
4451
|
+
command: result.command
|
|
4452
|
+
};
|
|
4453
|
+
} else {
|
|
4454
|
+
logger.error(`[PostgresInstrumentation] Invalid result format: ${JSON.stringify(result)}`);
|
|
4455
|
+
return;
|
|
4456
|
+
}
|
|
4383
4457
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
4384
4458
|
}
|
|
4385
4459
|
_wrap(target, propertyName, wrapper) {
|
|
@@ -4413,7 +4487,7 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
|
|
|
4413
4487
|
}
|
|
4414
4488
|
_patchNetModule(netModule) {
|
|
4415
4489
|
logger.debug(`[TcpInstrumentation] Patching NET module in ${this.mode} mode`);
|
|
4416
|
-
if (netModule
|
|
4490
|
+
if (this.isModulePatched(netModule)) {
|
|
4417
4491
|
logger.debug(`[TcpInstrumentation] NET module already patched, skipping`);
|
|
4418
4492
|
return netModule;
|
|
4419
4493
|
}
|
|
@@ -4430,7 +4504,7 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
|
|
|
4430
4504
|
netModule.Socket.prototype.write = function(...args) {
|
|
4431
4505
|
return self$1._handleTcpCall("write", originalWrite, args, this);
|
|
4432
4506
|
};
|
|
4433
|
-
netModule
|
|
4507
|
+
this.markModuleAsPatched(netModule);
|
|
4434
4508
|
return netModule;
|
|
4435
4509
|
}
|
|
4436
4510
|
_logUnpatchedDependency(methodName, currentSpanInfo, socketContext) {
|
|
@@ -4493,7 +4567,7 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
|
|
|
4493
4567
|
})];
|
|
4494
4568
|
}
|
|
4495
4569
|
_patchJsonwebtokenModule(jwtModule) {
|
|
4496
|
-
if (jwtModule
|
|
4570
|
+
if (this.isModulePatched(jwtModule)) {
|
|
4497
4571
|
logger.debug(`[JsonwebtokenInstrumentation] jsonwebtoken module already patched, skipping`);
|
|
4498
4572
|
return jwtModule;
|
|
4499
4573
|
}
|
|
@@ -4508,7 +4582,7 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
|
|
|
4508
4582
|
this._wrap(jwtModule, "sign", this._getSignPatchFn());
|
|
4509
4583
|
logger.debug(`[JsonwebtokenInstrumentation] Wrapped jwt.sign`);
|
|
4510
4584
|
}
|
|
4511
|
-
jwtModule
|
|
4585
|
+
this.markModuleAsPatched(jwtModule);
|
|
4512
4586
|
logger.debug(`[JsonwebtokenInstrumentation] jsonwebtoken module patching complete`);
|
|
4513
4587
|
return jwtModule;
|
|
4514
4588
|
}
|
|
@@ -4906,7 +4980,7 @@ var JwksRsaInstrumentation = class extends TdInstrumentationBase {
|
|
|
4906
4980
|
}
|
|
4907
4981
|
_patchJwksRsaModule(jwksModule) {
|
|
4908
4982
|
logger.debug(`[JwksRsaInstrumentation] Patching jwks-rsa module, current mode: ${this.tuskDrift.getMode()}`);
|
|
4909
|
-
if (jwksModule
|
|
4983
|
+
if (this.isModulePatched(jwksModule)) {
|
|
4910
4984
|
logger.debug(`[JwksRsaInstrumentation] jwks-rsa module already patched, skipping`);
|
|
4911
4985
|
return jwksModule;
|
|
4912
4986
|
}
|
|
@@ -4915,7 +4989,7 @@ var JwksRsaInstrumentation = class extends TdInstrumentationBase {
|
|
|
4915
4989
|
const originalExpressJwtSecret = jwksModule.expressJwtSecret;
|
|
4916
4990
|
jwksModule.expressJwtSecret = function(options) {
|
|
4917
4991
|
logger.debug(`[JwksRsaInstrumentation] expressJwtSecret called with options:`, options);
|
|
4918
|
-
|
|
4992
|
+
const modifiedOptions = { ...options };
|
|
4919
4993
|
if (self$1.tuskDrift.getMode() === TuskDriftMode.REPLAY) {
|
|
4920
4994
|
logger.debug(`[JwksRsaInstrumentation] REPLAY MODE - Disabling rate limiting for expressJwtSecret`);
|
|
4921
4995
|
modifiedOptions.rateLimit = false;
|
|
@@ -4926,7 +5000,7 @@ var JwksRsaInstrumentation = class extends TdInstrumentationBase {
|
|
|
4926
5000
|
};
|
|
4927
5001
|
logger.debug(`[JwksRsaInstrumentation] Patched expressJwtSecret method`);
|
|
4928
5002
|
}
|
|
4929
|
-
jwksModule
|
|
5003
|
+
this.markModuleAsPatched(jwksModule);
|
|
4930
5004
|
logger.debug(`[JwksRsaInstrumentation] jwks-rsa module patching complete`);
|
|
4931
5005
|
return jwksModule;
|
|
4932
5006
|
}
|
|
@@ -6060,9 +6134,9 @@ var require_types$3 = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/
|
|
|
6060
6134
|
var require_ExportResult = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/ExportResult.js": ((exports) => {
|
|
6061
6135
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6062
6136
|
exports.ExportResultCode = void 0;
|
|
6063
|
-
(function(ExportResultCode$
|
|
6064
|
-
ExportResultCode$
|
|
6065
|
-
ExportResultCode$
|
|
6137
|
+
(function(ExportResultCode$3) {
|
|
6138
|
+
ExportResultCode$3[ExportResultCode$3["SUCCESS"] = 0] = "SUCCESS";
|
|
6139
|
+
ExportResultCode$3[ExportResultCode$3["FAILED"] = 1] = "FAILED";
|
|
6066
6140
|
})(exports.ExportResultCode || (exports.ExportResultCode = {}));
|
|
6067
6141
|
}) });
|
|
6068
6142
|
|
|
@@ -6904,122 +6978,23 @@ var require_src$6 = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/co
|
|
|
6904
6978
|
}) });
|
|
6905
6979
|
|
|
6906
6980
|
//#endregion
|
|
6907
|
-
//#region src/core/tracing/
|
|
6908
|
-
var import_src$
|
|
6909
|
-
const DRIFT_API_PATH = "/api/drift";
|
|
6981
|
+
//#region src/core/tracing/SpanTransformer.ts
|
|
6982
|
+
var import_src$3 = /* @__PURE__ */ __toESM(require_src$6(), 1);
|
|
6910
6983
|
/**
|
|
6911
|
-
*
|
|
6912
|
-
* If useRemoteExport is false, TdTraceExporter stores spans organized by trace ID in separate files.
|
|
6913
|
-
* - Each trace gets its own JSONL file: `{baseDirectory}/{timestamp}_trace_{traceId}.jsonl`.
|
|
6984
|
+
* Utility class for transforming OpenTelemetry spans to CleanSpanData
|
|
6914
6985
|
*/
|
|
6915
|
-
var
|
|
6916
|
-
constructor(config) {
|
|
6917
|
-
this.traceFileMap = /* @__PURE__ */ new Map();
|
|
6918
|
-
this.baseDirectory = config.baseDirectory;
|
|
6919
|
-
this.mode = config.mode;
|
|
6920
|
-
this.useRemoteExport = config.useRemoteExport;
|
|
6921
|
-
this.observableServiceId = config.observableServiceId;
|
|
6922
|
-
this.apiKey = config.apiKey;
|
|
6923
|
-
this.tuskBackendBaseUrl = config.tuskBackendBaseUrl;
|
|
6924
|
-
this.environment = config.environment;
|
|
6925
|
-
this.sdkVersion = config.sdkVersion;
|
|
6926
|
-
this.sdkInstanceId = config.sdkInstanceId;
|
|
6927
|
-
if (!fs.existsSync(this.baseDirectory)) fs.mkdirSync(this.baseDirectory, { recursive: true });
|
|
6928
|
-
if (this.useRemoteExport && this.apiKey) {
|
|
6929
|
-
const transport = new __protobuf_ts_twirp_transport.TwirpFetchTransport({
|
|
6930
|
-
baseUrl: `${this.tuskBackendBaseUrl}${DRIFT_API_PATH}`,
|
|
6931
|
-
meta: {
|
|
6932
|
-
"x-api-key": this.apiKey,
|
|
6933
|
-
"x-td-skip-instrumentation": "true"
|
|
6934
|
-
}
|
|
6935
|
-
});
|
|
6936
|
-
this.spanExportClient = new __use_tusk_drift_schemas_backend_span_export_service_client.SpanExportServiceClient(transport);
|
|
6937
|
-
}
|
|
6938
|
-
logger.debug(`TdTraceExporter initialized - ${this.useRemoteExport ? "remote export enabled" : "local file export only"}`);
|
|
6939
|
-
}
|
|
6940
|
-
/**
|
|
6941
|
-
* Export spans to trace-specific files and optionally to remote endpoint
|
|
6942
|
-
*/
|
|
6943
|
-
export(spans, resultCallback) {
|
|
6944
|
-
logger.debug(`TdTraceExporter.export() called with ${spans.length} span(s)`);
|
|
6945
|
-
if (this.mode !== TuskDriftMode.RECORD) {
|
|
6946
|
-
logger.debug(`Not recording spans in tuskDriftMode: ${this.mode}`);
|
|
6947
|
-
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
6948
|
-
return;
|
|
6949
|
-
}
|
|
6950
|
-
if (this.useRemoteExport) if (this.spanExportClient) this.exportToRemote(spans).then(() => {
|
|
6951
|
-
logger.debug(`Successfully exported ${spans.length} spans to remote endpoint`);
|
|
6952
|
-
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
6953
|
-
}).catch((error) => {
|
|
6954
|
-
logger.error(`Failed to export spans to remote:`, error);
|
|
6955
|
-
resultCallback({
|
|
6956
|
-
code: import_src$1.ExportResultCode.FAILED,
|
|
6957
|
-
error: error instanceof Error ? error : /* @__PURE__ */ new Error("Remote export failed")
|
|
6958
|
-
});
|
|
6959
|
-
});
|
|
6960
|
-
else {
|
|
6961
|
-
logger.error("Remote export client not initialized, likely because apiKey is not provided");
|
|
6962
|
-
resultCallback({
|
|
6963
|
-
code: import_src$1.ExportResultCode.FAILED,
|
|
6964
|
-
error: /* @__PURE__ */ new Error("Remote export client not initialized, likely because apiKey is not provided")
|
|
6965
|
-
});
|
|
6966
|
-
}
|
|
6967
|
-
else {
|
|
6968
|
-
this.exportToLocalFiles(spans);
|
|
6969
|
-
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
6970
|
-
}
|
|
6971
|
-
}
|
|
6972
|
-
/**
|
|
6973
|
-
* Export spans to remote endpoint via protobuf
|
|
6974
|
-
*/
|
|
6975
|
-
async exportToRemote(spans) {
|
|
6976
|
-
if (!this.spanExportClient) throw new Error("Remote export client not initialized");
|
|
6977
|
-
if (!this.observableServiceId) throw new Error("Observable service ID not provided in config");
|
|
6978
|
-
const protoSpans = spans.map((span) => this.transformSpanToProtobuf(span));
|
|
6979
|
-
const request = {
|
|
6980
|
-
observableServiceId: this.observableServiceId,
|
|
6981
|
-
environment: this.environment,
|
|
6982
|
-
sdkVersion: this.sdkVersion,
|
|
6983
|
-
sdkInstanceId: this.sdkInstanceId,
|
|
6984
|
-
spans: protoSpans
|
|
6985
|
-
};
|
|
6986
|
-
const response = await this.spanExportClient.exportSpans(request);
|
|
6987
|
-
if (!response.response.success) throw new Error(`Remote export failed: ${response.response.message}`);
|
|
6988
|
-
}
|
|
6989
|
-
/**
|
|
6990
|
-
* Export spans to local files
|
|
6991
|
-
*/
|
|
6992
|
-
exportToLocalFiles(spans) {
|
|
6993
|
-
try {
|
|
6994
|
-
for (const span of spans) {
|
|
6995
|
-
const traceId = span.spanContext().traceId;
|
|
6996
|
-
const spanData = this.transformSpanToCleanJSON(span);
|
|
6997
|
-
let filePath = this.traceFileMap.get(traceId);
|
|
6998
|
-
if (!filePath) {
|
|
6999
|
-
const isoTimestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7000
|
-
filePath = path.join(this.baseDirectory, `${isoTimestamp}_trace_${traceId}.jsonl`);
|
|
7001
|
-
this.traceFileMap.set(traceId, filePath);
|
|
7002
|
-
}
|
|
7003
|
-
const jsonLine = JSON.stringify(spanData) + "\n";
|
|
7004
|
-
fs.appendFileSync(filePath, jsonLine, "utf8");
|
|
7005
|
-
}
|
|
7006
|
-
logger.debug(`Exported ${spans.length} span(s) to trace-specific files in ${this.baseDirectory}`);
|
|
7007
|
-
} catch (error) {
|
|
7008
|
-
logger.error(`Failed to export spans to local files:`, error);
|
|
7009
|
-
throw error;
|
|
7010
|
-
}
|
|
7011
|
-
}
|
|
6986
|
+
var SpanTransformer = class SpanTransformer {
|
|
7012
6987
|
/**
|
|
7013
6988
|
* Transform OpenTelemetry span to clean JSON format with compile-time type safety
|
|
7014
6989
|
* Return type is derived from protobuf schema but uses clean JSON.
|
|
7015
6990
|
* We use JSON because serialized protobuf is extremely verbose and not readable.
|
|
7016
6991
|
*/
|
|
7017
|
-
transformSpanToCleanJSON(span) {
|
|
6992
|
+
static transformSpanToCleanJSON(span) {
|
|
7018
6993
|
const isRootSpan = !span.parentSpanId || span.kind === __opentelemetry_api.SpanKind.SERVER;
|
|
7019
6994
|
const attributes = span.attributes;
|
|
7020
|
-
const packageName =
|
|
7021
|
-
const instrumentationName =
|
|
7022
|
-
const submoduleName =
|
|
6995
|
+
const packageName = SpanTransformer.extractPackageName(attributes);
|
|
6996
|
+
const instrumentationName = SpanTransformer.extractInstrumentationName(span, attributes);
|
|
6997
|
+
const submoduleName = SpanTransformer.extractSubmoduleName(attributes);
|
|
7023
6998
|
const inputValueString = attributes[TdSpanAttributes.INPUT_VALUE];
|
|
7024
6999
|
const inputData = JSON.parse(inputValueString);
|
|
7025
7000
|
const inputSchemaMergesString = attributes[TdSpanAttributes.INPUT_SCHEMA_MERGES];
|
|
@@ -7085,10 +7060,115 @@ var TdSpanExporter = class {
|
|
|
7085
7060
|
};
|
|
7086
7061
|
}
|
|
7087
7062
|
/**
|
|
7088
|
-
*
|
|
7063
|
+
* Extract package name from attributes or instrumentation library
|
|
7064
|
+
*/
|
|
7065
|
+
static extractPackageName(attributes) {
|
|
7066
|
+
if (attributes[TdSpanAttributes.PACKAGE_NAME]) return attributes[TdSpanAttributes.PACKAGE_NAME];
|
|
7067
|
+
return "unknown";
|
|
7068
|
+
}
|
|
7069
|
+
/**
|
|
7070
|
+
* Extract instrumentation name from span data
|
|
7089
7071
|
*/
|
|
7090
|
-
|
|
7091
|
-
|
|
7072
|
+
static extractInstrumentationName(span, attributes) {
|
|
7073
|
+
if (attributes[TdSpanAttributes.INSTRUMENTATION_NAME]) return attributes[TdSpanAttributes.INSTRUMENTATION_NAME];
|
|
7074
|
+
if (span.instrumentationLibrary?.name) return `tusk-instrumentation-${span.instrumentationLibrary.name}`;
|
|
7075
|
+
return `tusk-instrumentation-${SpanTransformer.extractPackageName(attributes)}`;
|
|
7076
|
+
}
|
|
7077
|
+
/**
|
|
7078
|
+
* Extract submodule name from attributes
|
|
7079
|
+
*/
|
|
7080
|
+
static extractSubmoduleName(attributes) {
|
|
7081
|
+
if (attributes[TdSpanAttributes.SUBMODULE_NAME]) return attributes[TdSpanAttributes.SUBMODULE_NAME];
|
|
7082
|
+
}
|
|
7083
|
+
};
|
|
7084
|
+
|
|
7085
|
+
//#endregion
|
|
7086
|
+
//#region src/core/tracing/adapters/FilesystemSpanAdapter.ts
|
|
7087
|
+
/**
|
|
7088
|
+
* Exports spans to local JSONL files organized by trace ID
|
|
7089
|
+
*/
|
|
7090
|
+
var FilesystemSpanAdapter = class {
|
|
7091
|
+
constructor(config) {
|
|
7092
|
+
this.name = "filesystem";
|
|
7093
|
+
this.traceFileMap = /* @__PURE__ */ new Map();
|
|
7094
|
+
this.baseDirectory = config.baseDirectory;
|
|
7095
|
+
if (!fs.existsSync(this.baseDirectory)) fs.mkdirSync(this.baseDirectory, { recursive: true });
|
|
7096
|
+
}
|
|
7097
|
+
async exportSpans(spans) {
|
|
7098
|
+
try {
|
|
7099
|
+
for (const span of spans) {
|
|
7100
|
+
const traceId = span.traceId;
|
|
7101
|
+
let filePath = this.traceFileMap.get(traceId);
|
|
7102
|
+
if (!filePath) {
|
|
7103
|
+
const isoTimestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7104
|
+
filePath = path.join(this.baseDirectory, `${isoTimestamp}_trace_${traceId}.jsonl`);
|
|
7105
|
+
this.traceFileMap.set(traceId, filePath);
|
|
7106
|
+
}
|
|
7107
|
+
const jsonLine = JSON.stringify(span) + "\n";
|
|
7108
|
+
fs.appendFileSync(filePath, jsonLine, "utf8");
|
|
7109
|
+
}
|
|
7110
|
+
logger.debug(`Exported ${spans.length} span(s) to trace-specific files in ${this.baseDirectory}`);
|
|
7111
|
+
return { code: import_src$3.ExportResultCode.SUCCESS };
|
|
7112
|
+
} catch (error) {
|
|
7113
|
+
logger.error(`Failed to export spans to local files:`, error);
|
|
7114
|
+
return {
|
|
7115
|
+
code: import_src$3.ExportResultCode.FAILED,
|
|
7116
|
+
error: error instanceof Error ? error : /* @__PURE__ */ new Error("Filesystem export failed")
|
|
7117
|
+
};
|
|
7118
|
+
}
|
|
7119
|
+
}
|
|
7120
|
+
async shutdown() {
|
|
7121
|
+
return Promise.resolve();
|
|
7122
|
+
}
|
|
7123
|
+
};
|
|
7124
|
+
|
|
7125
|
+
//#endregion
|
|
7126
|
+
//#region src/core/tracing/adapters/ApiSpanAdapter.ts
|
|
7127
|
+
var import_src$2 = /* @__PURE__ */ __toESM(require_src$6(), 1);
|
|
7128
|
+
const DRIFT_API_PATH = "/api/drift";
|
|
7129
|
+
/**
|
|
7130
|
+
* Exports spans to Tusk backend API via protobuf
|
|
7131
|
+
*/
|
|
7132
|
+
var ApiSpanAdapter = class {
|
|
7133
|
+
constructor(config) {
|
|
7134
|
+
this.name = "api";
|
|
7135
|
+
this.observableServiceId = config.observableServiceId;
|
|
7136
|
+
this.environment = config.environment;
|
|
7137
|
+
this.sdkVersion = config.sdkVersion;
|
|
7138
|
+
this.sdkInstanceId = config.sdkInstanceId;
|
|
7139
|
+
const transport = new __protobuf_ts_twirp_transport.TwirpFetchTransport({
|
|
7140
|
+
baseUrl: `${config.tuskBackendBaseUrl}${DRIFT_API_PATH}`,
|
|
7141
|
+
meta: {
|
|
7142
|
+
"x-api-key": config.apiKey,
|
|
7143
|
+
"x-td-skip-instrumentation": "true"
|
|
7144
|
+
}
|
|
7145
|
+
});
|
|
7146
|
+
this.spanExportClient = new __use_tusk_drift_schemas_backend_span_export_service_client.SpanExportServiceClient(transport);
|
|
7147
|
+
logger.debug("ApiSpanAdapter initialized");
|
|
7148
|
+
}
|
|
7149
|
+
async exportSpans(spans) {
|
|
7150
|
+
try {
|
|
7151
|
+
const protoSpans = spans.map((span) => this.transformSpanToProtobuf(span));
|
|
7152
|
+
const request = {
|
|
7153
|
+
observableServiceId: this.observableServiceId,
|
|
7154
|
+
environment: this.environment,
|
|
7155
|
+
sdkVersion: this.sdkVersion,
|
|
7156
|
+
sdkInstanceId: this.sdkInstanceId,
|
|
7157
|
+
spans: protoSpans
|
|
7158
|
+
};
|
|
7159
|
+
const response = await this.spanExportClient.exportSpans(request);
|
|
7160
|
+
if (!response.response.success) throw new Error(`Remote export failed: ${response.response.message}`);
|
|
7161
|
+
logger.debug(`Successfully exported ${spans.length} spans to remote endpoint`);
|
|
7162
|
+
return { code: import_src$2.ExportResultCode.SUCCESS };
|
|
7163
|
+
} catch (error) {
|
|
7164
|
+
logger.error(`Failed to export spans to remote:`, error);
|
|
7165
|
+
return {
|
|
7166
|
+
code: import_src$2.ExportResultCode.FAILED,
|
|
7167
|
+
error: error instanceof Error ? error : /* @__PURE__ */ new Error("API export failed")
|
|
7168
|
+
};
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
transformSpanToProtobuf(cleanSpan) {
|
|
7092
7172
|
return {
|
|
7093
7173
|
traceId: cleanSpan.traceId,
|
|
7094
7174
|
spanId: cleanSpan.spanId,
|
|
@@ -7106,7 +7186,7 @@ var TdSpanExporter = class {
|
|
|
7106
7186
|
outputSchemaHash: cleanSpan.outputSchemaHash || "",
|
|
7107
7187
|
inputValueHash: cleanSpan.inputValueHash || "",
|
|
7108
7188
|
outputValueHash: cleanSpan.outputValueHash || "",
|
|
7109
|
-
kind:
|
|
7189
|
+
kind: this.mapSpanKind(cleanSpan.kind),
|
|
7110
7190
|
status: cleanSpan.status,
|
|
7111
7191
|
isPreAppStart: cleanSpan.isPreAppStart,
|
|
7112
7192
|
timestamp: {
|
|
@@ -7121,32 +7201,111 @@ var TdSpanExporter = class {
|
|
|
7121
7201
|
metadata: toStruct(cleanSpan.metadata)
|
|
7122
7202
|
};
|
|
7123
7203
|
}
|
|
7204
|
+
mapSpanKind(kind) {
|
|
7205
|
+
switch (kind) {
|
|
7206
|
+
case __opentelemetry_api.SpanKind.CLIENT: return __use_tusk_drift_schemas_core_span.SpanKind.CLIENT;
|
|
7207
|
+
case __opentelemetry_api.SpanKind.SERVER: return __use_tusk_drift_schemas_core_span.SpanKind.SERVER;
|
|
7208
|
+
case __opentelemetry_api.SpanKind.PRODUCER: return __use_tusk_drift_schemas_core_span.SpanKind.PRODUCER;
|
|
7209
|
+
case __opentelemetry_api.SpanKind.CONSUMER: return __use_tusk_drift_schemas_core_span.SpanKind.CONSUMER;
|
|
7210
|
+
case __opentelemetry_api.SpanKind.INTERNAL: return __use_tusk_drift_schemas_core_span.SpanKind.INTERNAL;
|
|
7211
|
+
default: return __use_tusk_drift_schemas_core_span.SpanKind.UNSPECIFIED;
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
async shutdown() {
|
|
7215
|
+
return Promise.resolve();
|
|
7216
|
+
}
|
|
7217
|
+
};
|
|
7218
|
+
|
|
7219
|
+
//#endregion
|
|
7220
|
+
//#region src/core/tracing/TdSpanExporter.ts
|
|
7221
|
+
var import_src$1 = /* @__PURE__ */ __toESM(require_src$6(), 1);
|
|
7222
|
+
var TdSpanExporter = class {
|
|
7223
|
+
constructor(config) {
|
|
7224
|
+
this.adapters = [];
|
|
7225
|
+
this.mode = config.mode;
|
|
7226
|
+
this.setupDefaultAdapters(config);
|
|
7227
|
+
logger.debug(`TdSpanExporter initialized with ${this.adapters.length} adapter(s)`);
|
|
7228
|
+
}
|
|
7229
|
+
setupDefaultAdapters(config) {
|
|
7230
|
+
if (config.useRemoteExport && config.apiKey && config.observableServiceId) {
|
|
7231
|
+
logger.debug("TdSpanExporter using API adapter");
|
|
7232
|
+
this.addAdapter(new ApiSpanAdapter({
|
|
7233
|
+
apiKey: config.apiKey,
|
|
7234
|
+
tuskBackendBaseUrl: config.tuskBackendBaseUrl,
|
|
7235
|
+
observableServiceId: config.observableServiceId,
|
|
7236
|
+
environment: config.environment,
|
|
7237
|
+
sdkVersion: config.sdkVersion,
|
|
7238
|
+
sdkInstanceId: config.sdkInstanceId
|
|
7239
|
+
}));
|
|
7240
|
+
} else {
|
|
7241
|
+
logger.debug("TdSpanExporter falling back to filesystem adapter");
|
|
7242
|
+
this.addAdapter(new FilesystemSpanAdapter({ baseDirectory: config.baseDirectory }));
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
getAdapters() {
|
|
7246
|
+
return this.adapters;
|
|
7247
|
+
}
|
|
7124
7248
|
/**
|
|
7125
|
-
*
|
|
7249
|
+
* Add a custom export adapter
|
|
7126
7250
|
*/
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7251
|
+
addAdapter(adapter) {
|
|
7252
|
+
this.adapters.push(adapter);
|
|
7253
|
+
logger.debug(`Added ${adapter.name} adapter. Total adapters: ${this.adapters.length}`);
|
|
7130
7254
|
}
|
|
7131
7255
|
/**
|
|
7132
|
-
*
|
|
7256
|
+
* Remove a specific adapter
|
|
7133
7257
|
*/
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
if (
|
|
7137
|
-
|
|
7258
|
+
removeAdapter(adapter) {
|
|
7259
|
+
const index = this.adapters.indexOf(adapter);
|
|
7260
|
+
if (index > -1) {
|
|
7261
|
+
this.adapters.splice(index, 1);
|
|
7262
|
+
logger.debug(`Removed ${adapter.name} adapter. Total adapters: ${this.adapters.length}`);
|
|
7263
|
+
}
|
|
7138
7264
|
}
|
|
7139
7265
|
/**
|
|
7140
|
-
*
|
|
7266
|
+
* Clear all adapters
|
|
7141
7267
|
*/
|
|
7142
|
-
|
|
7143
|
-
|
|
7268
|
+
clearAdapters() {
|
|
7269
|
+
this.adapters = [];
|
|
7270
|
+
logger.debug("All adapters cleared");
|
|
7271
|
+
}
|
|
7272
|
+
/**
|
|
7273
|
+
* Set the mode for determining which adapters to run
|
|
7274
|
+
*/
|
|
7275
|
+
setMode(mode) {
|
|
7276
|
+
this.mode = mode;
|
|
7277
|
+
}
|
|
7278
|
+
/**
|
|
7279
|
+
* Export spans using all configured adapters
|
|
7280
|
+
*/
|
|
7281
|
+
export(spans, resultCallback) {
|
|
7282
|
+
logger.debug(`TdSpanExporter.export() called with ${spans.length} span(s)`);
|
|
7283
|
+
const cleanSpans = spans.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
|
|
7284
|
+
if (this.adapters.length === 0) {
|
|
7285
|
+
logger.debug("No adapters configured");
|
|
7286
|
+
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
7287
|
+
return;
|
|
7288
|
+
}
|
|
7289
|
+
const activeAdapters = this.getActiveAdapters();
|
|
7290
|
+
if (activeAdapters.length === 0) {
|
|
7291
|
+
logger.debug(`No active adapters for mode: ${this.mode}`);
|
|
7292
|
+
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
7293
|
+
return;
|
|
7294
|
+
}
|
|
7295
|
+
Promise.all(activeAdapters.map((adapter) => adapter.exportSpans(cleanSpans))).then(() => resultCallback({ code: import_src$1.ExportResultCode.SUCCESS })).catch((error) => resultCallback({
|
|
7296
|
+
code: import_src$1.ExportResultCode.FAILED,
|
|
7297
|
+
error
|
|
7298
|
+
}));
|
|
7299
|
+
}
|
|
7300
|
+
getActiveAdapters() {
|
|
7301
|
+
if (this.mode !== TuskDriftMode.RECORD) return this.adapters.filter((adapter) => adapter.name === "in-memory" || adapter.name === "callback");
|
|
7302
|
+
return this.adapters;
|
|
7144
7303
|
}
|
|
7145
7304
|
/**
|
|
7146
|
-
* Shutdown
|
|
7305
|
+
* Shutdown all adapters
|
|
7147
7306
|
*/
|
|
7148
7307
|
async shutdown() {
|
|
7149
|
-
|
|
7308
|
+
await Promise.all(this.adapters.map((adapter) => adapter.shutdown()));
|
|
7150
7309
|
}
|
|
7151
7310
|
/**
|
|
7152
7311
|
* Force flush any pending spans
|
|
@@ -10448,12 +10607,6 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10448
10607
|
default: return TuskDriftMode.DISABLED;
|
|
10449
10608
|
}
|
|
10450
10609
|
}
|
|
10451
|
-
initializeMemoryStore({ baseDirectory }) {
|
|
10452
|
-
if (this.isRunningIntegrationTest()) {
|
|
10453
|
-
logger.debug(`Initializing memory store with base directory: ${baseDirectory}`);
|
|
10454
|
-
this.memoryStore.initialize(baseDirectory);
|
|
10455
|
-
} else logger.debug("Not running integration test, skipping memory store initialization");
|
|
10456
|
-
}
|
|
10457
10610
|
registerDefaultInstrumentations() {
|
|
10458
10611
|
const transforms = this.config.transforms ?? this.initParams.transforms;
|
|
10459
10612
|
new HttpInstrumentation({
|
|
@@ -10501,20 +10654,20 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10501
10654
|
initializeTracing({ baseDirectory }) {
|
|
10502
10655
|
const serviceName = this.config.service?.name || "unknown";
|
|
10503
10656
|
logger.debug(`Initializing OpenTelemetry tracing for service: ${serviceName}`);
|
|
10504
|
-
|
|
10657
|
+
this.spanExporter = new TdSpanExporter({
|
|
10505
10658
|
baseDirectory,
|
|
10506
10659
|
mode: this.mode,
|
|
10507
10660
|
useRemoteExport: this.config.recording?.export_spans || false,
|
|
10508
10661
|
observableServiceId: this.config.service?.id,
|
|
10509
10662
|
apiKey: this.initParams.apiKey,
|
|
10510
10663
|
tuskBackendBaseUrl: this.config.tusk_api?.url || "https://api.usetusk.ai",
|
|
10511
|
-
environment: this.initParams.env,
|
|
10664
|
+
environment: this.initParams.env || "unknown",
|
|
10512
10665
|
sdkVersion: SDK_VERSION,
|
|
10513
10666
|
sdkInstanceId: this.generateSdkInstanceId()
|
|
10514
10667
|
});
|
|
10515
10668
|
this.sdk = new __opentelemetry_sdk_node.NodeSDK({
|
|
10516
10669
|
serviceName,
|
|
10517
|
-
spanProcessor: new import_src.BatchSpanProcessor(
|
|
10670
|
+
spanProcessor: new import_src.BatchSpanProcessor(this.spanExporter, {
|
|
10518
10671
|
maxQueueSize: 2048,
|
|
10519
10672
|
maxExportBatchSize: 512,
|
|
10520
10673
|
scheduledDelayMillis: 2e3,
|
|
@@ -10531,6 +10684,11 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10531
10684
|
initialize(initParams) {
|
|
10532
10685
|
this.samplingRate = this.config.recording?.sampling_rate ?? 1;
|
|
10533
10686
|
this.initParams = initParams;
|
|
10687
|
+
if (!this.initParams.env) {
|
|
10688
|
+
const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "unknown";
|
|
10689
|
+
logger.warn(`Environment not provided in initialization parameters. Using '${nodeEnv}' as the environment.`);
|
|
10690
|
+
this.initParams.env = nodeEnv;
|
|
10691
|
+
}
|
|
10534
10692
|
initializeGlobalLogger({
|
|
10535
10693
|
logLevel: initParams.logLevel || "info",
|
|
10536
10694
|
prefix: "TuskDrift"
|
|
@@ -10543,10 +10701,6 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10543
10701
|
logger.error("In record mode and export_spans is true, but API key not provided. API key is required to export spans to Tusk backend. Please provide an API key in the initialization parameters.");
|
|
10544
10702
|
return;
|
|
10545
10703
|
}
|
|
10546
|
-
if (!this.initParams.env) {
|
|
10547
|
-
logger.error("Environment not provided. Please provide an environment in the initialization parameters.");
|
|
10548
|
-
return;
|
|
10549
|
-
}
|
|
10550
10704
|
if (this.config.recording?.export_spans && !this.config.service?.id) {
|
|
10551
10705
|
logger.error("Observable service ID not provided. Please provide an observable service ID in the configuration file.");
|
|
10552
10706
|
return;
|
|
@@ -10588,9 +10742,7 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10588
10742
|
const baseDirectory = this.config.traces?.dir || path.default.join(process.cwd(), ".tusk/traces");
|
|
10589
10743
|
logger.debug(`Config: ${JSON.stringify(this.config)}`);
|
|
10590
10744
|
logger.debug(`Base directory: ${baseDirectory}`);
|
|
10591
|
-
this.config.transforms ?? this.initParams.transforms;
|
|
10592
10745
|
this.initializeTracing({ baseDirectory });
|
|
10593
|
-
this.initializeMemoryStore({ baseDirectory });
|
|
10594
10746
|
this.registerDefaultInstrumentations();
|
|
10595
10747
|
this.initialized = true;
|
|
10596
10748
|
logger.info("SDK initialized successfully");
|
|
@@ -10762,6 +10914,9 @@ var TuskDriftSDK = class {
|
|
|
10762
10914
|
markAppAsReady() {
|
|
10763
10915
|
return this.tuskDrift.markAppAsReady();
|
|
10764
10916
|
}
|
|
10917
|
+
isAppReady() {
|
|
10918
|
+
return this.tuskDrift.isAppReady();
|
|
10919
|
+
}
|
|
10765
10920
|
};
|
|
10766
10921
|
const TuskDrift = new TuskDriftSDK();
|
|
10767
10922
|
|