@brizz/sdk 0.1.0 → 0.1.2-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +339 -19
- package/dist/index.cjs +2936 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +178 -0
- package/dist/index.d.ts +177 -12
- package/dist/index.js +2895 -9
- package/dist/index.js.map +1 -1
- package/dist/loader.mjs +72 -0
- package/dist/preload.cjs +2786 -0
- package/dist/preload.cjs.map +1 -0
- package/dist/preload.d.cts +2 -0
- package/dist/preload.d.ts +2 -0
- package/dist/preload.js +2777 -0
- package/dist/preload.js.map +1 -0
- package/package.json +35 -13
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/config.d.ts +0 -17
- package/dist/internal/config.d.ts.map +0 -1
- package/dist/internal/config.js +0 -90
- package/dist/internal/config.js.map +0 -1
- package/dist/internal/instrumentation/auto-init.d.ts +0 -3
- package/dist/internal/instrumentation/auto-init.d.ts.map +0 -1
- package/dist/internal/instrumentation/auto-init.js +0 -82
- package/dist/internal/instrumentation/auto-init.js.map +0 -1
- package/dist/internal/instrumentation/index.d.ts +0 -2
- package/dist/internal/instrumentation/index.d.ts.map +0 -1
- package/dist/internal/instrumentation/index.js +0 -2
- package/dist/internal/instrumentation/index.js.map +0 -1
- package/dist/internal/instrumentation/registry.d.ts +0 -29
- package/dist/internal/instrumentation/registry.d.ts.map +0 -1
- package/dist/internal/instrumentation/registry.js +0 -88
- package/dist/internal/instrumentation/registry.js.map +0 -1
- package/dist/internal/log/index.d.ts +0 -2
- package/dist/internal/log/index.d.ts.map +0 -1
- package/dist/internal/log/index.js +0 -2
- package/dist/internal/log/index.js.map +0 -1
- package/dist/internal/log/logging.d.ts +0 -21
- package/dist/internal/log/logging.d.ts.map +0 -1
- package/dist/internal/log/logging.js +0 -149
- package/dist/internal/log/logging.js.map +0 -1
- package/dist/internal/logger.d.ts +0 -23
- package/dist/internal/logger.d.ts.map +0 -1
- package/dist/internal/logger.js +0 -155
- package/dist/internal/logger.js.map +0 -1
- package/dist/internal/masking/index.d.ts +0 -4
- package/dist/internal/masking/index.d.ts.map +0 -1
- package/dist/internal/masking/index.js +0 -3
- package/dist/internal/masking/index.js.map +0 -1
- package/dist/internal/masking/patterns.d.ts +0 -3
- package/dist/internal/masking/patterns.d.ts.map +0 -1
- package/dist/internal/masking/patterns.js +0 -375
- package/dist/internal/masking/patterns.js.map +0 -1
- package/dist/internal/masking/types.d.ts +0 -33
- package/dist/internal/masking/types.d.ts.map +0 -1
- package/dist/internal/masking/types.js +0 -2
- package/dist/internal/masking/types.js.map +0 -1
- package/dist/internal/masking/utils.d.ts +0 -6
- package/dist/internal/masking/utils.d.ts.map +0 -1
- package/dist/internal/masking/utils.js +0 -226
- package/dist/internal/masking/utils.js.map +0 -1
- package/dist/internal/metric/index.d.ts +0 -2
- package/dist/internal/metric/index.d.ts.map +0 -1
- package/dist/internal/metric/index.js +0 -2
- package/dist/internal/metric/index.js.map +0 -1
- package/dist/internal/metric/metrics.d.ts +0 -18
- package/dist/internal/metric/metrics.d.ts.map +0 -1
- package/dist/internal/metric/metrics.js +0 -82
- package/dist/internal/metric/metrics.js.map +0 -1
- package/dist/internal/sdk.d.ts +0 -36
- package/dist/internal/sdk.d.ts.map +0 -1
- package/dist/internal/sdk.js +0 -155
- package/dist/internal/sdk.js.map +0 -1
- package/dist/internal/trace/index.d.ts +0 -3
- package/dist/internal/trace/index.d.ts.map +0 -1
- package/dist/internal/trace/index.js +0 -3
- package/dist/internal/trace/index.js.map +0 -1
- package/dist/internal/trace/processors/log-masked.d.ts +0 -16
- package/dist/internal/trace/processors/log-masked.d.ts.map +0 -1
- package/dist/internal/trace/processors/log-masked.js +0 -75
- package/dist/internal/trace/processors/log-masked.js.map +0 -1
- package/dist/internal/trace/processors/span-masked.d.ts +0 -16
- package/dist/internal/trace/processors/span-masked.d.ts.map +0 -1
- package/dist/internal/trace/processors/span-masked.js +0 -81
- package/dist/internal/trace/processors/span-masked.js.map +0 -1
- package/dist/internal/trace/tracing.d.ts +0 -18
- package/dist/internal/trace/tracing.d.ts.map +0 -1
- package/dist/internal/trace/tracing.js +0 -97
- package/dist/internal/trace/tracing.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,2896 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/internal/instrumentation/auto-init.ts
|
|
9
|
+
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
10
|
+
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
11
|
+
import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
|
|
12
|
+
import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
|
|
13
|
+
import { ChromaDBInstrumentation } from "@traceloop/instrumentation-chromadb";
|
|
14
|
+
import { CohereInstrumentation } from "@traceloop/instrumentation-cohere";
|
|
15
|
+
import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
|
|
16
|
+
import { LlamaIndexInstrumentation } from "@traceloop/instrumentation-llamaindex";
|
|
17
|
+
import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
|
|
18
|
+
import { PineconeInstrumentation } from "@traceloop/instrumentation-pinecone";
|
|
19
|
+
import { QdrantInstrumentation } from "@traceloop/instrumentation-qdrant";
|
|
20
|
+
import { TogetherInstrumentation } from "@traceloop/instrumentation-together";
|
|
21
|
+
import { VertexAIInstrumentation } from "@traceloop/instrumentation-vertexai";
|
|
22
|
+
|
|
23
|
+
// src/internal/logger.ts
|
|
24
|
+
import { DiagLogLevel } from "@opentelemetry/api";
|
|
25
|
+
import pino from "pino";
|
|
26
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
27
|
+
LogLevel2[LogLevel2["NONE"] = 0] = "NONE";
|
|
28
|
+
LogLevel2[LogLevel2["ERROR"] = 1] = "ERROR";
|
|
29
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
30
|
+
LogLevel2[LogLevel2["INFO"] = 3] = "INFO";
|
|
31
|
+
LogLevel2[LogLevel2["DEBUG"] = 4] = "DEBUG";
|
|
32
|
+
return LogLevel2;
|
|
33
|
+
})(LogLevel || {});
|
|
34
|
+
var DEFAULT_LOG_LEVEL = 2 /* WARN */;
|
|
35
|
+
var PinoLogger = class {
|
|
36
|
+
_level = DEFAULT_LOG_LEVEL;
|
|
37
|
+
_pinoLogger = null;
|
|
38
|
+
constructor() {
|
|
39
|
+
const envLevel = this.getLogLevelFromEnv();
|
|
40
|
+
this._level = envLevel;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Lazy initialization of Pino logger to ensure it's created AFTER Jest spies
|
|
44
|
+
* are set up during tests. This prevents the pino-pretty transport from
|
|
45
|
+
* bypassing stdout/stderr spies.
|
|
46
|
+
*/
|
|
47
|
+
ensurePinoLogger() {
|
|
48
|
+
if (!this._pinoLogger) {
|
|
49
|
+
this._pinoLogger = pino({
|
|
50
|
+
name: "Brizz",
|
|
51
|
+
level: this.logLevelToPino(this._level),
|
|
52
|
+
// Disable transport in test environment to allow proper spy capture
|
|
53
|
+
transport: this.isProduction() || this.isTest() ? void 0 : {
|
|
54
|
+
target: "pino-pretty",
|
|
55
|
+
options: {
|
|
56
|
+
singleLine: true,
|
|
57
|
+
colorize: true,
|
|
58
|
+
translateTime: "HH:MM:ss",
|
|
59
|
+
ignore: "pid,hostname",
|
|
60
|
+
messageFormat: "[{name}] {msg}"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return this._pinoLogger;
|
|
66
|
+
}
|
|
67
|
+
isProduction() {
|
|
68
|
+
return process.env["NODE_ENV"] === "production";
|
|
69
|
+
}
|
|
70
|
+
isTest() {
|
|
71
|
+
return process.env["NODE_ENV"] === "test";
|
|
72
|
+
}
|
|
73
|
+
getLogLevelFromEnv() {
|
|
74
|
+
const envLevel = process.env["BRIZZ_LOG_LEVEL"];
|
|
75
|
+
return envLevel ? parseLogLevel(envLevel) : DEFAULT_LOG_LEVEL;
|
|
76
|
+
}
|
|
77
|
+
logLevelToPino(level) {
|
|
78
|
+
switch (level) {
|
|
79
|
+
case 4 /* DEBUG */:
|
|
80
|
+
return "debug";
|
|
81
|
+
case 3 /* INFO */:
|
|
82
|
+
return "info";
|
|
83
|
+
case 2 /* WARN */:
|
|
84
|
+
return "warn";
|
|
85
|
+
case 1 /* ERROR */:
|
|
86
|
+
return "error";
|
|
87
|
+
default:
|
|
88
|
+
return "silent";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
formatMeta(meta) {
|
|
92
|
+
if (meta.length === 0) {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
if (meta.length === 1 && typeof meta[0] === "object" && meta[0] !== null) {
|
|
96
|
+
return meta[0];
|
|
97
|
+
}
|
|
98
|
+
return { metadata: meta };
|
|
99
|
+
}
|
|
100
|
+
setLevel(level) {
|
|
101
|
+
this._level = level;
|
|
102
|
+
if (this._pinoLogger) {
|
|
103
|
+
this._pinoLogger.level = this.logLevelToPino(level);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
getLevel() {
|
|
107
|
+
return this._level;
|
|
108
|
+
}
|
|
109
|
+
debug = (msg, ...meta) => {
|
|
110
|
+
if (this._level >= 4 /* DEBUG */) {
|
|
111
|
+
this.ensurePinoLogger().debug(this.formatMeta(meta), msg);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
info = (msg, ...meta) => {
|
|
115
|
+
if (this._level >= 3 /* INFO */) {
|
|
116
|
+
this.ensurePinoLogger().info(this.formatMeta(meta), msg);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
warn = (msg, ...meta) => {
|
|
120
|
+
if (this._level >= 2 /* WARN */) {
|
|
121
|
+
this.ensurePinoLogger().warn(this.formatMeta(meta), msg);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
error = (msg, ...meta) => {
|
|
125
|
+
if (this._level >= 1 /* ERROR */) {
|
|
126
|
+
this.ensurePinoLogger().error(this.formatMeta(meta), msg);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
function parseLogLevel(level) {
|
|
131
|
+
if (!level) {
|
|
132
|
+
return DEFAULT_LOG_LEVEL;
|
|
133
|
+
}
|
|
134
|
+
const normalizedLevel = level.toLowerCase().trim();
|
|
135
|
+
switch (normalizedLevel) {
|
|
136
|
+
case "debug":
|
|
137
|
+
return 4 /* DEBUG */;
|
|
138
|
+
case "info":
|
|
139
|
+
return 3 /* INFO */;
|
|
140
|
+
case "warn":
|
|
141
|
+
case "warning":
|
|
142
|
+
return 2 /* WARN */;
|
|
143
|
+
case "error":
|
|
144
|
+
return 1 /* ERROR */;
|
|
145
|
+
case "none":
|
|
146
|
+
case "off":
|
|
147
|
+
case "silent":
|
|
148
|
+
return 0 /* NONE */;
|
|
149
|
+
default: {
|
|
150
|
+
const numLevel = Number.parseInt(normalizedLevel, 10);
|
|
151
|
+
if (!Number.isNaN(numLevel) && numLevel >= 0 && numLevel <= 4) {
|
|
152
|
+
return numLevel;
|
|
153
|
+
}
|
|
154
|
+
return DEFAULT_LOG_LEVEL;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
var logger = new PinoLogger();
|
|
159
|
+
function setLogLevel(level) {
|
|
160
|
+
const resolvedLevel = typeof level === "string" ? parseLogLevel(level) : level;
|
|
161
|
+
logger.setLevel(resolvedLevel);
|
|
162
|
+
}
|
|
163
|
+
function getLogLevel() {
|
|
164
|
+
return logger.getLevel();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/internal/instrumentation/vercel-ai/instrumentation.ts
|
|
168
|
+
import {
|
|
169
|
+
InstrumentationBase,
|
|
170
|
+
InstrumentationNodeModuleDefinition
|
|
171
|
+
} from "@opentelemetry/instrumentation";
|
|
172
|
+
|
|
173
|
+
// src/internal/instrumentation/vercel-ai/patchers/base-patcher.ts
|
|
174
|
+
import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
175
|
+
|
|
176
|
+
// src/internal/instrumentation/vercel-ai/semconv.ts
|
|
177
|
+
var ATTR_GEN_AI_SYSTEM = "gen_ai.system";
|
|
178
|
+
var ATTR_GEN_AI_OPERATION_NAME = "gen_ai.operation.name";
|
|
179
|
+
var ATTR_GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
180
|
+
var ATTR_GEN_AI_REQUEST_MAX_TOKENS = "gen_ai.request.max_tokens";
|
|
181
|
+
var ATTR_GEN_AI_REQUEST_TEMPERATURE = "gen_ai.request.temperature";
|
|
182
|
+
var ATTR_GEN_AI_REQUEST_TOP_P = "gen_ai.request.top_p";
|
|
183
|
+
var ATTR_GEN_AI_REQUEST_TOP_K = "gen_ai.request.top_k";
|
|
184
|
+
var ATTR_GEN_AI_REQUEST_STOP_SEQUENCES = "gen_ai.request.stop_sequences";
|
|
185
|
+
var ATTR_GEN_AI_REQUEST_FREQUENCY_PENALTY = "gen_ai.request.frequency_penalty";
|
|
186
|
+
var ATTR_GEN_AI_REQUEST_PRESENCE_PENALTY = "gen_ai.request.presence_penalty";
|
|
187
|
+
var ATTR_GEN_AI_RESPONSE_ID = "gen_ai.response.id";
|
|
188
|
+
var ATTR_GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
189
|
+
var ATTR_GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons";
|
|
190
|
+
var ATTR_GEN_AI_TOKEN_TYPE = "gen_ai.token.type";
|
|
191
|
+
var ATTR_GEN_AI_PROMPT = "gen_ai.prompt";
|
|
192
|
+
var ATTR_GEN_AI_COMPLETION = "gen_ai.completion";
|
|
193
|
+
var ATTR_GEN_AI_OPENAI_API_BASE = "gen_ai.openai.api_base";
|
|
194
|
+
var ATTR_EVENT_NAME = "event.name";
|
|
195
|
+
var EVENT_GEN_AI_USER_MESSAGE = "gen_ai.user.message";
|
|
196
|
+
var EVENT_GEN_AI_ASSISTANT_MESSAGE = "gen_ai.assistant.message";
|
|
197
|
+
var EVENT_GEN_AI_SYSTEM_MESSAGE = "gen_ai.system.message";
|
|
198
|
+
var EVENT_GEN_AI_TOOL_MESSAGE = "gen_ai.tool.message";
|
|
199
|
+
var METRIC_GEN_AI_CLIENT_OPERATION_DURATION = "gen_ai.client.operation.duration";
|
|
200
|
+
var METRIC_GEN_AI_CLIENT_TOKEN_USAGE = "gen_ai.client.token.usage";
|
|
201
|
+
var OPERATION_NAME_CHAT = "chat";
|
|
202
|
+
var OPERATION_NAME_EMBEDDINGS = "embeddings";
|
|
203
|
+
var TOKEN_TYPE_INPUT = "input";
|
|
204
|
+
var TOKEN_TYPE_OUTPUT = "output";
|
|
205
|
+
var PROVIDER_OPENAI = "openai";
|
|
206
|
+
var PROVIDER_ANTHROPIC = "anthropic";
|
|
207
|
+
var PROVIDER_GOOGLE = "google";
|
|
208
|
+
var PROVIDER_AMAZON = "amazon";
|
|
209
|
+
var PROVIDER_AZURE = "azure";
|
|
210
|
+
var PROVIDER_VERCEL = "vercel";
|
|
211
|
+
var PROVIDER_UNKNOWN = "unknown";
|
|
212
|
+
var SPAN_NAME_GEN_AI_CHAT = "gen_ai.chat";
|
|
213
|
+
var SPAN_NAME_GEN_AI_EMBEDDINGS = "gen_ai.embeddings";
|
|
214
|
+
|
|
215
|
+
// src/internal/instrumentation/vercel-ai/utils.ts
|
|
216
|
+
function detectProvider(model) {
|
|
217
|
+
if (typeof model === "object" && model !== null) {
|
|
218
|
+
const modelObj = model;
|
|
219
|
+
if (modelObj.provider) {
|
|
220
|
+
return {
|
|
221
|
+
system: normalizeProviderName(modelObj.provider),
|
|
222
|
+
apiBase: extractApiBase(modelObj)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (modelObj.modelId) {
|
|
226
|
+
return detectProviderFromModelId(modelObj.modelId);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (typeof model === "string") {
|
|
230
|
+
return detectProviderFromModelId(model);
|
|
231
|
+
}
|
|
232
|
+
return { system: PROVIDER_UNKNOWN };
|
|
233
|
+
}
|
|
234
|
+
function detectProviderFromModelId(modelId) {
|
|
235
|
+
const lowerModel = modelId.toLowerCase();
|
|
236
|
+
if (lowerModel.startsWith("gpt-") || lowerModel.startsWith("text-davinci-") || lowerModel.startsWith("text-embedding-") || lowerModel.startsWith("dall-e") || lowerModel.startsWith("whisper-") || lowerModel.startsWith("tts-")) {
|
|
237
|
+
return { system: PROVIDER_OPENAI };
|
|
238
|
+
}
|
|
239
|
+
if (lowerModel.startsWith("claude-")) {
|
|
240
|
+
return { system: PROVIDER_ANTHROPIC };
|
|
241
|
+
}
|
|
242
|
+
if (lowerModel.startsWith("gemini-") || lowerModel.startsWith("palm-") || lowerModel.includes("bison") || lowerModel.includes("gecko")) {
|
|
243
|
+
return { system: PROVIDER_GOOGLE };
|
|
244
|
+
}
|
|
245
|
+
if (lowerModel.startsWith("amazon.") || lowerModel.startsWith("anthropic.claude-") || lowerModel.startsWith("ai21.") || lowerModel.startsWith("cohere.") || lowerModel.startsWith("meta.llama")) {
|
|
246
|
+
return { system: PROVIDER_AMAZON };
|
|
247
|
+
}
|
|
248
|
+
if (lowerModel.includes("azure") || lowerModel.includes(".openai.azure.com")) {
|
|
249
|
+
return { system: PROVIDER_AZURE };
|
|
250
|
+
}
|
|
251
|
+
const parts = modelId.split(/[-._/]/);
|
|
252
|
+
if (parts.length > 0 && parts[0]) {
|
|
253
|
+
return { system: normalizeProviderName(parts[0]) };
|
|
254
|
+
}
|
|
255
|
+
return { system: PROVIDER_UNKNOWN };
|
|
256
|
+
}
|
|
257
|
+
function normalizeProviderName(provider) {
|
|
258
|
+
const normalized = provider.toLowerCase().trim();
|
|
259
|
+
switch (normalized) {
|
|
260
|
+
case "openai":
|
|
261
|
+
case "open-ai":
|
|
262
|
+
case "open_ai": {
|
|
263
|
+
return PROVIDER_OPENAI;
|
|
264
|
+
}
|
|
265
|
+
case "anthropic":
|
|
266
|
+
case "claude": {
|
|
267
|
+
return PROVIDER_ANTHROPIC;
|
|
268
|
+
}
|
|
269
|
+
case "google":
|
|
270
|
+
case "vertex":
|
|
271
|
+
case "vertexai":
|
|
272
|
+
case "vertex-ai":
|
|
273
|
+
case "gemini": {
|
|
274
|
+
return PROVIDER_GOOGLE;
|
|
275
|
+
}
|
|
276
|
+
case "amazon":
|
|
277
|
+
case "aws":
|
|
278
|
+
case "bedrock":
|
|
279
|
+
case "amazon-bedrock": {
|
|
280
|
+
return PROVIDER_AMAZON;
|
|
281
|
+
}
|
|
282
|
+
case "azure":
|
|
283
|
+
case "azure-openai":
|
|
284
|
+
case "microsoft": {
|
|
285
|
+
return PROVIDER_AZURE;
|
|
286
|
+
}
|
|
287
|
+
case "vercel":
|
|
288
|
+
case "vercel-ai": {
|
|
289
|
+
return PROVIDER_VERCEL;
|
|
290
|
+
}
|
|
291
|
+
default: {
|
|
292
|
+
return normalized;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function extractApiBase(model) {
|
|
297
|
+
if (typeof model === "object" && model !== null) {
|
|
298
|
+
const anyModel = model;
|
|
299
|
+
return anyModel.apiBase || anyModel.baseURL || anyModel.endpoint || void 0;
|
|
300
|
+
}
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
function extractModelId(model) {
|
|
304
|
+
if (typeof model === "string") {
|
|
305
|
+
return model;
|
|
306
|
+
}
|
|
307
|
+
if (typeof model === "object" && model !== null) {
|
|
308
|
+
return model.modelId || "unknown";
|
|
309
|
+
}
|
|
310
|
+
return "unknown";
|
|
311
|
+
}
|
|
312
|
+
function messagesToAttributes(messages, prefix, captureContent) {
|
|
313
|
+
const attributes = {};
|
|
314
|
+
for (const [index, msg] of messages.entries()) {
|
|
315
|
+
const baseKey = `${prefix}.${index}`;
|
|
316
|
+
attributes[`${baseKey}.role`] = msg.role;
|
|
317
|
+
if (captureContent && msg.content) {
|
|
318
|
+
if (typeof msg.content === "string") {
|
|
319
|
+
attributes[`${baseKey}.content`] = msg.content;
|
|
320
|
+
} else if (Array.isArray(msg.content)) {
|
|
321
|
+
const textParts = msg.content.filter((part) => part.type === "text" && part.text).map((part) => part.text).join(" ");
|
|
322
|
+
if (textParts) {
|
|
323
|
+
attributes[`${baseKey}.content`] = textParts;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (msg.toolInvocations && msg.toolInvocations.length > 0) {
|
|
328
|
+
attributes[`${baseKey}.tool_calls`] = msg.toolInvocations.length;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return attributes;
|
|
332
|
+
}
|
|
333
|
+
function promptToAttributes(prompt, captureContent) {
|
|
334
|
+
const attributes = {};
|
|
335
|
+
attributes[`${ATTR_GEN_AI_PROMPT}.0.role`] = "user";
|
|
336
|
+
if (captureContent) {
|
|
337
|
+
attributes[`${ATTR_GEN_AI_PROMPT}.0.content`] = prompt;
|
|
338
|
+
}
|
|
339
|
+
return attributes;
|
|
340
|
+
}
|
|
341
|
+
function completionToAttributes(text, finishReason, captureContent) {
|
|
342
|
+
const attributes = {};
|
|
343
|
+
attributes[`${ATTR_GEN_AI_COMPLETION}.0.role`] = "assistant";
|
|
344
|
+
if (captureContent) {
|
|
345
|
+
attributes[`${ATTR_GEN_AI_COMPLETION}.0.content`] = text;
|
|
346
|
+
}
|
|
347
|
+
if (finishReason) {
|
|
348
|
+
attributes[`${ATTR_GEN_AI_COMPLETION}.0.finish_reason`] = finishReason;
|
|
349
|
+
}
|
|
350
|
+
return attributes;
|
|
351
|
+
}
|
|
352
|
+
function tokenUsageToAttributes(usage) {
|
|
353
|
+
if (!usage) {
|
|
354
|
+
return {};
|
|
355
|
+
}
|
|
356
|
+
const attributes = {};
|
|
357
|
+
if (usage.inputTokens !== void 0) {
|
|
358
|
+
attributes["gen_ai.usage.prompt_tokens"] = usage.inputTokens;
|
|
359
|
+
attributes["gen_ai.usage.input_tokens"] = usage.inputTokens;
|
|
360
|
+
attributes["llm.usage.prompt_tokens"] = usage.inputTokens;
|
|
361
|
+
} else if (usage.promptTokens !== void 0) {
|
|
362
|
+
attributes["gen_ai.usage.prompt_tokens"] = usage.promptTokens;
|
|
363
|
+
attributes["gen_ai.usage.input_tokens"] = usage.promptTokens;
|
|
364
|
+
attributes["llm.usage.prompt_tokens"] = usage.promptTokens;
|
|
365
|
+
}
|
|
366
|
+
if (usage.outputTokens !== void 0) {
|
|
367
|
+
attributes["gen_ai.usage.completion_tokens"] = usage.outputTokens;
|
|
368
|
+
attributes["gen_ai.usage.output_tokens"] = usage.outputTokens;
|
|
369
|
+
attributes["llm.usage.completion_tokens"] = usage.outputTokens;
|
|
370
|
+
} else if (usage.completionTokens !== void 0) {
|
|
371
|
+
attributes["gen_ai.usage.completion_tokens"] = usage.completionTokens;
|
|
372
|
+
attributes["gen_ai.usage.output_tokens"] = usage.completionTokens;
|
|
373
|
+
attributes["llm.usage.completion_tokens"] = usage.completionTokens;
|
|
374
|
+
}
|
|
375
|
+
if (usage.totalTokens === void 0) {
|
|
376
|
+
const inputTokens = usage.inputTokens || usage.promptTokens;
|
|
377
|
+
const outputTokens = usage.outputTokens || usage.completionTokens;
|
|
378
|
+
if (inputTokens !== void 0 && outputTokens !== void 0) {
|
|
379
|
+
const totalTokens = inputTokens + outputTokens;
|
|
380
|
+
attributes["gen_ai.usage.total_tokens"] = totalTokens;
|
|
381
|
+
attributes["llm.usage.total_tokens"] = totalTokens;
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
attributes["gen_ai.usage.total_tokens"] = usage.totalTokens;
|
|
385
|
+
attributes["llm.usage.total_tokens"] = usage.totalTokens;
|
|
386
|
+
}
|
|
387
|
+
return attributes;
|
|
388
|
+
}
|
|
389
|
+
function shouldRecordError(error) {
|
|
390
|
+
if (error instanceof Error) {
|
|
391
|
+
const message = error.message.toLowerCase();
|
|
392
|
+
if (message.includes("abort") || message.includes("cancel")) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
function getEnvBool(name) {
|
|
399
|
+
const value = process.env[name];
|
|
400
|
+
if (value === void 0) {
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
return value.toLowerCase() === "true" || value === "1";
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/internal/instrumentation/vercel-ai/patchers/base-patcher.ts
|
|
407
|
+
var BasePatcher = class {
|
|
408
|
+
constructor(context8) {
|
|
409
|
+
this.context = context8;
|
|
410
|
+
}
|
|
411
|
+
createSpan(spanName, params, operationName, additionalAttributes) {
|
|
412
|
+
const provider = detectProvider(params.model);
|
|
413
|
+
const modelId = extractModelId(params.model);
|
|
414
|
+
const span = this.context.tracer.startSpan(spanName, {
|
|
415
|
+
kind: SpanKind.CLIENT,
|
|
416
|
+
attributes: {
|
|
417
|
+
[ATTR_GEN_AI_SYSTEM]: provider.system,
|
|
418
|
+
[ATTR_GEN_AI_OPERATION_NAME]: operationName,
|
|
419
|
+
[ATTR_GEN_AI_REQUEST_MODEL]: modelId,
|
|
420
|
+
...params.maxTokens && { [ATTR_GEN_AI_REQUEST_MAX_TOKENS]: params.maxTokens },
|
|
421
|
+
...params.temperature !== void 0 && {
|
|
422
|
+
[ATTR_GEN_AI_REQUEST_TEMPERATURE]: params.temperature
|
|
423
|
+
},
|
|
424
|
+
...params.topP !== void 0 && { [ATTR_GEN_AI_REQUEST_TOP_P]: params.topP },
|
|
425
|
+
...params.topK !== void 0 && { [ATTR_GEN_AI_REQUEST_TOP_K]: params.topK },
|
|
426
|
+
...params.frequencyPenalty !== void 0 && {
|
|
427
|
+
[ATTR_GEN_AI_REQUEST_FREQUENCY_PENALTY]: params.frequencyPenalty
|
|
428
|
+
},
|
|
429
|
+
...params.presencePenalty !== void 0 && {
|
|
430
|
+
[ATTR_GEN_AI_REQUEST_PRESENCE_PENALTY]: params.presencePenalty
|
|
431
|
+
},
|
|
432
|
+
...params.stopSequences && {
|
|
433
|
+
[ATTR_GEN_AI_REQUEST_STOP_SEQUENCES]: params.stopSequences
|
|
434
|
+
},
|
|
435
|
+
...provider.apiBase && { [ATTR_GEN_AI_OPENAI_API_BASE]: provider.apiBase },
|
|
436
|
+
...additionalAttributes
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
return { span, provider, modelId };
|
|
440
|
+
}
|
|
441
|
+
handleError(error, span) {
|
|
442
|
+
if (shouldRecordError(error)) {
|
|
443
|
+
span.recordException(error);
|
|
444
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
finalizeDuration(span, startTime, config, provider, modelId, operationName) {
|
|
448
|
+
if (config.enableMetrics) {
|
|
449
|
+
const duration = (globalThis.performance.now() - startTime) / 1e3;
|
|
450
|
+
this.context.recordDurationMetric(duration, provider.system, modelId, operationName);
|
|
451
|
+
}
|
|
452
|
+
span.end();
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
// src/internal/instrumentation/vercel-ai/patchers/generate-text-patcher.ts
|
|
457
|
+
import { context, SpanStatusCode as SpanStatusCode2, trace } from "@opentelemetry/api";
|
|
458
|
+
var GenerateTextPatcher = class extends BasePatcher {
|
|
459
|
+
patch(original) {
|
|
460
|
+
return async (params) => {
|
|
461
|
+
const config = this.context.getConfig();
|
|
462
|
+
const startTime = globalThis.performance.now();
|
|
463
|
+
const { span, provider, modelId } = this.createSpan(
|
|
464
|
+
SPAN_NAME_GEN_AI_CHAT,
|
|
465
|
+
params,
|
|
466
|
+
OPERATION_NAME_CHAT
|
|
467
|
+
);
|
|
468
|
+
if (params.prompt) {
|
|
469
|
+
span.setAttributes(
|
|
470
|
+
promptToAttributes(params.prompt, config.captureMessageContent || false)
|
|
471
|
+
);
|
|
472
|
+
} else if (params.messages) {
|
|
473
|
+
span.setAttributes(
|
|
474
|
+
messagesToAttributes(
|
|
475
|
+
params.messages,
|
|
476
|
+
"gen_ai.prompt",
|
|
477
|
+
config.captureMessageContent || false
|
|
478
|
+
)
|
|
479
|
+
);
|
|
480
|
+
if (config.emitEvents) {
|
|
481
|
+
this.context.emitMessageEvents(params.messages, provider.system, span);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
const result = await context.with(
|
|
486
|
+
trace.setSpan(context.active(), span),
|
|
487
|
+
() => original(params)
|
|
488
|
+
);
|
|
489
|
+
if (result.response) {
|
|
490
|
+
span.setAttributes({
|
|
491
|
+
...result.response.id && { [ATTR_GEN_AI_RESPONSE_ID]: result.response.id },
|
|
492
|
+
...result.response.model && { [ATTR_GEN_AI_RESPONSE_MODEL]: result.response.model }
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (result.finishReason) {
|
|
496
|
+
span.setAttribute(ATTR_GEN_AI_RESPONSE_FINISH_REASONS, [result.finishReason]);
|
|
497
|
+
}
|
|
498
|
+
span.setAttributes(
|
|
499
|
+
completionToAttributes(
|
|
500
|
+
result.text,
|
|
501
|
+
result.finishReason,
|
|
502
|
+
config.captureMessageContent || false
|
|
503
|
+
)
|
|
504
|
+
);
|
|
505
|
+
const usage = result.usage || result.totalUsage || result.steps?.[0]?.usage;
|
|
506
|
+
if (usage) {
|
|
507
|
+
span.setAttributes(tokenUsageToAttributes(usage));
|
|
508
|
+
if (config.enableMetrics) {
|
|
509
|
+
this.context.recordTokenMetrics(usage, provider.system, modelId);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (config.emitEvents) {
|
|
513
|
+
this.context.emitAssistantMessageEvent(result.text, provider.system, span);
|
|
514
|
+
}
|
|
515
|
+
span.setStatus({ code: SpanStatusCode2.OK });
|
|
516
|
+
return result;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
this.handleError(error, span);
|
|
519
|
+
throw error;
|
|
520
|
+
} finally {
|
|
521
|
+
this.finalizeDuration(span, startTime, config, provider, modelId, OPERATION_NAME_CHAT);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
// src/internal/instrumentation/vercel-ai/patchers/stream-text-patcher.ts
|
|
528
|
+
import { context as context2, trace as trace2 } from "@opentelemetry/api";
|
|
529
|
+
var StreamTextPatcher = class extends BasePatcher {
|
|
530
|
+
constructor(context8, streamHandler) {
|
|
531
|
+
super(context8);
|
|
532
|
+
this.streamHandler = streamHandler;
|
|
533
|
+
}
|
|
534
|
+
patch(original) {
|
|
535
|
+
return async (params) => {
|
|
536
|
+
const config = this.context.getConfig();
|
|
537
|
+
const startTime = globalThis.performance.now();
|
|
538
|
+
const { span, provider, modelId } = this.createSpan(
|
|
539
|
+
SPAN_NAME_GEN_AI_CHAT,
|
|
540
|
+
params,
|
|
541
|
+
OPERATION_NAME_CHAT,
|
|
542
|
+
{ "gen_ai.streaming": true }
|
|
543
|
+
);
|
|
544
|
+
if (params.prompt) {
|
|
545
|
+
span.setAttributes(
|
|
546
|
+
promptToAttributes(params.prompt, config.captureMessageContent || false)
|
|
547
|
+
);
|
|
548
|
+
} else if (params.messages) {
|
|
549
|
+
span.setAttributes(
|
|
550
|
+
messagesToAttributes(
|
|
551
|
+
params.messages,
|
|
552
|
+
"gen_ai.prompt",
|
|
553
|
+
config.captureMessageContent || false
|
|
554
|
+
)
|
|
555
|
+
);
|
|
556
|
+
if (config.emitEvents) {
|
|
557
|
+
this.context.emitMessageEvents(params.messages, provider.system, span);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
const stream = await context2.with(
|
|
562
|
+
trace2.setSpan(context2.active(), span),
|
|
563
|
+
() => original(params)
|
|
564
|
+
);
|
|
565
|
+
return this.streamHandler.wrapStream(stream, span, config, provider, modelId, startTime);
|
|
566
|
+
} catch (error) {
|
|
567
|
+
this.handleError(error, span);
|
|
568
|
+
span.end();
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
// src/internal/instrumentation/vercel-ai/patchers/embeddings-patcher.ts
|
|
576
|
+
import { context as context3, SpanStatusCode as SpanStatusCode3, trace as trace3 } from "@opentelemetry/api";
|
|
577
|
+
var EmbeddingsPatcher = class extends BasePatcher {
|
|
578
|
+
patch(original, isMany = false) {
|
|
579
|
+
return async (params) => {
|
|
580
|
+
const config = this.context.getConfig();
|
|
581
|
+
const startTime = globalThis.performance.now();
|
|
582
|
+
const additionalAttributes = isMany ? { "gen_ai.embeddings.count": params.values ? params.values.length : 0 } : {};
|
|
583
|
+
const { span, provider, modelId } = this.createSpan(
|
|
584
|
+
SPAN_NAME_GEN_AI_EMBEDDINGS,
|
|
585
|
+
params,
|
|
586
|
+
OPERATION_NAME_EMBEDDINGS,
|
|
587
|
+
additionalAttributes
|
|
588
|
+
);
|
|
589
|
+
if (!isMany && config.captureMessageContent && params.value) {
|
|
590
|
+
span.setAttribute("gen_ai.prompt.0.content", params.value);
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
const result = await context3.with(
|
|
594
|
+
trace3.setSpan(context3.active(), span),
|
|
595
|
+
() => original(params)
|
|
596
|
+
);
|
|
597
|
+
if (result.response) {
|
|
598
|
+
span.setAttributes({
|
|
599
|
+
...result.response.id && { [ATTR_GEN_AI_RESPONSE_ID]: result.response.id },
|
|
600
|
+
...result.response.model && { [ATTR_GEN_AI_RESPONSE_MODEL]: result.response.model }
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
if (isMany) {
|
|
604
|
+
if (result.embeddings && result.embeddings.length > 0 && result.embeddings[0]) {
|
|
605
|
+
span.setAttribute("gen_ai.response.embedding_dimensions", result.embeddings[0].length);
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
if (result.embedding) {
|
|
609
|
+
span.setAttribute("gen_ai.response.embedding_dimensions", result.embedding.length);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (result.usage) {
|
|
613
|
+
span.setAttributes(tokenUsageToAttributes(result.usage));
|
|
614
|
+
if (config.enableMetrics) {
|
|
615
|
+
this.context.recordTokenMetrics(result.usage, provider.system, modelId);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
619
|
+
return result;
|
|
620
|
+
} catch (error) {
|
|
621
|
+
this.handleError(error, span);
|
|
622
|
+
throw error;
|
|
623
|
+
} finally {
|
|
624
|
+
this.finalizeDuration(span, startTime, config, provider, modelId, OPERATION_NAME_EMBEDDINGS);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// src/internal/instrumentation/vercel-ai/stream-handler.ts
|
|
631
|
+
import { SpanStatusCode as SpanStatusCode4 } from "@opentelemetry/api";
|
|
632
|
+
var StreamHandler = class {
|
|
633
|
+
constructor(context8) {
|
|
634
|
+
this.context = context8;
|
|
635
|
+
}
|
|
636
|
+
wrapStream(stream, span, config, provider, modelId, startTime) {
|
|
637
|
+
const self = this;
|
|
638
|
+
let fullText = "";
|
|
639
|
+
let finishReason;
|
|
640
|
+
let usage;
|
|
641
|
+
let response;
|
|
642
|
+
const wrappedStream = new Proxy(stream, {
|
|
643
|
+
get(target, prop) {
|
|
644
|
+
if (prop === Symbol.asyncIterator) {
|
|
645
|
+
return async function* () {
|
|
646
|
+
try {
|
|
647
|
+
for await (const chunk of target) {
|
|
648
|
+
if (chunk.type === "text-delta" && chunk.textDelta) {
|
|
649
|
+
fullText += chunk.textDelta;
|
|
650
|
+
} else if (chunk.type === "finish") {
|
|
651
|
+
finishReason = chunk.finishReason;
|
|
652
|
+
usage = chunk.usage;
|
|
653
|
+
} else if (chunk.type === "response-metadata") {
|
|
654
|
+
response = chunk.response;
|
|
655
|
+
}
|
|
656
|
+
yield chunk;
|
|
657
|
+
}
|
|
658
|
+
} finally {
|
|
659
|
+
self.finalizeStream(
|
|
660
|
+
span,
|
|
661
|
+
config,
|
|
662
|
+
provider,
|
|
663
|
+
modelId,
|
|
664
|
+
startTime,
|
|
665
|
+
fullText,
|
|
666
|
+
finishReason,
|
|
667
|
+
usage,
|
|
668
|
+
response
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
if (prop === "textStream" || prop === "fullStream") {
|
|
674
|
+
const originalStream = target[prop];
|
|
675
|
+
return {
|
|
676
|
+
[Symbol.asyncIterator]: async function* () {
|
|
677
|
+
try {
|
|
678
|
+
for await (const chunk of originalStream) {
|
|
679
|
+
if (prop === "textStream") {
|
|
680
|
+
fullText += chunk;
|
|
681
|
+
}
|
|
682
|
+
yield chunk;
|
|
683
|
+
}
|
|
684
|
+
} finally {
|
|
685
|
+
const streamUsage = await target.usage.catch(() => null);
|
|
686
|
+
if (streamUsage) {
|
|
687
|
+
usage = streamUsage;
|
|
688
|
+
}
|
|
689
|
+
self.finalizeStream(
|
|
690
|
+
span,
|
|
691
|
+
config,
|
|
692
|
+
provider,
|
|
693
|
+
modelId,
|
|
694
|
+
startTime,
|
|
695
|
+
fullText,
|
|
696
|
+
finishReason,
|
|
697
|
+
usage,
|
|
698
|
+
response
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
const value = target[prop];
|
|
705
|
+
if (typeof value === "function") {
|
|
706
|
+
return value.bind(target);
|
|
707
|
+
}
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
return wrappedStream;
|
|
712
|
+
}
|
|
713
|
+
finalizeStream(span, config, provider, modelId, startTime, fullText, finishReason, usage, response) {
|
|
714
|
+
if (response) {
|
|
715
|
+
span.setAttributes({
|
|
716
|
+
...response.id && { [ATTR_GEN_AI_RESPONSE_ID]: response.id },
|
|
717
|
+
...response.model && { [ATTR_GEN_AI_RESPONSE_MODEL]: response.model }
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
if (finishReason) {
|
|
721
|
+
span.setAttribute(ATTR_GEN_AI_RESPONSE_FINISH_REASONS, [finishReason]);
|
|
722
|
+
}
|
|
723
|
+
if (fullText) {
|
|
724
|
+
span.setAttributes(
|
|
725
|
+
completionToAttributes(
|
|
726
|
+
fullText,
|
|
727
|
+
finishReason,
|
|
728
|
+
config.captureMessageContent || false
|
|
729
|
+
)
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
if (usage) {
|
|
733
|
+
span.setAttributes(tokenUsageToAttributes(usage));
|
|
734
|
+
if (config.enableMetrics) {
|
|
735
|
+
this.context.recordTokenMetrics(usage, provider.system, modelId);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (config.enableMetrics) {
|
|
739
|
+
const duration = (performance.now() - startTime) / 1e3;
|
|
740
|
+
this.context.recordDurationMetric(duration, provider.system, modelId, OPERATION_NAME_CHAT);
|
|
741
|
+
}
|
|
742
|
+
span.setStatus({ code: SpanStatusCode4.OK });
|
|
743
|
+
span.end();
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// src/internal/instrumentation/vercel-ai/telemetry-recorder.ts
|
|
748
|
+
import { context as context4, trace as trace4 } from "@opentelemetry/api";
|
|
749
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
750
|
+
var TelemetryRecorder = class {
|
|
751
|
+
constructor(genaiClientOperationDuration, genaiClientTokenUsage, logger2) {
|
|
752
|
+
this.genaiClientOperationDuration = genaiClientOperationDuration;
|
|
753
|
+
this.genaiClientTokenUsage = genaiClientTokenUsage;
|
|
754
|
+
this.logger = logger2;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Record token usage metrics
|
|
758
|
+
*/
|
|
759
|
+
recordTokenMetrics(usage, system, model) {
|
|
760
|
+
if (!this.genaiClientTokenUsage) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const commonAttrs = {
|
|
764
|
+
[ATTR_GEN_AI_SYSTEM]: system,
|
|
765
|
+
[ATTR_GEN_AI_REQUEST_MODEL]: model
|
|
766
|
+
};
|
|
767
|
+
const inputTokens = usage.inputTokens || usage.promptTokens;
|
|
768
|
+
const outputTokens = usage.outputTokens || usage.completionTokens;
|
|
769
|
+
if (inputTokens !== void 0) {
|
|
770
|
+
this.genaiClientTokenUsage.record(inputTokens, {
|
|
771
|
+
...commonAttrs,
|
|
772
|
+
[ATTR_GEN_AI_TOKEN_TYPE]: TOKEN_TYPE_INPUT
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
if (outputTokens !== void 0) {
|
|
776
|
+
this.genaiClientTokenUsage.record(outputTokens, {
|
|
777
|
+
...commonAttrs,
|
|
778
|
+
[ATTR_GEN_AI_TOKEN_TYPE]: TOKEN_TYPE_OUTPUT
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Record operation duration metric
|
|
784
|
+
*/
|
|
785
|
+
recordDurationMetric(duration, system, model, operation) {
|
|
786
|
+
if (!this.genaiClientOperationDuration) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
this.genaiClientOperationDuration.record(duration, {
|
|
790
|
+
[ATTR_GEN_AI_SYSTEM]: system,
|
|
791
|
+
[ATTR_GEN_AI_REQUEST_MODEL]: model,
|
|
792
|
+
[ATTR_GEN_AI_OPERATION_NAME]: operation
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Emit message events
|
|
797
|
+
*/
|
|
798
|
+
emitMessageEvents(messages, system, span) {
|
|
799
|
+
if (!this.logger) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const ctx = trace4.setSpan(context4.active(), span);
|
|
803
|
+
for (const msg of messages) {
|
|
804
|
+
let eventName;
|
|
805
|
+
switch (msg.role) {
|
|
806
|
+
case "system": {
|
|
807
|
+
eventName = EVENT_GEN_AI_SYSTEM_MESSAGE;
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
case "user": {
|
|
811
|
+
eventName = EVENT_GEN_AI_USER_MESSAGE;
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
case "assistant": {
|
|
815
|
+
eventName = EVENT_GEN_AI_ASSISTANT_MESSAGE;
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
case "tool":
|
|
819
|
+
case "function": {
|
|
820
|
+
eventName = EVENT_GEN_AI_TOOL_MESSAGE;
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
823
|
+
default: {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
this.logger.emit({
|
|
828
|
+
timestamp: Date.now(),
|
|
829
|
+
context: ctx,
|
|
830
|
+
severityNumber: SeverityNumber.INFO,
|
|
831
|
+
attributes: {
|
|
832
|
+
[ATTR_EVENT_NAME]: eventName,
|
|
833
|
+
[ATTR_GEN_AI_SYSTEM]: system
|
|
834
|
+
},
|
|
835
|
+
body: {
|
|
836
|
+
role: msg.role,
|
|
837
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
838
|
+
name: msg.name
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Emit assistant message event
|
|
845
|
+
*/
|
|
846
|
+
emitAssistantMessageEvent(text, system, span) {
|
|
847
|
+
if (!this.logger) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const ctx = trace4.setSpan(context4.active(), span);
|
|
851
|
+
this.logger.emit({
|
|
852
|
+
timestamp: Date.now(),
|
|
853
|
+
context: ctx,
|
|
854
|
+
severityNumber: SeverityNumber.INFO,
|
|
855
|
+
attributes: {
|
|
856
|
+
[ATTR_EVENT_NAME]: EVENT_GEN_AI_ASSISTANT_MESSAGE,
|
|
857
|
+
[ATTR_GEN_AI_SYSTEM]: system
|
|
858
|
+
},
|
|
859
|
+
body: {
|
|
860
|
+
role: "assistant",
|
|
861
|
+
content: text
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
// src/internal/instrumentation/vercel-ai/instrumentation.ts
|
|
868
|
+
var PACKAGE_NAME = "@brizz/vercel-ai-instrumentation";
|
|
869
|
+
var PACKAGE_VERSION = "0.1.0";
|
|
870
|
+
var VercelAIInstrumentation = class _VercelAIInstrumentation extends InstrumentationBase {
|
|
871
|
+
_genaiClientOperationDuration;
|
|
872
|
+
_genaiClientTokenUsage;
|
|
873
|
+
_telemetryRecorder;
|
|
874
|
+
_streamHandler;
|
|
875
|
+
_patchers = /* @__PURE__ */ new Map();
|
|
876
|
+
// Holds last patched namespace when available (reserved for future factory wrapping)
|
|
877
|
+
_vercelAiNamespace = null;
|
|
878
|
+
static _WRAPPED_SYMBOL = Symbol.for("brizz.vercel-ai.patched");
|
|
879
|
+
constructor(config = {}) {
|
|
880
|
+
super(PACKAGE_NAME, PACKAGE_VERSION, config);
|
|
881
|
+
const cfg = this.getConfig();
|
|
882
|
+
const envCC = getEnvBool("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT");
|
|
883
|
+
if (envCC !== void 0) {
|
|
884
|
+
cfg.captureMessageContent = envCC;
|
|
885
|
+
}
|
|
886
|
+
this._initializeComponents();
|
|
887
|
+
}
|
|
888
|
+
setConfig(config = {}) {
|
|
889
|
+
const {
|
|
890
|
+
captureMessageContent = true,
|
|
891
|
+
enableMetrics = true,
|
|
892
|
+
emitEvents = true,
|
|
893
|
+
...validConfig
|
|
894
|
+
} = config;
|
|
895
|
+
const fullConfig = {
|
|
896
|
+
...validConfig,
|
|
897
|
+
captureMessageContent,
|
|
898
|
+
enableMetrics,
|
|
899
|
+
emitEvents
|
|
900
|
+
};
|
|
901
|
+
super.setConfig(fullConfig);
|
|
902
|
+
}
|
|
903
|
+
_initializeComponents() {
|
|
904
|
+
this._telemetryRecorder = new TelemetryRecorder(
|
|
905
|
+
this._genaiClientOperationDuration,
|
|
906
|
+
this._genaiClientTokenUsage,
|
|
907
|
+
this.logger
|
|
908
|
+
);
|
|
909
|
+
this._streamHandler = new StreamHandler({
|
|
910
|
+
recordTokenMetrics: this._telemetryRecorder.recordTokenMetrics.bind(this._telemetryRecorder),
|
|
911
|
+
recordDurationMetric: this._telemetryRecorder.recordDurationMetric.bind(
|
|
912
|
+
this._telemetryRecorder
|
|
913
|
+
)
|
|
914
|
+
});
|
|
915
|
+
const patcherContext = {
|
|
916
|
+
tracer: this.tracer,
|
|
917
|
+
getConfig: this.getConfig.bind(this),
|
|
918
|
+
recordTokenMetrics: this._telemetryRecorder.recordTokenMetrics.bind(this._telemetryRecorder),
|
|
919
|
+
recordDurationMetric: this._telemetryRecorder.recordDurationMetric.bind(
|
|
920
|
+
this._telemetryRecorder
|
|
921
|
+
),
|
|
922
|
+
emitMessageEvents: this._telemetryRecorder.emitMessageEvents.bind(this._telemetryRecorder),
|
|
923
|
+
emitAssistantMessageEvent: this._telemetryRecorder.emitAssistantMessageEvent.bind(
|
|
924
|
+
this._telemetryRecorder
|
|
925
|
+
)
|
|
926
|
+
};
|
|
927
|
+
this._patchers.set("generateText", new GenerateTextPatcher(patcherContext));
|
|
928
|
+
this._patchers.set("streamText", new StreamTextPatcher(patcherContext, this._streamHandler));
|
|
929
|
+
this._patchers.set("embed", new EmbeddingsPatcher(patcherContext));
|
|
930
|
+
this._patchers.set("embedMany", new EmbeddingsPatcher(patcherContext));
|
|
931
|
+
}
|
|
932
|
+
init() {
|
|
933
|
+
return [
|
|
934
|
+
new InstrumentationNodeModuleDefinition(
|
|
935
|
+
"ai",
|
|
936
|
+
[">=4.0.0 <6"],
|
|
937
|
+
(moduleExports) => {
|
|
938
|
+
logger.info("Starting instrumentation of Vercel AI SDK module");
|
|
939
|
+
this._vercelAiNamespace = moduleExports;
|
|
940
|
+
const patched = this._patchModuleExports(moduleExports);
|
|
941
|
+
return patched ?? moduleExports;
|
|
942
|
+
},
|
|
943
|
+
(moduleExports) => {
|
|
944
|
+
logger.debug("Uninstrumenting @vercel/ai module");
|
|
945
|
+
return moduleExports;
|
|
946
|
+
}
|
|
947
|
+
)
|
|
948
|
+
];
|
|
949
|
+
}
|
|
950
|
+
_updateMetricInstruments() {
|
|
951
|
+
const config = this.getConfig();
|
|
952
|
+
if (!config.enableMetrics) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
this._genaiClientOperationDuration = this.meter.createHistogram(
|
|
956
|
+
METRIC_GEN_AI_CLIENT_OPERATION_DURATION,
|
|
957
|
+
{
|
|
958
|
+
description: "GenAI operation duration",
|
|
959
|
+
unit: "s",
|
|
960
|
+
advice: {
|
|
961
|
+
explicitBucketBoundaries: [
|
|
962
|
+
0.01,
|
|
963
|
+
0.02,
|
|
964
|
+
0.04,
|
|
965
|
+
0.08,
|
|
966
|
+
0.16,
|
|
967
|
+
0.32,
|
|
968
|
+
0.64,
|
|
969
|
+
1.28,
|
|
970
|
+
2.56,
|
|
971
|
+
5.12,
|
|
972
|
+
10.24,
|
|
973
|
+
20.48,
|
|
974
|
+
40.96,
|
|
975
|
+
81.92
|
|
976
|
+
]
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
);
|
|
980
|
+
this._genaiClientTokenUsage = this.meter.createHistogram(METRIC_GEN_AI_CLIENT_TOKEN_USAGE, {
|
|
981
|
+
description: "Measures number of input and output tokens used",
|
|
982
|
+
unit: "{token}",
|
|
983
|
+
advice: {
|
|
984
|
+
explicitBucketBoundaries: [
|
|
985
|
+
1,
|
|
986
|
+
4,
|
|
987
|
+
16,
|
|
988
|
+
64,
|
|
989
|
+
256,
|
|
990
|
+
1024,
|
|
991
|
+
4096,
|
|
992
|
+
16384,
|
|
993
|
+
65536,
|
|
994
|
+
262144,
|
|
995
|
+
1048576,
|
|
996
|
+
4194304,
|
|
997
|
+
16777216,
|
|
998
|
+
67108864
|
|
999
|
+
]
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
this._telemetryRecorder = new TelemetryRecorder(
|
|
1003
|
+
this._genaiClientOperationDuration,
|
|
1004
|
+
this._genaiClientTokenUsage,
|
|
1005
|
+
this.logger
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Patch known AI SDK functions in-place on the provided module exports object.
|
|
1010
|
+
* This approach is compatible with both CJS and ESM module loaders.
|
|
1011
|
+
*/
|
|
1012
|
+
_patchModuleExports(moduleExports) {
|
|
1013
|
+
if (!moduleExports || typeof moduleExports !== "object") {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
let inPlacePatched = true;
|
|
1017
|
+
const wrapFunction = (name, isEmbedMany = false) => {
|
|
1018
|
+
const current = moduleExports[name];
|
|
1019
|
+
if (typeof current !== "function") {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const currentFn = current;
|
|
1023
|
+
if (currentFn[_VercelAIInstrumentation._WRAPPED_SYMBOL]) {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
const descriptor = Object.getOwnPropertyDescriptor(moduleExports, name);
|
|
1027
|
+
if (descriptor && (!descriptor.writable || !descriptor.configurable) && !descriptor.set) {
|
|
1028
|
+
inPlacePatched = false;
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
const patcher = this._patchers.get(name);
|
|
1032
|
+
if (!patcher) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const patched = isEmbedMany ? patcher.patch(currentFn, true) : patcher.patch(currentFn);
|
|
1036
|
+
try {
|
|
1037
|
+
Object.defineProperty(patched, _VercelAIInstrumentation._WRAPPED_SYMBOL, {
|
|
1038
|
+
value: true,
|
|
1039
|
+
enumerable: false,
|
|
1040
|
+
configurable: false
|
|
1041
|
+
});
|
|
1042
|
+
} catch {
|
|
1043
|
+
}
|
|
1044
|
+
try {
|
|
1045
|
+
moduleExports[name] = patched;
|
|
1046
|
+
} catch {
|
|
1047
|
+
inPlacePatched = false;
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
wrapFunction("generateText");
|
|
1051
|
+
wrapFunction("streamText");
|
|
1052
|
+
wrapFunction("embed");
|
|
1053
|
+
wrapFunction("embedMany", true);
|
|
1054
|
+
if (!inPlacePatched) {
|
|
1055
|
+
const proxiedModule = new Proxy(moduleExports, {
|
|
1056
|
+
get: (target, prop, receiver) => {
|
|
1057
|
+
const originalValue = Reflect.get(target, prop, receiver);
|
|
1058
|
+
if (typeof originalValue === "function" && typeof prop === "string" && this._patchers.has(prop)) {
|
|
1059
|
+
const patcher = this._patchers.get(prop);
|
|
1060
|
+
const isEmbedMany = prop === "embedMany";
|
|
1061
|
+
const wrapped = isEmbedMany ? patcher.patch(originalValue, true) : patcher.patch(originalValue);
|
|
1062
|
+
return wrapped;
|
|
1063
|
+
}
|
|
1064
|
+
return originalValue;
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
return proxiedModule;
|
|
1068
|
+
}
|
|
1069
|
+
return moduleExports;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Manual instrumentation hook for bundlers/Next.js. Applies in-place wrapping
|
|
1073
|
+
* on the provided module namespace.
|
|
1074
|
+
*/
|
|
1075
|
+
manuallyInstrument(module3) {
|
|
1076
|
+
try {
|
|
1077
|
+
const result = this._patchModuleExports(module3);
|
|
1078
|
+
if (result !== null) {
|
|
1079
|
+
logger.debug("Applied manual Vercel AI instrumentation");
|
|
1080
|
+
this._vercelAiNamespace = result;
|
|
1081
|
+
return result;
|
|
1082
|
+
}
|
|
1083
|
+
logger.warn("Manual Vercel AI instrumentation received invalid module");
|
|
1084
|
+
return module3;
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
logger.error(`Failed manual Vercel AI instrumentation: ${String(error)}`);
|
|
1087
|
+
return this._vercelAiNamespace || module3;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Wrap a created provider/client instance (factory return) when possible.
|
|
1092
|
+
* Call this from wrappers that construct provider clients (e.g., OpenAI SDK).
|
|
1093
|
+
*/
|
|
1094
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1095
|
+
wrapFactoryReturn(instance) {
|
|
1096
|
+
return instance;
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1100
|
+
// src/internal/instrumentation/auto-init.ts
|
|
1101
|
+
var autoInstrumentationsLoaded = false;
|
|
1102
|
+
var exceptionLogger = (error) => {
|
|
1103
|
+
logger.error(`Exception in instrumentation: ${String(error)}`);
|
|
1104
|
+
};
|
|
1105
|
+
function loadNodeAutoInstrumentations() {
|
|
1106
|
+
try {
|
|
1107
|
+
const nodeInstrumentations = getNodeAutoInstrumentations();
|
|
1108
|
+
registerInstrumentations({ instrumentations: nodeInstrumentations });
|
|
1109
|
+
return nodeInstrumentations;
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
logger.error(`Failed to load Node.js auto-instrumentations: ${String(error)}`);
|
|
1112
|
+
return [];
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function loadGenAIInstrumentations() {
|
|
1116
|
+
const instrumentations = [];
|
|
1117
|
+
const genAIInstrumentationClasses = [
|
|
1118
|
+
{ class: VercelAIInstrumentation, name: "Vercel AI" },
|
|
1119
|
+
// Load first to avoid conflicts
|
|
1120
|
+
{ class: OpenAIInstrumentation, name: "OpenAI" },
|
|
1121
|
+
{ class: AnthropicInstrumentation, name: "Anthropic" },
|
|
1122
|
+
{ class: CohereInstrumentation, name: "Cohere" },
|
|
1123
|
+
{ class: VertexAIInstrumentation, name: "Vertex AI" },
|
|
1124
|
+
{ class: BedrockInstrumentation, name: "Bedrock" },
|
|
1125
|
+
{ class: PineconeInstrumentation, name: "Pinecone" },
|
|
1126
|
+
{ class: LangChainInstrumentation, name: "LangChain" },
|
|
1127
|
+
{ class: LlamaIndexInstrumentation, name: "LlamaIndex" },
|
|
1128
|
+
{ class: ChromaDBInstrumentation, name: "ChromaDB" },
|
|
1129
|
+
{ class: QdrantInstrumentation, name: "Qdrant" },
|
|
1130
|
+
{ class: TogetherInstrumentation, name: "Together" }
|
|
1131
|
+
];
|
|
1132
|
+
for (const config of genAIInstrumentationClasses) {
|
|
1133
|
+
try {
|
|
1134
|
+
const instrumentation = new config.class({ exceptionLogger });
|
|
1135
|
+
instrumentations.push(instrumentation);
|
|
1136
|
+
logger.debug(`Auto-loaded ${config.name} instrumentation`);
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
logger.error(`Failed to auto-load ${config.name} instrumentation: ${String(error)}`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
try {
|
|
1142
|
+
registerInstrumentations({ instrumentations });
|
|
1143
|
+
logger.info(`Auto-registered ${instrumentations.length} GenAI instrumentations`);
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
logger.error(`Failed to register GenAI instrumentations: ${String(error)}`);
|
|
1146
|
+
}
|
|
1147
|
+
return instrumentations;
|
|
1148
|
+
}
|
|
1149
|
+
function autoInitializeInstrumentations() {
|
|
1150
|
+
if (autoInstrumentationsLoaded) {
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
const nodeInstrumentations = loadNodeAutoInstrumentations();
|
|
1155
|
+
const genAIInstrumentations = loadGenAIInstrumentations();
|
|
1156
|
+
autoInstrumentationsLoaded = true;
|
|
1157
|
+
logger.info(
|
|
1158
|
+
`Auto-initialization complete: ${nodeInstrumentations.length} node + ${genAIInstrumentations.length} GenAI instrumentations`
|
|
1159
|
+
);
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
logger.error(`Auto-initialization failed: ${String(error)}`);
|
|
1162
|
+
autoInstrumentationsLoaded = false;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
autoInitializeInstrumentations();
|
|
1166
|
+
|
|
1167
|
+
// src/internal/sdk.ts
|
|
1168
|
+
import { resourceFromAttributes as resourceFromAttributes2 } from "@opentelemetry/resources";
|
|
1169
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
1170
|
+
|
|
1171
|
+
// src/internal/config.ts
|
|
1172
|
+
function resolveConfig(options) {
|
|
1173
|
+
const envLogLevel = process.env["BRIZZ_LOG_LEVEL"] || options.logLevel?.toString() || DEFAULT_LOG_LEVEL.toString();
|
|
1174
|
+
let resolvedLogLevel;
|
|
1175
|
+
if (envLogLevel) {
|
|
1176
|
+
resolvedLogLevel = parseLogLevel(envLogLevel);
|
|
1177
|
+
} else if (typeof options.logLevel === "string") {
|
|
1178
|
+
resolvedLogLevel = parseLogLevel(options.logLevel);
|
|
1179
|
+
} else {
|
|
1180
|
+
resolvedLogLevel = options.logLevel || DEFAULT_LOG_LEVEL;
|
|
1181
|
+
}
|
|
1182
|
+
setLogLevel(resolvedLogLevel);
|
|
1183
|
+
let maskingStatus;
|
|
1184
|
+
if (options.masking === true) {
|
|
1185
|
+
maskingStatus = "enabled";
|
|
1186
|
+
} else if (options.masking === false) {
|
|
1187
|
+
maskingStatus = "disabled";
|
|
1188
|
+
} else if (typeof options.masking === "object") {
|
|
1189
|
+
maskingStatus = "custom";
|
|
1190
|
+
} else {
|
|
1191
|
+
maskingStatus = "default-disabled";
|
|
1192
|
+
}
|
|
1193
|
+
logger.debug("Starting configuration resolution", {
|
|
1194
|
+
appName: options.appName,
|
|
1195
|
+
baseUrl: options.baseUrl,
|
|
1196
|
+
hasApiKey: !!options.apiKey,
|
|
1197
|
+
disableBatch: options.disableBatch,
|
|
1198
|
+
logLevel: resolvedLogLevel,
|
|
1199
|
+
headersCount: Object.keys(options.headers || {}).length,
|
|
1200
|
+
masking: maskingStatus
|
|
1201
|
+
});
|
|
1202
|
+
let resolvedMasking;
|
|
1203
|
+
if (options.masking === true) {
|
|
1204
|
+
resolvedMasking = {
|
|
1205
|
+
spanMasking: {},
|
|
1206
|
+
eventMasking: {}
|
|
1207
|
+
};
|
|
1208
|
+
} else if (options.masking && typeof options.masking === "object") {
|
|
1209
|
+
resolvedMasking = options.masking;
|
|
1210
|
+
}
|
|
1211
|
+
const resolvedConfig = {
|
|
1212
|
+
...options,
|
|
1213
|
+
appName: process.env["BRIZZ_APP_NAME"] || options.appName || "unknown-app",
|
|
1214
|
+
baseUrl: process.env["BRIZZ_BASE_URL"] || options.baseUrl || "https://telemetry.brizz.dev",
|
|
1215
|
+
headers: { ...options.headers },
|
|
1216
|
+
apiKey: process.env["BRIZZ_API_KEY"] || options.apiKey,
|
|
1217
|
+
disableBatch: process.env["BRIZZ_DISABLE_BATCH"] === "true" || !!options.disableBatch,
|
|
1218
|
+
logLevel: resolvedLogLevel,
|
|
1219
|
+
masking: resolvedMasking
|
|
1220
|
+
};
|
|
1221
|
+
if (resolvedConfig.apiKey) {
|
|
1222
|
+
resolvedConfig.headers["Authorization"] = `Bearer ${resolvedConfig.apiKey}`;
|
|
1223
|
+
}
|
|
1224
|
+
if (process.env["BRIZZ_HEADERS"]) {
|
|
1225
|
+
try {
|
|
1226
|
+
const envHeaders = JSON.parse(process.env["BRIZZ_HEADERS"]);
|
|
1227
|
+
Object.assign(resolvedConfig.headers, envHeaders);
|
|
1228
|
+
logger.debug("Added headers from environment variable", {
|
|
1229
|
+
headers: Object.keys(envHeaders)
|
|
1230
|
+
});
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
logger.error("Failed to parse BRIZZ_HEADERS environment variable", { error });
|
|
1233
|
+
throw new Error("Invalid JSON in BRIZZ_HEADERS environment variable");
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
logger.debug("Configuration resolved with environment variables", {
|
|
1237
|
+
appName: resolvedConfig.appName,
|
|
1238
|
+
baseUrl: resolvedConfig.baseUrl,
|
|
1239
|
+
hasApiKey: !!resolvedConfig.apiKey,
|
|
1240
|
+
disableBatch: resolvedConfig.disableBatch,
|
|
1241
|
+
envOverrides: {
|
|
1242
|
+
hasEnvApiKey: !!process.env["BRIZZ_API_KEY"],
|
|
1243
|
+
hasEnvBaseUrl: !!process.env["BRIZZ_BASE_URL"],
|
|
1244
|
+
hasEnvBatch: !!process.env["BRIZZ_DISABLE_BATCH"],
|
|
1245
|
+
hasEnvHeaders: !!process.env["BRIZZ_HEADERS"]
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1248
|
+
return resolvedConfig;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// src/internal/instrumentation/registry.ts
|
|
1252
|
+
import { AnthropicInstrumentation as AnthropicInstrumentation2 } from "@traceloop/instrumentation-anthropic";
|
|
1253
|
+
import { BedrockInstrumentation as BedrockInstrumentation2 } from "@traceloop/instrumentation-bedrock";
|
|
1254
|
+
import { ChromaDBInstrumentation as ChromaDBInstrumentation2 } from "@traceloop/instrumentation-chromadb";
|
|
1255
|
+
import { CohereInstrumentation as CohereInstrumentation2 } from "@traceloop/instrumentation-cohere";
|
|
1256
|
+
import { LangChainInstrumentation as LangChainInstrumentation2 } from "@traceloop/instrumentation-langchain";
|
|
1257
|
+
import { LlamaIndexInstrumentation as LlamaIndexInstrumentation2 } from "@traceloop/instrumentation-llamaindex";
|
|
1258
|
+
import { OpenAIInstrumentation as OpenAIInstrumentation2 } from "@traceloop/instrumentation-openai";
|
|
1259
|
+
import { PineconeInstrumentation as PineconeInstrumentation2 } from "@traceloop/instrumentation-pinecone";
|
|
1260
|
+
import { QdrantInstrumentation as QdrantInstrumentation2 } from "@traceloop/instrumentation-qdrant";
|
|
1261
|
+
import { TogetherInstrumentation as TogetherInstrumentation2 } from "@traceloop/instrumentation-together";
|
|
1262
|
+
import { VertexAIInstrumentation as VertexAIInstrumentation2 } from "@traceloop/instrumentation-vertexai";
|
|
1263
|
+
var InstrumentationRegistry = class _InstrumentationRegistry {
|
|
1264
|
+
static instance;
|
|
1265
|
+
manualModules = null;
|
|
1266
|
+
static getInstance() {
|
|
1267
|
+
if (!_InstrumentationRegistry.instance) {
|
|
1268
|
+
_InstrumentationRegistry.instance = new _InstrumentationRegistry();
|
|
1269
|
+
}
|
|
1270
|
+
return _InstrumentationRegistry.instance;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Set manual instrumentation modules for Next.js/Webpack environments
|
|
1274
|
+
*/
|
|
1275
|
+
setManualModules(modules) {
|
|
1276
|
+
this.manualModules = modules;
|
|
1277
|
+
logger.info("Manual instrumentation modules configured for Next.js/Webpack compatibility");
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Helper to load instrumentation with optional manual module
|
|
1281
|
+
*/
|
|
1282
|
+
loadInstrumentation(InstrumentationClass, name, manualModule, exceptionLogger2) {
|
|
1283
|
+
try {
|
|
1284
|
+
const inst = new InstrumentationClass({
|
|
1285
|
+
exceptionLogger: exceptionLogger2 || ((error) => logger.error(`Exception in instrumentation: ${String(error)}`))
|
|
1286
|
+
});
|
|
1287
|
+
if (manualModule) {
|
|
1288
|
+
inst.manuallyInstrument(manualModule);
|
|
1289
|
+
logger.debug(`Manual instrumentation enabled for ${name}`);
|
|
1290
|
+
}
|
|
1291
|
+
return inst;
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
logger.error(`Failed to load ${name} instrumentation: ${String(error)}`);
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Get manual instrumentations only.
|
|
1299
|
+
* Auto-instrumentations are handled at import time.
|
|
1300
|
+
*/
|
|
1301
|
+
getManualInstrumentations() {
|
|
1302
|
+
if (!this.manualModules || Object.keys(this.manualModules).length === 0) {
|
|
1303
|
+
logger.debug("No manual instrumentation modules configured");
|
|
1304
|
+
return [];
|
|
1305
|
+
}
|
|
1306
|
+
const instrumentations = [];
|
|
1307
|
+
const exceptionLogger2 = (error) => {
|
|
1308
|
+
logger.error(`Exception in manual instrumentation: ${String(error)}`);
|
|
1309
|
+
};
|
|
1310
|
+
this.loadManualInstrumentations(instrumentations, exceptionLogger2);
|
|
1311
|
+
logger.info(`Loaded ${instrumentations.length} manual instrumentations`);
|
|
1312
|
+
return instrumentations;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Load manual instrumentations only (with modules provided by user).
|
|
1316
|
+
* @private
|
|
1317
|
+
*/
|
|
1318
|
+
loadManualInstrumentations(instrumentations, exceptionLogger2) {
|
|
1319
|
+
const instrumentationConfigs = [
|
|
1320
|
+
{ class: OpenAIInstrumentation2, name: "OpenAI", module: this.manualModules?.openAI },
|
|
1321
|
+
{ class: AnthropicInstrumentation2, name: "Anthropic", module: this.manualModules?.anthropic },
|
|
1322
|
+
{ class: CohereInstrumentation2, name: "Cohere", module: this.manualModules?.cohere },
|
|
1323
|
+
{
|
|
1324
|
+
class: VertexAIInstrumentation2,
|
|
1325
|
+
name: "Vertex AI",
|
|
1326
|
+
module: this.manualModules?.google_vertexai
|
|
1327
|
+
},
|
|
1328
|
+
{ class: BedrockInstrumentation2, name: "Bedrock", module: this.manualModules?.bedrock },
|
|
1329
|
+
{ class: PineconeInstrumentation2, name: "Pinecone", module: this.manualModules?.pinecone },
|
|
1330
|
+
{ class: LangChainInstrumentation2, name: "LangChain", module: this.manualModules?.langchain },
|
|
1331
|
+
{
|
|
1332
|
+
class: LlamaIndexInstrumentation2,
|
|
1333
|
+
name: "LlamaIndex",
|
|
1334
|
+
module: this.manualModules?.llamaindex
|
|
1335
|
+
},
|
|
1336
|
+
{ class: ChromaDBInstrumentation2, name: "ChromaDB", module: this.manualModules?.chromadb },
|
|
1337
|
+
{ class: QdrantInstrumentation2, name: "Qdrant", module: this.manualModules?.qdrant },
|
|
1338
|
+
{ class: TogetherInstrumentation2, name: "Together", module: this.manualModules?.together },
|
|
1339
|
+
{ class: VercelAIInstrumentation, name: "Vercel AI", module: this.manualModules?.vercelAI }
|
|
1340
|
+
];
|
|
1341
|
+
for (const config of instrumentationConfigs) {
|
|
1342
|
+
if (config.module) {
|
|
1343
|
+
const instrumentation = this.loadInstrumentation(
|
|
1344
|
+
config.class,
|
|
1345
|
+
config.name,
|
|
1346
|
+
config.module,
|
|
1347
|
+
exceptionLogger2
|
|
1348
|
+
);
|
|
1349
|
+
if (instrumentation) {
|
|
1350
|
+
instrumentations.push(instrumentation);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
|
|
1357
|
+
// src/internal/log/logging.ts
|
|
1358
|
+
import { SeverityNumber as SeverityNumber2 } from "@opentelemetry/api-logs";
|
|
1359
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
1360
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
1361
|
+
import {
|
|
1362
|
+
LoggerProvider
|
|
1363
|
+
} from "@opentelemetry/sdk-logs";
|
|
1364
|
+
|
|
1365
|
+
// src/internal/log/processors/log-processor.ts
|
|
1366
|
+
import { context as context5 } from "@opentelemetry/api";
|
|
1367
|
+
import { BatchLogRecordProcessor, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
1368
|
+
|
|
1369
|
+
// src/internal/masking/patterns.ts
|
|
1370
|
+
var DEFAULT_PII_PATTERNS = [
|
|
1371
|
+
// Email addresses
|
|
1372
|
+
{
|
|
1373
|
+
name: "email_addresses",
|
|
1374
|
+
pattern: String.raw`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`
|
|
1375
|
+
},
|
|
1376
|
+
// Phone numbers (US format)
|
|
1377
|
+
{
|
|
1378
|
+
name: "us_phone_numbers",
|
|
1379
|
+
pattern: String.raw`(?:^|[\s])(?:\+?1[-.\s]*)?(?:\([0-9]{3}\)\s?[0-9]{3}[-.\s]?[0-9]{4}|[0-9]{3}[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}|[0-9]{10})(?=[\s]|$)`
|
|
1380
|
+
},
|
|
1381
|
+
// Social Security Numbers
|
|
1382
|
+
{
|
|
1383
|
+
name: "ssn",
|
|
1384
|
+
pattern: String.raw`\b(?!000|666|9\d{2})\d{3}[-\s]?(?!00)\d{2}[-\s]?(?!0000)\d{4}\b`
|
|
1385
|
+
},
|
|
1386
|
+
// Credit card numbers
|
|
1387
|
+
{
|
|
1388
|
+
name: "credit_cards",
|
|
1389
|
+
pattern: String.raw`\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11})\b`
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
name: "credit_cards_with_separators",
|
|
1393
|
+
pattern: String.raw`\b(?:4\\d{3}|5[1-5]\\d{2}|3[47]\\d{2})[-\s]?\\d{4}[-\s]?\\d{4}[-\s]?\\d{4}\b`
|
|
1394
|
+
},
|
|
1395
|
+
// IP addresses (IPv4)
|
|
1396
|
+
{
|
|
1397
|
+
name: "ipv4_addresses",
|
|
1398
|
+
pattern: String.raw`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?!\.[0-9])\b`
|
|
1399
|
+
},
|
|
1400
|
+
// API keys/tokens
|
|
1401
|
+
{
|
|
1402
|
+
name: "generic_api_keys",
|
|
1403
|
+
pattern: String.raw`\b(?:[Aa][Pp][Ii][_-]?[Kk][Ee][Yy]|[Tt][Oo][Kk][Ee][Nn]|[Ss][Ee][Cc][Rr][Ee][Tt])[_-]?[=:]?\s*["']?(?:[a-zA-Z0-9\-_.]{20,})["']?\b`
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
name: "openai_keys",
|
|
1407
|
+
pattern: String.raw`\bsk[-_][a-zA-Z0-9]{20,}\b`
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
name: "base64_secrets",
|
|
1411
|
+
pattern: String.raw`\b[A-Za-z0-9+/]{64,}={0,2}\b`
|
|
1412
|
+
},
|
|
1413
|
+
// AWS Keys
|
|
1414
|
+
{
|
|
1415
|
+
name: "aws_access_keys",
|
|
1416
|
+
pattern: String.raw`\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\b`
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
name: "aws_secret_keys",
|
|
1420
|
+
pattern: String.raw`\b[A-Za-z0-9/+=]*[A-Z][A-Za-z0-9/+=]*[a-z][A-Za-z0-9/+=]*[/+=][A-Za-z0-9/+=]{30,}\b`
|
|
1421
|
+
},
|
|
1422
|
+
// GitHub tokens
|
|
1423
|
+
{
|
|
1424
|
+
name: "github_tokens",
|
|
1425
|
+
pattern: String.raw`\bghp_[a-zA-Z0-9]{36}\b`
|
|
1426
|
+
},
|
|
1427
|
+
// Slack tokens
|
|
1428
|
+
{
|
|
1429
|
+
name: "slack_tokens",
|
|
1430
|
+
pattern: String.raw`\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}\b`
|
|
1431
|
+
},
|
|
1432
|
+
// Stripe keys
|
|
1433
|
+
{
|
|
1434
|
+
name: "stripe_keys",
|
|
1435
|
+
pattern: String.raw`\b(?:sk|pk)_(?:test|live)_[a-zA-Z0-9]{24,}\b`
|
|
1436
|
+
},
|
|
1437
|
+
// JWT tokens
|
|
1438
|
+
{
|
|
1439
|
+
name: "jwt_tokens",
|
|
1440
|
+
pattern: String.raw`\beyJ[A-Za-z0-9_-]{2,}\\.[A-Za-z0-9_-]{2,}\\.[A-Za-z0-9_-]{2,}\b`
|
|
1441
|
+
},
|
|
1442
|
+
// MAC addresses
|
|
1443
|
+
{
|
|
1444
|
+
name: "mac_addresses",
|
|
1445
|
+
pattern: String.raw`\b(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\b`
|
|
1446
|
+
},
|
|
1447
|
+
// IPv6 addresses
|
|
1448
|
+
{
|
|
1449
|
+
name: "ipv6_addresses",
|
|
1450
|
+
pattern: String.raw`\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b`
|
|
1451
|
+
},
|
|
1452
|
+
// Medical records
|
|
1453
|
+
{
|
|
1454
|
+
name: "medical_record_numbers",
|
|
1455
|
+
pattern: String.raw`\b(?:[Mm][Rr][Nn])[-\s]?\d{6,10}\b`
|
|
1456
|
+
},
|
|
1457
|
+
// Bitcoin addresses
|
|
1458
|
+
{
|
|
1459
|
+
name: "bitcoin_addresses",
|
|
1460
|
+
pattern: String.raw`\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b`
|
|
1461
|
+
},
|
|
1462
|
+
// Ethereum addresses
|
|
1463
|
+
{
|
|
1464
|
+
name: "ethereum_addresses",
|
|
1465
|
+
pattern: String.raw`\b0x[a-fA-F0-9]{40}(?![a-fA-F0-9])\b`
|
|
1466
|
+
},
|
|
1467
|
+
// UUIDs
|
|
1468
|
+
{
|
|
1469
|
+
name: "uuids",
|
|
1470
|
+
pattern: String.raw`\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(?![0-9a-fA-F-])\b`
|
|
1471
|
+
},
|
|
1472
|
+
// Database connection strings
|
|
1473
|
+
{
|
|
1474
|
+
name: "database_connections",
|
|
1475
|
+
pattern: String.raw`\b(?:[Mm][Oo][Nn][Gg][Oo][Dd][Bb]|[Pp][Oo][Ss][Tt][Gg][Rr][Ee][Ss]|[Mm][Yy][Ss][Qq][Ll]|[Rr][Ee][Dd][Ii][Ss]|[Mm][Ss][Ss][Qq][Ll]|[Oo][Rr][Aa][Cc][Ll][Ee]):\\/\\/[^\\s]+\b`
|
|
1476
|
+
},
|
|
1477
|
+
// Private keys
|
|
1478
|
+
{
|
|
1479
|
+
name: "rsa_private_keys",
|
|
1480
|
+
pattern: "-----BEGIN (?:RSA )?PRIVATE KEY-----"
|
|
1481
|
+
},
|
|
1482
|
+
{
|
|
1483
|
+
name: "pgp_private_keys",
|
|
1484
|
+
pattern: "-----BEGIN PGP PRIVATE KEY BLOCK-----"
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
name: "certificates",
|
|
1488
|
+
pattern: "-----BEGIN CERTIFICATE-----"
|
|
1489
|
+
},
|
|
1490
|
+
// Date of birth patterns
|
|
1491
|
+
{
|
|
1492
|
+
name: "date_of_birth_us",
|
|
1493
|
+
pattern: String.raw`\b(?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12]\\d|3[01])[-/](?:19|20)\\d{2}\b`
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
name: "date_of_birth_iso",
|
|
1497
|
+
pattern: String.raw`\b(?:19|20)\\d{2}[-/](?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12]\\d|3[01])\b`
|
|
1498
|
+
},
|
|
1499
|
+
// US Identification Numbers
|
|
1500
|
+
{
|
|
1501
|
+
name: "us_passport_numbers",
|
|
1502
|
+
pattern: String.raw`\b[A-Z]?\\d{6,9}\b`
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
name: "drivers_license",
|
|
1506
|
+
pattern: String.raw`\b[A-Z]{1,2}\\d{3,8}[-\s]?\\d{2,5}[-\s]?\\d{2,5}[-\s]?\\d{1,5}[-\s]?\\d?\b`
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
name: "bank_account_numbers",
|
|
1510
|
+
pattern: String.raw`\b\\d{10,17}\b`
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
name: "aba_routing_numbers",
|
|
1514
|
+
pattern: String.raw`\b((0[0-9])|(1[0-2])|(2[1-9])|(3[0-2])|(6[1-9])|(7[0-2])|80)([0-9]{7})\b`
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
name: "health_insurance_numbers",
|
|
1518
|
+
pattern: String.raw`\b\\d{10}[A-Z]\b`
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
name: "employee_ids",
|
|
1522
|
+
pattern: String.raw`\b(?:[Ee][Mm][Pp]|[Ee][Mm][Pp][Ll][Oo][Yy][Ee][Ee]|[Ee])[-\s]?\\d{5,8}\b`
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
name: "tax_ein",
|
|
1526
|
+
pattern: String.raw`\b\\d{2}-\\d{7}\b`
|
|
1527
|
+
},
|
|
1528
|
+
{
|
|
1529
|
+
name: "medicare_beneficiary_id",
|
|
1530
|
+
pattern: String.raw`\b[1-9][A-Z][A-Z0-9]\\d-[A-Z][A-Z0-9]\\d-[A-Z][A-Z0-9]\\d{2}\b`
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
name: "national_provider_id",
|
|
1534
|
+
pattern: String.raw`\b1\\d{9}\b`
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
name: "dea_numbers",
|
|
1538
|
+
pattern: String.raw`\b[A-Z]{2}\\d{7}\b`
|
|
1539
|
+
},
|
|
1540
|
+
{
|
|
1541
|
+
name: "itin",
|
|
1542
|
+
pattern: String.raw`\b9\\d{2}(?:[ \\-]?)[7,8]\\d(?:[ \\-]?)\\d{4}\b`
|
|
1543
|
+
},
|
|
1544
|
+
// Vehicle and Location
|
|
1545
|
+
{
|
|
1546
|
+
name: "vin_numbers",
|
|
1547
|
+
pattern: String.raw`\b[A-HJ-NPR-Z0-9]{17}\b`
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
name: "coordinates",
|
|
1551
|
+
pattern: String.raw`\b[-+]?(?:[0-8]?\\d(?:\\.\\d+)?|90(?:\\.0+)?),\\s*[-+]?(?:1[0-7]\\d(?:\\.\\d+)?|180(?:\\.0+)?|[0-9]?\\d(?:\\.\\d+)?)\b`
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
name: "us_license_plates",
|
|
1555
|
+
pattern: String.raw`\b[A-Z]{1,3}[-\s]\\d{1,4}[A-Z]?\b|\b\\d{1,2}[A-Z]{1,3}\\d{1,4}\b`
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
name: "us_zip_codes",
|
|
1559
|
+
pattern: String.raw`\b(\\d{5}-\\d{4}|\\d{5})\b`
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: "us_street_addresses",
|
|
1563
|
+
pattern: String.raw`\b\\d{1,8}\b[\\s\\S]{10,100}?\b(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NC|ND|NE|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY)\b\\s\\d{5}\b`
|
|
1564
|
+
},
|
|
1565
|
+
// International Banking
|
|
1566
|
+
{
|
|
1567
|
+
name: "iban",
|
|
1568
|
+
pattern: String.raw`\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b`
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
name: "swift_bic",
|
|
1572
|
+
pattern: String.raw`\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?\b`
|
|
1573
|
+
},
|
|
1574
|
+
// Additional API Keys and Tokens
|
|
1575
|
+
{
|
|
1576
|
+
name: "google_oauth",
|
|
1577
|
+
pattern: String.raw`\bya29\\.[a-zA-Z0-9_-]{60,}\b`
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
name: "firebase_tokens",
|
|
1581
|
+
pattern: String.raw`\bAAAA[A-Za-z0-9_-]{100,}\b`
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
name: "google_app_ids",
|
|
1585
|
+
pattern: String.raw`\b[0-9]+-\w+\.apps\.googleusercontent\.com\b`
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
name: "facebook_secrets",
|
|
1589
|
+
pattern: String.raw`\b(facebook_secret|FACEBOOK_SECRET|facebook_app_secret|FACEBOOK_APP_SECRET)[a-z_ =\\s"'\\:]{0,5}[^a-zA-Z0-9][a-f0-9]{32}[^a-zA-Z0-9]`
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
name: "github_keys",
|
|
1593
|
+
pattern: String.raw`\b(GITHUB_SECRET|GITHUB_KEY|github_secret|github_key|github_token|GITHUB_TOKEN|github_api_key|GITHUB_API_KEY)[a-z_ =\\s\"'\\:]{0,10}[^a-zA-Z0-9][a-zA-Z0-9]{40}[^a-zA-Z0-9]`
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
name: "heroku_keys",
|
|
1597
|
+
pattern: String.raw`\b(heroku_api_key|HEROKU_API_KEY|heroku_secret|HEROKU_SECRET)[a-z_ =\\s\"'\\:]{0,10}[^a-zA-Z0-9-]\\w{8}(?:-\\w{4}){3}-\\w{12}[^a-zA-Z0-9\\-]`
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
name: "slack_api_keys",
|
|
1601
|
+
pattern: String.raw`\b(slack_api_key|SLACK_API_KEY|slack_key|SLACK_KEY)[a-z_ =\\s\"'\\:]{0,10}[^a-f0-9][a-f0-9]{32}[^a-f0-9]`
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
name: "slack_api_tokens",
|
|
1605
|
+
pattern: String.raw`\b(xox[pb](?:-[a-zA-Z0-9]+){4,})\b`
|
|
1606
|
+
},
|
|
1607
|
+
// Extended Private Keys and Certificates
|
|
1608
|
+
{
|
|
1609
|
+
name: "dsa_private_keys",
|
|
1610
|
+
pattern: String.raw`-----BEGIN DSA PRIVATE KEY-----(?:[a-zA-Z0-9\+\=\/"']|\s)+?-----END DSA PRIVATE KEY-----`
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
name: "ec_private_keys",
|
|
1614
|
+
pattern: String.raw`-----BEGIN (?:EC|ECDSA) PRIVATE KEY-----(?:[a-zA-Z0-9\+\=\/"']|\s)+?-----END (?:EC|ECDSA) PRIVATE KEY-----`
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
name: "encrypted_private_keys",
|
|
1618
|
+
pattern: String.raw`-----BEGIN ENCRYPTED PRIVATE KEY-----(?:.|\s)+?-----END ENCRYPTED PRIVATE KEY-----`
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
name: "ssl_certificates",
|
|
1622
|
+
pattern: String.raw`-----BEGIN CERTIFICATE-----(?:.|\n)+?\s-----END CERTIFICATE-----`
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
name: "ssh_dss_public",
|
|
1626
|
+
pattern: String.raw`\bssh-dss [0-9A-Za-z+/]+[=]{2}\b`
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
name: "ssh_rsa_public",
|
|
1630
|
+
pattern: String.raw`\bssh-rsa AAAA[0-9A-Za-z+/]+[=]{0,3} [^@]+@[^@]+\b`
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
name: "putty_ssh_keys",
|
|
1634
|
+
pattern: String.raw`PuTTY-User-Key-File-2: ssh-(?:rsa|dss)\s*Encryption: none(?:.|\s?)*?Private-MAC:`
|
|
1635
|
+
},
|
|
1636
|
+
// International Phone Numbers
|
|
1637
|
+
{
|
|
1638
|
+
name: "france_phone_numbers",
|
|
1639
|
+
pattern: String.raw`\b([0O]?[1lI][1lI])?[3E][3E][0O]?[\\dOIlZEASB]{9}\b`
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
name: "german_phone_numbers",
|
|
1643
|
+
pattern: String.raw`\b[\d\w]\d{2}[\d\w]{6}\d[\d\w]\b`
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
name: "uk_phone_numbers",
|
|
1647
|
+
pattern: String.raw`\b([0O]?[1lI][1lI])?[4A][4A][\\dOIlZEASB]{10,11}\b`
|
|
1648
|
+
},
|
|
1649
|
+
// International IDs
|
|
1650
|
+
{
|
|
1651
|
+
name: "uk_drivers_license",
|
|
1652
|
+
pattern: String.raw`\b[A-Z]{5}\d{6}[A-Z]{2}\d{1}[A-Z]{2}\b`
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
name: "uk_passport",
|
|
1656
|
+
pattern: String.raw`\b\\d{10}GB[RP]\\d{7}[UMF]{1}\\d{9}\b`
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
name: "argentina_dni",
|
|
1660
|
+
pattern: String.raw`\b\\d{2}\\.\\d{3}\\.\\d{3}\b`
|
|
1661
|
+
},
|
|
1662
|
+
{
|
|
1663
|
+
name: "australia_tfn",
|
|
1664
|
+
pattern: String.raw`\b[Tt][Ff][Nn](:|:\\s|\\s|)(\\d{8,9})\b`
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
name: "canada_passport",
|
|
1668
|
+
pattern: String.raw`\b[\\w]{2}[\\d]{6}\b`
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
name: "croatia_vat",
|
|
1672
|
+
pattern: String.raw`\bHR\\d{11}\b`
|
|
1673
|
+
},
|
|
1674
|
+
{
|
|
1675
|
+
name: "czech_vat",
|
|
1676
|
+
pattern: String.raw`\bCZ\\d{8,10}\b`
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
name: "denmark_personal_id",
|
|
1680
|
+
pattern: String.raw`\b(?:\\d{10}|\\d{6}[-\\s]\\d{4})\b`
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
name: "france_national_id",
|
|
1684
|
+
pattern: String.raw`\b\\d{12}\b`
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
name: "france_ssn",
|
|
1688
|
+
pattern: String.raw`\b(?:\\d{13}|\\d{13}\\s\\d{2})\b`
|
|
1689
|
+
},
|
|
1690
|
+
{
|
|
1691
|
+
name: "france_passport",
|
|
1692
|
+
pattern: String.raw`\b\\d{2}11\\d{5}\b`
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
name: "california_drivers_license",
|
|
1696
|
+
pattern: String.raw`\b[A-Z]{1}\\d{7}\b`
|
|
1697
|
+
},
|
|
1698
|
+
// Medical and Healthcare
|
|
1699
|
+
{
|
|
1700
|
+
name: "hipaa_ndc",
|
|
1701
|
+
pattern: String.raw`\b\\d{4,5}-\\d{3,4}-\\d{1,2}\b`
|
|
1702
|
+
},
|
|
1703
|
+
// Security and Network
|
|
1704
|
+
{
|
|
1705
|
+
name: "cve_numbers",
|
|
1706
|
+
pattern: String.raw`\b[Cc][Vv][Ee]-\\d{4}-\\d{4,7}\b`
|
|
1707
|
+
},
|
|
1708
|
+
{
|
|
1709
|
+
name: "microsoft_oauth",
|
|
1710
|
+
pattern: String.raw`https://login\.microsoftonline\.com/common/oauth2/v2\.0/token|https://login\.windows\.net/common/oauth2/token`
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
name: "postgres_connections",
|
|
1714
|
+
pattern: String.raw`\b(?:[Pp][Oo][Ss][Tt][Gg][Rr][Ee][Ss]|[Pp][Gg][Ss][Qq][Ll])\\:\\/\\/`
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
name: "box_links",
|
|
1718
|
+
pattern: String.raw`https://app\.box\.com/[s|l]/\S+`
|
|
1719
|
+
},
|
|
1720
|
+
{
|
|
1721
|
+
name: "dropbox_links",
|
|
1722
|
+
pattern: String.raw`https://www\.dropbox\.com/(?:s|l)/\S+`
|
|
1723
|
+
},
|
|
1724
|
+
// Credit Card Variants
|
|
1725
|
+
{
|
|
1726
|
+
name: "amex_cards",
|
|
1727
|
+
pattern: String.raw`\b3[47][0-9]{13}\b`
|
|
1728
|
+
},
|
|
1729
|
+
{
|
|
1730
|
+
name: "visa_cards",
|
|
1731
|
+
pattern: String.raw`\b4[0-9]{12}(?:[0-9]{3})?\b`
|
|
1732
|
+
},
|
|
1733
|
+
{
|
|
1734
|
+
name: "discover_cards",
|
|
1735
|
+
pattern: String.raw`\b65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}\b`
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
name: "enhanced_credit_cards",
|
|
1739
|
+
pattern: String.raw`\b((4\\d{3}|5[1-5]\\d{2}|2\\d{3}|3[47]\\d{1,2})[\\s\\-]?\\d{4,6}[\\s\\-]?\\d{4,6}?([\\s\\-]\\d{3,4})?(\\d{3})?)\b`
|
|
1740
|
+
},
|
|
1741
|
+
// Bank Routing Numbers (US specific)
|
|
1742
|
+
{
|
|
1743
|
+
name: "bbva_routing_ca",
|
|
1744
|
+
pattern: String.raw`\\b321170538\\b`
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
name: "boa_routing_ca",
|
|
1748
|
+
pattern: String.raw`\\b(?:121|026)00(?:0|9)(?:358|593)\\b`
|
|
1749
|
+
},
|
|
1750
|
+
{
|
|
1751
|
+
name: "chase_routing_ca",
|
|
1752
|
+
pattern: String.raw`\\b322271627\\b`
|
|
1753
|
+
},
|
|
1754
|
+
{
|
|
1755
|
+
name: "citibank_routing_ca",
|
|
1756
|
+
pattern: String.raw`\\b32(?:11|22)71(?:18|72)4\\b`
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
name: "usbank_routing_ca",
|
|
1760
|
+
pattern: String.raw`\\b12(?:1122676|2235821)\\b`
|
|
1761
|
+
},
|
|
1762
|
+
{
|
|
1763
|
+
name: "united_bank_routing_ca",
|
|
1764
|
+
pattern: String.raw`\\b122243350\\b`
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
name: "wells_fargo_routing_ca",
|
|
1768
|
+
pattern: String.raw`\\b121042882\\b`
|
|
1769
|
+
},
|
|
1770
|
+
// Unrealistic alphanumeric identifiers
|
|
1771
|
+
{
|
|
1772
|
+
name: "generic_non_usual",
|
|
1773
|
+
pattern: String.raw`(?:^|\s)(?=[A-Za-z0-9_\)\*\=@]*[A-Za-z])(?=[A-Za-z0-9_\)\*\=@]*[0-9])([A-Za-z0-9_\)\*\=@]{5,})(?=\s|$)`
|
|
1774
|
+
}
|
|
1775
|
+
];
|
|
1776
|
+
|
|
1777
|
+
// src/internal/masking/utils.ts
|
|
1778
|
+
function isValidPatternName(name) {
|
|
1779
|
+
return /^[a-zA-Z0-9_]+$/.test(name);
|
|
1780
|
+
}
|
|
1781
|
+
function isLikelyReDoSPattern(pattern) {
|
|
1782
|
+
const dangerousPatterns = [
|
|
1783
|
+
// Nested quantifiers like (a+)+, (a*)+, (a+)*
|
|
1784
|
+
/\([^)]*[+*]\)[+*]/,
|
|
1785
|
+
// Alternation with overlapping groups like (a|a)*
|
|
1786
|
+
/\([^)]*\|[^)]*\)[+*]/,
|
|
1787
|
+
// Complex backtracking patterns - but more specific
|
|
1788
|
+
/\([^)]*[+*][^)]*[+*][^)]*\)[+*]/
|
|
1789
|
+
];
|
|
1790
|
+
return dangerousPatterns.some((dangerous) => dangerous.test(pattern));
|
|
1791
|
+
}
|
|
1792
|
+
function getGroupedPattern(patternEntry) {
|
|
1793
|
+
let name = patternEntry.name;
|
|
1794
|
+
if (!name || name === "") {
|
|
1795
|
+
name = `pattern_${Math.random().toString(16).slice(2)}`;
|
|
1796
|
+
}
|
|
1797
|
+
if (!isValidPatternName(name)) {
|
|
1798
|
+
throw new Error(
|
|
1799
|
+
`Pattern name '${name}' must only contain alphanumeric characters and underscores`
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
if (isLikelyReDoSPattern(patternEntry.pattern)) {
|
|
1803
|
+
throw new Error(`Potentially dangerous ReDoS pattern detected: '${patternEntry.pattern}'`);
|
|
1804
|
+
}
|
|
1805
|
+
try {
|
|
1806
|
+
new RegExp(patternEntry.pattern);
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
throw new Error(`Invalid regex pattern '${patternEntry.pattern}': ${String(error)}`);
|
|
1809
|
+
}
|
|
1810
|
+
return `(?<${name}>${patternEntry.pattern})`;
|
|
1811
|
+
}
|
|
1812
|
+
function isIPatternEntry(obj) {
|
|
1813
|
+
return typeof obj === "object" && obj !== null && typeof obj.pattern === "string" && (obj.name === void 0 || typeof obj.name === "string");
|
|
1814
|
+
}
|
|
1815
|
+
function isIEventMaskingRule(obj) {
|
|
1816
|
+
return typeof obj === "object" && obj !== null && typeof obj.mode === "string" && Array.isArray(obj.patterns) && obj.patterns.every(
|
|
1817
|
+
(p) => isIPatternEntry(p) || typeof p === "string"
|
|
1818
|
+
) && (obj.attributePattern === void 0 || typeof obj.attributePattern === "string");
|
|
1819
|
+
}
|
|
1820
|
+
function convertPatternsToPatternEntries(patterns) {
|
|
1821
|
+
const patternEntries = [];
|
|
1822
|
+
for (const pattern of patterns) {
|
|
1823
|
+
if (typeof pattern === "string") {
|
|
1824
|
+
patternEntries.push({ pattern, name: "" });
|
|
1825
|
+
} else if (isIPatternEntry(pattern)) {
|
|
1826
|
+
patternEntries.push(pattern);
|
|
1827
|
+
} else {
|
|
1828
|
+
throw new Error("Patterns must be either strings or PatternEntry instances");
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
return patternEntries;
|
|
1832
|
+
}
|
|
1833
|
+
function compilePatternEntries(patternEntries) {
|
|
1834
|
+
const patternGroups = [];
|
|
1835
|
+
for (const patternEntry of patternEntries) {
|
|
1836
|
+
try {
|
|
1837
|
+
patternGroups.push(getGroupedPattern(patternEntry));
|
|
1838
|
+
} catch (error) {
|
|
1839
|
+
logger.warn(`Invalid pattern '${patternEntry.name}': ${patternEntry.pattern}`, error);
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (patternGroups.length === 0) {
|
|
1844
|
+
return null;
|
|
1845
|
+
}
|
|
1846
|
+
try {
|
|
1847
|
+
return new RegExp(patternGroups.join("|"));
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
logger.warn("Failed to compile pattern entries into regex", error);
|
|
1850
|
+
return null;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
function getCompiledAttributeNamePattern(rule) {
|
|
1854
|
+
if (!rule.attributePattern) {
|
|
1855
|
+
logger.debug("No attribute pattern provided, using default .*");
|
|
1856
|
+
return /.*/;
|
|
1857
|
+
}
|
|
1858
|
+
let compiledPatternString = rule.attributePattern;
|
|
1859
|
+
if (isIEventMaskingRule(rule) && rule.eventNamePattern) {
|
|
1860
|
+
compiledPatternString = `(${compiledPatternString})|(${rule.eventNamePattern})`;
|
|
1861
|
+
}
|
|
1862
|
+
return new RegExp(compiledPatternString);
|
|
1863
|
+
}
|
|
1864
|
+
function shouldContinueExecution(startTime, iterations, timeoutMs) {
|
|
1865
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
1866
|
+
logger.warn("Regex execution timed out - potential ReDoS pattern detected");
|
|
1867
|
+
return false;
|
|
1868
|
+
}
|
|
1869
|
+
const maxIterations = 1e3;
|
|
1870
|
+
if (iterations > maxIterations) {
|
|
1871
|
+
logger.warn("Regex execution exceeded maximum iterations - potential ReDoS pattern detected");
|
|
1872
|
+
return false;
|
|
1873
|
+
}
|
|
1874
|
+
return true;
|
|
1875
|
+
}
|
|
1876
|
+
function createGlobalPattern(pattern) {
|
|
1877
|
+
return new RegExp(
|
|
1878
|
+
pattern.source,
|
|
1879
|
+
pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g"
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1882
|
+
function executeRegexWithTimeout(pattern, value, timeoutMs = 100) {
|
|
1883
|
+
const matches = [];
|
|
1884
|
+
const globalPattern = createGlobalPattern(pattern);
|
|
1885
|
+
const startTime = Date.now();
|
|
1886
|
+
let match;
|
|
1887
|
+
let iterations = 0;
|
|
1888
|
+
while ((match = globalPattern.exec(value)) !== null) {
|
|
1889
|
+
if (!shouldContinueExecution(startTime, ++iterations, timeoutMs)) {
|
|
1890
|
+
break;
|
|
1891
|
+
}
|
|
1892
|
+
matches.push(match);
|
|
1893
|
+
if (match[0].length === 0) {
|
|
1894
|
+
globalPattern.lastIndex = match.index + 1;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
return matches;
|
|
1898
|
+
}
|
|
1899
|
+
function maskStringByPattern(value, pattern, mode = "full") {
|
|
1900
|
+
const matches = [];
|
|
1901
|
+
try {
|
|
1902
|
+
const regexMatches = executeRegexWithTimeout(pattern, value);
|
|
1903
|
+
for (const match of regexMatches) {
|
|
1904
|
+
matches.push({
|
|
1905
|
+
start: match.index,
|
|
1906
|
+
end: match.index + match[0].length,
|
|
1907
|
+
text: match[0],
|
|
1908
|
+
groups: match.groups
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
} catch (error) {
|
|
1912
|
+
logger.warn("Regex execution failed, skipping masking", error);
|
|
1913
|
+
return value;
|
|
1914
|
+
}
|
|
1915
|
+
for (const matchInfo of matches.reverse()) {
|
|
1916
|
+
let patternName = "unknown";
|
|
1917
|
+
if (matchInfo.groups) {
|
|
1918
|
+
for (const [groupName, groupValue] of Object.entries(matchInfo.groups)) {
|
|
1919
|
+
if (groupValue !== void 0) {
|
|
1920
|
+
if (groupName.includes("_") && /\d$/.test(groupName)) {
|
|
1921
|
+
const parts = groupName.split("_");
|
|
1922
|
+
patternName = parts[0] || groupName;
|
|
1923
|
+
} else {
|
|
1924
|
+
patternName = groupName;
|
|
1925
|
+
}
|
|
1926
|
+
break;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
logger.info(`Masking detected: pattern '${patternName}' found match in value`);
|
|
1931
|
+
const masked = mode === "partial" ? matchInfo.text[0] + "*****" : "*****";
|
|
1932
|
+
value = value.slice(0, matchInfo.start) + masked + value.slice(matchInfo.end);
|
|
1933
|
+
}
|
|
1934
|
+
return value;
|
|
1935
|
+
}
|
|
1936
|
+
function maskStringByPatternBasedRule(value, rule) {
|
|
1937
|
+
const patternEntries = convertPatternsToPatternEntries(rule.patterns);
|
|
1938
|
+
if (patternEntries.length === 0) {
|
|
1939
|
+
logger.warn("No patterns provided for masking rule, returning original value");
|
|
1940
|
+
return value;
|
|
1941
|
+
}
|
|
1942
|
+
const mode = rule.mode;
|
|
1943
|
+
if (!patternEntries || patternEntries.length === 0) {
|
|
1944
|
+
return mode === "partial" && value ? value[0] + "*****" : "*****";
|
|
1945
|
+
}
|
|
1946
|
+
const compiledPatterns = compilePatternEntries(patternEntries);
|
|
1947
|
+
if (!compiledPatterns) {
|
|
1948
|
+
return value;
|
|
1949
|
+
}
|
|
1950
|
+
return maskStringByPattern(value, compiledPatterns, mode);
|
|
1951
|
+
}
|
|
1952
|
+
function maskValue(value, rule) {
|
|
1953
|
+
if (typeof value === "string") {
|
|
1954
|
+
return maskStringByPatternBasedRule(value, rule);
|
|
1955
|
+
} else if (typeof value === "boolean" || typeof value === "number") {
|
|
1956
|
+
return maskStringByPatternBasedRule(String(value), rule);
|
|
1957
|
+
} else if (Array.isArray(value)) {
|
|
1958
|
+
return value.map((v) => maskStringByPatternBasedRule(String(v), rule));
|
|
1959
|
+
} else if (value !== null && typeof value === "object") {
|
|
1960
|
+
const result = {};
|
|
1961
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1962
|
+
result[k] = maskValue(v, rule);
|
|
1963
|
+
}
|
|
1964
|
+
return result;
|
|
1965
|
+
} else {
|
|
1966
|
+
throw new Error(`Unsupported value type for masking: ${typeof value}`);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
function maskAttributes(attributes, rules, outputOriginalValue = false) {
|
|
1970
|
+
if (!attributes || Object.keys(attributes).length === 0) {
|
|
1971
|
+
return {};
|
|
1972
|
+
}
|
|
1973
|
+
const maskedAttributes = { ...attributes };
|
|
1974
|
+
for (const rule of rules) {
|
|
1975
|
+
const compiledAttributeNamePattern = getCompiledAttributeNamePattern(rule);
|
|
1976
|
+
const attributesToMask = compiledAttributeNamePattern ? Object.keys(maskedAttributes).filter((attr) => compiledAttributeNamePattern.test(attr)) : Object.keys(maskedAttributes);
|
|
1977
|
+
for (const attribute of attributesToMask) {
|
|
1978
|
+
const value = maskedAttributes[attribute];
|
|
1979
|
+
if (value === null || value === void 0) {
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (outputOriginalValue) {
|
|
1983
|
+
logger.debug(`Masking attribute '${attribute}' with original value`, { value });
|
|
1984
|
+
}
|
|
1985
|
+
maskedAttributes[attribute] = maskValue(value, rule);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
return maskedAttributes;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// src/internal/semantic-conventions.ts
|
|
1992
|
+
import { createContextKey } from "@opentelemetry/api";
|
|
1993
|
+
var BRIZZ = "brizz";
|
|
1994
|
+
var PROPERTIES = "properties";
|
|
1995
|
+
var SESSION_ID = "session.id";
|
|
1996
|
+
var PROPERTIES_CONTEXT_KEY = createContextKey(PROPERTIES);
|
|
1997
|
+
|
|
1998
|
+
// src/internal/log/processors/log-processor.ts
|
|
1999
|
+
var DEFAULT_LOG_MASKING_RULES = [
|
|
2000
|
+
{
|
|
2001
|
+
mode: "partial",
|
|
2002
|
+
attributePattern: "event.name",
|
|
2003
|
+
patterns: DEFAULT_PII_PATTERNS
|
|
2004
|
+
}
|
|
2005
|
+
];
|
|
2006
|
+
var BrizzSimpleLogRecordProcessor = class extends SimpleLogRecordProcessor {
|
|
2007
|
+
config;
|
|
2008
|
+
constructor(exporter, config) {
|
|
2009
|
+
super(exporter);
|
|
2010
|
+
this.config = config;
|
|
2011
|
+
}
|
|
2012
|
+
onEmit(logRecord) {
|
|
2013
|
+
const maskingConfig = this.config.masking?.eventMasking;
|
|
2014
|
+
if (maskingConfig) {
|
|
2015
|
+
maskLog(logRecord, maskingConfig);
|
|
2016
|
+
}
|
|
2017
|
+
const associationProperties = context5.active().getValue(PROPERTIES_CONTEXT_KEY);
|
|
2018
|
+
if (associationProperties) {
|
|
2019
|
+
for (const [key, value] of Object.entries(associationProperties)) {
|
|
2020
|
+
logRecord.setAttribute(`${BRIZZ}.${key}`, value);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
super.onEmit(logRecord);
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
var BrizzBatchLogRecordProcessor = class extends BatchLogRecordProcessor {
|
|
2027
|
+
config;
|
|
2028
|
+
constructor(exporter, config) {
|
|
2029
|
+
super(exporter);
|
|
2030
|
+
this.config = config;
|
|
2031
|
+
}
|
|
2032
|
+
onEmit(logRecord) {
|
|
2033
|
+
const maskingConfig = this.config.masking?.eventMasking;
|
|
2034
|
+
if (maskingConfig) {
|
|
2035
|
+
maskLog(logRecord, maskingConfig);
|
|
2036
|
+
}
|
|
2037
|
+
const associationProperties = context5.active().getValue(PROPERTIES_CONTEXT_KEY);
|
|
2038
|
+
if (associationProperties) {
|
|
2039
|
+
for (const [key, value] of Object.entries(associationProperties)) {
|
|
2040
|
+
logRecord.setAttribute(`${BRIZZ}.${key}`, value);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
super.onEmit(logRecord);
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
function maskLog(logRecord, config) {
|
|
2047
|
+
if (!logRecord.attributes) {
|
|
2048
|
+
return logRecord;
|
|
2049
|
+
}
|
|
2050
|
+
let rules = config.rules || [];
|
|
2051
|
+
if (!config.disableDefaultRules) {
|
|
2052
|
+
rules = [...DEFAULT_LOG_MASKING_RULES, ...rules];
|
|
2053
|
+
}
|
|
2054
|
+
try {
|
|
2055
|
+
if (logRecord.attributes && Object.keys(logRecord.attributes).length > 0) {
|
|
2056
|
+
const attributesRecord = {};
|
|
2057
|
+
for (const [key, value] of Object.entries(logRecord.attributes)) {
|
|
2058
|
+
attributesRecord[key] = value;
|
|
2059
|
+
}
|
|
2060
|
+
const maskedAttributes = maskAttributes(attributesRecord, rules);
|
|
2061
|
+
if (maskedAttributes) {
|
|
2062
|
+
const newAttributes = {};
|
|
2063
|
+
for (const [key, value] of Object.entries(maskedAttributes)) {
|
|
2064
|
+
newAttributes[key] = value;
|
|
2065
|
+
}
|
|
2066
|
+
logRecord.setAttributes(newAttributes);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
if (config.maskBody && logRecord.body !== void 0 && logRecord.body !== null) {
|
|
2070
|
+
let maskedBody = logRecord.body;
|
|
2071
|
+
for (const rule of rules) {
|
|
2072
|
+
maskedBody = maskValue(maskedBody, rule);
|
|
2073
|
+
}
|
|
2074
|
+
logRecord.setBody(maskedBody);
|
|
2075
|
+
}
|
|
2076
|
+
return logRecord;
|
|
2077
|
+
} catch (error) {
|
|
2078
|
+
logger.error("Error masking log record:", error);
|
|
2079
|
+
return logRecord;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// src/internal/log/logging.ts
|
|
2084
|
+
var LoggingModule = class _LoggingModule {
|
|
2085
|
+
static instance = null;
|
|
2086
|
+
logExporter = null;
|
|
2087
|
+
logProcessor = null;
|
|
2088
|
+
loggerProvider = null;
|
|
2089
|
+
static getInstance() {
|
|
2090
|
+
if (!_LoggingModule.instance) {
|
|
2091
|
+
throw new Error("Brizz must be initialized before accessing LoggingModule");
|
|
2092
|
+
}
|
|
2093
|
+
return _LoggingModule.instance;
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Initialize the logging module with the provided configuration
|
|
2097
|
+
*/
|
|
2098
|
+
setup(config) {
|
|
2099
|
+
logger.info("Setting up logging module");
|
|
2100
|
+
this.initLogExporter(config);
|
|
2101
|
+
this.initLogProcessor(config);
|
|
2102
|
+
this.initLoggerProvider(config);
|
|
2103
|
+
_LoggingModule.instance = this;
|
|
2104
|
+
logger.info("Logging module setup completed");
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Initialize the log exporter
|
|
2108
|
+
*/
|
|
2109
|
+
initLogExporter(config) {
|
|
2110
|
+
if (this.logExporter) {
|
|
2111
|
+
logger.debug("Log exporter already initialized, skipping re-initialization");
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
if (config.customLogExporter) {
|
|
2115
|
+
logger.debug("Using custom log exporter");
|
|
2116
|
+
this.logExporter = config.customLogExporter;
|
|
2117
|
+
logger.debug("Custom log exporter initialized successfully");
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
const logsUrl = config.baseUrl.replace(/\/$/, "") + "/v1/logs";
|
|
2121
|
+
logger.debug("Initializing default OTLP log exporter", { url: logsUrl });
|
|
2122
|
+
const headers = { ...config.headers };
|
|
2123
|
+
this.logExporter = new OTLPLogExporter({
|
|
2124
|
+
url: logsUrl,
|
|
2125
|
+
headers
|
|
2126
|
+
});
|
|
2127
|
+
logger.debug("OTLP log exporter initialized successfully");
|
|
2128
|
+
}
|
|
2129
|
+
/**
|
|
2130
|
+
* Initialize the log processor with masking support
|
|
2131
|
+
*/
|
|
2132
|
+
initLogProcessor(config) {
|
|
2133
|
+
if (this.logProcessor) {
|
|
2134
|
+
logger.debug("Log processor already initialized, skipping re-initialization");
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
if (!this.logExporter) {
|
|
2138
|
+
throw new Error("Log exporter must be initialized before processor");
|
|
2139
|
+
}
|
|
2140
|
+
logger.debug("Initializing log processor", {
|
|
2141
|
+
disableBatch: config.disableBatch,
|
|
2142
|
+
hasMasking: !!config.masking?.eventMasking
|
|
2143
|
+
});
|
|
2144
|
+
this.logProcessor = config.disableBatch ? new BrizzSimpleLogRecordProcessor(this.logExporter, config) : new BrizzBatchLogRecordProcessor(this.logExporter, config);
|
|
2145
|
+
logger.debug("Log processor initialized successfully");
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Initialize the logger provider
|
|
2149
|
+
*/
|
|
2150
|
+
initLoggerProvider(config) {
|
|
2151
|
+
if (this.loggerProvider) {
|
|
2152
|
+
logger.debug("Logger provider already initialized, skipping re-initialization");
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (!this.logProcessor) {
|
|
2156
|
+
throw new Error("Log processor must be initialized before logger provider");
|
|
2157
|
+
}
|
|
2158
|
+
logger.debug("Creating resource with service name", {
|
|
2159
|
+
serviceName: config.appName
|
|
2160
|
+
});
|
|
2161
|
+
const resource = resourceFromAttributes({
|
|
2162
|
+
"service.name": config.appName
|
|
2163
|
+
});
|
|
2164
|
+
logger.debug("Creating logger provider with resource");
|
|
2165
|
+
this.loggerProvider = new LoggerProvider({
|
|
2166
|
+
resource,
|
|
2167
|
+
processors: [this.logProcessor]
|
|
2168
|
+
});
|
|
2169
|
+
logger.debug("Logger provider initialization completed");
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Emit a custom event to the telemetry pipeline
|
|
2173
|
+
*/
|
|
2174
|
+
emitEvent(name, attributes, body, severityNumber = SeverityNumber2.INFO) {
|
|
2175
|
+
logger.debug("Attempting to emit event", {
|
|
2176
|
+
name,
|
|
2177
|
+
hasAttributes: !!attributes,
|
|
2178
|
+
attributesCount: attributes ? Object.keys(attributes).length : 0,
|
|
2179
|
+
hasBody: !!body,
|
|
2180
|
+
severityNumber
|
|
2181
|
+
});
|
|
2182
|
+
if (!this.loggerProvider) {
|
|
2183
|
+
logger.error("Cannot emit event: Logger provider not initialized");
|
|
2184
|
+
throw new Error("Logging module not initialized");
|
|
2185
|
+
}
|
|
2186
|
+
const logAttributes = { "event.name": name };
|
|
2187
|
+
if (attributes) {
|
|
2188
|
+
Object.assign(logAttributes, attributes);
|
|
2189
|
+
logger.debug("Combined log attributes", { attributes: Object.keys(logAttributes) });
|
|
2190
|
+
}
|
|
2191
|
+
logger.debug("Getting logger instance for brizz_events");
|
|
2192
|
+
const eventLogger = this.loggerProvider.getLogger("brizz.events");
|
|
2193
|
+
logger.debug("Emitting log record with eventName", { eventName: name });
|
|
2194
|
+
try {
|
|
2195
|
+
eventLogger.emit({
|
|
2196
|
+
eventName: name,
|
|
2197
|
+
attributes: logAttributes,
|
|
2198
|
+
severityNumber,
|
|
2199
|
+
body
|
|
2200
|
+
});
|
|
2201
|
+
logger.debug("Event successfully emitted", { eventName: name });
|
|
2202
|
+
} catch (error) {
|
|
2203
|
+
logger.error(`Failed to emit event '${name}'`, { error, eventName: name });
|
|
2204
|
+
logger.error("Log record that failed", {
|
|
2205
|
+
eventName: name,
|
|
2206
|
+
hasAttributes: logAttributes,
|
|
2207
|
+
severityNumber,
|
|
2208
|
+
hasBody: body
|
|
2209
|
+
});
|
|
2210
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
/**
|
|
2214
|
+
* Check if the module is initialized
|
|
2215
|
+
*/
|
|
2216
|
+
isInitialized() {
|
|
2217
|
+
return this.loggerProvider !== null;
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Get the logger provider
|
|
2221
|
+
*/
|
|
2222
|
+
getLoggerProvider() {
|
|
2223
|
+
return this.loggerProvider;
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Shutdown the logging module
|
|
2227
|
+
*/
|
|
2228
|
+
async shutdown() {
|
|
2229
|
+
logger.debug("Shutting down logging module");
|
|
2230
|
+
if (this.loggerProvider) {
|
|
2231
|
+
await this.loggerProvider.shutdown();
|
|
2232
|
+
this.loggerProvider = null;
|
|
2233
|
+
}
|
|
2234
|
+
this.logProcessor = null;
|
|
2235
|
+
this.logExporter = null;
|
|
2236
|
+
if (_LoggingModule.instance === this) {
|
|
2237
|
+
_LoggingModule.instance = null;
|
|
2238
|
+
}
|
|
2239
|
+
logger.debug("Logging module shutdown completed");
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
function emitEvent(name, attributes, body, severityNumber = SeverityNumber2.INFO) {
|
|
2243
|
+
return LoggingModule.getInstance().emitEvent(name, attributes, body, severityNumber);
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// src/internal/metric/metrics.ts
|
|
2247
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
2248
|
+
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
2249
|
+
var MetricsModule = class _MetricsModule {
|
|
2250
|
+
static instance = null;
|
|
2251
|
+
metricsExporter = null;
|
|
2252
|
+
metricsReader = null;
|
|
2253
|
+
static getInstance() {
|
|
2254
|
+
if (!_MetricsModule.instance) {
|
|
2255
|
+
throw new Error("Brizz must be initialized before accessing MetricsModule");
|
|
2256
|
+
}
|
|
2257
|
+
return _MetricsModule.instance;
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Initialize the metrics module with the provided configuration
|
|
2261
|
+
*/
|
|
2262
|
+
setup(config) {
|
|
2263
|
+
logger.info("Setting up metrics module");
|
|
2264
|
+
this.initMetricsReader(config);
|
|
2265
|
+
_MetricsModule.instance = this;
|
|
2266
|
+
logger.info("Metrics module setup completed");
|
|
2267
|
+
}
|
|
2268
|
+
/**
|
|
2269
|
+
* Initialize the metrics exporter
|
|
2270
|
+
*/
|
|
2271
|
+
initMetricsExporter(config) {
|
|
2272
|
+
if (this.metricsExporter) {
|
|
2273
|
+
logger.debug("Metrics exporter already initialized, skipping re-initialization");
|
|
2274
|
+
return;
|
|
2275
|
+
}
|
|
2276
|
+
const metricsUrl = config.baseUrl.replace(/\/$/, "") + "/v1/metrics";
|
|
2277
|
+
logger.debug("Initializing metrics exporter", { url: metricsUrl });
|
|
2278
|
+
this.metricsExporter = new OTLPMetricExporter({
|
|
2279
|
+
url: metricsUrl,
|
|
2280
|
+
headers: config.headers
|
|
2281
|
+
});
|
|
2282
|
+
logger.debug("Metrics exporter initialized successfully");
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Initialize the metrics reader
|
|
2286
|
+
*/
|
|
2287
|
+
initMetricsReader(config) {
|
|
2288
|
+
if (this.metricsReader) {
|
|
2289
|
+
logger.debug("Metrics reader already initialized, skipping re-initialization");
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
if (config.customMetricReader) {
|
|
2293
|
+
logger.debug("Using custom metric reader");
|
|
2294
|
+
this.metricsReader = config.customMetricReader;
|
|
2295
|
+
logger.debug("Custom metric reader initialized successfully");
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
logger.debug(
|
|
2299
|
+
"Using default metrics flow - creating OTLP exporter and PeriodicExportingMetricReader"
|
|
2300
|
+
);
|
|
2301
|
+
this.initMetricsExporter(config);
|
|
2302
|
+
if (!this.metricsExporter) {
|
|
2303
|
+
throw new Error("Failed to initialize metrics exporter");
|
|
2304
|
+
}
|
|
2305
|
+
this.metricsReader = new PeriodicExportingMetricReader({
|
|
2306
|
+
exporter: this.metricsExporter
|
|
2307
|
+
});
|
|
2308
|
+
logger.debug("Default metrics reader initialized successfully");
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Get the metrics exporter
|
|
2312
|
+
*/
|
|
2313
|
+
getMetricsExporter() {
|
|
2314
|
+
if (!this.metricsExporter) {
|
|
2315
|
+
throw new Error("Metrics module not initialized");
|
|
2316
|
+
}
|
|
2317
|
+
return this.metricsExporter;
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Get the metrics reader
|
|
2321
|
+
*/
|
|
2322
|
+
getMetricsReader() {
|
|
2323
|
+
if (!this.metricsReader) {
|
|
2324
|
+
throw new Error("Metrics module not initialized");
|
|
2325
|
+
}
|
|
2326
|
+
return this.metricsReader;
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Shutdown the metrics module
|
|
2330
|
+
*/
|
|
2331
|
+
shutdown() {
|
|
2332
|
+
logger.debug("Shutting down metrics module");
|
|
2333
|
+
this.metricsReader = null;
|
|
2334
|
+
this.metricsExporter = null;
|
|
2335
|
+
if (_MetricsModule.instance === this) {
|
|
2336
|
+
_MetricsModule.instance = null;
|
|
2337
|
+
}
|
|
2338
|
+
logger.debug("Metrics module shutdown completed");
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
function getMetricsExporter() {
|
|
2342
|
+
return MetricsModule.getInstance().getMetricsExporter();
|
|
2343
|
+
}
|
|
2344
|
+
function getMetricsReader() {
|
|
2345
|
+
return MetricsModule.getInstance().getMetricsReader();
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// src/internal/trace/tracing.ts
|
|
2349
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
2350
|
+
|
|
2351
|
+
// src/internal/trace/processors/span-processor.ts
|
|
2352
|
+
import { context as context6 } from "@opentelemetry/api";
|
|
2353
|
+
import {
|
|
2354
|
+
BatchSpanProcessor,
|
|
2355
|
+
SimpleSpanProcessor
|
|
2356
|
+
} from "@opentelemetry/sdk-trace-base";
|
|
2357
|
+
var DEFAULT_MASKING_RULES = [
|
|
2358
|
+
{
|
|
2359
|
+
mode: "partial",
|
|
2360
|
+
attributePattern: "gen_ai.prompt",
|
|
2361
|
+
patterns: DEFAULT_PII_PATTERNS
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
mode: "partial",
|
|
2365
|
+
attributePattern: "gen_ai.completion",
|
|
2366
|
+
patterns: DEFAULT_PII_PATTERNS
|
|
2367
|
+
},
|
|
2368
|
+
{
|
|
2369
|
+
mode: "partial",
|
|
2370
|
+
attributePattern: "traceloop.entity.input",
|
|
2371
|
+
patterns: DEFAULT_PII_PATTERNS
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
mode: "partial",
|
|
2375
|
+
attributePattern: "traceloop.entity.output",
|
|
2376
|
+
patterns: DEFAULT_PII_PATTERNS
|
|
2377
|
+
}
|
|
2378
|
+
];
|
|
2379
|
+
var BrizzSimpleSpanProcessor = class extends SimpleSpanProcessor {
|
|
2380
|
+
config;
|
|
2381
|
+
constructor(spanExporter, config) {
|
|
2382
|
+
super(spanExporter);
|
|
2383
|
+
this.config = config;
|
|
2384
|
+
}
|
|
2385
|
+
// Will work with the following code:
|
|
2386
|
+
// const span = tracer.startSpan('sensitive-operation',{attributes:{
|
|
2387
|
+
// 'user.password': 'secret123',
|
|
2388
|
+
// 'user.email': 'user@example.com',
|
|
2389
|
+
// }});
|
|
2390
|
+
//
|
|
2391
|
+
// Won't work because onStart is called before attributes are set:
|
|
2392
|
+
// span.setAttributes({
|
|
2393
|
+
// 'user.password': 'secret123',
|
|
2394
|
+
// 'user.email': 'user@example.com'
|
|
2395
|
+
// });
|
|
2396
|
+
onStart(span, parentContext) {
|
|
2397
|
+
const maskingConfig = this.config.masking?.spanMasking;
|
|
2398
|
+
if (maskingConfig) {
|
|
2399
|
+
maskSpan(span, maskingConfig);
|
|
2400
|
+
}
|
|
2401
|
+
const associationProperties = context6.active().getValue(PROPERTIES_CONTEXT_KEY);
|
|
2402
|
+
if (associationProperties) {
|
|
2403
|
+
for (const [key, value] of Object.entries(associationProperties)) {
|
|
2404
|
+
span.setAttribute(`${BRIZZ}.${key}`, value);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
super.onStart(span, parentContext);
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
var BrizzBatchSpanProcessor = class extends BatchSpanProcessor {
|
|
2411
|
+
config;
|
|
2412
|
+
constructor(spanExporter, config) {
|
|
2413
|
+
super(spanExporter);
|
|
2414
|
+
this.config = config;
|
|
2415
|
+
}
|
|
2416
|
+
onStart(span, parentContext) {
|
|
2417
|
+
const maskingConfig = this.config.masking?.spanMasking;
|
|
2418
|
+
if (maskingConfig) {
|
|
2419
|
+
maskSpan(span, maskingConfig);
|
|
2420
|
+
}
|
|
2421
|
+
const associationProperties = context6.active().getValue(PROPERTIES_CONTEXT_KEY);
|
|
2422
|
+
if (associationProperties) {
|
|
2423
|
+
for (const [key, value] of Object.entries(associationProperties)) {
|
|
2424
|
+
span.setAttribute(`${BRIZZ}.${key}`, value);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
super.onStart(span, parentContext);
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
function maskSpan(span, config) {
|
|
2431
|
+
if (!span.attributes || Object.keys(span.attributes).length === 0) {
|
|
2432
|
+
return span;
|
|
2433
|
+
}
|
|
2434
|
+
let rules = config.rules || [];
|
|
2435
|
+
if (!config.disableDefaultRules) {
|
|
2436
|
+
rules = [...DEFAULT_MASKING_RULES, ...rules];
|
|
2437
|
+
}
|
|
2438
|
+
try {
|
|
2439
|
+
const attributesRecord = {};
|
|
2440
|
+
for (const [key, value] of Object.entries(span.attributes)) {
|
|
2441
|
+
attributesRecord[key] = value;
|
|
2442
|
+
}
|
|
2443
|
+
const maskedAttributes = maskAttributes(attributesRecord, rules);
|
|
2444
|
+
if (maskedAttributes && Object.keys(maskedAttributes).length > 0) {
|
|
2445
|
+
const merged = { ...span.attributes };
|
|
2446
|
+
for (const [key, value] of Object.entries(maskedAttributes)) {
|
|
2447
|
+
merged[key] = value;
|
|
2448
|
+
}
|
|
2449
|
+
span.setAttributes(merged);
|
|
2450
|
+
}
|
|
2451
|
+
return span;
|
|
2452
|
+
} catch (error) {
|
|
2453
|
+
logger.error("Error masking span:", error);
|
|
2454
|
+
return span;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
// src/internal/trace/tracing.ts
|
|
2459
|
+
var TracingModule = class _TracingModule {
|
|
2460
|
+
static instance = null;
|
|
2461
|
+
spanExporter = null;
|
|
2462
|
+
spanProcessor = null;
|
|
2463
|
+
static getInstance() {
|
|
2464
|
+
if (!_TracingModule.instance) {
|
|
2465
|
+
throw new Error("Brizz must be initialized before accessing TracingModule");
|
|
2466
|
+
}
|
|
2467
|
+
return _TracingModule.instance;
|
|
2468
|
+
}
|
|
2469
|
+
/**
|
|
2470
|
+
* Initialize the tracing module with the provided configuration
|
|
2471
|
+
*/
|
|
2472
|
+
setup(config) {
|
|
2473
|
+
logger.info("Setting up tracing module");
|
|
2474
|
+
this.initSpanExporter(config);
|
|
2475
|
+
this.initSpanProcessor(config);
|
|
2476
|
+
_TracingModule.instance = this;
|
|
2477
|
+
logger.info("Tracing module setup completed");
|
|
2478
|
+
}
|
|
2479
|
+
/**
|
|
2480
|
+
* Initialize the span exporter
|
|
2481
|
+
*/
|
|
2482
|
+
initSpanExporter(config) {
|
|
2483
|
+
if (this.spanExporter) {
|
|
2484
|
+
logger.debug("Span exporter already initialized, skipping re-initialization");
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
if (config.customSpanExporter) {
|
|
2488
|
+
logger.debug("Using custom span exporter");
|
|
2489
|
+
this.spanExporter = config.customSpanExporter;
|
|
2490
|
+
logger.debug("Custom span exporter initialized successfully");
|
|
2491
|
+
return;
|
|
2492
|
+
}
|
|
2493
|
+
const tracesUrl = config.baseUrl.replace(/\/$/, "") + "/v1/traces";
|
|
2494
|
+
logger.debug("Initializing default OTLP span exporter", { url: tracesUrl });
|
|
2495
|
+
this.spanExporter = new OTLPTraceExporter({
|
|
2496
|
+
url: tracesUrl,
|
|
2497
|
+
headers: config.headers
|
|
2498
|
+
});
|
|
2499
|
+
logger.debug("OTLP span exporter initialized successfully");
|
|
2500
|
+
}
|
|
2501
|
+
/**
|
|
2502
|
+
* Initialize the span processor with masking support
|
|
2503
|
+
*/
|
|
2504
|
+
initSpanProcessor(config) {
|
|
2505
|
+
if (this.spanProcessor) {
|
|
2506
|
+
logger.debug("Span processor already initialized, skipping re-initialization");
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
if (!this.spanExporter) {
|
|
2510
|
+
throw new Error("Span exporter must be initialized before processor");
|
|
2511
|
+
}
|
|
2512
|
+
logger.debug("Initializing span processor", {
|
|
2513
|
+
disableBatch: config.disableBatch,
|
|
2514
|
+
hasMasking: !!config.masking?.spanMasking
|
|
2515
|
+
});
|
|
2516
|
+
this.spanProcessor = config.disableBatch ? new BrizzSimpleSpanProcessor(this.spanExporter, config) : new BrizzBatchSpanProcessor(this.spanExporter, config);
|
|
2517
|
+
logger.debug("Span processor initialized successfully");
|
|
2518
|
+
}
|
|
2519
|
+
/**
|
|
2520
|
+
* Get the span exporter
|
|
2521
|
+
*/
|
|
2522
|
+
getSpanExporter() {
|
|
2523
|
+
if (!this.spanExporter) {
|
|
2524
|
+
throw new Error("Tracing module not initialized");
|
|
2525
|
+
}
|
|
2526
|
+
return this.spanExporter;
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Get the span processor
|
|
2530
|
+
*/
|
|
2531
|
+
getSpanProcessor() {
|
|
2532
|
+
if (!this.spanProcessor) {
|
|
2533
|
+
throw new Error("Tracing module not initialized");
|
|
2534
|
+
}
|
|
2535
|
+
return this.spanProcessor;
|
|
2536
|
+
}
|
|
2537
|
+
async shutdown() {
|
|
2538
|
+
logger.debug("Shutting down tracing module");
|
|
2539
|
+
if (this.spanProcessor) {
|
|
2540
|
+
await this.spanProcessor.shutdown();
|
|
2541
|
+
this.spanProcessor = null;
|
|
2542
|
+
}
|
|
2543
|
+
if (this.spanExporter) {
|
|
2544
|
+
await this.spanExporter.shutdown();
|
|
2545
|
+
this.spanExporter = null;
|
|
2546
|
+
}
|
|
2547
|
+
_TracingModule.instance = null;
|
|
2548
|
+
logger.debug("Tracing module shutdown completed");
|
|
2549
|
+
}
|
|
2550
|
+
};
|
|
2551
|
+
function getSpanExporter() {
|
|
2552
|
+
return TracingModule.getInstance().getSpanExporter();
|
|
2553
|
+
}
|
|
2554
|
+
function getSpanProcessor() {
|
|
2555
|
+
return TracingModule.getInstance().getSpanProcessor();
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// src/internal/trace/session.ts
|
|
2559
|
+
import { context as context7 } from "@opentelemetry/api";
|
|
2560
|
+
function withProperties(properties, fn, thisArg, ...args) {
|
|
2561
|
+
if (Object.keys(properties).length === 0) {
|
|
2562
|
+
return fn.apply(thisArg, args);
|
|
2563
|
+
}
|
|
2564
|
+
const newContext = context7.active().setValue(PROPERTIES_CONTEXT_KEY, properties);
|
|
2565
|
+
return context7.with(newContext, fn, thisArg, ...args);
|
|
2566
|
+
}
|
|
2567
|
+
function WithSessionId(sessionId, fn, thisArg, ...args) {
|
|
2568
|
+
return withProperties({ [SESSION_ID]: sessionId }, fn, thisArg, ...args);
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
// src/internal/sdk.ts
|
|
2572
|
+
var _Brizz = class __Brizz {
|
|
2573
|
+
static instance = null;
|
|
2574
|
+
/** Flag indicating if SDK initialization has completed successfully */
|
|
2575
|
+
_initialized = false;
|
|
2576
|
+
/** OpenTelemetry sdk instance */
|
|
2577
|
+
_sdk = null;
|
|
2578
|
+
static getInstance() {
|
|
2579
|
+
if (!__Brizz.instance) {
|
|
2580
|
+
throw new Error("Brizz must be initialized before accessing TracingModule");
|
|
2581
|
+
}
|
|
2582
|
+
return __Brizz.instance;
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* Initialize the Brizz SDK with the provided configuration.
|
|
2586
|
+
*
|
|
2587
|
+
* @param {IBrizzInitializeOptions} options - Configuration options for the SDK
|
|
2588
|
+
* @throws {Error} - If initialization fails
|
|
2589
|
+
*
|
|
2590
|
+
* @example
|
|
2591
|
+
* ```typescript
|
|
2592
|
+
* Brizz.initialize({
|
|
2593
|
+
* appName: 'my-app',
|
|
2594
|
+
* baseUrl: 'http://localhost:4318',
|
|
2595
|
+
* apiKey: 'your-api-key'
|
|
2596
|
+
* });
|
|
2597
|
+
* ```
|
|
2598
|
+
*/
|
|
2599
|
+
initialize(options) {
|
|
2600
|
+
if (this._initialized) {
|
|
2601
|
+
logger.warn("Brizz SDK already initialized");
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
logger.info("Starting Brizz SDK initialization");
|
|
2605
|
+
try {
|
|
2606
|
+
const resolvedConfig = resolveConfig(options);
|
|
2607
|
+
this.initializeModules(resolvedConfig);
|
|
2608
|
+
this.setupInstrumentation(options);
|
|
2609
|
+
this.createAndStartNodeSDK(options, resolvedConfig);
|
|
2610
|
+
this._initialized = true;
|
|
2611
|
+
logger.info("Brizz SDK initialization completed successfully", {
|
|
2612
|
+
moduleSystem: this.detectModuleSystem()
|
|
2613
|
+
});
|
|
2614
|
+
} catch (error) {
|
|
2615
|
+
logger.error("Failed to initialize Brizz SDK", { error });
|
|
2616
|
+
throw new Error(`Failed to initialize SDK: ${String(error)}`);
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Set up instrumentation registry and configure manual modules.
|
|
2621
|
+
* @private
|
|
2622
|
+
*/
|
|
2623
|
+
setupInstrumentation(options) {
|
|
2624
|
+
const registry = InstrumentationRegistry.getInstance();
|
|
2625
|
+
if (options.instrumentModules && Object.keys(options.instrumentModules).length > 0) {
|
|
2626
|
+
registry.setManualModules(options.instrumentModules);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Create and start NodeSDK if not disabled.
|
|
2631
|
+
* Only handles manual instrumentations now - auto instrumentations are loaded at import time.
|
|
2632
|
+
* @private
|
|
2633
|
+
*/
|
|
2634
|
+
createAndStartNodeSDK(options, resolvedConfig) {
|
|
2635
|
+
if (options.disableNodeSdk) {
|
|
2636
|
+
logger.info("NodeSDK disabled - only telemetry modules initialized");
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
const registry = InstrumentationRegistry.getInstance();
|
|
2640
|
+
const manualInstrumentations = registry.getManualInstrumentations();
|
|
2641
|
+
this._sdk = new NodeSDK({
|
|
2642
|
+
spanProcessors: [getSpanProcessor()],
|
|
2643
|
+
metricReader: getMetricsReader(),
|
|
2644
|
+
resource: resourceFromAttributes2({
|
|
2645
|
+
"service.name": resolvedConfig.appName
|
|
2646
|
+
}),
|
|
2647
|
+
instrumentations: manualInstrumentations
|
|
2648
|
+
});
|
|
2649
|
+
this._sdk.start();
|
|
2650
|
+
if (manualInstrumentations.length > 0) {
|
|
2651
|
+
logger.info(`NodeSDK started with ${manualInstrumentations.length} manual instrumentations`);
|
|
2652
|
+
} else {
|
|
2653
|
+
logger.info("NodeSDK started - using auto-instrumentations loaded at import time");
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Initialize telemetry modules.
|
|
2658
|
+
*
|
|
2659
|
+
* Sets up the tracing, metrics, and logging modules with their
|
|
2660
|
+
* respective exporters and processors.
|
|
2661
|
+
*
|
|
2662
|
+
* @param {IResolvedBrizzConfig} config - Resolved configuration
|
|
2663
|
+
* @private
|
|
2664
|
+
*/
|
|
2665
|
+
initializeModules(config) {
|
|
2666
|
+
logger.info("Initializing telemetry modules");
|
|
2667
|
+
logger.debug("Initializing tracing module");
|
|
2668
|
+
const tracingModule = new TracingModule();
|
|
2669
|
+
tracingModule.setup(config);
|
|
2670
|
+
logger.debug("Initializing metrics module");
|
|
2671
|
+
const metricsModule = new MetricsModule();
|
|
2672
|
+
metricsModule.setup(config);
|
|
2673
|
+
logger.debug("Initializing logging module");
|
|
2674
|
+
const loggingModule = new LoggingModule();
|
|
2675
|
+
loggingModule.setup(config);
|
|
2676
|
+
logger.info("All telemetry modules initialized successfully");
|
|
2677
|
+
}
|
|
2678
|
+
/**
|
|
2679
|
+
* Detect the current module system (ESM or CJS) using reliable indicators
|
|
2680
|
+
*
|
|
2681
|
+
* @returns {string} - 'ESM' or 'CJS'
|
|
2682
|
+
* @private
|
|
2683
|
+
*/
|
|
2684
|
+
detectModuleSystem() {
|
|
2685
|
+
const inCJS = typeof module !== "undefined" && typeof exports !== "undefined" && typeof __require === "function" && typeof __filename === "string" && typeof __dirname === "string" && this === (typeof exports !== "undefined" ? exports : void 0);
|
|
2686
|
+
return inCJS ? "CJS" : "ESM";
|
|
2687
|
+
}
|
|
2688
|
+
/**
|
|
2689
|
+
* Gracefully shutdown the Brizz SDK.
|
|
2690
|
+
*
|
|
2691
|
+
* This method stops all telemetry collection, flushes any pending data,
|
|
2692
|
+
* and releases resources. Should be called before application termination.
|
|
2693
|
+
*
|
|
2694
|
+
* @returns {Promise<void>} - Resolves when shutdown is complete
|
|
2695
|
+
* @throws {Error} - If shutdown fails
|
|
2696
|
+
*
|
|
2697
|
+
* @example
|
|
2698
|
+
* ```typescript
|
|
2699
|
+
* // Shutdown before app exit
|
|
2700
|
+
* await Brizz.shutdown();
|
|
2701
|
+
* ```
|
|
2702
|
+
*/
|
|
2703
|
+
async shutdown() {
|
|
2704
|
+
if (!this._initialized) {
|
|
2705
|
+
logger.warn("Brizz SDK not initialized");
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
try {
|
|
2709
|
+
await this.shutdownModules();
|
|
2710
|
+
await this._sdk?.shutdown();
|
|
2711
|
+
this._initialized = false;
|
|
2712
|
+
this._sdk = null;
|
|
2713
|
+
__Brizz.instance = null;
|
|
2714
|
+
logger.info("Brizz SDK shut down successfully");
|
|
2715
|
+
} catch (error) {
|
|
2716
|
+
if (error instanceof Error && error.message.includes("ENOTFOUND")) {
|
|
2717
|
+
logger.debug(`Network error during shutdown (expected in tests): ${error.message}`);
|
|
2718
|
+
} else {
|
|
2719
|
+
logger.error(`Failed to shutdown Brizz SDK: ${String(error)}`);
|
|
2720
|
+
throw error;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* Shutdown all telemetry modules.
|
|
2726
|
+
* @private
|
|
2727
|
+
*/
|
|
2728
|
+
async shutdownModules() {
|
|
2729
|
+
logger.info("Shutting down telemetry modules");
|
|
2730
|
+
try {
|
|
2731
|
+
try {
|
|
2732
|
+
const tracingModule = TracingModule.getInstance();
|
|
2733
|
+
await tracingModule.shutdown();
|
|
2734
|
+
} catch {
|
|
2735
|
+
logger.debug("Tracing module already shut down or not initialized");
|
|
2736
|
+
}
|
|
2737
|
+
try {
|
|
2738
|
+
const metricsModule = MetricsModule.getInstance();
|
|
2739
|
+
metricsModule.shutdown();
|
|
2740
|
+
} catch {
|
|
2741
|
+
logger.debug("Metrics module already shut down or not initialized");
|
|
2742
|
+
}
|
|
2743
|
+
try {
|
|
2744
|
+
const loggingModule = LoggingModule.getInstance();
|
|
2745
|
+
await loggingModule.shutdown();
|
|
2746
|
+
} catch {
|
|
2747
|
+
logger.debug("Logging module already shut down or not initialized");
|
|
2748
|
+
}
|
|
2749
|
+
logger.info("All telemetry modules shut down successfully");
|
|
2750
|
+
} catch (error) {
|
|
2751
|
+
logger.error("Error shutting down modules", { error });
|
|
2752
|
+
throw error;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
};
|
|
2756
|
+
var Brizz = new _Brizz();
|
|
2757
|
+
|
|
2758
|
+
// src/index.ts
|
|
2759
|
+
import { SeverityNumber as SeverityNumber3 } from "@opentelemetry/api-logs";
|
|
2760
|
+
|
|
2761
|
+
// src/node/runtime.ts
|
|
2762
|
+
function detectRuntime() {
|
|
2763
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
2764
|
+
const noNodeJSGlobals = typeof __filename === "undefined" && typeof __dirname === "undefined";
|
|
2765
|
+
const noModuleSystem = typeof module === "undefined" && typeof exports === "undefined";
|
|
2766
|
+
const hasRequire = typeof __require === "function";
|
|
2767
|
+
const isESM = noNodeJSGlobals && noModuleSystem;
|
|
2768
|
+
const isCJS = hasRequire && typeof module !== "undefined" && typeof exports !== "undefined" && typeof __filename === "string" && typeof __dirname === "string";
|
|
2769
|
+
const supportsLoaderAPI = (major ?? 0) >= 21 || (major ?? 0) === 20 && (minor ?? 0) >= 6 || (major ?? 0) === 18 && (minor ?? 0) >= 19;
|
|
2770
|
+
const supportsRegister = !!process.features?.typescript || !!globalThis.module?.register;
|
|
2771
|
+
logger.debug("Runtime detection results:", {
|
|
2772
|
+
nodeVersion: `${major ?? 0}.${minor ?? 0}`,
|
|
2773
|
+
isESM,
|
|
2774
|
+
isCJS,
|
|
2775
|
+
supportsLoaderAPI,
|
|
2776
|
+
supportsRegister,
|
|
2777
|
+
detectionLogic: {
|
|
2778
|
+
noNodeJSGlobals,
|
|
2779
|
+
noModuleSystem,
|
|
2780
|
+
hasRequire,
|
|
2781
|
+
esmCondition: `${noNodeJSGlobals} && ${noModuleSystem} = ${isESM}`,
|
|
2782
|
+
cjsCondition: `require && module && exports && __filename && __dirname = ${isCJS}`
|
|
2783
|
+
},
|
|
2784
|
+
checks: {
|
|
2785
|
+
__filename: typeof __filename,
|
|
2786
|
+
__dirname: typeof __dirname,
|
|
2787
|
+
require: typeof __require,
|
|
2788
|
+
module: typeof module,
|
|
2789
|
+
exports: typeof exports,
|
|
2790
|
+
"process.features": process.features,
|
|
2791
|
+
"globalThis.module": globalThis.module
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2794
|
+
return {
|
|
2795
|
+
isESM,
|
|
2796
|
+
isCJS,
|
|
2797
|
+
nodeVersion: process.versions.node,
|
|
2798
|
+
supportsLoaderAPI,
|
|
2799
|
+
supportsRegister
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
// src/node/loader.ts
|
|
2804
|
+
import module2 from "module";
|
|
2805
|
+
import { createAddHookMessageChannel } from "import-in-the-middle";
|
|
2806
|
+
var loaderDebug = (() => {
|
|
2807
|
+
const isDebug = process.env["BRIZZ_ESM_DEBUG"] === "true" || process.env["BRIZZ_LOG_LEVEL"] === "debug";
|
|
2808
|
+
return {
|
|
2809
|
+
log: (msg, data) => {
|
|
2810
|
+
if (!isDebug) {
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2814
|
+
const dataStr = data ? ` ${JSON.stringify(data)}` : "";
|
|
2815
|
+
console.log(`[${timestamp}] [ESM-LOADER] ${msg}${dataStr}`);
|
|
2816
|
+
},
|
|
2817
|
+
error: (msg, error) => {
|
|
2818
|
+
if (!isDebug) {
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2822
|
+
const errorStr = error instanceof Error ? ` ${error.message}` : error ? ` ${JSON.stringify(error)}` : "";
|
|
2823
|
+
console.error(`[${timestamp}] [ESM-LOADER] ERROR: ${msg}${errorStr}`);
|
|
2824
|
+
}
|
|
2825
|
+
};
|
|
2826
|
+
})();
|
|
2827
|
+
var LOADER_REGISTERED_KEY = Symbol.for("__brizz_esm_loader_registered__");
|
|
2828
|
+
function checkLoaderAPISupport() {
|
|
2829
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
2830
|
+
if (major === void 0 || minor === void 0) {
|
|
2831
|
+
loaderDebug.log("Failed to detect Node version, assuming loader API is supported");
|
|
2832
|
+
return true;
|
|
2833
|
+
}
|
|
2834
|
+
const supported = major >= 21 || major === 20 && minor >= 6 || major === 18 && minor >= 19;
|
|
2835
|
+
loaderDebug.log("Loader API support check:", {
|
|
2836
|
+
nodeVersion: `${major}.${minor}`,
|
|
2837
|
+
supportsLoaderAPI: supported
|
|
2838
|
+
});
|
|
2839
|
+
return supported;
|
|
2840
|
+
}
|
|
2841
|
+
function maybeRegisterESMLoader() {
|
|
2842
|
+
if (globalThis[LOADER_REGISTERED_KEY] === true) {
|
|
2843
|
+
loaderDebug.log("ESM loader already registered, skipping");
|
|
2844
|
+
return false;
|
|
2845
|
+
}
|
|
2846
|
+
globalThis[LOADER_REGISTERED_KEY] = true;
|
|
2847
|
+
loaderDebug.log("Starting ESM loader registration...");
|
|
2848
|
+
if (!checkLoaderAPISupport()) {
|
|
2849
|
+
loaderDebug.log("Node.js version does not support loader API, skipping");
|
|
2850
|
+
return false;
|
|
2851
|
+
}
|
|
2852
|
+
try {
|
|
2853
|
+
loaderDebug.log("Creating MessageChannel for import-in-the-middle...");
|
|
2854
|
+
const { addHookMessagePort } = createAddHookMessageChannel();
|
|
2855
|
+
loaderDebug.log("Registering import-in-the-middle/hook.mjs...");
|
|
2856
|
+
module2.register("import-in-the-middle/hook.mjs", {
|
|
2857
|
+
data: { addHookMessagePort },
|
|
2858
|
+
transferList: [addHookMessagePort]
|
|
2859
|
+
});
|
|
2860
|
+
loaderDebug.log("ESM loader successfully registered!");
|
|
2861
|
+
return true;
|
|
2862
|
+
} catch (error) {
|
|
2863
|
+
loaderDebug.error("Failed to register ESM loader:", error);
|
|
2864
|
+
globalThis[LOADER_REGISTERED_KEY] = false;
|
|
2865
|
+
return false;
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
if (globalThis[LOADER_REGISTERED_KEY] !== true) {
|
|
2869
|
+
loaderDebug.log("Loader module imported, attempting to register...");
|
|
2870
|
+
maybeRegisterESMLoader();
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
// src/init.ts
|
|
2874
|
+
var init_exports = {};
|
|
2875
|
+
export {
|
|
2876
|
+
Brizz,
|
|
2877
|
+
DEFAULT_PII_PATTERNS,
|
|
2878
|
+
LogLevel,
|
|
2879
|
+
SeverityNumber3 as SeverityNumber,
|
|
2880
|
+
VercelAIInstrumentation,
|
|
2881
|
+
WithSessionId,
|
|
2882
|
+
detectRuntime,
|
|
2883
|
+
emitEvent,
|
|
2884
|
+
getLogLevel,
|
|
2885
|
+
getMetricsExporter,
|
|
2886
|
+
getMetricsReader,
|
|
2887
|
+
getSpanExporter,
|
|
2888
|
+
getSpanProcessor,
|
|
2889
|
+
init_exports as init,
|
|
2890
|
+
logger,
|
|
2891
|
+
maskAttributes,
|
|
2892
|
+
maskValue,
|
|
2893
|
+
maybeRegisterESMLoader,
|
|
2894
|
+
setLogLevel
|
|
2895
|
+
};
|
|
10
2896
|
//# sourceMappingURL=index.js.map
|