@use-tusk/drift-node-sdk 0.1.5 → 0.1.7
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 +7 -1
- package/dist/index.cjs +1242 -455
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.js +1172 -386
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import * as fs$1 from "fs";
|
|
3
3
|
import fs from "fs";
|
|
4
|
-
import os from "os";
|
|
5
4
|
import * as path$1 from "path";
|
|
6
5
|
import path, { normalize } from "path";
|
|
7
|
-
import { satisfies } from "semver";
|
|
6
|
+
import { parse, satisfies } from "semver";
|
|
7
|
+
import os from "os";
|
|
8
8
|
import { Hook } from "require-in-the-middle";
|
|
9
9
|
import { Hook as Hook$1 } from "import-in-the-middle";
|
|
10
10
|
import { Struct, Value } from "@use-tusk/drift-schemas/google/protobuf/struct";
|
|
@@ -53,6 +53,182 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
53
53
|
}) : target, mod));
|
|
54
54
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
55
55
|
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/nextjs/utils.ts
|
|
58
|
+
/**
|
|
59
|
+
* Get the installed Next.js version by reading package.json from node_modules.
|
|
60
|
+
*
|
|
61
|
+
* @returns The Next.js version string, or undefined if not found
|
|
62
|
+
*/
|
|
63
|
+
function getNextjsVersion() {
|
|
64
|
+
try {
|
|
65
|
+
const nextPackageJsonPath = path$1.join(process.cwd(), "node_modules", "next", "package.json");
|
|
66
|
+
if (fs$1.existsSync(nextPackageJsonPath)) return JSON.parse(fs$1.readFileSync(nextPackageJsonPath, "utf-8")).version;
|
|
67
|
+
} catch (error) {}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse a semantic version string into its components.
|
|
71
|
+
*
|
|
72
|
+
* @param version - The version string to parse (e.g., "15.0.0-canary.124")
|
|
73
|
+
* @returns Parsed version object with major, minor, patch, and prerelease
|
|
74
|
+
*/
|
|
75
|
+
function parseVersion(version$1) {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = parse(version$1);
|
|
78
|
+
if (!parsed) return {
|
|
79
|
+
major: void 0,
|
|
80
|
+
minor: void 0,
|
|
81
|
+
patch: void 0,
|
|
82
|
+
prerelease: void 0
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
major: parsed.major,
|
|
86
|
+
minor: parsed.minor,
|
|
87
|
+
patch: parsed.patch,
|
|
88
|
+
prerelease: parsed.prerelease.length > 0 ? parsed.prerelease.join(".") : void 0
|
|
89
|
+
};
|
|
90
|
+
} catch {
|
|
91
|
+
return {
|
|
92
|
+
major: void 0,
|
|
93
|
+
minor: void 0,
|
|
94
|
+
patch: void 0,
|
|
95
|
+
prerelease: void 0
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if the Next.js version requires the instrumentationHook to be set.
|
|
101
|
+
* From Next.js 15.0.0-rc.1 onwards, the instrumentationHook is no longer needed
|
|
102
|
+
* and Next.js will warn if it's set.
|
|
103
|
+
*
|
|
104
|
+
* @param version - The Next.js version string
|
|
105
|
+
* @returns true if instrumentationHook should be set, false otherwise
|
|
106
|
+
*/
|
|
107
|
+
function shouldSetInstrumentationHook(version$1) {
|
|
108
|
+
if (!version$1) return true;
|
|
109
|
+
const { major, minor, patch, prerelease } = parseVersion(version$1);
|
|
110
|
+
if (major === void 0 || minor === void 0 || patch === void 0) return true;
|
|
111
|
+
if (major >= 16) return false;
|
|
112
|
+
if (major < 15) return true;
|
|
113
|
+
if (major === 15) {
|
|
114
|
+
if (minor > 0 || patch > 0) return false;
|
|
115
|
+
if (minor === 0 && patch === 0 && prerelease === void 0) return false;
|
|
116
|
+
if (prerelease?.startsWith("rc.")) {
|
|
117
|
+
if (parseInt(prerelease.split(".")[1] || "0", 10) >= 1) return false;
|
|
118
|
+
}
|
|
119
|
+
if (prerelease?.startsWith("canary.")) {
|
|
120
|
+
if (parseInt(prerelease.split(".")[1] || "0", 10) >= 124) return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Log a message if debug mode is enabled.
|
|
128
|
+
*
|
|
129
|
+
* @param debug - Whether debug mode is enabled
|
|
130
|
+
* @param message - The message to log
|
|
131
|
+
*/
|
|
132
|
+
function debugLog(debug, message) {
|
|
133
|
+
if (debug) console.log(`[Tusk Drift] ${message}`);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Log a warning message if warnings are not suppressed.
|
|
137
|
+
*
|
|
138
|
+
* @param suppress - Whether to suppress the warning
|
|
139
|
+
* @param message - The warning message to log
|
|
140
|
+
*/
|
|
141
|
+
function warn(suppress, message) {
|
|
142
|
+
if (!suppress) console.warn(`[Tusk Drift] ${message}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/nextjs/withTuskDrift.ts
|
|
147
|
+
/**
|
|
148
|
+
* Wraps your Next.js configuration with Tusk Drift instrumentation setup.
|
|
149
|
+
*
|
|
150
|
+
* This function automatically configures Next.js to work with Tusk Drift by:
|
|
151
|
+
* - Enabling the Next.js instrumentation hook (for Next.js < 15.0.0-rc.1)
|
|
152
|
+
* - Configuring webpack externals to prevent bundling of core instrumentation packages
|
|
153
|
+
* - Preserving your existing Next.js configuration and webpack customizations
|
|
154
|
+
*
|
|
155
|
+
* @param nextConfig - Your existing Next.js configuration object (optional)
|
|
156
|
+
* @param options - Additional options to configure Tusk Drift's behavior (optional)
|
|
157
|
+
* @returns The wrapped Next.js configuration with Tusk Drift instrumentation enabled
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* Basic usage:
|
|
161
|
+
* ```javascript
|
|
162
|
+
* // next.config.js
|
|
163
|
+
* const { withTuskDrift } = require('@use-tusk/drift-node-sdk');
|
|
164
|
+
*
|
|
165
|
+
* module.exports = withTuskDrift({
|
|
166
|
+
* // Your Next.js config
|
|
167
|
+
* });
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* With debug logging:
|
|
172
|
+
* ```javascript
|
|
173
|
+
* // next.config.js
|
|
174
|
+
* const { withTuskDrift } = require('@use-tusk/drift-node-sdk');
|
|
175
|
+
*
|
|
176
|
+
* module.exports = withTuskDrift(
|
|
177
|
+
* {
|
|
178
|
+
* // Your Next.js config
|
|
179
|
+
* },
|
|
180
|
+
* {
|
|
181
|
+
* debug: true,
|
|
182
|
+
* }
|
|
183
|
+
* );
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @remarks
|
|
187
|
+
* The following webpack externals are added for server-side builds:
|
|
188
|
+
* - `require-in-the-middle` - Required for CommonJS module interception
|
|
189
|
+
* - `jsonpath` - Required for schema manipulation
|
|
190
|
+
*/
|
|
191
|
+
function withTuskDrift(nextConfig = {}, options = {}) {
|
|
192
|
+
const config = nextConfig;
|
|
193
|
+
const debug = options.debug || false;
|
|
194
|
+
const suppressAllWarnings = options.suppressWarnings || false;
|
|
195
|
+
const nextjsVersion = getNextjsVersion();
|
|
196
|
+
if (nextjsVersion) debugLog(debug, `Detected Next.js version: ${nextjsVersion}`);
|
|
197
|
+
else warn(suppressAllWarnings || false, "Could not detect Next.js version. Some features may not work correctly. If you encounter issues, please ensure Next.js is properly installed.");
|
|
198
|
+
const needsInstrumentationHook = !options.disableInstrumentationHook && shouldSetInstrumentationHook(nextjsVersion);
|
|
199
|
+
const wrappedConfig = {
|
|
200
|
+
...config,
|
|
201
|
+
...needsInstrumentationHook ? { experimental: {
|
|
202
|
+
...config.experimental,
|
|
203
|
+
instrumentationHook: true
|
|
204
|
+
} } : { experimental: config.experimental },
|
|
205
|
+
webpack: (webpackConfig, webpackOptions) => {
|
|
206
|
+
if (webpackOptions.isServer) {
|
|
207
|
+
const originalExternals = webpackConfig.externals;
|
|
208
|
+
const coreExternals = ["require-in-the-middle", "jsonpath"];
|
|
209
|
+
if (!originalExternals) {
|
|
210
|
+
webpackConfig.externals = coreExternals;
|
|
211
|
+
debugLog(debug, "Created new externals array with core packages");
|
|
212
|
+
} else if (Array.isArray(originalExternals)) {
|
|
213
|
+
for (const pkg of coreExternals) if (!originalExternals.includes(pkg)) {
|
|
214
|
+
originalExternals.push(pkg);
|
|
215
|
+
debugLog(debug, `Added ${pkg} to webpack externals`);
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
webpackConfig.externals = [originalExternals, ...coreExternals];
|
|
219
|
+
debugLog(debug, "Wrapped existing externals with core packages");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (typeof config.webpack === "function") return config.webpack(webpackConfig, webpackOptions);
|
|
223
|
+
return webpackConfig;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
if (needsInstrumentationHook) debugLog(debug, "Set experimental.instrumentationHook to true");
|
|
227
|
+
else debugLog(debug, "Skipped setting experimental.instrumentationHook (not needed for Next.js 15.0.0-rc.1+)");
|
|
228
|
+
if (options.disableInstrumentationHook && nextjsVersion && shouldSetInstrumentationHook(nextjsVersion)) warn(suppressAllWarnings || false, "You disabled instrumentationHook, but your Next.js version requires it. Tusk Drift may not initialize properly. Please remove the disableInstrumentationHook option.");
|
|
229
|
+
return wrappedConfig;
|
|
230
|
+
}
|
|
231
|
+
|
|
56
232
|
//#endregion
|
|
57
233
|
//#region src/instrumentation/core/baseClasses/TdInstrumentationAbstract.ts
|
|
58
234
|
var TdInstrumentationAbstract = class {
|
|
@@ -76,7 +252,7 @@ var TdInstrumentationAbstract = class {
|
|
|
76
252
|
|
|
77
253
|
//#endregion
|
|
78
254
|
//#region package.json
|
|
79
|
-
var version = "0.1.
|
|
255
|
+
var version = "0.1.7";
|
|
80
256
|
|
|
81
257
|
//#endregion
|
|
82
258
|
//#region src/version.ts
|
|
@@ -1172,7 +1348,7 @@ var JsonSchemaHelper = class JsonSchemaHelper {
|
|
|
1172
1348
|
}
|
|
1173
1349
|
/**
|
|
1174
1350
|
* Generate schema from data object using standardized types
|
|
1175
|
-
*
|
|
1351
|
+
*
|
|
1176
1352
|
* Note: We properties always exists on JsonSchema because proto3 maps cannot be marked optional.
|
|
1177
1353
|
* The JSON data is a bit inefficient because of this, but the easiest way to handle this is to keep it for now.
|
|
1178
1354
|
*/
|
|
@@ -1274,7 +1450,7 @@ var JsonSchemaHelper = class JsonSchemaHelper {
|
|
|
1274
1450
|
if (schema.decodedType === DecodedType$1.JSON && typeof decodedValue === "string") decodedValue = JSON.parse(decodedValue);
|
|
1275
1451
|
decodedData[key] = decodedValue;
|
|
1276
1452
|
} catch (error) {
|
|
1277
|
-
logger.
|
|
1453
|
+
logger.debug(`[JsonSchemaHelper] Failed to decode ${key}:`, error);
|
|
1278
1454
|
decodedData[key] = data[key];
|
|
1279
1455
|
}
|
|
1280
1456
|
return decodedData;
|
|
@@ -1362,7 +1538,8 @@ function convertMockRequestDataToCleanSpanData(mockRequestData, tuskDrift, input
|
|
|
1362
1538
|
status: {
|
|
1363
1539
|
code: StatusCode.OK,
|
|
1364
1540
|
message: "OK"
|
|
1365
|
-
}
|
|
1541
|
+
},
|
|
1542
|
+
stackTrace: mockRequestData.stackTrace
|
|
1366
1543
|
};
|
|
1367
1544
|
}
|
|
1368
1545
|
/**
|
|
@@ -1407,7 +1584,7 @@ let ClientRequest;
|
|
|
1407
1584
|
* Mock ClientRequest implementation for Tusk Drift HTTP replay
|
|
1408
1585
|
*/
|
|
1409
1586
|
var TdMockClientRequest = class TdMockClientRequest extends EventEmitter {
|
|
1410
|
-
constructor(options, spanInfo, callback) {
|
|
1587
|
+
constructor(options, spanInfo, callback, stackTrace) {
|
|
1411
1588
|
super();
|
|
1412
1589
|
this.INSTRUMENTATION_NAME = "HttpInstrumentation";
|
|
1413
1590
|
this.finished = false;
|
|
@@ -1418,6 +1595,7 @@ var TdMockClientRequest = class TdMockClientRequest extends EventEmitter {
|
|
|
1418
1595
|
TdMockClientRequest._setupPrototype();
|
|
1419
1596
|
this.tuskDrift = TuskDriftCore.getInstance();
|
|
1420
1597
|
this.spanInfo = spanInfo;
|
|
1598
|
+
this.stackTrace = stackTrace;
|
|
1421
1599
|
if (!options || Object.keys(options).length === 0) throw new Error("Making a request with empty `options` is not supported in TdMockClientRequest");
|
|
1422
1600
|
this.options = {
|
|
1423
1601
|
...options,
|
|
@@ -1553,7 +1731,8 @@ var TdMockClientRequest = class TdMockClientRequest extends EventEmitter {
|
|
|
1553
1731
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
1554
1732
|
submoduleName: rawInputValue.method,
|
|
1555
1733
|
inputValue,
|
|
1556
|
-
kind: SpanKind.CLIENT
|
|
1734
|
+
kind: SpanKind.CLIENT,
|
|
1735
|
+
stackTrace: this.stackTrace
|
|
1557
1736
|
},
|
|
1558
1737
|
tuskDrift: this.tuskDrift,
|
|
1559
1738
|
inputValueSchemaMerges: {
|
|
@@ -1697,7 +1876,7 @@ var HttpReplayHooks = class {
|
|
|
1697
1876
|
* Handle outbound HTTP requests in replay mode
|
|
1698
1877
|
* Uses TdMockClientRequest for simplified mocking approach
|
|
1699
1878
|
*/
|
|
1700
|
-
handleOutboundReplayRequest({ method, requestOptions, protocol, args, spanInfo }) {
|
|
1879
|
+
handleOutboundReplayRequest({ method, requestOptions, protocol, args, spanInfo, stackTrace }) {
|
|
1701
1880
|
logger.debug(`[HttpReplayHooks] Handling outbound ${protocol.toUpperCase()} ${method} request in replay mode`);
|
|
1702
1881
|
let callback;
|
|
1703
1882
|
if (args.length > 1 && typeof args[1] === "function") callback = args[1];
|
|
@@ -1714,7 +1893,7 @@ var HttpReplayHooks = class {
|
|
|
1714
1893
|
port: requestOptions.port ? Number(requestOptions.port) : void 0,
|
|
1715
1894
|
method
|
|
1716
1895
|
};
|
|
1717
|
-
const mockRequest = new TdMockClientRequest(mockOptions, spanInfo, callback);
|
|
1896
|
+
const mockRequest = new TdMockClientRequest(mockOptions, spanInfo, callback, stackTrace);
|
|
1718
1897
|
if (method === "GET" || method === "HEAD") process.nextTick(() => {
|
|
1719
1898
|
mockRequest.end();
|
|
1720
1899
|
});
|
|
@@ -1783,17 +1962,17 @@ function isWrapped$1(func) {
|
|
|
1783
1962
|
*/
|
|
1784
1963
|
function wrap(target, propertyName, wrapper) {
|
|
1785
1964
|
if (typeof target[propertyName] !== "function") {
|
|
1786
|
-
logger.
|
|
1965
|
+
logger.debug(`Cannot wrap non-function property: ${propertyName}`);
|
|
1787
1966
|
return;
|
|
1788
1967
|
}
|
|
1789
1968
|
if (isWrapped$1(target[propertyName])) {
|
|
1790
|
-
logger.
|
|
1969
|
+
logger.debug(`Property ${propertyName} is already wrapped`);
|
|
1791
1970
|
return;
|
|
1792
1971
|
}
|
|
1793
1972
|
const original = target[propertyName];
|
|
1794
1973
|
const wrapped = wrapper(original);
|
|
1795
1974
|
if (typeof wrapped !== "function") {
|
|
1796
|
-
logger.
|
|
1975
|
+
logger.debug(`Wrapper must return a function for property: ${propertyName}`);
|
|
1797
1976
|
return;
|
|
1798
1977
|
}
|
|
1799
1978
|
wrapped._isWrapped = true;
|
|
@@ -1803,6 +1982,45 @@ function wrap(target, propertyName, wrapper) {
|
|
|
1803
1982
|
return wrapped;
|
|
1804
1983
|
}
|
|
1805
1984
|
|
|
1985
|
+
//#endregion
|
|
1986
|
+
//#region src/instrumentation/core/utils/stackTraceUtils.ts
|
|
1987
|
+
/**
|
|
1988
|
+
* Helper functions for capturing stack traces in replay mode
|
|
1989
|
+
*
|
|
1990
|
+
* TODO: Consider using a structured format for stack frames:
|
|
1991
|
+
*
|
|
1992
|
+
* {
|
|
1993
|
+
* "frames": [
|
|
1994
|
+
* {
|
|
1995
|
+
* "fileName": "file.js",
|
|
1996
|
+
* "lineNumber": 10,
|
|
1997
|
+
* "columnNumber": 20,
|
|
1998
|
+
* "functionName": "functionName"
|
|
1999
|
+
* }
|
|
2000
|
+
* ]
|
|
2001
|
+
* }
|
|
2002
|
+
*
|
|
2003
|
+
* This would allow for more efficient matching and filtering of stack frames.
|
|
2004
|
+
* It would also allow for more accurate stack trace reconstruction in replay mode.
|
|
2005
|
+
*/
|
|
2006
|
+
/**
|
|
2007
|
+
*
|
|
2008
|
+
* @param excludeClassNames - Class names to exclude from the stack trace
|
|
2009
|
+
* @returns The stack trace as a string
|
|
2010
|
+
*/
|
|
2011
|
+
function captureStackTrace(excludeClassNames = []) {
|
|
2012
|
+
const originalStackTraceLimit = Error.stackTraceLimit;
|
|
2013
|
+
Error.stackTraceLimit = 100;
|
|
2014
|
+
const s = (/* @__PURE__ */ new Error()).stack || "";
|
|
2015
|
+
Error.stackTraceLimit = originalStackTraceLimit;
|
|
2016
|
+
const allExcludes = [...[
|
|
2017
|
+
"drift-node-sdk/src/instrumentation",
|
|
2018
|
+
"drift-node-sdk/src/core",
|
|
2019
|
+
"node_modules/@use-tusk"
|
|
2020
|
+
], ...excludeClassNames];
|
|
2021
|
+
return s.split("\n").slice(2).filter((l) => !allExcludes.some((exclude) => l.includes(exclude))).join("\n");
|
|
2022
|
+
}
|
|
2023
|
+
|
|
1806
2024
|
//#endregion
|
|
1807
2025
|
//#region src/instrumentation/libraries/http/HttpTransformEngine.ts
|
|
1808
2026
|
/**
|
|
@@ -2241,6 +2459,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2241
2459
|
};
|
|
2242
2460
|
const replayTraceId = this.replayHooks.extractTraceIdFromHeaders(req);
|
|
2243
2461
|
if (!replayTraceId) return originalHandler.call(this);
|
|
2462
|
+
logger.debug(`[HttpInstrumentation] Setting replay trace id`, replayTraceId);
|
|
2244
2463
|
const envVars = this.replayHooks.extractEnvVarsFromHeaders(req);
|
|
2245
2464
|
if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
|
|
2246
2465
|
const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
|
|
@@ -2750,37 +2969,40 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
2750
2969
|
} else requestOptions = args[0] || {};
|
|
2751
2970
|
const method = requestOptions.method || "GET";
|
|
2752
2971
|
const requestProtocol = self._normalizeProtocol(requestOptions.protocol || void 0, protocol);
|
|
2753
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
2754
|
-
const
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
headers,
|
|
2759
|
-
protocol: requestProtocol,
|
|
2760
|
-
hostname: requestOptions.hostname || requestOptions.host || void 0,
|
|
2761
|
-
port: requestOptions.port ? Number(requestOptions.port) : void 0,
|
|
2762
|
-
timeout: requestOptions.timeout || void 0
|
|
2763
|
-
};
|
|
2764
|
-
return SpanUtils.createAndExecuteSpan(self.mode, () => originalRequest.apply(this, args), {
|
|
2765
|
-
name: requestOptions.path || `${requestProtocol.toUpperCase()} ${method}`,
|
|
2766
|
-
kind: SpanKind.CLIENT,
|
|
2767
|
-
packageName: requestProtocol,
|
|
2768
|
-
packageType: PackageType.HTTP,
|
|
2769
|
-
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
2770
|
-
submodule: method,
|
|
2771
|
-
inputValue,
|
|
2772
|
-
isPreAppStart: false
|
|
2773
|
-
}, (spanInfo) => {
|
|
2774
|
-
return self.replayHooks.handleOutboundReplayRequest({
|
|
2972
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
2973
|
+
const stackTrace = captureStackTrace(["HttpInstrumentation"]);
|
|
2974
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
2975
|
+
const headers = normalizeHeaders(requestOptions.headers || {});
|
|
2976
|
+
const inputValue = {
|
|
2775
2977
|
method,
|
|
2776
|
-
requestOptions,
|
|
2978
|
+
path: requestOptions.path || void 0,
|
|
2979
|
+
headers,
|
|
2777
2980
|
protocol: requestProtocol,
|
|
2778
|
-
|
|
2779
|
-
|
|
2981
|
+
hostname: requestOptions.hostname || requestOptions.host || void 0,
|
|
2982
|
+
port: requestOptions.port ? Number(requestOptions.port) : void 0,
|
|
2983
|
+
timeout: requestOptions.timeout || void 0
|
|
2984
|
+
};
|
|
2985
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalRequest.apply(this, args), {
|
|
2986
|
+
name: requestOptions.path || `${requestProtocol.toUpperCase()} ${method}`,
|
|
2987
|
+
kind: SpanKind.CLIENT,
|
|
2988
|
+
packageName: requestProtocol,
|
|
2989
|
+
packageType: PackageType.HTTP,
|
|
2990
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
2991
|
+
submodule: method,
|
|
2992
|
+
inputValue,
|
|
2993
|
+
isPreAppStart: false
|
|
2994
|
+
}, (spanInfo) => {
|
|
2995
|
+
return self.replayHooks.handleOutboundReplayRequest({
|
|
2996
|
+
method,
|
|
2997
|
+
requestOptions,
|
|
2998
|
+
protocol: requestProtocol,
|
|
2999
|
+
args,
|
|
3000
|
+
spanInfo,
|
|
3001
|
+
stackTrace
|
|
3002
|
+
});
|
|
2780
3003
|
});
|
|
2781
|
-
});
|
|
2782
|
-
}
|
|
2783
|
-
else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
3004
|
+
} });
|
|
3005
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
2784
3006
|
originalFunctionCall: () => originalRequest.apply(this, args),
|
|
2785
3007
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
2786
3008
|
const headers = normalizeHeaders(requestOptions.headers || {});
|
|
@@ -3235,6 +3457,7 @@ var TdPgClientMock = class extends EventEmitter {
|
|
|
3235
3457
|
}
|
|
3236
3458
|
query(...args) {
|
|
3237
3459
|
logger.debug(`[TdPgClientMock] Mock pool client query intercepted in REPLAY mode`);
|
|
3460
|
+
const stackTrace = captureStackTrace(["TdPgClientMock"]);
|
|
3238
3461
|
const queryConfig = this.pgInstrumentation.parseQueryArgs(args);
|
|
3239
3462
|
if (!queryConfig || !queryConfig.text) {
|
|
3240
3463
|
logger.debug(`[TdPgClientMock] Could not parse mock client query, returning empty result`);
|
|
@@ -3249,7 +3472,7 @@ var TdPgClientMock = class extends EventEmitter {
|
|
|
3249
3472
|
clientType: "client"
|
|
3250
3473
|
};
|
|
3251
3474
|
const inputValue = createMockInputValue(rawInputValue);
|
|
3252
|
-
return this.pgInstrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo);
|
|
3475
|
+
return this.pgInstrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
|
|
3253
3476
|
}
|
|
3254
3477
|
release() {
|
|
3255
3478
|
this.emit("end");
|
|
@@ -3352,23 +3575,25 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3352
3575
|
values: queryConfig.values || [],
|
|
3353
3576
|
clientType
|
|
3354
3577
|
};
|
|
3355
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
3356
|
-
const
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3578
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
3579
|
+
const stackTrace = captureStackTrace(["PgInstrumentation"]);
|
|
3580
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
3581
|
+
const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
|
|
3582
|
+
const spanName = inputValue.clientType === "pool" ? "pg-pool.query" : "pg.query";
|
|
3583
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalQuery.apply(this, args), {
|
|
3584
|
+
name: spanName,
|
|
3585
|
+
kind: SpanKind.CLIENT,
|
|
3586
|
+
submodule: "query",
|
|
3587
|
+
packageType: PackageType.PG,
|
|
3588
|
+
packageName,
|
|
3589
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
3590
|
+
inputValue,
|
|
3591
|
+
isPreAppStart: false
|
|
3592
|
+
}, (spanInfo) => {
|
|
3593
|
+
return self.handleReplayQuery(queryConfig, inputValue, spanInfo, stackTrace);
|
|
3594
|
+
});
|
|
3595
|
+
} });
|
|
3596
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
3372
3597
|
originalFunctionCall: () => originalQuery.apply(this, args),
|
|
3373
3598
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
3374
3599
|
const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
|
|
@@ -3514,7 +3739,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3514
3739
|
throw error;
|
|
3515
3740
|
});
|
|
3516
3741
|
}
|
|
3517
|
-
async handleReplayQuery(queryConfig, inputValue, spanInfo) {
|
|
3742
|
+
async handleReplayQuery(queryConfig, inputValue, spanInfo, stackTrace) {
|
|
3518
3743
|
logger.debug(`[PgInstrumentation] Replaying PG query`);
|
|
3519
3744
|
const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
|
|
3520
3745
|
const spanName = inputValue.clientType === "pool" ? "pg-pool.query" : "pg.query";
|
|
@@ -3527,7 +3752,8 @@ var PgInstrumentation = class extends TdInstrumentationBase {
|
|
|
3527
3752
|
packageName,
|
|
3528
3753
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
3529
3754
|
submoduleName: "query",
|
|
3530
|
-
kind: SpanKind.CLIENT
|
|
3755
|
+
kind: SpanKind.CLIENT,
|
|
3756
|
+
stackTrace
|
|
3531
3757
|
},
|
|
3532
3758
|
tuskDrift: this.tuskDrift
|
|
3533
3759
|
});
|
|
@@ -3995,26 +4221,29 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
3995
4221
|
query: query.trim(),
|
|
3996
4222
|
parameters: values
|
|
3997
4223
|
};
|
|
3998
|
-
if (this.mode === TuskDriftMode.REPLAY)
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
packageName: "postgres",
|
|
4005
|
-
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4006
|
-
inputValue,
|
|
4007
|
-
isPreAppStart: false
|
|
4008
|
-
}, (spanInfo) => {
|
|
4009
|
-
return this.handleReplaySqlQuery({
|
|
4010
|
-
inputValue,
|
|
4011
|
-
spanInfo,
|
|
4224
|
+
if (this.mode === TuskDriftMode.REPLAY) {
|
|
4225
|
+
const stackTrace = captureStackTrace(["PostgresInstrumentation"]);
|
|
4226
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
4227
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => originalSql.call(this, strings, ...values), {
|
|
4228
|
+
name: "postgres.query",
|
|
4229
|
+
kind: SpanKind.CLIENT,
|
|
4012
4230
|
submodule: "query",
|
|
4013
|
-
|
|
4231
|
+
packageType: PackageType.PG,
|
|
4232
|
+
packageName: "postgres",
|
|
4233
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4234
|
+
inputValue,
|
|
4235
|
+
isPreAppStart: false
|
|
4236
|
+
}, (spanInfo) => {
|
|
4237
|
+
return this.handleReplaySqlQuery({
|
|
4238
|
+
inputValue,
|
|
4239
|
+
spanInfo,
|
|
4240
|
+
submodule: "query",
|
|
4241
|
+
name: "postgres.query",
|
|
4242
|
+
stackTrace
|
|
4243
|
+
});
|
|
4014
4244
|
});
|
|
4015
|
-
});
|
|
4016
|
-
}
|
|
4017
|
-
else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4245
|
+
} });
|
|
4246
|
+
} else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4018
4247
|
originalFunctionCall: () => originalSql.call(this, strings, ...values),
|
|
4019
4248
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
4020
4249
|
return SpanUtils.createAndExecuteSpan(this.mode, () => originalSql.call(this, strings, ...values), {
|
|
@@ -4045,28 +4274,31 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4045
4274
|
parameters: parameters || [],
|
|
4046
4275
|
options: queryOptions
|
|
4047
4276
|
};
|
|
4048
|
-
if (this.mode === TuskDriftMode.REPLAY)
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
packageName: "postgres",
|
|
4056
|
-
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4057
|
-
inputValue,
|
|
4058
|
-
isPreAppStart: false
|
|
4059
|
-
}, (spanInfo) => {
|
|
4060
|
-
return this.handleReplayUnsafeQuery({
|
|
4061
|
-
inputValue,
|
|
4062
|
-
spanInfo,
|
|
4277
|
+
if (this.mode === TuskDriftMode.REPLAY) {
|
|
4278
|
+
const stackTrace = captureStackTrace(["PostgresInstrumentation"]);
|
|
4279
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
4280
|
+
return this._createPendingQueryWrapper(() => {
|
|
4281
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => executeUnsafe(), {
|
|
4282
|
+
name: "postgres.unsafe",
|
|
4283
|
+
kind: SpanKind.CLIENT,
|
|
4063
4284
|
submodule: "unsafe",
|
|
4064
|
-
|
|
4285
|
+
packageType: PackageType.PG,
|
|
4286
|
+
packageName: "postgres",
|
|
4287
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4288
|
+
inputValue,
|
|
4289
|
+
isPreAppStart: false
|
|
4290
|
+
}, (spanInfo) => {
|
|
4291
|
+
return this.handleReplayUnsafeQuery({
|
|
4292
|
+
inputValue,
|
|
4293
|
+
spanInfo,
|
|
4294
|
+
submodule: "unsafe",
|
|
4295
|
+
name: "postgres.unsafe",
|
|
4296
|
+
stackTrace
|
|
4297
|
+
});
|
|
4065
4298
|
});
|
|
4066
4299
|
});
|
|
4067
|
-
});
|
|
4068
|
-
}
|
|
4069
|
-
else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4300
|
+
} });
|
|
4301
|
+
} else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4070
4302
|
originalFunctionCall: executeUnsafe,
|
|
4071
4303
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
4072
4304
|
return SpanUtils.createAndExecuteSpan(this.mode, executeUnsafe, {
|
|
@@ -4096,21 +4328,23 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4096
4328
|
else if (transactionCallback) return originalBegin.call(sqlInstance, transactionCallback);
|
|
4097
4329
|
else return originalBegin.call(sqlInstance, options || void 0);
|
|
4098
4330
|
};
|
|
4099
|
-
if (this.mode === TuskDriftMode.REPLAY)
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4331
|
+
if (this.mode === TuskDriftMode.REPLAY) {
|
|
4332
|
+
const stackTrace = captureStackTrace(["PostgresInstrumentation"]);
|
|
4333
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
4334
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => executeBegin(), {
|
|
4335
|
+
name: "postgres.begin",
|
|
4336
|
+
kind: SpanKind.CLIENT,
|
|
4337
|
+
submodule: "transaction",
|
|
4338
|
+
packageType: PackageType.PG,
|
|
4339
|
+
packageName: "postgres",
|
|
4340
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4341
|
+
inputValue,
|
|
4342
|
+
isPreAppStart: false
|
|
4343
|
+
}, (spanInfo) => {
|
|
4344
|
+
return this._handleReplayBeginTransaction(spanInfo, options, stackTrace);
|
|
4345
|
+
});
|
|
4346
|
+
} });
|
|
4347
|
+
} else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4114
4348
|
originalFunctionCall: executeBegin,
|
|
4115
4349
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
4116
4350
|
return SpanUtils.createAndExecuteSpan(this.mode, executeBegin, {
|
|
@@ -4204,7 +4438,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4204
4438
|
});
|
|
4205
4439
|
return promise;
|
|
4206
4440
|
}
|
|
4207
|
-
async _handleReplayBeginTransaction(spanInfo, options) {
|
|
4441
|
+
async _handleReplayBeginTransaction(spanInfo, options, stackTrace) {
|
|
4208
4442
|
logger.debug(`[PostgresInstrumentation] Replaying Postgres transaction`);
|
|
4209
4443
|
const mockData = await findMockResponseAsync({
|
|
4210
4444
|
mockRequestData: {
|
|
@@ -4218,7 +4452,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4218
4452
|
packageName: "postgres",
|
|
4219
4453
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4220
4454
|
submoduleName: "transaction",
|
|
4221
|
-
kind: SpanKind.CLIENT
|
|
4455
|
+
kind: SpanKind.CLIENT,
|
|
4456
|
+
stackTrace
|
|
4222
4457
|
},
|
|
4223
4458
|
tuskDrift: this.tuskDrift
|
|
4224
4459
|
});
|
|
@@ -4273,7 +4508,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4273
4508
|
throw error;
|
|
4274
4509
|
}
|
|
4275
4510
|
}
|
|
4276
|
-
async handleReplaySqlQuery({ inputValue, spanInfo, submodule, name }) {
|
|
4511
|
+
async handleReplaySqlQuery({ inputValue, spanInfo, submodule, name, stackTrace }) {
|
|
4277
4512
|
logger.debug(`[PostgresInstrumentation] Replaying Postgres sql query`);
|
|
4278
4513
|
const mockData = await findMockResponseAsync({
|
|
4279
4514
|
mockRequestData: {
|
|
@@ -4284,7 +4519,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4284
4519
|
packageName: "postgres",
|
|
4285
4520
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4286
4521
|
submoduleName: submodule,
|
|
4287
|
-
kind: SpanKind.CLIENT
|
|
4522
|
+
kind: SpanKind.CLIENT,
|
|
4523
|
+
stackTrace
|
|
4288
4524
|
},
|
|
4289
4525
|
tuskDrift: this.tuskDrift
|
|
4290
4526
|
});
|
|
@@ -4303,7 +4539,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4303
4539
|
count: isResultObject ? processedResult.count : void 0
|
|
4304
4540
|
});
|
|
4305
4541
|
}
|
|
4306
|
-
async handleReplayUnsafeQuery({ inputValue, spanInfo, submodule, name }) {
|
|
4542
|
+
async handleReplayUnsafeQuery({ inputValue, spanInfo, submodule, name, stackTrace }) {
|
|
4307
4543
|
logger.debug(`[PostgresInstrumentation] Replaying Postgres unsafe query`);
|
|
4308
4544
|
const mockData = await findMockResponseAsync({
|
|
4309
4545
|
mockRequestData: {
|
|
@@ -4314,7 +4550,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
|
|
|
4314
4550
|
packageName: "postgres",
|
|
4315
4551
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4316
4552
|
submoduleName: submodule,
|
|
4317
|
-
kind: SpanKind.CLIENT
|
|
4553
|
+
kind: SpanKind.CLIENT,
|
|
4554
|
+
stackTrace
|
|
4318
4555
|
},
|
|
4319
4556
|
tuskDrift: this.tuskDrift
|
|
4320
4557
|
});
|
|
@@ -4426,6 +4663,7 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
|
|
|
4426
4663
|
}
|
|
4427
4664
|
query(...args) {
|
|
4428
4665
|
logger.debug(`[TdMysql2ConnectionMock] Mock connection query intercepted in REPLAY mode`);
|
|
4666
|
+
const stackTrace = captureStackTrace(["TdMysql2ConnectionMock"]);
|
|
4429
4667
|
const queryConfig = this.mysql2Instrumentation.parseQueryArgs(args);
|
|
4430
4668
|
if (!queryConfig || !queryConfig.sql) {
|
|
4431
4669
|
logger.debug(`[TdMysql2ConnectionMock] Could not parse mock connection query, returning empty result`);
|
|
@@ -4445,10 +4683,11 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
|
|
|
4445
4683
|
clientType: this.clientType
|
|
4446
4684
|
};
|
|
4447
4685
|
const inputValue = createMockInputValue(rawInputValue);
|
|
4448
|
-
return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo);
|
|
4686
|
+
return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
|
|
4449
4687
|
}
|
|
4450
4688
|
execute(...args) {
|
|
4451
4689
|
logger.debug(`[TdMysql2ConnectionMock] Mock connection execute intercepted in REPLAY mode`);
|
|
4690
|
+
const stackTrace = captureStackTrace(["TdMysql2ConnectionMock"]);
|
|
4452
4691
|
const queryConfig = this.mysql2Instrumentation.parseQueryArgs(args);
|
|
4453
4692
|
if (!queryConfig || !queryConfig.sql) {
|
|
4454
4693
|
logger.debug(`[TdMysql2ConnectionMock] Could not parse mock connection execute, returning empty result`);
|
|
@@ -4468,7 +4707,7 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
|
|
|
4468
4707
|
clientType: this.clientType
|
|
4469
4708
|
};
|
|
4470
4709
|
const inputValue = createMockInputValue(rawInputValue);
|
|
4471
|
-
return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo);
|
|
4710
|
+
return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
|
|
4472
4711
|
}
|
|
4473
4712
|
release() {
|
|
4474
4713
|
this.emit("end");
|
|
@@ -4562,20 +4801,33 @@ var TdMysql2QueryMock = class {
|
|
|
4562
4801
|
/**
|
|
4563
4802
|
* Handle replay of a MySQL2 query (query or execute)
|
|
4564
4803
|
*/
|
|
4565
|
-
handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query") {
|
|
4804
|
+
handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query", stackTrace) {
|
|
4566
4805
|
logger.debug(`[Mysql2Instrumentation] Replaying MySQL2 query`);
|
|
4567
4806
|
const spanName = `mysql2.${inputValue.clientType}.${submoduleName}`;
|
|
4568
|
-
return this._handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName);
|
|
4807
|
+
return this._handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName, stackTrace);
|
|
4569
4808
|
}
|
|
4570
4809
|
/**
|
|
4571
4810
|
* Handle query - always returns an EventEmitter (like mysql2 does)
|
|
4572
4811
|
* This handles both callback and streaming modes
|
|
4812
|
+
* The EventEmitter is also thenable (has a .then() method) to support await/Promise usage
|
|
4573
4813
|
*/
|
|
4574
|
-
_handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName) {
|
|
4814
|
+
_handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName, stackTrace) {
|
|
4575
4815
|
const emitter = new EventEmitter();
|
|
4816
|
+
let storedRows = null;
|
|
4817
|
+
let storedFields = null;
|
|
4818
|
+
emitter.then = function(onResolve, onReject) {
|
|
4819
|
+
return new Promise((resolve, reject) => {
|
|
4820
|
+
emitter.once("end", () => {
|
|
4821
|
+
resolve([storedRows, storedFields]);
|
|
4822
|
+
});
|
|
4823
|
+
emitter.once("error", (error) => {
|
|
4824
|
+
reject(error);
|
|
4825
|
+
});
|
|
4826
|
+
}).then(onResolve, onReject);
|
|
4827
|
+
};
|
|
4576
4828
|
(async () => {
|
|
4577
4829
|
try {
|
|
4578
|
-
const mockData = await this._fetchMockData(inputValue, spanInfo, spanName, submoduleName);
|
|
4830
|
+
const mockData = await this._fetchMockData(inputValue, spanInfo, spanName, submoduleName, stackTrace);
|
|
4579
4831
|
if (!mockData) {
|
|
4580
4832
|
const sql = queryConfig.sql || inputValue.sql || "UNKNOWN_QUERY";
|
|
4581
4833
|
logger.warn(`[Mysql2Instrumentation] No mock data found for MySQL2 query: ${sql}`);
|
|
@@ -4587,6 +4839,8 @@ var TdMysql2QueryMock = class {
|
|
|
4587
4839
|
return;
|
|
4588
4840
|
}
|
|
4589
4841
|
const processedResult = this._convertMysql2Types(mockData.result);
|
|
4842
|
+
storedRows = processedResult.rows;
|
|
4843
|
+
storedFields = processedResult.fields;
|
|
4590
4844
|
process.nextTick(() => {
|
|
4591
4845
|
if (processedResult.fields) emitter.emit("fields", processedResult.fields);
|
|
4592
4846
|
if (queryConfig.callback) queryConfig.callback(null, processedResult.rows, processedResult.fields);
|
|
@@ -4606,7 +4860,7 @@ var TdMysql2QueryMock = class {
|
|
|
4606
4860
|
/**
|
|
4607
4861
|
* Fetch mock data from CLI
|
|
4608
4862
|
*/
|
|
4609
|
-
async _fetchMockData(inputValue, spanInfo, spanName, submoduleName) {
|
|
4863
|
+
async _fetchMockData(inputValue, spanInfo, spanName, submoduleName, stackTrace) {
|
|
4610
4864
|
return await findMockResponseAsync({
|
|
4611
4865
|
mockRequestData: {
|
|
4612
4866
|
traceId: spanInfo.traceId,
|
|
@@ -4616,7 +4870,8 @@ var TdMysql2QueryMock = class {
|
|
|
4616
4870
|
packageName: "mysql2",
|
|
4617
4871
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4618
4872
|
submoduleName,
|
|
4619
|
-
kind: SpanKind.CLIENT
|
|
4873
|
+
kind: SpanKind.CLIENT,
|
|
4874
|
+
stackTrace
|
|
4620
4875
|
},
|
|
4621
4876
|
tuskDrift: this.tuskDrift
|
|
4622
4877
|
});
|
|
@@ -4640,9 +4895,57 @@ var TdMysql2QueryMock = class {
|
|
|
4640
4895
|
}
|
|
4641
4896
|
};
|
|
4642
4897
|
|
|
4898
|
+
//#endregion
|
|
4899
|
+
//#region src/instrumentation/libraries/mysql2/mocks/TdMysql2ConnectionEventMock.ts
|
|
4900
|
+
/**
|
|
4901
|
+
* Mock for MySQL2 connection events (connect/error)
|
|
4902
|
+
* Handles replay of recorded connection establishment events in REPLAY mode
|
|
4903
|
+
* Recording happens through normal SpanUtils flow in RECORD mode
|
|
4904
|
+
*/
|
|
4905
|
+
var TdMysql2ConnectionEventMock = class {
|
|
4906
|
+
constructor(spanInfo) {
|
|
4907
|
+
this.INSTRUMENTATION_NAME = "Mysql2Instrumentation";
|
|
4908
|
+
this.spanInfo = spanInfo;
|
|
4909
|
+
this.tuskDrift = TuskDriftCore.getInstance();
|
|
4910
|
+
}
|
|
4911
|
+
/**
|
|
4912
|
+
* Get recorded connection event for replay
|
|
4913
|
+
* Returns { output } for success, or throws error if connection failed
|
|
4914
|
+
* The connection events are recorded automatically via SpanUtils in record mode
|
|
4915
|
+
*/
|
|
4916
|
+
async getReplayedConnectionEvent(inputValue) {
|
|
4917
|
+
logger.debug(`[TdMysql2ConnectionEventMock] Retrieving recorded connection event`);
|
|
4918
|
+
try {
|
|
4919
|
+
const mockData = await findMockResponseAsync({
|
|
4920
|
+
mockRequestData: {
|
|
4921
|
+
traceId: this.spanInfo.traceId,
|
|
4922
|
+
spanId: this.spanInfo.spanId,
|
|
4923
|
+
name: "mysql2.connection.create",
|
|
4924
|
+
inputValue,
|
|
4925
|
+
packageName: "mysql2",
|
|
4926
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
4927
|
+
submoduleName: "connectEvent",
|
|
4928
|
+
kind: SpanKind.CLIENT
|
|
4929
|
+
},
|
|
4930
|
+
tuskDrift: this.tuskDrift
|
|
4931
|
+
});
|
|
4932
|
+
if (!mockData) {
|
|
4933
|
+
logger.warn(`[TdMysql2ConnectionEventMock] No mock data found, using default success`);
|
|
4934
|
+
return { output: {} };
|
|
4935
|
+
}
|
|
4936
|
+
return { output: mockData.result || {} };
|
|
4937
|
+
} catch (error) {
|
|
4938
|
+
logger.error(`[TdMysql2ConnectionEventMock] Error getting replay value:`, error);
|
|
4939
|
+
return { output: {} };
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
};
|
|
4943
|
+
|
|
4643
4944
|
//#endregion
|
|
4644
4945
|
//#region src/instrumentation/libraries/mysql2/Instrumentation.ts
|
|
4645
|
-
const
|
|
4946
|
+
const COMPLETE_SUPPORTED_VERSIONS = ">=2.3.3 <4.0.0";
|
|
4947
|
+
const V2_3_3_TO_3_11_4 = ">=2.3.3 <3.11.5";
|
|
4948
|
+
const V3_11_5_TO_4_0 = ">=3.11.5 <4.0.0";
|
|
4646
4949
|
var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
4647
4950
|
constructor(config = {}) {
|
|
4648
4951
|
super("mysql2", config);
|
|
@@ -4653,42 +4956,103 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
4653
4956
|
init() {
|
|
4654
4957
|
return [new TdInstrumentationNodeModule({
|
|
4655
4958
|
name: "mysql2",
|
|
4656
|
-
supportedVersions:
|
|
4959
|
+
supportedVersions: [COMPLETE_SUPPORTED_VERSIONS],
|
|
4657
4960
|
files: [
|
|
4658
4961
|
new TdInstrumentationNodeModuleFile({
|
|
4659
4962
|
name: "mysql2/lib/connection.js",
|
|
4660
|
-
supportedVersions:
|
|
4661
|
-
patch: (moduleExports) => this.
|
|
4963
|
+
supportedVersions: [V2_3_3_TO_3_11_4],
|
|
4964
|
+
patch: (moduleExports) => this._patchConnectionV2(moduleExports)
|
|
4965
|
+
}),
|
|
4966
|
+
new TdInstrumentationNodeModuleFile({
|
|
4967
|
+
name: "mysql2/lib/base/connection.js",
|
|
4968
|
+
supportedVersions: [V3_11_5_TO_4_0],
|
|
4969
|
+
patch: (moduleExports) => this._patchBaseConnection(moduleExports)
|
|
4970
|
+
}),
|
|
4971
|
+
new TdInstrumentationNodeModuleFile({
|
|
4972
|
+
name: "mysql2/lib/connection.js",
|
|
4973
|
+
supportedVersions: [V3_11_5_TO_4_0],
|
|
4974
|
+
patch: (moduleExports) => this._patchConnectionV3(moduleExports)
|
|
4662
4975
|
}),
|
|
4663
4976
|
new TdInstrumentationNodeModuleFile({
|
|
4664
4977
|
name: "mysql2/lib/pool.js",
|
|
4665
|
-
supportedVersions:
|
|
4666
|
-
patch: (moduleExports) => this.
|
|
4978
|
+
supportedVersions: [V2_3_3_TO_3_11_4],
|
|
4979
|
+
patch: (moduleExports) => this._patchPoolV2(moduleExports)
|
|
4667
4980
|
}),
|
|
4668
4981
|
new TdInstrumentationNodeModuleFile({
|
|
4669
|
-
name: "mysql2/lib/
|
|
4670
|
-
supportedVersions:
|
|
4671
|
-
patch: (moduleExports) => this.
|
|
4982
|
+
name: "mysql2/lib/pool.js",
|
|
4983
|
+
supportedVersions: [V3_11_5_TO_4_0],
|
|
4984
|
+
patch: (moduleExports) => this._patchPoolV3(moduleExports)
|
|
4672
4985
|
}),
|
|
4673
4986
|
new TdInstrumentationNodeModuleFile({
|
|
4674
|
-
name: "mysql2/lib/
|
|
4675
|
-
supportedVersions:
|
|
4676
|
-
patch: (moduleExports) => this.
|
|
4987
|
+
name: "mysql2/lib/pool_connection.js",
|
|
4988
|
+
supportedVersions: [V2_3_3_TO_3_11_4],
|
|
4989
|
+
patch: (moduleExports) => this._patchPoolConnectionV2(moduleExports)
|
|
4677
4990
|
}),
|
|
4678
4991
|
new TdInstrumentationNodeModuleFile({
|
|
4679
|
-
name: "mysql2/lib/
|
|
4680
|
-
supportedVersions:
|
|
4681
|
-
patch: (moduleExports) => this.
|
|
4992
|
+
name: "mysql2/lib/pool_connection.js",
|
|
4993
|
+
supportedVersions: [V3_11_5_TO_4_0],
|
|
4994
|
+
patch: (moduleExports) => this._patchPoolConnectionV3(moduleExports)
|
|
4682
4995
|
})
|
|
4683
4996
|
]
|
|
4684
4997
|
})];
|
|
4685
4998
|
}
|
|
4686
|
-
|
|
4687
|
-
logger.debug(`[Mysql2Instrumentation] Patching
|
|
4999
|
+
_patchBaseConnection(BaseConnectionClass) {
|
|
5000
|
+
logger.debug(`[Mysql2Instrumentation] Patching BaseConnection class`);
|
|
5001
|
+
if (this.isModulePatched(BaseConnectionClass)) {
|
|
5002
|
+
logger.debug(`[Mysql2Instrumentation] BaseConnection class already patched, skipping`);
|
|
5003
|
+
return BaseConnectionClass;
|
|
5004
|
+
}
|
|
5005
|
+
if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.query) {
|
|
5006
|
+
if (!isWrapped$1(BaseConnectionClass.prototype.query)) {
|
|
5007
|
+
this._wrap(BaseConnectionClass.prototype, "query", this._getQueryPatchFn("connection"));
|
|
5008
|
+
logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.query`);
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.execute) {
|
|
5012
|
+
if (!isWrapped$1(BaseConnectionClass.prototype.execute)) {
|
|
5013
|
+
this._wrap(BaseConnectionClass.prototype, "execute", this._getExecutePatchFn("connection"));
|
|
5014
|
+
logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.execute`);
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.connect) {
|
|
5018
|
+
if (!isWrapped$1(BaseConnectionClass.prototype.connect)) {
|
|
5019
|
+
this._wrap(BaseConnectionClass.prototype, "connect", this._getConnectPatchFn("connection"));
|
|
5020
|
+
logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.connect`);
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
5023
|
+
if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.ping) {
|
|
5024
|
+
if (!isWrapped$1(BaseConnectionClass.prototype.ping)) {
|
|
5025
|
+
this._wrap(BaseConnectionClass.prototype, "ping", this._getPingPatchFn("connection"));
|
|
5026
|
+
logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.ping`);
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.end) {
|
|
5030
|
+
if (!isWrapped$1(BaseConnectionClass.prototype.end)) {
|
|
5031
|
+
this._wrap(BaseConnectionClass.prototype, "end", this._getEndPatchFn("connection"));
|
|
5032
|
+
logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.end`);
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
this.markModuleAsPatched(BaseConnectionClass);
|
|
5036
|
+
logger.debug(`[Mysql2Instrumentation] BaseConnection class patching complete`);
|
|
5037
|
+
return BaseConnectionClass;
|
|
5038
|
+
}
|
|
5039
|
+
_patchConnectionV2(ConnectionClass) {
|
|
5040
|
+
logger.debug(`[Mysql2Instrumentation] Patching Connection class (v2)`);
|
|
4688
5041
|
if (this.isModulePatched(ConnectionClass)) {
|
|
4689
5042
|
logger.debug(`[Mysql2Instrumentation] Connection class already patched, skipping`);
|
|
4690
5043
|
return ConnectionClass;
|
|
4691
5044
|
}
|
|
5045
|
+
this._patchConnectionPrototypes(ConnectionClass);
|
|
5046
|
+
const patchedConnectionClass = this._getPatchedConnectionClass(ConnectionClass);
|
|
5047
|
+
this.markModuleAsPatched(ConnectionClass);
|
|
5048
|
+
logger.debug(`[Mysql2Instrumentation] Connection class (v2) patching complete`);
|
|
5049
|
+
return patchedConnectionClass;
|
|
5050
|
+
}
|
|
5051
|
+
_patchConnectionV3(ConnectionClass) {
|
|
5052
|
+
logger.debug(`[Mysql2Instrumentation] Connection class (v3) - wrapping constructor only`);
|
|
5053
|
+
return this._getPatchedConnectionClass(ConnectionClass);
|
|
5054
|
+
}
|
|
5055
|
+
_patchConnectionPrototypes(ConnectionClass) {
|
|
4692
5056
|
if (ConnectionClass.prototype && ConnectionClass.prototype.query) {
|
|
4693
5057
|
if (!isWrapped$1(ConnectionClass.prototype.query)) {
|
|
4694
5058
|
this._wrap(ConnectionClass.prototype, "query", this._getQueryPatchFn("connection"));
|
|
@@ -4719,16 +5083,30 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
4719
5083
|
logger.debug(`[Mysql2Instrumentation] Wrapped Connection.prototype.end`);
|
|
4720
5084
|
}
|
|
4721
5085
|
}
|
|
4722
|
-
this.markModuleAsPatched(ConnectionClass);
|
|
4723
|
-
logger.debug(`[Mysql2Instrumentation] Connection class patching complete`);
|
|
4724
|
-
return ConnectionClass;
|
|
4725
5086
|
}
|
|
4726
|
-
|
|
4727
|
-
logger.debug(`[Mysql2Instrumentation] Patching Pool class`);
|
|
5087
|
+
_patchPoolV2(PoolClass) {
|
|
5088
|
+
logger.debug(`[Mysql2Instrumentation] Patching Pool class (v2)`);
|
|
5089
|
+
if (this.isModulePatched(PoolClass)) {
|
|
5090
|
+
logger.debug(`[Mysql2Instrumentation] Pool class already patched, skipping`);
|
|
5091
|
+
return PoolClass;
|
|
5092
|
+
}
|
|
5093
|
+
this._patchPoolPrototypes(PoolClass);
|
|
5094
|
+
this.markModuleAsPatched(PoolClass);
|
|
5095
|
+
logger.debug(`[Mysql2Instrumentation] Pool class (v2) patching complete`);
|
|
5096
|
+
return PoolClass;
|
|
5097
|
+
}
|
|
5098
|
+
_patchPoolV3(PoolClass) {
|
|
5099
|
+
logger.debug(`[Mysql2Instrumentation] Patching Pool class (v3)`);
|
|
4728
5100
|
if (this.isModulePatched(PoolClass)) {
|
|
4729
5101
|
logger.debug(`[Mysql2Instrumentation] Pool class already patched, skipping`);
|
|
4730
5102
|
return PoolClass;
|
|
4731
5103
|
}
|
|
5104
|
+
this._patchPoolPrototypes(PoolClass);
|
|
5105
|
+
this.markModuleAsPatched(PoolClass);
|
|
5106
|
+
logger.debug(`[Mysql2Instrumentation] Pool class (v3) patching complete`);
|
|
5107
|
+
return PoolClass;
|
|
5108
|
+
}
|
|
5109
|
+
_patchPoolPrototypes(PoolClass) {
|
|
4732
5110
|
if (PoolClass.prototype && PoolClass.prototype.query) {
|
|
4733
5111
|
if (!isWrapped$1(PoolClass.prototype.query)) {
|
|
4734
5112
|
this._wrap(PoolClass.prototype, "query", this._getQueryPatchFn("pool"));
|
|
@@ -4747,30 +5125,19 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
4747
5125
|
logger.debug(`[Mysql2Instrumentation] Wrapped Pool.prototype.getConnection`);
|
|
4748
5126
|
}
|
|
4749
5127
|
}
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
_patchPoolConnection(PoolConnectionClass) {
|
|
4755
|
-
logger.debug(`[Mysql2Instrumentation] Patching PoolConnection class`);
|
|
4756
|
-
if (this.isModulePatched(PoolConnectionClass)) {
|
|
4757
|
-
logger.debug(`[Mysql2Instrumentation] PoolConnection class already patched, skipping`);
|
|
4758
|
-
return PoolConnectionClass;
|
|
4759
|
-
}
|
|
4760
|
-
if (PoolConnectionClass.prototype && PoolConnectionClass.prototype.query) {
|
|
4761
|
-
if (!isWrapped$1(PoolConnectionClass.prototype.query)) {
|
|
4762
|
-
this._wrap(PoolConnectionClass.prototype, "query", this._getQueryPatchFn("poolConnection"));
|
|
4763
|
-
logger.debug(`[Mysql2Instrumentation] Wrapped PoolConnection.prototype.query`);
|
|
5128
|
+
if (PoolClass.prototype && PoolClass.prototype.end) {
|
|
5129
|
+
if (!isWrapped$1(PoolClass.prototype.end)) {
|
|
5130
|
+
this._wrap(PoolClass.prototype, "end", this._getEndPatchFn("pool"));
|
|
5131
|
+
logger.debug(`[Mysql2Instrumentation] Wrapped Pool.prototype.end`);
|
|
4764
5132
|
}
|
|
4765
5133
|
}
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
logger.debug(`[Mysql2Instrumentation] PoolConnection class patching complete`);
|
|
5134
|
+
}
|
|
5135
|
+
_patchPoolConnectionV2(PoolConnectionClass) {
|
|
5136
|
+
logger.debug(`[Mysql2Instrumentation] PoolConnection class (v2) - skipping (inherits from Connection)`);
|
|
5137
|
+
return PoolConnectionClass;
|
|
5138
|
+
}
|
|
5139
|
+
_patchPoolConnectionV3(PoolConnectionClass) {
|
|
5140
|
+
logger.debug(`[Mysql2Instrumentation] PoolConnection class (v3) - skipping (inherits from Connection)`);
|
|
4774
5141
|
return PoolConnectionClass;
|
|
4775
5142
|
}
|
|
4776
5143
|
_getQueryPatchFn(clientType) {
|
|
@@ -4792,22 +5159,24 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
4792
5159
|
values: queryConfig.values || [],
|
|
4793
5160
|
clientType
|
|
4794
5161
|
};
|
|
4795
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
4796
|
-
const
|
|
4797
|
-
return
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
5162
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
5163
|
+
const stackTrace = captureStackTrace(["Mysql2Instrumentation"]);
|
|
5164
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
5165
|
+
const spanName = `mysql2.${clientType}.query`;
|
|
5166
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalQuery.apply(this, args), {
|
|
5167
|
+
name: spanName,
|
|
5168
|
+
kind: SpanKind.CLIENT,
|
|
5169
|
+
submodule: "query",
|
|
5170
|
+
packageType: PackageType.MYSQL,
|
|
5171
|
+
packageName: "mysql2",
|
|
5172
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
5173
|
+
inputValue,
|
|
5174
|
+
isPreAppStart: false
|
|
5175
|
+
}, (spanInfo) => {
|
|
5176
|
+
return self.handleReplayQuery(queryConfig, inputValue, spanInfo, "query", stackTrace);
|
|
5177
|
+
});
|
|
5178
|
+
} });
|
|
5179
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4811
5180
|
originalFunctionCall: () => originalQuery.apply(this, args),
|
|
4812
5181
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
4813
5182
|
const spanName = `mysql2.${clientType}.query`;
|
|
@@ -4849,22 +5218,24 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
4849
5218
|
values: queryConfig.values || [],
|
|
4850
5219
|
clientType
|
|
4851
5220
|
};
|
|
4852
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
4853
|
-
const
|
|
4854
|
-
return
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
5221
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
5222
|
+
const stackTrace = captureStackTrace(["Mysql2Instrumentation"]);
|
|
5223
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
5224
|
+
const spanName = `mysql2.${clientType}.execute`;
|
|
5225
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalExecute.apply(this, args), {
|
|
5226
|
+
name: spanName,
|
|
5227
|
+
kind: SpanKind.CLIENT,
|
|
5228
|
+
submodule: "execute",
|
|
5229
|
+
packageType: PackageType.MYSQL,
|
|
5230
|
+
packageName: "mysql2",
|
|
5231
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
5232
|
+
inputValue,
|
|
5233
|
+
isPreAppStart: false
|
|
5234
|
+
}, (spanInfo) => {
|
|
5235
|
+
return self.handleReplayQuery(queryConfig, inputValue, spanInfo, "execute", stackTrace);
|
|
5236
|
+
});
|
|
5237
|
+
} });
|
|
5238
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
4868
5239
|
originalFunctionCall: () => originalExecute.apply(this, args),
|
|
4869
5240
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
4870
5241
|
const spanName = `mysql2.${clientType}.execute`;
|
|
@@ -5212,8 +5583,8 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
5212
5583
|
return result;
|
|
5213
5584
|
}
|
|
5214
5585
|
}
|
|
5215
|
-
handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query") {
|
|
5216
|
-
return this.queryMock.handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName);
|
|
5586
|
+
handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query", stackTrace) {
|
|
5587
|
+
return this.queryMock.handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName, stackTrace);
|
|
5217
5588
|
}
|
|
5218
5589
|
_handleRecordPoolGetConnectionInSpan(spanInfo, originalGetConnection, callback, context$1) {
|
|
5219
5590
|
if (callback) {
|
|
@@ -5276,117 +5647,137 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
|
|
|
5276
5647
|
return;
|
|
5277
5648
|
} else return Promise.resolve(mockConnection);
|
|
5278
5649
|
}
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
affectedRows: result.affectedRows,
|
|
5289
|
-
insertId: result.insertId,
|
|
5290
|
-
warningCount: result.warningCount
|
|
5291
|
-
};
|
|
5292
|
-
else outputValue = result;
|
|
5293
|
-
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
5294
|
-
}
|
|
5295
|
-
_patchCreateConnectionFile(createConnectionFn) {
|
|
5296
|
-
logger.debug(`[Mysql2Instrumentation] Patching create_connection.js file`);
|
|
5650
|
+
/**
|
|
5651
|
+
* Creates a patched Connection class that intercepts the constructor
|
|
5652
|
+
* to handle connection event recording/replay.
|
|
5653
|
+
*
|
|
5654
|
+
* Wrap the Connection constructor to:
|
|
5655
|
+
* - In RECORD mode: listen for 'connect'/'error' events and record them
|
|
5656
|
+
* - In REPLAY mode: create a MockConnection that fakes the connection and emits recorded events
|
|
5657
|
+
*/
|
|
5658
|
+
_getPatchedConnectionClass(OriginalConnection) {
|
|
5297
5659
|
const self = this;
|
|
5298
|
-
|
|
5660
|
+
function TdPatchedConnection(...args) {
|
|
5299
5661
|
const inputValue = { method: "createConnection" };
|
|
5300
|
-
if (self.mode === TuskDriftMode.
|
|
5301
|
-
|
|
5302
|
-
name: `mysql2.createConnection`,
|
|
5303
|
-
kind: SpanKind.CLIENT,
|
|
5304
|
-
submodule: "createConnection",
|
|
5305
|
-
packageName: "mysql2",
|
|
5306
|
-
packageType: PackageType.MYSQL,
|
|
5307
|
-
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
5308
|
-
inputValue,
|
|
5309
|
-
isPreAppStart: false
|
|
5310
|
-
}, (spanInfo) => {
|
|
5311
|
-
const connection = createConnectionFn.apply(this, args);
|
|
5312
|
-
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { created: true } });
|
|
5313
|
-
SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
|
|
5314
|
-
return connection;
|
|
5315
|
-
});
|
|
5316
|
-
} });
|
|
5317
|
-
else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
5318
|
-
originalFunctionCall: () => createConnectionFn.apply(this, args),
|
|
5662
|
+
if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
5663
|
+
originalFunctionCall: () => new OriginalConnection(...args),
|
|
5319
5664
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
5320
|
-
return SpanUtils.createAndExecuteSpan(self.mode, () =>
|
|
5321
|
-
name: `mysql2.
|
|
5665
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => new OriginalConnection(...args), {
|
|
5666
|
+
name: `mysql2.connection.create`,
|
|
5322
5667
|
kind: SpanKind.CLIENT,
|
|
5323
|
-
submodule: "
|
|
5324
|
-
packageName: "mysql2",
|
|
5668
|
+
submodule: "connectEvent",
|
|
5325
5669
|
packageType: PackageType.MYSQL,
|
|
5670
|
+
packageName: "mysql2",
|
|
5326
5671
|
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
5327
5672
|
inputValue,
|
|
5328
5673
|
isPreAppStart
|
|
5329
5674
|
}, (spanInfo) => {
|
|
5330
|
-
const connection =
|
|
5331
|
-
|
|
5332
|
-
|
|
5675
|
+
const connection = new OriginalConnection(...args);
|
|
5676
|
+
connection.on("connect", (connectionObj) => {
|
|
5677
|
+
try {
|
|
5678
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
|
|
5679
|
+
connected: true,
|
|
5680
|
+
connectionObj
|
|
5681
|
+
} });
|
|
5682
|
+
SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
|
|
5683
|
+
} catch {
|
|
5684
|
+
logger.error(`[Mysql2Instrumentation] error adding span attributes:`);
|
|
5685
|
+
}
|
|
5686
|
+
});
|
|
5687
|
+
connection.on("error", (err) => {
|
|
5688
|
+
try {
|
|
5689
|
+
logger.debug(`[Mysql2Instrumentation] Connection error, recording: ${err.message}`);
|
|
5690
|
+
SpanUtils.endSpan(spanInfo.span, {
|
|
5691
|
+
code: SpanStatusCode.ERROR,
|
|
5692
|
+
message: err.message
|
|
5693
|
+
});
|
|
5694
|
+
} catch {
|
|
5695
|
+
logger.error(`[Mysql2Instrumentation] error ending span`);
|
|
5696
|
+
}
|
|
5697
|
+
});
|
|
5333
5698
|
return connection;
|
|
5334
5699
|
});
|
|
5335
5700
|
},
|
|
5336
5701
|
spanKind: SpanKind.CLIENT
|
|
5337
5702
|
});
|
|
5338
|
-
else return createConnectionFn.apply(this, args);
|
|
5339
|
-
};
|
|
5340
|
-
logger.debug(`[Mysql2Instrumentation] Patched create_connection.js file`);
|
|
5341
|
-
return wrappedFn;
|
|
5342
|
-
}
|
|
5343
|
-
_patchCreatePoolFile(createPoolFn) {
|
|
5344
|
-
logger.debug(`[Mysql2Instrumentation] Patching create_pool.js file`);
|
|
5345
|
-
const self = this;
|
|
5346
|
-
const wrappedFn = function(...args) {
|
|
5347
|
-
const inputValue = { method: "createPool" };
|
|
5348
5703
|
if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
|
|
5349
|
-
return SpanUtils.createAndExecuteSpan(self.mode, () =>
|
|
5350
|
-
name: `mysql2.
|
|
5704
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => new OriginalConnection(...args), {
|
|
5705
|
+
name: `mysql2.connection.create`,
|
|
5351
5706
|
kind: SpanKind.CLIENT,
|
|
5352
|
-
submodule: "
|
|
5353
|
-
packageName: "mysql2",
|
|
5707
|
+
submodule: "connectEvent",
|
|
5354
5708
|
packageType: PackageType.MYSQL,
|
|
5709
|
+
packageName: "mysql2",
|
|
5355
5710
|
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
5356
5711
|
inputValue,
|
|
5357
5712
|
isPreAppStart: false
|
|
5358
5713
|
}, (spanInfo) => {
|
|
5359
|
-
|
|
5360
|
-
|
|
5714
|
+
class MockConnection extends OriginalConnection {
|
|
5715
|
+
constructor(...mockConnectionArgs) {
|
|
5716
|
+
const clonedArgs = JSON.parse(JSON.stringify(mockConnectionArgs));
|
|
5717
|
+
if (clonedArgs[0] && clonedArgs[0].config) {
|
|
5718
|
+
clonedArgs[0].config.host = "127.0.0.1";
|
|
5719
|
+
clonedArgs[0].config.port = 127;
|
|
5720
|
+
} else if (clonedArgs[0]) {
|
|
5721
|
+
clonedArgs[0].host = "127.0.0.1";
|
|
5722
|
+
clonedArgs[0].port = 127;
|
|
5723
|
+
}
|
|
5724
|
+
super(...clonedArgs);
|
|
5725
|
+
this._isConnectOrErrorEmitted = false;
|
|
5726
|
+
this._connectEventMock = new TdMysql2ConnectionEventMock(spanInfo);
|
|
5727
|
+
}
|
|
5728
|
+
on(event, listener) {
|
|
5729
|
+
if (!this._connectEventMock) return super.on(event, listener);
|
|
5730
|
+
if (event === "connect" && !this._isConnectOrErrorEmitted) {
|
|
5731
|
+
this._connectEventMock.getReplayedConnectionEvent(inputValue).then(({ output }) => {
|
|
5732
|
+
if (output !== void 0) process.nextTick(() => {
|
|
5733
|
+
listener.call(this, output);
|
|
5734
|
+
this._isConnectOrErrorEmitted = true;
|
|
5735
|
+
});
|
|
5736
|
+
}).catch((err) => {
|
|
5737
|
+
logger.error(`[Mysql2Instrumentation] Error replaying connection event:`, err);
|
|
5738
|
+
});
|
|
5739
|
+
return this;
|
|
5740
|
+
}
|
|
5741
|
+
if (event === "error" && !this._isConnectOrErrorEmitted) return this;
|
|
5742
|
+
return super.on(event, listener);
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5745
|
+
const mockConnection = new MockConnection(...args);
|
|
5746
|
+
mockConnection.addListener("error", (_err) => {});
|
|
5747
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
|
|
5748
|
+
connected: true,
|
|
5749
|
+
mock: true
|
|
5750
|
+
} });
|
|
5361
5751
|
SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
|
|
5362
|
-
return
|
|
5752
|
+
return mockConnection;
|
|
5363
5753
|
});
|
|
5364
5754
|
} });
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5755
|
+
return new OriginalConnection(...args);
|
|
5756
|
+
}
|
|
5757
|
+
const staticProps = Object.getOwnPropertyNames(OriginalConnection).filter((key) => ![
|
|
5758
|
+
"length",
|
|
5759
|
+
"name",
|
|
5760
|
+
"prototype"
|
|
5761
|
+
].includes(key));
|
|
5762
|
+
for (const staticProp of staticProps) TdPatchedConnection[staticProp] = OriginalConnection[staticProp];
|
|
5763
|
+
Object.setPrototypeOf(TdPatchedConnection.prototype, OriginalConnection.prototype);
|
|
5764
|
+
return TdPatchedConnection;
|
|
5765
|
+
}
|
|
5766
|
+
_addOutputAttributesToSpan(spanInfo, result, fields) {
|
|
5767
|
+
if (!result) return;
|
|
5768
|
+
let outputValue = {};
|
|
5769
|
+
if (Array.isArray(result)) outputValue = {
|
|
5770
|
+
rowCount: result.length,
|
|
5771
|
+
rows: result,
|
|
5772
|
+
fields: fields || []
|
|
5773
|
+
};
|
|
5774
|
+
else if (result.affectedRows !== void 0) outputValue = {
|
|
5775
|
+
affectedRows: result.affectedRows,
|
|
5776
|
+
insertId: result.insertId,
|
|
5777
|
+
warningCount: result.warningCount
|
|
5387
5778
|
};
|
|
5388
|
-
|
|
5389
|
-
|
|
5779
|
+
else outputValue = result;
|
|
5780
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
5390
5781
|
}
|
|
5391
5782
|
_wrap(target, propertyName, wrapper) {
|
|
5392
5783
|
wrap(target, propertyName, wrapper);
|
|
@@ -5536,28 +5927,30 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
|
|
|
5536
5927
|
token: verifyConfig.token,
|
|
5537
5928
|
secretOrPublicKey: typeof verifyConfig.secretOrPublicKey === "function" ? "[Function:secretProvider]" : verifyConfig.secretOrPublicKey,
|
|
5538
5929
|
options: verifyConfig.options
|
|
5539
|
-
};
|
|
5540
|
-
let inputValue;
|
|
5541
|
-
try {
|
|
5542
|
-
inputValue = createMockInputValue(rawInputValue);
|
|
5543
|
-
} catch (error) {
|
|
5544
|
-
logger.error(`[JsonwebtokenInstrumentation] error creating mock input value:`, error);
|
|
5545
|
-
return originalVerify.apply(this, args);
|
|
5546
|
-
}
|
|
5547
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5930
|
+
};
|
|
5931
|
+
let inputValue;
|
|
5932
|
+
try {
|
|
5933
|
+
inputValue = createMockInputValue(rawInputValue);
|
|
5934
|
+
} catch (error) {
|
|
5935
|
+
logger.error(`[JsonwebtokenInstrumentation] error creating mock input value:`, error);
|
|
5936
|
+
return originalVerify.apply(this, args);
|
|
5937
|
+
}
|
|
5938
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
5939
|
+
const stackTrace = captureStackTrace(["JsonwebtokenInstrumentation"]);
|
|
5940
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
5941
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalVerify.apply(this, args), {
|
|
5942
|
+
name: "jsonwebtoken.verify",
|
|
5943
|
+
kind: SpanKind.CLIENT,
|
|
5944
|
+
submodule: "verify",
|
|
5945
|
+
packageName: "jsonwebtoken",
|
|
5946
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
5947
|
+
inputValue,
|
|
5948
|
+
isPreAppStart: false
|
|
5949
|
+
}, (spanInfo) => {
|
|
5950
|
+
return self.handleReplayVerify(verifyConfig, inputValue, spanInfo, stackTrace);
|
|
5951
|
+
});
|
|
5952
|
+
} });
|
|
5953
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
5561
5954
|
originalFunctionCall: () => originalVerify.apply(this, args),
|
|
5562
5955
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
5563
5956
|
return SpanUtils.createAndExecuteSpan(self.mode, () => originalVerify.apply(this, args), {
|
|
@@ -5597,20 +5990,22 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
|
|
|
5597
5990
|
secretOrPrivateKey: signConfig.secretOrPrivateKey,
|
|
5598
5991
|
options: signConfig.options
|
|
5599
5992
|
};
|
|
5600
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5993
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
5994
|
+
const stackTrace = captureStackTrace(["JsonwebtokenInstrumentation"]);
|
|
5995
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
5996
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalSign.apply(this, args), {
|
|
5997
|
+
name: "jsonwebtoken.sign",
|
|
5998
|
+
kind: SpanKind.CLIENT,
|
|
5999
|
+
submodule: "sign",
|
|
6000
|
+
packageName: "jsonwebtoken",
|
|
6001
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
6002
|
+
inputValue,
|
|
6003
|
+
isPreAppStart: false
|
|
6004
|
+
}, (spanInfo) => {
|
|
6005
|
+
return self.handleReplaySign(signConfig, inputValue, spanInfo, stackTrace);
|
|
6006
|
+
});
|
|
6007
|
+
} });
|
|
6008
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
5614
6009
|
originalFunctionCall: () => originalSign.apply(this, args),
|
|
5615
6010
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
5616
6011
|
return SpanUtils.createAndExecuteSpan(self.mode, () => originalSign.apply(this, args), {
|
|
@@ -5778,18 +6173,19 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
|
|
|
5778
6173
|
throw error;
|
|
5779
6174
|
}
|
|
5780
6175
|
}
|
|
5781
|
-
async handleReplayVerify(verifyConfig, inputValue, spanInfo) {
|
|
6176
|
+
async handleReplayVerify(verifyConfig, inputValue, spanInfo, stackTrace) {
|
|
5782
6177
|
logger.debug(`[JsonwebtokenInstrumentation] Replaying JWT verify`);
|
|
5783
6178
|
const mockData = await findMockResponseAsync({
|
|
5784
6179
|
mockRequestData: {
|
|
5785
6180
|
traceId: spanInfo.traceId,
|
|
5786
6181
|
spanId: spanInfo.spanId,
|
|
5787
|
-
name:
|
|
6182
|
+
name: "jsonwebtoken.verify",
|
|
5788
6183
|
packageName: "jsonwebtoken",
|
|
5789
6184
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
5790
6185
|
submoduleName: "verify",
|
|
5791
6186
|
inputValue,
|
|
5792
|
-
kind: SpanKind.CLIENT
|
|
6187
|
+
kind: SpanKind.CLIENT,
|
|
6188
|
+
stackTrace
|
|
5793
6189
|
},
|
|
5794
6190
|
tuskDrift: this.tuskDrift
|
|
5795
6191
|
});
|
|
@@ -5824,18 +6220,19 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
|
|
|
5824
6220
|
return;
|
|
5825
6221
|
} else return result;
|
|
5826
6222
|
}
|
|
5827
|
-
async handleReplaySign(signConfig, inputValue, spanInfo) {
|
|
6223
|
+
async handleReplaySign(signConfig, inputValue, spanInfo, stackTrace) {
|
|
5828
6224
|
logger.debug(`[JsonwebtokenInstrumentation] Replaying JWT sign`);
|
|
5829
6225
|
const mockData = await findMockResponseAsync({
|
|
5830
6226
|
mockRequestData: {
|
|
5831
6227
|
traceId: spanInfo?.traceId,
|
|
5832
6228
|
spanId: spanInfo?.spanId,
|
|
5833
|
-
name:
|
|
6229
|
+
name: "jsonwebtoken.sign",
|
|
5834
6230
|
packageName: "jsonwebtoken",
|
|
5835
6231
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
5836
6232
|
submoduleName: "sign",
|
|
5837
6233
|
inputValue,
|
|
5838
|
-
kind: SpanKind.CLIENT
|
|
6234
|
+
kind: SpanKind.CLIENT,
|
|
6235
|
+
stackTrace
|
|
5839
6236
|
},
|
|
5840
6237
|
tuskDrift: this.tuskDrift
|
|
5841
6238
|
});
|
|
@@ -6239,11 +6636,12 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
|
|
|
6239
6636
|
this.originalFetch = globalThis.fetch;
|
|
6240
6637
|
const self = this;
|
|
6241
6638
|
globalThis.fetch = function(input, init) {
|
|
6242
|
-
|
|
6639
|
+
const stackTrace = captureStackTrace(["FetchInstrumentation"]);
|
|
6640
|
+
return self._handleFetchRequest(input, init, stackTrace);
|
|
6243
6641
|
};
|
|
6244
6642
|
logger.debug("Global fetch patching complete");
|
|
6245
6643
|
}
|
|
6246
|
-
async _handleFetchRequest(input, init) {
|
|
6644
|
+
async _handleFetchRequest(input, init, stackTrace) {
|
|
6247
6645
|
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
6248
6646
|
const method = init?.method || "GET";
|
|
6249
6647
|
const headers = init?.headers || {};
|
|
@@ -6273,7 +6671,7 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
|
|
|
6273
6671
|
inputValue,
|
|
6274
6672
|
isPreAppStart: false
|
|
6275
6673
|
}, (spanInfo) => {
|
|
6276
|
-
return this._handleReplayFetch(inputValue, spanInfo);
|
|
6674
|
+
return this._handleReplayFetch(inputValue, spanInfo, stackTrace);
|
|
6277
6675
|
});
|
|
6278
6676
|
} });
|
|
6279
6677
|
else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
@@ -6354,7 +6752,7 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
|
|
|
6354
6752
|
});
|
|
6355
6753
|
});
|
|
6356
6754
|
}
|
|
6357
|
-
async _handleReplayFetch(inputValue, spanInfo) {
|
|
6755
|
+
async _handleReplayFetch(inputValue, spanInfo, stackTrace) {
|
|
6358
6756
|
const mockData = await findMockResponseAsync({
|
|
6359
6757
|
mockRequestData: {
|
|
6360
6758
|
traceId: spanInfo.traceId,
|
|
@@ -6365,7 +6763,8 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
|
|
|
6365
6763
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
6366
6764
|
submoduleName: inputValue.method,
|
|
6367
6765
|
inputValue,
|
|
6368
|
-
kind: SpanKind.CLIENT
|
|
6766
|
+
kind: SpanKind.CLIENT,
|
|
6767
|
+
stackTrace
|
|
6369
6768
|
},
|
|
6370
6769
|
tuskDrift: this.tuskDrift,
|
|
6371
6770
|
inputValueSchemaMerges: {
|
|
@@ -6602,7 +7001,7 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
|
|
|
6602
7001
|
}
|
|
6603
7002
|
const actualExports = moduleExports[Symbol.toStringTag] === "Module" ? moduleExports.default : moduleExports;
|
|
6604
7003
|
if (!actualExports || !actualExports.prototype) {
|
|
6605
|
-
logger.
|
|
7004
|
+
logger.debug(`[IORedisInstrumentation] Invalid Pipeline module exports, cannot patch`);
|
|
6606
7005
|
return moduleExports;
|
|
6607
7006
|
}
|
|
6608
7007
|
if (actualExports.prototype.exec) {
|
|
@@ -6640,21 +7039,23 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
|
|
|
6640
7039
|
port: this.options?.port
|
|
6641
7040
|
}
|
|
6642
7041
|
};
|
|
6643
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
7042
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
7043
|
+
const stackTrace = captureStackTrace(["IORedisInstrumentation"]);
|
|
7044
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
7045
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalSendCommand.apply(this, arguments), {
|
|
7046
|
+
name: `ioredis.${commandName}`,
|
|
7047
|
+
kind: SpanKind.CLIENT,
|
|
7048
|
+
submodule: commandName,
|
|
7049
|
+
packageType: PackageType.REDIS,
|
|
7050
|
+
packageName: "ioredis",
|
|
7051
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
7052
|
+
inputValue,
|
|
7053
|
+
isPreAppStart: false
|
|
7054
|
+
}, (spanInfo) => {
|
|
7055
|
+
return self._handleReplaySendCommand(spanInfo, cmd, inputValue, commandName, stackTrace);
|
|
7056
|
+
});
|
|
7057
|
+
} });
|
|
7058
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
6658
7059
|
originalFunctionCall: () => originalSendCommand.apply(this, arguments),
|
|
6659
7060
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
6660
7061
|
return SpanUtils.createAndExecuteSpan(self.mode, () => originalSendCommand.apply(this, arguments), {
|
|
@@ -6816,7 +7217,7 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
|
|
|
6816
7217
|
});
|
|
6817
7218
|
return promise;
|
|
6818
7219
|
}
|
|
6819
|
-
async _handleReplaySendCommand(spanInfo, cmd, inputValue, commandName) {
|
|
7220
|
+
async _handleReplaySendCommand(spanInfo, cmd, inputValue, commandName, stackTrace) {
|
|
6820
7221
|
logger.debug(`[IORedisInstrumentation] Replaying IORedis command ${cmd.name}`);
|
|
6821
7222
|
const mockData = await findMockResponseAsync({
|
|
6822
7223
|
mockRequestData: {
|
|
@@ -6827,7 +7228,8 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
|
|
|
6827
7228
|
packageName: "ioredis",
|
|
6828
7229
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
6829
7230
|
submoduleName: cmd.name,
|
|
6830
|
-
kind: SpanKind.CLIENT
|
|
7231
|
+
kind: SpanKind.CLIENT,
|
|
7232
|
+
stackTrace
|
|
6831
7233
|
},
|
|
6832
7234
|
tuskDrift: this.tuskDrift
|
|
6833
7235
|
});
|
|
@@ -7276,21 +7678,23 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
|
|
|
7276
7678
|
jsonableStringMap
|
|
7277
7679
|
}
|
|
7278
7680
|
};
|
|
7279
|
-
if (self.mode === TuskDriftMode.REPLAY)
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7681
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
7682
|
+
const stackTrace = captureStackTrace(["GrpcInstrumentation"]);
|
|
7683
|
+
return handleReplayMode({ replayModeHandler: () => {
|
|
7684
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
|
|
7685
|
+
name: "grpc.client.unary",
|
|
7686
|
+
kind: SpanKind.CLIENT,
|
|
7687
|
+
submodule: "client",
|
|
7688
|
+
packageType: PackageType.GRPC,
|
|
7689
|
+
packageName: GRPC_MODULE_NAME,
|
|
7690
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
7691
|
+
inputValue,
|
|
7692
|
+
isPreAppStart: false
|
|
7693
|
+
}, (spanInfo) => {
|
|
7694
|
+
return self._handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor, stackTrace);
|
|
7695
|
+
});
|
|
7696
|
+
} });
|
|
7697
|
+
} else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
7294
7698
|
originalFunctionCall: () => original.apply(this, args),
|
|
7295
7699
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
7296
7700
|
return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
|
|
@@ -7422,7 +7826,7 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
|
|
|
7422
7826
|
isGrpcErrorOutput(result) {
|
|
7423
7827
|
return "error" in result;
|
|
7424
7828
|
}
|
|
7425
|
-
async _handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor) {
|
|
7829
|
+
async _handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor, stackTrace) {
|
|
7426
7830
|
logger.debug(`[GrpcInstrumentation] Replaying gRPC unary request`);
|
|
7427
7831
|
const mockData = await findMockResponseAsync({
|
|
7428
7832
|
mockRequestData: {
|
|
@@ -7433,7 +7837,8 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
|
|
|
7433
7837
|
packageName: GRPC_MODULE_NAME,
|
|
7434
7838
|
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
7435
7839
|
submoduleName: "client",
|
|
7436
|
-
kind: SpanKind.CLIENT
|
|
7840
|
+
kind: SpanKind.CLIENT,
|
|
7841
|
+
stackTrace
|
|
7437
7842
|
},
|
|
7438
7843
|
tuskDrift: this.tuskDrift
|
|
7439
7844
|
});
|
|
@@ -7521,6 +7926,372 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
|
|
|
7521
7926
|
};
|
|
7522
7927
|
GrpcInstrumentation.metadataStore = /* @__PURE__ */ new Map();
|
|
7523
7928
|
|
|
7929
|
+
//#endregion
|
|
7930
|
+
//#region src/instrumentation/libraries/nextjs/Instrumentation.ts
|
|
7931
|
+
var NextjsInstrumentation = class extends TdInstrumentationBase {
|
|
7932
|
+
constructor(config = {}) {
|
|
7933
|
+
super("nextjs", config);
|
|
7934
|
+
this.INSTRUMENTATION_NAME = "NextjsInstrumentation";
|
|
7935
|
+
this.mode = config.mode || TuskDriftMode.DISABLED;
|
|
7936
|
+
this.replayHooks = new HttpReplayHooks();
|
|
7937
|
+
this.tuskDrift = TuskDriftCore.getInstance();
|
|
7938
|
+
logger.debug(`[NextjsInstrumentation] Constructor called with mode: ${this.mode}`);
|
|
7939
|
+
this._patchLoadedModules();
|
|
7940
|
+
}
|
|
7941
|
+
init() {
|
|
7942
|
+
logger.debug(`[NextjsInstrumentation] Initializing in ${this.mode} mode`);
|
|
7943
|
+
return [new TdInstrumentationNodeModule({
|
|
7944
|
+
name: "next/dist/server/base-server",
|
|
7945
|
+
supportedVersions: ["*"],
|
|
7946
|
+
patch: (moduleExports) => {
|
|
7947
|
+
return this._patchBaseServer(moduleExports);
|
|
7948
|
+
}
|
|
7949
|
+
})];
|
|
7950
|
+
}
|
|
7951
|
+
_patchLoadedModules() {
|
|
7952
|
+
logger.debug(`[NextjsInstrumentation] Checking for already-loaded Next.js modules`);
|
|
7953
|
+
const pattern = "/next/dist/server/base-server.js";
|
|
7954
|
+
let patchedCount = 0;
|
|
7955
|
+
for (const [modulePath, cached] of Object.entries(__require.cache)) if (modulePath.includes(pattern) && cached && cached.exports) {
|
|
7956
|
+
logger.debug(`[NextjsInstrumentation] Found ${pattern} at ${modulePath}, patching now`);
|
|
7957
|
+
this._patchBaseServer(cached.exports);
|
|
7958
|
+
patchedCount++;
|
|
7959
|
+
break;
|
|
7960
|
+
}
|
|
7961
|
+
if (patchedCount === 0) {
|
|
7962
|
+
logger.debug(`[NextjsInstrumentation] No Next.js server modules found in require.cache yet`);
|
|
7963
|
+
logger.debug(`[NextjsInstrumentation] Will wait for require-in-the-middle hooks to catch them`);
|
|
7964
|
+
} else logger.debug(`[NextjsInstrumentation] Patched ${patchedCount} already-loaded Next.js modules`);
|
|
7965
|
+
}
|
|
7966
|
+
_patchBaseServer(baseServerModule) {
|
|
7967
|
+
logger.debug(`[NextjsInstrumentation] Patching Next.js BaseServer in ${this.mode} mode`);
|
|
7968
|
+
const BaseServer = baseServerModule.default || baseServerModule.BaseServer || baseServerModule;
|
|
7969
|
+
if (BaseServer && BaseServer.prototype) {
|
|
7970
|
+
if (this.isModulePatched(BaseServer.prototype)) {
|
|
7971
|
+
logger.debug(`[NextjsInstrumentation] BaseServer.prototype already patched, skipping`);
|
|
7972
|
+
return baseServerModule;
|
|
7973
|
+
}
|
|
7974
|
+
if (BaseServer.prototype.handleRequest) {
|
|
7975
|
+
this._wrap(BaseServer.prototype, "handleRequest", this._getHandleRequestPatchFn());
|
|
7976
|
+
logger.debug(`[NextjsInstrumentation] Wrapped BaseServer.prototype.handleRequest`);
|
|
7977
|
+
this.markModuleAsPatched(BaseServer.prototype);
|
|
7978
|
+
} else logger.warn(`[NextjsInstrumentation] Could not find BaseServer.prototype.handleRequest`);
|
|
7979
|
+
} else logger.warn(`[NextjsInstrumentation] Could not find BaseServer class`);
|
|
7980
|
+
logger.debug(`[NextjsInstrumentation] Next.js BaseServer patching complete`);
|
|
7981
|
+
return baseServerModule;
|
|
7982
|
+
}
|
|
7983
|
+
_getHandleRequestPatchFn() {
|
|
7984
|
+
const self = this;
|
|
7985
|
+
return (originalHandleRequest) => {
|
|
7986
|
+
return async function(req, res, parsedUrl) {
|
|
7987
|
+
const method = req.method || "GET";
|
|
7988
|
+
const url = req.url || "/";
|
|
7989
|
+
logger.debug(`[NextjsInstrumentation] Intercepted Next.js request: ${method} ${url}`);
|
|
7990
|
+
if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
|
|
7991
|
+
const inputValue = {
|
|
7992
|
+
method,
|
|
7993
|
+
url,
|
|
7994
|
+
target: url,
|
|
7995
|
+
headers: self._normalizeHeaders(req.headers || {})
|
|
7996
|
+
};
|
|
7997
|
+
const replayTraceId = self.replayHooks.extractTraceIdFromHeaders(req);
|
|
7998
|
+
if (!replayTraceId) {
|
|
7999
|
+
logger.debug(`[NextjsInstrumentation] No trace ID found, calling original handler`);
|
|
8000
|
+
return originalHandleRequest.call(this, req, res, parsedUrl);
|
|
8001
|
+
}
|
|
8002
|
+
const envVars = self.replayHooks.extractEnvVarsFromHeaders(req);
|
|
8003
|
+
if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
|
|
8004
|
+
const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
|
|
8005
|
+
if (!ctxWithReplayTraceId) throw new Error("Error setting current replay trace id");
|
|
8006
|
+
return context.with(ctxWithReplayTraceId, () => {
|
|
8007
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalHandleRequest.call(this, req, res, parsedUrl), {
|
|
8008
|
+
name: url,
|
|
8009
|
+
kind: SpanKind.SERVER,
|
|
8010
|
+
packageName: "nextjs",
|
|
8011
|
+
submodule: method,
|
|
8012
|
+
packageType: PackageType.HTTP,
|
|
8013
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
8014
|
+
inputValue,
|
|
8015
|
+
inputSchemaMerges: { headers: { matchImportance: 0 } },
|
|
8016
|
+
isPreAppStart: false
|
|
8017
|
+
}, (spanInfo) => {
|
|
8018
|
+
return self._handleNextjsRequestInSpan({
|
|
8019
|
+
req,
|
|
8020
|
+
res,
|
|
8021
|
+
parsedUrl,
|
|
8022
|
+
originalHandleRequest,
|
|
8023
|
+
spanInfo,
|
|
8024
|
+
inputValue,
|
|
8025
|
+
thisContext: this
|
|
8026
|
+
});
|
|
8027
|
+
});
|
|
8028
|
+
});
|
|
8029
|
+
} });
|
|
8030
|
+
else if (self.mode === TuskDriftMode.RECORD) {
|
|
8031
|
+
if (method.toUpperCase() === "OPTIONS" || !!req.headers["access-control-request-method"]) return originalHandleRequest.call(this, req, res, parsedUrl);
|
|
8032
|
+
if (!shouldSample({
|
|
8033
|
+
samplingRate: self.tuskDrift.getSamplingRate(),
|
|
8034
|
+
isAppReady: self.tuskDrift.isAppReady()
|
|
8035
|
+
})) {
|
|
8036
|
+
logger.debug(`[NextjsInstrumentation] Skipping server span due to sampling rate: ${url}`);
|
|
8037
|
+
return originalHandleRequest.call(this, req, res, parsedUrl);
|
|
8038
|
+
}
|
|
8039
|
+
logger.debug(`[NextjsInstrumentation] Creating server span for ${method} ${url}`);
|
|
8040
|
+
return handleRecordMode({
|
|
8041
|
+
originalFunctionCall: () => originalHandleRequest.call(this, req, res, parsedUrl),
|
|
8042
|
+
recordModeHandler: ({ isPreAppStart }) => {
|
|
8043
|
+
const inputValue = {
|
|
8044
|
+
method,
|
|
8045
|
+
url,
|
|
8046
|
+
target: url,
|
|
8047
|
+
headers: self._normalizeHeaders(req.headers || {})
|
|
8048
|
+
};
|
|
8049
|
+
return SpanUtils.createAndExecuteSpan(self.mode, () => originalHandleRequest.call(this, req, res, parsedUrl), {
|
|
8050
|
+
name: url,
|
|
8051
|
+
kind: SpanKind.SERVER,
|
|
8052
|
+
packageName: "nextjs",
|
|
8053
|
+
packageType: PackageType.HTTP,
|
|
8054
|
+
submodule: method,
|
|
8055
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
8056
|
+
inputValue,
|
|
8057
|
+
inputSchemaMerges: { headers: { matchImportance: 0 } },
|
|
8058
|
+
isPreAppStart
|
|
8059
|
+
}, (spanInfo) => {
|
|
8060
|
+
return self._handleNextjsRequestInSpan({
|
|
8061
|
+
req,
|
|
8062
|
+
res,
|
|
8063
|
+
parsedUrl,
|
|
8064
|
+
originalHandleRequest,
|
|
8065
|
+
spanInfo,
|
|
8066
|
+
inputValue,
|
|
8067
|
+
thisContext: this
|
|
8068
|
+
});
|
|
8069
|
+
});
|
|
8070
|
+
},
|
|
8071
|
+
spanKind: SpanKind.SERVER
|
|
8072
|
+
});
|
|
8073
|
+
} else return originalHandleRequest.call(this, req, res, parsedUrl);
|
|
8074
|
+
};
|
|
8075
|
+
};
|
|
8076
|
+
}
|
|
8077
|
+
async _handleNextjsRequestInSpan({ req, res, parsedUrl, originalHandleRequest, spanInfo, inputValue, thisContext }) {
|
|
8078
|
+
const self = this;
|
|
8079
|
+
context.bind(spanInfo.context, req);
|
|
8080
|
+
context.bind(spanInfo.context, res);
|
|
8081
|
+
let completeInputValue = inputValue;
|
|
8082
|
+
this._captureRequestBody(req, spanInfo, inputValue, (updatedInputValue) => {
|
|
8083
|
+
completeInputValue = updatedInputValue;
|
|
8084
|
+
});
|
|
8085
|
+
let capturedStatusCode;
|
|
8086
|
+
let capturedStatusMessage;
|
|
8087
|
+
let capturedHeaders = {};
|
|
8088
|
+
const responseChunks = [];
|
|
8089
|
+
const actualRes = res.originalResponse || res;
|
|
8090
|
+
const originalWriteHead = actualRes.writeHead?.bind(actualRes);
|
|
8091
|
+
if (originalWriteHead) actualRes.writeHead = function(statusCode, statusMessage, headers) {
|
|
8092
|
+
capturedStatusCode = statusCode;
|
|
8093
|
+
if (typeof statusMessage === "object") capturedHeaders = self._normalizeHeaders(statusMessage);
|
|
8094
|
+
else {
|
|
8095
|
+
capturedStatusMessage = statusMessage;
|
|
8096
|
+
if (headers) capturedHeaders = self._normalizeHeaders(headers);
|
|
8097
|
+
}
|
|
8098
|
+
return originalWriteHead.call(this, statusCode, statusMessage, headers);
|
|
8099
|
+
};
|
|
8100
|
+
const originalSetHeader = actualRes.setHeader?.bind(actualRes);
|
|
8101
|
+
if (originalSetHeader) actualRes.setHeader = function(name, value) {
|
|
8102
|
+
capturedHeaders[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
|
|
8103
|
+
return originalSetHeader.call(this, name, value);
|
|
8104
|
+
};
|
|
8105
|
+
const originalWrite = actualRes.write?.bind(actualRes);
|
|
8106
|
+
if (originalWrite) actualRes.write = function(chunk, encoding, callback) {
|
|
8107
|
+
if (chunk) responseChunks.push(chunk);
|
|
8108
|
+
return originalWrite.call(this, chunk, encoding, callback);
|
|
8109
|
+
};
|
|
8110
|
+
const originalEnd = actualRes.end?.bind(actualRes);
|
|
8111
|
+
if (originalEnd) actualRes.end = function(chunk, encoding, callback) {
|
|
8112
|
+
if (chunk) responseChunks.push(chunk);
|
|
8113
|
+
return originalEnd.call(this, chunk, encoding, callback);
|
|
8114
|
+
};
|
|
8115
|
+
try {
|
|
8116
|
+
await originalHandleRequest.call(thisContext, req, res, parsedUrl);
|
|
8117
|
+
if (!capturedStatusCode) {
|
|
8118
|
+
capturedStatusCode = res.statusCode;
|
|
8119
|
+
capturedStatusMessage = res.statusMessage;
|
|
8120
|
+
}
|
|
8121
|
+
if (Object.keys(capturedHeaders).length === 0 && res.getHeaders) {
|
|
8122
|
+
const rawHeaders = res.getHeaders();
|
|
8123
|
+
capturedHeaders = self._normalizeHeaders(rawHeaders);
|
|
8124
|
+
}
|
|
8125
|
+
logger.debug(`[NextjsInstrumentation] Next.js request completed: ${capturedStatusCode} (${SpanUtils.getTraceInfo()})`);
|
|
8126
|
+
const outputValue = {
|
|
8127
|
+
statusCode: capturedStatusCode,
|
|
8128
|
+
statusMessage: capturedStatusMessage,
|
|
8129
|
+
headers: capturedHeaders
|
|
8130
|
+
};
|
|
8131
|
+
if (responseChunks.length > 0) try {
|
|
8132
|
+
const responseBuffer = combineChunks(responseChunks);
|
|
8133
|
+
const contentEncoding = outputValue.headers?.["content-encoding"];
|
|
8134
|
+
outputValue.body = await httpBodyEncoder({
|
|
8135
|
+
bodyBuffer: responseBuffer,
|
|
8136
|
+
contentEncoding
|
|
8137
|
+
});
|
|
8138
|
+
outputValue.bodySize = responseBuffer.length;
|
|
8139
|
+
} catch (error) {
|
|
8140
|
+
logger.error(`[NextjsInstrumentation] Error processing response body:`, error);
|
|
8141
|
+
outputValue.bodyProcessingError = error instanceof Error ? error.message : String(error);
|
|
8142
|
+
}
|
|
8143
|
+
SpanUtils.addSpanAttributes(spanInfo.span, {
|
|
8144
|
+
inputValue: completeInputValue,
|
|
8145
|
+
outputValue,
|
|
8146
|
+
outputSchemaMerges: {
|
|
8147
|
+
body: {
|
|
8148
|
+
encoding: EncodingType.BASE64,
|
|
8149
|
+
decodedType: getDecodedType(outputValue.headers?.["content-type"] || "")
|
|
8150
|
+
},
|
|
8151
|
+
headers: { matchImportance: 0 }
|
|
8152
|
+
},
|
|
8153
|
+
metadata: { ENV_VARS: EnvVarTracker.getEnvVars(spanInfo.traceId) }
|
|
8154
|
+
});
|
|
8155
|
+
EnvVarTracker.clearEnvVars(spanInfo.traceId);
|
|
8156
|
+
const status = (capturedStatusCode || 200) >= 400 ? {
|
|
8157
|
+
code: SpanStatusCode.ERROR,
|
|
8158
|
+
message: `HTTP ${capturedStatusCode}`
|
|
8159
|
+
} : { code: SpanStatusCode.OK };
|
|
8160
|
+
SpanUtils.setStatus(spanInfo.span, status);
|
|
8161
|
+
SpanUtils.endSpan(spanInfo.span);
|
|
8162
|
+
if (self.mode === TuskDriftMode.REPLAY) try {
|
|
8163
|
+
const now = OriginalGlobalUtils.getOriginalDate();
|
|
8164
|
+
const replayTraceId = SpanUtils.getCurrentReplayTraceId() || spanInfo.traceId;
|
|
8165
|
+
const { schema: inputSchema, decodedValueHash: inputValueHash } = JsonSchemaHelper.generateSchemaAndHash(completeInputValue, {
|
|
8166
|
+
body: {
|
|
8167
|
+
encoding: EncodingType.BASE64,
|
|
8168
|
+
decodedType: getDecodedType(completeInputValue.headers && completeInputValue.headers["content-type"] || "")
|
|
8169
|
+
},
|
|
8170
|
+
headers: { matchImportance: 0 }
|
|
8171
|
+
});
|
|
8172
|
+
const { schema: outputSchema, decodedValueHash: outputValueHash } = JsonSchemaHelper.generateSchemaAndHash(outputValue, {
|
|
8173
|
+
body: {
|
|
8174
|
+
encoding: EncodingType.BASE64,
|
|
8175
|
+
decodedType: getDecodedType(outputValue.headers["content-type"] || "")
|
|
8176
|
+
},
|
|
8177
|
+
headers: { matchImportance: 0 }
|
|
8178
|
+
});
|
|
8179
|
+
const cleanSpan = {
|
|
8180
|
+
traceId: replayTraceId,
|
|
8181
|
+
spanId: spanInfo.spanId,
|
|
8182
|
+
parentSpanId: "",
|
|
8183
|
+
name: completeInputValue.url,
|
|
8184
|
+
packageName: "nextjs",
|
|
8185
|
+
instrumentationName: self.INSTRUMENTATION_NAME,
|
|
8186
|
+
submoduleName: completeInputValue.method,
|
|
8187
|
+
inputValue: completeInputValue,
|
|
8188
|
+
outputValue,
|
|
8189
|
+
inputSchema,
|
|
8190
|
+
outputSchema,
|
|
8191
|
+
inputSchemaHash: JsonSchemaHelper.generateDeterministicHash(inputSchema),
|
|
8192
|
+
outputSchemaHash: JsonSchemaHelper.generateDeterministicHash(outputSchema),
|
|
8193
|
+
inputValueHash,
|
|
8194
|
+
outputValueHash,
|
|
8195
|
+
kind: SpanKind.SERVER,
|
|
8196
|
+
packageType: PackageType.HTTP,
|
|
8197
|
+
status: {
|
|
8198
|
+
code: (capturedStatusCode || 200) >= 400 ? StatusCode.ERROR : StatusCode.OK,
|
|
8199
|
+
message: (capturedStatusCode || 200) >= 400 ? `HTTP ${capturedStatusCode}` : ""
|
|
8200
|
+
},
|
|
8201
|
+
timestamp: {
|
|
8202
|
+
seconds: Math.floor(now.getTime() / 1e3),
|
|
8203
|
+
nanos: now.getTime() % 1e3 * 1e6
|
|
8204
|
+
},
|
|
8205
|
+
duration: {
|
|
8206
|
+
seconds: 0,
|
|
8207
|
+
nanos: 0
|
|
8208
|
+
},
|
|
8209
|
+
isRootSpan: true,
|
|
8210
|
+
isPreAppStart: false,
|
|
8211
|
+
metadata: void 0
|
|
8212
|
+
};
|
|
8213
|
+
await self.tuskDrift.sendInboundSpanForReplay(cleanSpan);
|
|
8214
|
+
} catch (e) {
|
|
8215
|
+
logger.error("[NextjsInstrumentation] Failed to build/send inbound replay span:", e);
|
|
8216
|
+
}
|
|
8217
|
+
} catch (error) {
|
|
8218
|
+
logger.error(`[NextjsInstrumentation] Error in Next.js request: ${error instanceof Error ? error.message : String(error)}`);
|
|
8219
|
+
SpanUtils.endSpan(spanInfo.span, {
|
|
8220
|
+
code: SpanStatusCode.ERROR,
|
|
8221
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8222
|
+
});
|
|
8223
|
+
throw error;
|
|
8224
|
+
}
|
|
8225
|
+
}
|
|
8226
|
+
/**
|
|
8227
|
+
* Captures the request body from an IncomingMessage stream by patching req.read() and listening for data events.
|
|
8228
|
+
* Similar to HTTP instrumentation's request body capture, but adapted for Next.js.
|
|
8229
|
+
*/
|
|
8230
|
+
_captureRequestBody(req, spanInfo, inputValue, onBodyCaptured) {
|
|
8231
|
+
const actualReq = req.originalRequest || req._req || req;
|
|
8232
|
+
const requestBodyChunks = [];
|
|
8233
|
+
let streamConsumptionMode = "NOT_CONSUMING";
|
|
8234
|
+
const originalRead = actualReq.read?.bind(actualReq);
|
|
8235
|
+
if (originalRead) actualReq.read = function read(size) {
|
|
8236
|
+
const chunk = originalRead(size);
|
|
8237
|
+
if (chunk && (streamConsumptionMode === "READ" || streamConsumptionMode === "NOT_CONSUMING")) {
|
|
8238
|
+
streamConsumptionMode = "READ";
|
|
8239
|
+
requestBodyChunks.push(Buffer.from(chunk));
|
|
8240
|
+
}
|
|
8241
|
+
return chunk;
|
|
8242
|
+
};
|
|
8243
|
+
if (typeof actualReq.once !== "function" || typeof actualReq.addListener !== "function") {
|
|
8244
|
+
logger.debug(`[NextjsInstrumentation] Request object doesn't have event emitter methods, skipping body capture`);
|
|
8245
|
+
return;
|
|
8246
|
+
}
|
|
8247
|
+
actualReq.once("resume", () => {
|
|
8248
|
+
actualReq.addListener("data", (chunk) => {
|
|
8249
|
+
if (chunk && (streamConsumptionMode === "PIPE" || streamConsumptionMode === "NOT_CONSUMING")) {
|
|
8250
|
+
streamConsumptionMode = "PIPE";
|
|
8251
|
+
requestBodyChunks.push(Buffer.from(chunk));
|
|
8252
|
+
}
|
|
8253
|
+
});
|
|
8254
|
+
});
|
|
8255
|
+
actualReq.addListener("end", async (chunk) => {
|
|
8256
|
+
if (chunk) requestBodyChunks.push(Buffer.from(chunk));
|
|
8257
|
+
if (requestBodyChunks.length > 0) try {
|
|
8258
|
+
const bodyBuffer = Buffer.concat(requestBodyChunks);
|
|
8259
|
+
const encodedBody = await httpBodyEncoder({
|
|
8260
|
+
bodyBuffer,
|
|
8261
|
+
contentEncoding: actualReq.headers["content-encoding"]
|
|
8262
|
+
});
|
|
8263
|
+
const updatedInputValue = {
|
|
8264
|
+
...inputValue,
|
|
8265
|
+
body: encodedBody,
|
|
8266
|
+
bodySize: bodyBuffer.length
|
|
8267
|
+
};
|
|
8268
|
+
if (onBodyCaptured) onBodyCaptured(updatedInputValue);
|
|
8269
|
+
SpanUtils.addSpanAttributes(spanInfo.span, {
|
|
8270
|
+
inputValue: updatedInputValue,
|
|
8271
|
+
inputSchemaMerges: {
|
|
8272
|
+
body: {
|
|
8273
|
+
encoding: EncodingType.BASE64,
|
|
8274
|
+
decodedType: getDecodedType(actualReq.headers["content-type"] || "")
|
|
8275
|
+
},
|
|
8276
|
+
headers: { matchImportance: 0 }
|
|
8277
|
+
}
|
|
8278
|
+
});
|
|
8279
|
+
logger.debug(`[NextjsInstrumentation] Captured request body for ${actualReq.method} ${actualReq.url}: ${bodyBuffer.length} bytes`);
|
|
8280
|
+
} catch (error) {
|
|
8281
|
+
logger.error(`[NextjsInstrumentation] Error processing request body:`, error);
|
|
8282
|
+
}
|
|
8283
|
+
});
|
|
8284
|
+
}
|
|
8285
|
+
_normalizeHeaders(headers) {
|
|
8286
|
+
const normalized = {};
|
|
8287
|
+
for (const [key, value] of Object.entries(headers)) if (value !== void 0) normalized[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
8288
|
+
return normalized;
|
|
8289
|
+
}
|
|
8290
|
+
_wrap(target, propertyName, wrapper) {
|
|
8291
|
+
wrap(target, propertyName, wrapper);
|
|
8292
|
+
}
|
|
8293
|
+
};
|
|
8294
|
+
|
|
7524
8295
|
//#endregion
|
|
7525
8296
|
//#region node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js
|
|
7526
8297
|
var require_suppress_tracing = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js": ((exports) => {
|
|
@@ -9697,7 +10468,7 @@ var SpanTransformer = class SpanTransformer {
|
|
|
9697
10468
|
if (transformMetadataString) try {
|
|
9698
10469
|
transformMetadata = JSON.parse(transformMetadataString);
|
|
9699
10470
|
} catch (error) {
|
|
9700
|
-
logger.warn("Failed to parse transform metadata", error);
|
|
10471
|
+
logger.warn("[SpanTransformer] Failed to parse transform metadata", error);
|
|
9701
10472
|
}
|
|
9702
10473
|
const originalDate = OriginalGlobalUtils.getOriginalDate();
|
|
9703
10474
|
return {
|
|
@@ -9957,7 +10728,18 @@ var TdSpanExporter = class {
|
|
|
9957
10728
|
*/
|
|
9958
10729
|
export(spans, resultCallback) {
|
|
9959
10730
|
logger.debug(`TdSpanExporter.export() called with ${spans.length} span(s)`);
|
|
9960
|
-
const
|
|
10731
|
+
const filteredSpans = spans.filter((span) => {
|
|
10732
|
+
if (span.instrumentationLibrary?.name === "next.js") return false;
|
|
10733
|
+
return true;
|
|
10734
|
+
});
|
|
10735
|
+
logger.debug(`After filtering: ${filteredSpans.length} span(s) remaining`);
|
|
10736
|
+
let cleanSpans;
|
|
10737
|
+
try {
|
|
10738
|
+
cleanSpans = filteredSpans.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
|
|
10739
|
+
} catch (error) {
|
|
10740
|
+
logger.error("Error transforming spans to CleanSpanData", error);
|
|
10741
|
+
throw error;
|
|
10742
|
+
}
|
|
9961
10743
|
if (this.adapters.length === 0) {
|
|
9962
10744
|
logger.debug("No adapters configured");
|
|
9963
10745
|
resultCallback({ code: import_src.ExportResultCode.SUCCESS });
|
|
@@ -10072,7 +10854,7 @@ var ProtobufCommunicator = class {
|
|
|
10072
10854
|
requestId,
|
|
10073
10855
|
tags: {},
|
|
10074
10856
|
outboundSpan: cleanSpan,
|
|
10075
|
-
stackTrace:
|
|
10857
|
+
stackTrace: cleanSpan?.stackTrace
|
|
10076
10858
|
});
|
|
10077
10859
|
const sdkMessage = SDKMessage.create({
|
|
10078
10860
|
type: MessageType.MOCK_REQUEST,
|
|
@@ -10113,7 +10895,7 @@ var ProtobufCommunicator = class {
|
|
|
10113
10895
|
requestId,
|
|
10114
10896
|
tags: {},
|
|
10115
10897
|
outboundSpan: cleanSpan,
|
|
10116
|
-
stackTrace:
|
|
10898
|
+
stackTrace: cleanSpan?.stackTrace
|
|
10117
10899
|
});
|
|
10118
10900
|
const sdkMessage = SDKMessage.create({
|
|
10119
10901
|
type: MessageType.MOCK_REQUEST,
|
|
@@ -10405,7 +11187,7 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10405
11187
|
const packageName = this.getPackageName(modulePath);
|
|
10406
11188
|
if (packageName && TuskDriftInstrumentationModuleNames.includes(packageName)) alreadyRequiredModuleNames.add(packageName);
|
|
10407
11189
|
}
|
|
10408
|
-
} else logger.
|
|
11190
|
+
} else logger.debug("Running in ES Module mode. Cannot detect pre-loaded instrumentation modules.");
|
|
10409
11191
|
return alreadyRequiredModuleNames;
|
|
10410
11192
|
}
|
|
10411
11193
|
static getInstance() {
|
|
@@ -10477,6 +11259,10 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10477
11259
|
enabled: true,
|
|
10478
11260
|
mode: this.mode
|
|
10479
11261
|
});
|
|
11262
|
+
new NextjsInstrumentation({
|
|
11263
|
+
enabled: true,
|
|
11264
|
+
mode: this.mode
|
|
11265
|
+
});
|
|
10480
11266
|
}
|
|
10481
11267
|
initializeTracing({ baseDirectory }) {
|
|
10482
11268
|
const serviceName = this.config.service?.name || "unknown";
|
|
@@ -10506,17 +11292,17 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10506
11292
|
return `sdk-${OriginalGlobalUtils.getOriginalDate().getTime()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
10507
11293
|
}
|
|
10508
11294
|
initialize(initParams) {
|
|
11295
|
+
initializeGlobalLogger({
|
|
11296
|
+
logLevel: initParams.logLevel || "info",
|
|
11297
|
+
prefix: "TuskDrift"
|
|
11298
|
+
});
|
|
10509
11299
|
this.samplingRate = this.config.recording?.sampling_rate ?? 1;
|
|
10510
11300
|
this.initParams = initParams;
|
|
10511
11301
|
if (!this.initParams.env) {
|
|
10512
|
-
const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "
|
|
11302
|
+
const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "development";
|
|
10513
11303
|
logger.warn(`Environment not provided in initialization parameters. Using '${nodeEnv}' as the environment.`);
|
|
10514
11304
|
this.initParams.env = nodeEnv;
|
|
10515
11305
|
}
|
|
10516
|
-
initializeGlobalLogger({
|
|
10517
|
-
logLevel: initParams.logLevel || "silent",
|
|
10518
|
-
prefix: "TuskDrift"
|
|
10519
|
-
});
|
|
10520
11306
|
if (this.initialized) {
|
|
10521
11307
|
logger.debug("Already initialized, skipping...");
|
|
10522
11308
|
return;
|
|
@@ -10591,8 +11377,8 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10591
11377
|
}
|
|
10592
11378
|
this.appReady = true;
|
|
10593
11379
|
logger.debug("Application marked as ready");
|
|
10594
|
-
if (this.mode === TuskDriftMode.REPLAY) logger.
|
|
10595
|
-
else if (this.mode === TuskDriftMode.RECORD) logger.
|
|
11380
|
+
if (this.mode === TuskDriftMode.REPLAY) logger.debug("Replay mode active - ready to serve mocked responses");
|
|
11381
|
+
else if (this.mode === TuskDriftMode.RECORD) logger.debug("Record mode active - capturing inbound requests and responses");
|
|
10596
11382
|
}
|
|
10597
11383
|
async sendInboundSpanForReplay(span) {
|
|
10598
11384
|
try {
|
|
@@ -10712,5 +11498,5 @@ var TuskDriftSDK = class {
|
|
|
10712
11498
|
const TuskDrift = new TuskDriftSDK();
|
|
10713
11499
|
|
|
10714
11500
|
//#endregion
|
|
10715
|
-
export { TuskDrift };
|
|
11501
|
+
export { TuskDrift, withTuskDrift };
|
|
10716
11502
|
//# sourceMappingURL=index.js.map
|