@use-tusk/drift-node-sdk 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -16
- package/dist/index.cjs +261 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +262 -152
- package/dist/index.js.map +1 -1
- package/package.json +17 -8
package/dist/index.cjs
CHANGED
|
@@ -91,7 +91,7 @@ var TdInstrumentationAbstract = class {
|
|
|
91
91
|
|
|
92
92
|
//#endregion
|
|
93
93
|
//#region package.json
|
|
94
|
-
var version = "0.1.
|
|
94
|
+
var version = "0.1.4";
|
|
95
95
|
|
|
96
96
|
//#endregion
|
|
97
97
|
//#region src/version.ts
|
|
@@ -1579,10 +1579,11 @@ async function findMockResponseAsync({ mockRequestData, tuskDrift, inputValueSch
|
|
|
1579
1579
|
|
|
1580
1580
|
//#endregion
|
|
1581
1581
|
//#region src/instrumentation/libraries/http/mocks/TdMockClientRequest.ts
|
|
1582
|
+
let ClientRequest;
|
|
1582
1583
|
/**
|
|
1583
1584
|
* Mock ClientRequest implementation for Tusk Drift HTTP replay
|
|
1584
1585
|
*/
|
|
1585
|
-
var TdMockClientRequest = class extends events.EventEmitter {
|
|
1586
|
+
var TdMockClientRequest = class TdMockClientRequest extends events.EventEmitter {
|
|
1586
1587
|
constructor(options, spanInfo, callback) {
|
|
1587
1588
|
super();
|
|
1588
1589
|
this.INSTRUMENTATION_NAME = "HttpInstrumentation";
|
|
@@ -1591,6 +1592,7 @@ var TdMockClientRequest = class extends events.EventEmitter {
|
|
|
1591
1592
|
this.playbackStarted = false;
|
|
1592
1593
|
this.readyToStartPlaybackOnSocketEvent = false;
|
|
1593
1594
|
this.headers = {};
|
|
1595
|
+
TdMockClientRequest._setupPrototype();
|
|
1594
1596
|
this.tuskDrift = TuskDriftCore.getInstance();
|
|
1595
1597
|
this.spanInfo = spanInfo;
|
|
1596
1598
|
if (!options || Object.keys(options).length === 0) throw new Error("Making a request with empty `options` is not supported in TdMockClientRequest");
|
|
@@ -1831,8 +1833,16 @@ var TdMockClientRequest = class extends events.EventEmitter {
|
|
|
1831
1833
|
this.socket.destroy();
|
|
1832
1834
|
return this;
|
|
1833
1835
|
}
|
|
1836
|
+
static _setupPrototype() {
|
|
1837
|
+
if (!ClientRequest) {
|
|
1838
|
+
ClientRequest = require("http").ClientRequest;
|
|
1839
|
+
Object.setPrototypeOf(TdMockClientRequest.prototype, ClientRequest.prototype);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
static initialize() {
|
|
1843
|
+
TdMockClientRequest._setupPrototype();
|
|
1844
|
+
}
|
|
1834
1845
|
};
|
|
1835
|
-
Object.setPrototypeOf(TdMockClientRequest.prototype, http.ClientRequest.prototype);
|
|
1836
1846
|
|
|
1837
1847
|
//#endregion
|
|
1838
1848
|
//#region src/instrumentation/libraries/http/HttpReplayHooks.ts
|
|
@@ -2103,7 +2113,19 @@ var HttpTransformEngine = class {
|
|
|
2103
2113
|
const target = span[selector];
|
|
2104
2114
|
if (!target) return false;
|
|
2105
2115
|
try {
|
|
2106
|
-
|
|
2116
|
+
const body = target.body;
|
|
2117
|
+
if (!body) return false;
|
|
2118
|
+
let bodyObj;
|
|
2119
|
+
if (typeof body === "string") try {
|
|
2120
|
+
const decoded = Buffer.from(body, "base64").toString("utf8");
|
|
2121
|
+
bodyObj = JSON.parse(decoded);
|
|
2122
|
+
} catch (e) {
|
|
2123
|
+
return false;
|
|
2124
|
+
}
|
|
2125
|
+
else bodyObj = body;
|
|
2126
|
+
const nodes = jsonpath.default.apply(bodyObj, jsonPath, actionFunction);
|
|
2127
|
+
if (typeof body === "string" && nodes.length > 0) target.body = Buffer.from(JSON.stringify(bodyObj)).toString("base64");
|
|
2128
|
+
return nodes.length > 0;
|
|
2107
2129
|
} catch (error) {
|
|
2108
2130
|
return false;
|
|
2109
2131
|
}
|
|
@@ -2111,9 +2133,8 @@ var HttpTransformEngine = class {
|
|
|
2111
2133
|
}
|
|
2112
2134
|
compileHeaderAction(headerName, actionFunction, direction) {
|
|
2113
2135
|
const lowerHeader = headerName.toLowerCase();
|
|
2114
|
-
const selector = direction === "inbound" ? "inputValue" : "outputValue";
|
|
2115
2136
|
return (span) => {
|
|
2116
|
-
const target = span
|
|
2137
|
+
const target = span.inputValue;
|
|
2117
2138
|
if (!target?.headers) return false;
|
|
2118
2139
|
let applied = false;
|
|
2119
2140
|
for (const key of Object.keys(target.headers)) if (key.toLowerCase() === lowerHeader) {
|
|
@@ -2134,7 +2155,15 @@ var HttpTransformEngine = class {
|
|
|
2134
2155
|
return (span) => {
|
|
2135
2156
|
const target = span[selector];
|
|
2136
2157
|
if (!target || target.body === void 0) return false;
|
|
2137
|
-
|
|
2158
|
+
if (direction === "outbound") if (typeof target.body === "string") try {
|
|
2159
|
+
const decoded = Buffer.from(target.body, "base64").toString("utf8");
|
|
2160
|
+
const transformed = actionFunction(decoded);
|
|
2161
|
+
target.body = Buffer.from(transformed).toString("base64");
|
|
2162
|
+
} catch (error) {
|
|
2163
|
+
target.body = Buffer.from(actionFunction(target.body)).toString("base64");
|
|
2164
|
+
}
|
|
2165
|
+
else target.body = Buffer.from(actionFunction(JSON.stringify(target.body))).toString("base64");
|
|
2166
|
+
else target.body = actionFunction(typeof target.body === "string" ? target.body : JSON.stringify(target.body));
|
|
2138
2167
|
return true;
|
|
2139
2168
|
};
|
|
2140
2169
|
}
|
|
@@ -4960,7 +4989,7 @@ var JwksRsaInstrumentation = class extends TdInstrumentationBase {
|
|
|
4960
4989
|
const originalExpressJwtSecret = jwksModule.expressJwtSecret;
|
|
4961
4990
|
jwksModule.expressJwtSecret = function(options) {
|
|
4962
4991
|
logger.debug(`[JwksRsaInstrumentation] expressJwtSecret called with options:`, options);
|
|
4963
|
-
|
|
4992
|
+
const modifiedOptions = { ...options };
|
|
4964
4993
|
if (self$1.tuskDrift.getMode() === TuskDriftMode.REPLAY) {
|
|
4965
4994
|
logger.debug(`[JwksRsaInstrumentation] REPLAY MODE - Disabling rate limiting for expressJwtSecret`);
|
|
4966
4995
|
modifiedOptions.rateLimit = false;
|
|
@@ -6105,9 +6134,9 @@ var require_types$3 = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/
|
|
|
6105
6134
|
var require_ExportResult = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/ExportResult.js": ((exports) => {
|
|
6106
6135
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6107
6136
|
exports.ExportResultCode = void 0;
|
|
6108
|
-
(function(ExportResultCode$
|
|
6109
|
-
ExportResultCode$
|
|
6110
|
-
ExportResultCode$
|
|
6137
|
+
(function(ExportResultCode$3) {
|
|
6138
|
+
ExportResultCode$3[ExportResultCode$3["SUCCESS"] = 0] = "SUCCESS";
|
|
6139
|
+
ExportResultCode$3[ExportResultCode$3["FAILED"] = 1] = "FAILED";
|
|
6111
6140
|
})(exports.ExportResultCode || (exports.ExportResultCode = {}));
|
|
6112
6141
|
}) });
|
|
6113
6142
|
|
|
@@ -6949,122 +6978,23 @@ var require_src$6 = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/co
|
|
|
6949
6978
|
}) });
|
|
6950
6979
|
|
|
6951
6980
|
//#endregion
|
|
6952
|
-
//#region src/core/tracing/
|
|
6953
|
-
var import_src$
|
|
6954
|
-
const DRIFT_API_PATH = "/api/drift";
|
|
6981
|
+
//#region src/core/tracing/SpanTransformer.ts
|
|
6982
|
+
var import_src$3 = /* @__PURE__ */ __toESM(require_src$6(), 1);
|
|
6955
6983
|
/**
|
|
6956
|
-
*
|
|
6957
|
-
* If useRemoteExport is false, TdTraceExporter stores spans organized by trace ID in separate files.
|
|
6958
|
-
* - Each trace gets its own JSONL file: `{baseDirectory}/{timestamp}_trace_{traceId}.jsonl`.
|
|
6984
|
+
* Utility class for transforming OpenTelemetry spans to CleanSpanData
|
|
6959
6985
|
*/
|
|
6960
|
-
var
|
|
6961
|
-
constructor(config) {
|
|
6962
|
-
this.traceFileMap = /* @__PURE__ */ new Map();
|
|
6963
|
-
this.baseDirectory = config.baseDirectory;
|
|
6964
|
-
this.mode = config.mode;
|
|
6965
|
-
this.useRemoteExport = config.useRemoteExport;
|
|
6966
|
-
this.observableServiceId = config.observableServiceId;
|
|
6967
|
-
this.apiKey = config.apiKey;
|
|
6968
|
-
this.tuskBackendBaseUrl = config.tuskBackendBaseUrl;
|
|
6969
|
-
this.environment = config.environment;
|
|
6970
|
-
this.sdkVersion = config.sdkVersion;
|
|
6971
|
-
this.sdkInstanceId = config.sdkInstanceId;
|
|
6972
|
-
if (!fs.existsSync(this.baseDirectory)) fs.mkdirSync(this.baseDirectory, { recursive: true });
|
|
6973
|
-
if (this.useRemoteExport && this.apiKey) {
|
|
6974
|
-
const transport = new __protobuf_ts_twirp_transport.TwirpFetchTransport({
|
|
6975
|
-
baseUrl: `${this.tuskBackendBaseUrl}${DRIFT_API_PATH}`,
|
|
6976
|
-
meta: {
|
|
6977
|
-
"x-api-key": this.apiKey,
|
|
6978
|
-
"x-td-skip-instrumentation": "true"
|
|
6979
|
-
}
|
|
6980
|
-
});
|
|
6981
|
-
this.spanExportClient = new __use_tusk_drift_schemas_backend_span_export_service_client.SpanExportServiceClient(transport);
|
|
6982
|
-
}
|
|
6983
|
-
logger.debug(`TdTraceExporter initialized - ${this.useRemoteExport ? "remote export enabled" : "local file export only"}`);
|
|
6984
|
-
}
|
|
6985
|
-
/**
|
|
6986
|
-
* Export spans to trace-specific files and optionally to remote endpoint
|
|
6987
|
-
*/
|
|
6988
|
-
export(spans, resultCallback) {
|
|
6989
|
-
logger.debug(`TdTraceExporter.export() called with ${spans.length} span(s)`);
|
|
6990
|
-
if (this.mode !== TuskDriftMode.RECORD) {
|
|
6991
|
-
logger.debug(`Not recording spans in tuskDriftMode: ${this.mode}`);
|
|
6992
|
-
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
6993
|
-
return;
|
|
6994
|
-
}
|
|
6995
|
-
if (this.useRemoteExport) if (this.spanExportClient) this.exportToRemote(spans).then(() => {
|
|
6996
|
-
logger.debug(`Successfully exported ${spans.length} spans to remote endpoint`);
|
|
6997
|
-
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
6998
|
-
}).catch((error) => {
|
|
6999
|
-
logger.error(`Failed to export spans to remote:`, error);
|
|
7000
|
-
resultCallback({
|
|
7001
|
-
code: import_src$1.ExportResultCode.FAILED,
|
|
7002
|
-
error: error instanceof Error ? error : /* @__PURE__ */ new Error("Remote export failed")
|
|
7003
|
-
});
|
|
7004
|
-
});
|
|
7005
|
-
else {
|
|
7006
|
-
logger.error("Remote export client not initialized, likely because apiKey is not provided");
|
|
7007
|
-
resultCallback({
|
|
7008
|
-
code: import_src$1.ExportResultCode.FAILED,
|
|
7009
|
-
error: /* @__PURE__ */ new Error("Remote export client not initialized, likely because apiKey is not provided")
|
|
7010
|
-
});
|
|
7011
|
-
}
|
|
7012
|
-
else {
|
|
7013
|
-
this.exportToLocalFiles(spans);
|
|
7014
|
-
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
7015
|
-
}
|
|
7016
|
-
}
|
|
7017
|
-
/**
|
|
7018
|
-
* Export spans to remote endpoint via protobuf
|
|
7019
|
-
*/
|
|
7020
|
-
async exportToRemote(spans) {
|
|
7021
|
-
if (!this.spanExportClient) throw new Error("Remote export client not initialized");
|
|
7022
|
-
if (!this.observableServiceId) throw new Error("Observable service ID not provided in config");
|
|
7023
|
-
const protoSpans = spans.map((span) => this.transformSpanToProtobuf(span));
|
|
7024
|
-
const request = {
|
|
7025
|
-
observableServiceId: this.observableServiceId,
|
|
7026
|
-
environment: this.environment,
|
|
7027
|
-
sdkVersion: this.sdkVersion,
|
|
7028
|
-
sdkInstanceId: this.sdkInstanceId,
|
|
7029
|
-
spans: protoSpans
|
|
7030
|
-
};
|
|
7031
|
-
const response = await this.spanExportClient.exportSpans(request);
|
|
7032
|
-
if (!response.response.success) throw new Error(`Remote export failed: ${response.response.message}`);
|
|
7033
|
-
}
|
|
7034
|
-
/**
|
|
7035
|
-
* Export spans to local files
|
|
7036
|
-
*/
|
|
7037
|
-
exportToLocalFiles(spans) {
|
|
7038
|
-
try {
|
|
7039
|
-
for (const span of spans) {
|
|
7040
|
-
const traceId = span.spanContext().traceId;
|
|
7041
|
-
const spanData = this.transformSpanToCleanJSON(span);
|
|
7042
|
-
let filePath = this.traceFileMap.get(traceId);
|
|
7043
|
-
if (!filePath) {
|
|
7044
|
-
const isoTimestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7045
|
-
filePath = path.join(this.baseDirectory, `${isoTimestamp}_trace_${traceId}.jsonl`);
|
|
7046
|
-
this.traceFileMap.set(traceId, filePath);
|
|
7047
|
-
}
|
|
7048
|
-
const jsonLine = JSON.stringify(spanData) + "\n";
|
|
7049
|
-
fs.appendFileSync(filePath, jsonLine, "utf8");
|
|
7050
|
-
}
|
|
7051
|
-
logger.debug(`Exported ${spans.length} span(s) to trace-specific files in ${this.baseDirectory}`);
|
|
7052
|
-
} catch (error) {
|
|
7053
|
-
logger.error(`Failed to export spans to local files:`, error);
|
|
7054
|
-
throw error;
|
|
7055
|
-
}
|
|
7056
|
-
}
|
|
6986
|
+
var SpanTransformer = class SpanTransformer {
|
|
7057
6987
|
/**
|
|
7058
6988
|
* Transform OpenTelemetry span to clean JSON format with compile-time type safety
|
|
7059
6989
|
* Return type is derived from protobuf schema but uses clean JSON.
|
|
7060
6990
|
* We use JSON because serialized protobuf is extremely verbose and not readable.
|
|
7061
6991
|
*/
|
|
7062
|
-
transformSpanToCleanJSON(span) {
|
|
6992
|
+
static transformSpanToCleanJSON(span) {
|
|
7063
6993
|
const isRootSpan = !span.parentSpanId || span.kind === __opentelemetry_api.SpanKind.SERVER;
|
|
7064
6994
|
const attributes = span.attributes;
|
|
7065
|
-
const packageName =
|
|
7066
|
-
const instrumentationName =
|
|
7067
|
-
const submoduleName =
|
|
6995
|
+
const packageName = SpanTransformer.extractPackageName(attributes);
|
|
6996
|
+
const instrumentationName = SpanTransformer.extractInstrumentationName(span, attributes);
|
|
6997
|
+
const submoduleName = SpanTransformer.extractSubmoduleName(attributes);
|
|
7068
6998
|
const inputValueString = attributes[TdSpanAttributes.INPUT_VALUE];
|
|
7069
6999
|
const inputData = JSON.parse(inputValueString);
|
|
7070
7000
|
const inputSchemaMergesString = attributes[TdSpanAttributes.INPUT_SCHEMA_MERGES];
|
|
@@ -7130,10 +7060,115 @@ var TdSpanExporter = class {
|
|
|
7130
7060
|
};
|
|
7131
7061
|
}
|
|
7132
7062
|
/**
|
|
7133
|
-
*
|
|
7063
|
+
* Extract package name from attributes or instrumentation library
|
|
7134
7064
|
*/
|
|
7135
|
-
|
|
7136
|
-
|
|
7065
|
+
static extractPackageName(attributes) {
|
|
7066
|
+
if (attributes[TdSpanAttributes.PACKAGE_NAME]) return attributes[TdSpanAttributes.PACKAGE_NAME];
|
|
7067
|
+
return "unknown";
|
|
7068
|
+
}
|
|
7069
|
+
/**
|
|
7070
|
+
* Extract instrumentation name from span data
|
|
7071
|
+
*/
|
|
7072
|
+
static extractInstrumentationName(span, attributes) {
|
|
7073
|
+
if (attributes[TdSpanAttributes.INSTRUMENTATION_NAME]) return attributes[TdSpanAttributes.INSTRUMENTATION_NAME];
|
|
7074
|
+
if (span.instrumentationLibrary?.name) return `tusk-instrumentation-${span.instrumentationLibrary.name}`;
|
|
7075
|
+
return `tusk-instrumentation-${SpanTransformer.extractPackageName(attributes)}`;
|
|
7076
|
+
}
|
|
7077
|
+
/**
|
|
7078
|
+
* Extract submodule name from attributes
|
|
7079
|
+
*/
|
|
7080
|
+
static extractSubmoduleName(attributes) {
|
|
7081
|
+
if (attributes[TdSpanAttributes.SUBMODULE_NAME]) return attributes[TdSpanAttributes.SUBMODULE_NAME];
|
|
7082
|
+
}
|
|
7083
|
+
};
|
|
7084
|
+
|
|
7085
|
+
//#endregion
|
|
7086
|
+
//#region src/core/tracing/adapters/FilesystemSpanAdapter.ts
|
|
7087
|
+
/**
|
|
7088
|
+
* Exports spans to local JSONL files organized by trace ID
|
|
7089
|
+
*/
|
|
7090
|
+
var FilesystemSpanAdapter = class {
|
|
7091
|
+
constructor(config) {
|
|
7092
|
+
this.name = "filesystem";
|
|
7093
|
+
this.traceFileMap = /* @__PURE__ */ new Map();
|
|
7094
|
+
this.baseDirectory = config.baseDirectory;
|
|
7095
|
+
if (!fs.existsSync(this.baseDirectory)) fs.mkdirSync(this.baseDirectory, { recursive: true });
|
|
7096
|
+
}
|
|
7097
|
+
async exportSpans(spans) {
|
|
7098
|
+
try {
|
|
7099
|
+
for (const span of spans) {
|
|
7100
|
+
const traceId = span.traceId;
|
|
7101
|
+
let filePath = this.traceFileMap.get(traceId);
|
|
7102
|
+
if (!filePath) {
|
|
7103
|
+
const isoTimestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7104
|
+
filePath = path.join(this.baseDirectory, `${isoTimestamp}_trace_${traceId}.jsonl`);
|
|
7105
|
+
this.traceFileMap.set(traceId, filePath);
|
|
7106
|
+
}
|
|
7107
|
+
const jsonLine = JSON.stringify(span) + "\n";
|
|
7108
|
+
fs.appendFileSync(filePath, jsonLine, "utf8");
|
|
7109
|
+
}
|
|
7110
|
+
logger.debug(`Exported ${spans.length} span(s) to trace-specific files in ${this.baseDirectory}`);
|
|
7111
|
+
return { code: import_src$3.ExportResultCode.SUCCESS };
|
|
7112
|
+
} catch (error) {
|
|
7113
|
+
logger.error(`Failed to export spans to local files:`, error);
|
|
7114
|
+
return {
|
|
7115
|
+
code: import_src$3.ExportResultCode.FAILED,
|
|
7116
|
+
error: error instanceof Error ? error : /* @__PURE__ */ new Error("Filesystem export failed")
|
|
7117
|
+
};
|
|
7118
|
+
}
|
|
7119
|
+
}
|
|
7120
|
+
async shutdown() {
|
|
7121
|
+
return Promise.resolve();
|
|
7122
|
+
}
|
|
7123
|
+
};
|
|
7124
|
+
|
|
7125
|
+
//#endregion
|
|
7126
|
+
//#region src/core/tracing/adapters/ApiSpanAdapter.ts
|
|
7127
|
+
var import_src$2 = /* @__PURE__ */ __toESM(require_src$6(), 1);
|
|
7128
|
+
const DRIFT_API_PATH = "/api/drift";
|
|
7129
|
+
/**
|
|
7130
|
+
* Exports spans to Tusk backend API via protobuf
|
|
7131
|
+
*/
|
|
7132
|
+
var ApiSpanAdapter = class {
|
|
7133
|
+
constructor(config) {
|
|
7134
|
+
this.name = "api";
|
|
7135
|
+
this.observableServiceId = config.observableServiceId;
|
|
7136
|
+
this.environment = config.environment;
|
|
7137
|
+
this.sdkVersion = config.sdkVersion;
|
|
7138
|
+
this.sdkInstanceId = config.sdkInstanceId;
|
|
7139
|
+
const transport = new __protobuf_ts_twirp_transport.TwirpFetchTransport({
|
|
7140
|
+
baseUrl: `${config.tuskBackendBaseUrl}${DRIFT_API_PATH}`,
|
|
7141
|
+
meta: {
|
|
7142
|
+
"x-api-key": config.apiKey,
|
|
7143
|
+
"x-td-skip-instrumentation": "true"
|
|
7144
|
+
}
|
|
7145
|
+
});
|
|
7146
|
+
this.spanExportClient = new __use_tusk_drift_schemas_backend_span_export_service_client.SpanExportServiceClient(transport);
|
|
7147
|
+
logger.debug("ApiSpanAdapter initialized");
|
|
7148
|
+
}
|
|
7149
|
+
async exportSpans(spans) {
|
|
7150
|
+
try {
|
|
7151
|
+
const protoSpans = spans.map((span) => this.transformSpanToProtobuf(span));
|
|
7152
|
+
const request = {
|
|
7153
|
+
observableServiceId: this.observableServiceId,
|
|
7154
|
+
environment: this.environment,
|
|
7155
|
+
sdkVersion: this.sdkVersion,
|
|
7156
|
+
sdkInstanceId: this.sdkInstanceId,
|
|
7157
|
+
spans: protoSpans
|
|
7158
|
+
};
|
|
7159
|
+
const response = await this.spanExportClient.exportSpans(request);
|
|
7160
|
+
if (!response.response.success) throw new Error(`Remote export failed: ${response.response.message}`);
|
|
7161
|
+
logger.debug(`Successfully exported ${spans.length} spans to remote endpoint`);
|
|
7162
|
+
return { code: import_src$2.ExportResultCode.SUCCESS };
|
|
7163
|
+
} catch (error) {
|
|
7164
|
+
logger.error(`Failed to export spans to remote:`, error);
|
|
7165
|
+
return {
|
|
7166
|
+
code: import_src$2.ExportResultCode.FAILED,
|
|
7167
|
+
error: error instanceof Error ? error : /* @__PURE__ */ new Error("API export failed")
|
|
7168
|
+
};
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
transformSpanToProtobuf(cleanSpan) {
|
|
7137
7172
|
return {
|
|
7138
7173
|
traceId: cleanSpan.traceId,
|
|
7139
7174
|
spanId: cleanSpan.spanId,
|
|
@@ -7151,7 +7186,7 @@ var TdSpanExporter = class {
|
|
|
7151
7186
|
outputSchemaHash: cleanSpan.outputSchemaHash || "",
|
|
7152
7187
|
inputValueHash: cleanSpan.inputValueHash || "",
|
|
7153
7188
|
outputValueHash: cleanSpan.outputValueHash || "",
|
|
7154
|
-
kind:
|
|
7189
|
+
kind: this.mapSpanKind(cleanSpan.kind),
|
|
7155
7190
|
status: cleanSpan.status,
|
|
7156
7191
|
isPreAppStart: cleanSpan.isPreAppStart,
|
|
7157
7192
|
timestamp: {
|
|
@@ -7166,32 +7201,111 @@ var TdSpanExporter = class {
|
|
|
7166
7201
|
metadata: toStruct(cleanSpan.metadata)
|
|
7167
7202
|
};
|
|
7168
7203
|
}
|
|
7204
|
+
mapSpanKind(kind) {
|
|
7205
|
+
switch (kind) {
|
|
7206
|
+
case __opentelemetry_api.SpanKind.CLIENT: return __use_tusk_drift_schemas_core_span.SpanKind.CLIENT;
|
|
7207
|
+
case __opentelemetry_api.SpanKind.SERVER: return __use_tusk_drift_schemas_core_span.SpanKind.SERVER;
|
|
7208
|
+
case __opentelemetry_api.SpanKind.PRODUCER: return __use_tusk_drift_schemas_core_span.SpanKind.PRODUCER;
|
|
7209
|
+
case __opentelemetry_api.SpanKind.CONSUMER: return __use_tusk_drift_schemas_core_span.SpanKind.CONSUMER;
|
|
7210
|
+
case __opentelemetry_api.SpanKind.INTERNAL: return __use_tusk_drift_schemas_core_span.SpanKind.INTERNAL;
|
|
7211
|
+
default: return __use_tusk_drift_schemas_core_span.SpanKind.UNSPECIFIED;
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
async shutdown() {
|
|
7215
|
+
return Promise.resolve();
|
|
7216
|
+
}
|
|
7217
|
+
};
|
|
7218
|
+
|
|
7219
|
+
//#endregion
|
|
7220
|
+
//#region src/core/tracing/TdSpanExporter.ts
|
|
7221
|
+
var import_src$1 = /* @__PURE__ */ __toESM(require_src$6(), 1);
|
|
7222
|
+
var TdSpanExporter = class {
|
|
7223
|
+
constructor(config) {
|
|
7224
|
+
this.adapters = [];
|
|
7225
|
+
this.mode = config.mode;
|
|
7226
|
+
this.setupDefaultAdapters(config);
|
|
7227
|
+
logger.debug(`TdSpanExporter initialized with ${this.adapters.length} adapter(s)`);
|
|
7228
|
+
}
|
|
7229
|
+
setupDefaultAdapters(config) {
|
|
7230
|
+
if (config.useRemoteExport && config.apiKey && config.observableServiceId) {
|
|
7231
|
+
logger.debug("TdSpanExporter using API adapter");
|
|
7232
|
+
this.addAdapter(new ApiSpanAdapter({
|
|
7233
|
+
apiKey: config.apiKey,
|
|
7234
|
+
tuskBackendBaseUrl: config.tuskBackendBaseUrl,
|
|
7235
|
+
observableServiceId: config.observableServiceId,
|
|
7236
|
+
environment: config.environment,
|
|
7237
|
+
sdkVersion: config.sdkVersion,
|
|
7238
|
+
sdkInstanceId: config.sdkInstanceId
|
|
7239
|
+
}));
|
|
7240
|
+
} else {
|
|
7241
|
+
logger.debug("TdSpanExporter falling back to filesystem adapter");
|
|
7242
|
+
this.addAdapter(new FilesystemSpanAdapter({ baseDirectory: config.baseDirectory }));
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
getAdapters() {
|
|
7246
|
+
return this.adapters;
|
|
7247
|
+
}
|
|
7169
7248
|
/**
|
|
7170
|
-
*
|
|
7249
|
+
* Add a custom export adapter
|
|
7171
7250
|
*/
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7251
|
+
addAdapter(adapter) {
|
|
7252
|
+
this.adapters.push(adapter);
|
|
7253
|
+
logger.debug(`Added ${adapter.name} adapter. Total adapters: ${this.adapters.length}`);
|
|
7175
7254
|
}
|
|
7176
7255
|
/**
|
|
7177
|
-
*
|
|
7256
|
+
* Remove a specific adapter
|
|
7178
7257
|
*/
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
if (
|
|
7182
|
-
|
|
7258
|
+
removeAdapter(adapter) {
|
|
7259
|
+
const index = this.adapters.indexOf(adapter);
|
|
7260
|
+
if (index > -1) {
|
|
7261
|
+
this.adapters.splice(index, 1);
|
|
7262
|
+
logger.debug(`Removed ${adapter.name} adapter. Total adapters: ${this.adapters.length}`);
|
|
7263
|
+
}
|
|
7183
7264
|
}
|
|
7184
7265
|
/**
|
|
7185
|
-
*
|
|
7266
|
+
* Clear all adapters
|
|
7186
7267
|
*/
|
|
7187
|
-
|
|
7188
|
-
|
|
7268
|
+
clearAdapters() {
|
|
7269
|
+
this.adapters = [];
|
|
7270
|
+
logger.debug("All adapters cleared");
|
|
7189
7271
|
}
|
|
7190
7272
|
/**
|
|
7191
|
-
*
|
|
7273
|
+
* Set the mode for determining which adapters to run
|
|
7274
|
+
*/
|
|
7275
|
+
setMode(mode) {
|
|
7276
|
+
this.mode = mode;
|
|
7277
|
+
}
|
|
7278
|
+
/**
|
|
7279
|
+
* Export spans using all configured adapters
|
|
7280
|
+
*/
|
|
7281
|
+
export(spans, resultCallback) {
|
|
7282
|
+
logger.debug(`TdSpanExporter.export() called with ${spans.length} span(s)`);
|
|
7283
|
+
const cleanSpans = spans.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
|
|
7284
|
+
if (this.adapters.length === 0) {
|
|
7285
|
+
logger.debug("No adapters configured");
|
|
7286
|
+
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
7287
|
+
return;
|
|
7288
|
+
}
|
|
7289
|
+
const activeAdapters = this.getActiveAdapters();
|
|
7290
|
+
if (activeAdapters.length === 0) {
|
|
7291
|
+
logger.debug(`No active adapters for mode: ${this.mode}`);
|
|
7292
|
+
resultCallback({ code: import_src$1.ExportResultCode.SUCCESS });
|
|
7293
|
+
return;
|
|
7294
|
+
}
|
|
7295
|
+
Promise.all(activeAdapters.map((adapter) => adapter.exportSpans(cleanSpans))).then(() => resultCallback({ code: import_src$1.ExportResultCode.SUCCESS })).catch((error) => resultCallback({
|
|
7296
|
+
code: import_src$1.ExportResultCode.FAILED,
|
|
7297
|
+
error
|
|
7298
|
+
}));
|
|
7299
|
+
}
|
|
7300
|
+
getActiveAdapters() {
|
|
7301
|
+
if (this.mode !== TuskDriftMode.RECORD) return this.adapters.filter((adapter) => adapter.name === "in-memory" || adapter.name === "callback");
|
|
7302
|
+
return this.adapters;
|
|
7303
|
+
}
|
|
7304
|
+
/**
|
|
7305
|
+
* Shutdown all adapters
|
|
7192
7306
|
*/
|
|
7193
7307
|
async shutdown() {
|
|
7194
|
-
|
|
7308
|
+
await Promise.all(this.adapters.map((adapter) => adapter.shutdown()));
|
|
7195
7309
|
}
|
|
7196
7310
|
/**
|
|
7197
7311
|
* Force flush any pending spans
|
|
@@ -10493,12 +10607,6 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10493
10607
|
default: return TuskDriftMode.DISABLED;
|
|
10494
10608
|
}
|
|
10495
10609
|
}
|
|
10496
|
-
initializeMemoryStore({ baseDirectory }) {
|
|
10497
|
-
if (this.isRunningIntegrationTest()) {
|
|
10498
|
-
logger.debug(`Initializing memory store with base directory: ${baseDirectory}`);
|
|
10499
|
-
this.memoryStore.initialize(baseDirectory);
|
|
10500
|
-
} else logger.debug("Not running integration test, skipping memory store initialization");
|
|
10501
|
-
}
|
|
10502
10610
|
registerDefaultInstrumentations() {
|
|
10503
10611
|
const transforms = this.config.transforms ?? this.initParams.transforms;
|
|
10504
10612
|
new HttpInstrumentation({
|
|
@@ -10546,20 +10654,20 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10546
10654
|
initializeTracing({ baseDirectory }) {
|
|
10547
10655
|
const serviceName = this.config.service?.name || "unknown";
|
|
10548
10656
|
logger.debug(`Initializing OpenTelemetry tracing for service: ${serviceName}`);
|
|
10549
|
-
|
|
10657
|
+
this.spanExporter = new TdSpanExporter({
|
|
10550
10658
|
baseDirectory,
|
|
10551
10659
|
mode: this.mode,
|
|
10552
10660
|
useRemoteExport: this.config.recording?.export_spans || false,
|
|
10553
10661
|
observableServiceId: this.config.service?.id,
|
|
10554
10662
|
apiKey: this.initParams.apiKey,
|
|
10555
10663
|
tuskBackendBaseUrl: this.config.tusk_api?.url || "https://api.usetusk.ai",
|
|
10556
|
-
environment: this.initParams.env,
|
|
10664
|
+
environment: this.initParams.env || "unknown",
|
|
10557
10665
|
sdkVersion: SDK_VERSION,
|
|
10558
10666
|
sdkInstanceId: this.generateSdkInstanceId()
|
|
10559
10667
|
});
|
|
10560
10668
|
this.sdk = new __opentelemetry_sdk_node.NodeSDK({
|
|
10561
10669
|
serviceName,
|
|
10562
|
-
spanProcessor: new import_src.BatchSpanProcessor(
|
|
10670
|
+
spanProcessor: new import_src.BatchSpanProcessor(this.spanExporter, {
|
|
10563
10671
|
maxQueueSize: 2048,
|
|
10564
10672
|
maxExportBatchSize: 512,
|
|
10565
10673
|
scheduledDelayMillis: 2e3,
|
|
@@ -10576,6 +10684,11 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10576
10684
|
initialize(initParams) {
|
|
10577
10685
|
this.samplingRate = this.config.recording?.sampling_rate ?? 1;
|
|
10578
10686
|
this.initParams = initParams;
|
|
10687
|
+
if (!this.initParams.env) {
|
|
10688
|
+
const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "unknown";
|
|
10689
|
+
logger.warn(`Environment not provided in initialization parameters. Using '${nodeEnv}' as the environment.`);
|
|
10690
|
+
this.initParams.env = nodeEnv;
|
|
10691
|
+
}
|
|
10579
10692
|
initializeGlobalLogger({
|
|
10580
10693
|
logLevel: initParams.logLevel || "info",
|
|
10581
10694
|
prefix: "TuskDrift"
|
|
@@ -10588,10 +10701,6 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10588
10701
|
logger.error("In record mode and export_spans is true, but API key not provided. API key is required to export spans to Tusk backend. Please provide an API key in the initialization parameters.");
|
|
10589
10702
|
return;
|
|
10590
10703
|
}
|
|
10591
|
-
if (!this.initParams.env) {
|
|
10592
|
-
logger.error("Environment not provided. Please provide an environment in the initialization parameters.");
|
|
10593
|
-
return;
|
|
10594
|
-
}
|
|
10595
10704
|
if (this.config.recording?.export_spans && !this.config.service?.id) {
|
|
10596
10705
|
logger.error("Observable service ID not provided. Please provide an observable service ID in the configuration file.");
|
|
10597
10706
|
return;
|
|
@@ -10633,9 +10742,7 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
10633
10742
|
const baseDirectory = this.config.traces?.dir || path.default.join(process.cwd(), ".tusk/traces");
|
|
10634
10743
|
logger.debug(`Config: ${JSON.stringify(this.config)}`);
|
|
10635
10744
|
logger.debug(`Base directory: ${baseDirectory}`);
|
|
10636
|
-
this.config.transforms ?? this.initParams.transforms;
|
|
10637
10745
|
this.initializeTracing({ baseDirectory });
|
|
10638
|
-
this.initializeMemoryStore({ baseDirectory });
|
|
10639
10746
|
this.registerDefaultInstrumentations();
|
|
10640
10747
|
this.initialized = true;
|
|
10641
10748
|
logger.info("SDK initialized successfully");
|
|
@@ -10807,6 +10914,9 @@ var TuskDriftSDK = class {
|
|
|
10807
10914
|
markAppAsReady() {
|
|
10808
10915
|
return this.tuskDrift.markAppAsReady();
|
|
10809
10916
|
}
|
|
10917
|
+
isAppReady() {
|
|
10918
|
+
return this.tuskDrift.isAppReady();
|
|
10919
|
+
}
|
|
10810
10920
|
};
|
|
10811
10921
|
const TuskDrift = new TuskDriftSDK();
|
|
10812
10922
|
|