UNPKG

@brizz/sdk

Version:

OpenTelemetry-based observability SDK for AI applications

1,488 lines (1,472 loc) 67 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/internal/logger.ts var import_api = require("@opentelemetry/api"); var import_pino = __toESM(require("pino"), 1); var DEFAULT_LOG_LEVEL = 2 /* WARN */; var PinoLogger = class { _level = DEFAULT_LOG_LEVEL; _pinoLogger = null; constructor() { const envLevel = this.getLogLevelFromEnv(); this._level = envLevel; } /** * Lazy initialization of Pino logger to ensure it's created AFTER Jest spies * are set up during tests. This prevents the pino-pretty transport from * bypassing stdout/stderr spies. */ ensurePinoLogger() { if (!this._pinoLogger) { this._pinoLogger = (0, import_pino.default)({ name: "Brizz", level: this.logLevelToPino(this._level), // Disable transport in test environment to allow proper spy capture transport: this.isProduction() || this.isTest() ? void 0 : { target: "pino-pretty", options: { singleLine: true, colorize: true, translateTime: "HH:MM:ss", ignore: "pid,hostname", messageFormat: "[{name}] {msg}" } } }); } return this._pinoLogger; } isProduction() { return process.env["NODE_ENV"] === "production"; } isTest() { return process.env["NODE_ENV"] === "test"; } getLogLevelFromEnv() { const envLevel = process.env["BRIZZ_LOG_LEVEL"]; return envLevel ? parseLogLevel(envLevel) : DEFAULT_LOG_LEVEL; } logLevelToPino(level) { switch (level) { case 4 /* DEBUG */: return "debug"; case 3 /* INFO */: return "info"; case 2 /* WARN */: return "warn"; case 1 /* ERROR */: return "error"; default: return "silent"; } } formatMeta(meta) { if (meta.length === 0) { return {}; } if (meta.length === 1 && typeof meta[0] === "object" && meta[0] !== null) { return meta[0]; } return { metadata: meta }; } setLevel(level) { this._level = level; if (this._pinoLogger) { this._pinoLogger.level = this.logLevelToPino(level); } } getLevel() { return this._level; } debug = (msg, ...meta) => { if (this._level >= 4 /* DEBUG */) { this.ensurePinoLogger().debug(this.formatMeta(meta), msg); } }; info = (msg, ...meta) => { if (this._level >= 3 /* INFO */) { this.ensurePinoLogger().info(this.formatMeta(meta), msg); } }; warn = (msg, ...meta) => { if (this._level >= 2 /* WARN */) { this.ensurePinoLogger().warn(this.formatMeta(meta), msg); } }; error = (msg, ...meta) => { if (this._level >= 1 /* ERROR */) { this.ensurePinoLogger().error(this.formatMeta(meta), msg); } }; }; function parseLogLevel(level) { if (!level) { return DEFAULT_LOG_LEVEL; } const normalizedLevel = level.toLowerCase().trim(); switch (normalizedLevel) { case "debug": return 4 /* DEBUG */; case "info": return 3 /* INFO */; case "warn": case "warning": return 2 /* WARN */; case "error": return 1 /* ERROR */; case "none": case "off": case "silent": return 0 /* NONE */; default: { const numLevel = Number.parseInt(normalizedLevel, 10); if (!Number.isNaN(numLevel) && numLevel >= 0 && numLevel <= 4) { return numLevel; } return DEFAULT_LOG_LEVEL; } } } var logger = new PinoLogger(); function setLogLevel(level) { const resolvedLevel = typeof level === "string" ? parseLogLevel(level) : level; logger.setLevel(resolvedLevel); } // src/internal/sdk.ts var import_resources2 = require("@opentelemetry/resources"); var import_sdk_node = require("@opentelemetry/sdk-node"); // src/internal/config.ts function resolveConfig(options) { const envLogLevel = process.env["BRIZZ_LOG_LEVEL"] || options.logLevel?.toString() || DEFAULT_LOG_LEVEL.toString(); let resolvedLogLevel; if (envLogLevel) { resolvedLogLevel = parseLogLevel(envLogLevel); } else if (typeof options.logLevel === "string") { resolvedLogLevel = parseLogLevel(options.logLevel); } else { resolvedLogLevel = options.logLevel || DEFAULT_LOG_LEVEL; } setLogLevel(resolvedLogLevel); let maskingStatus; if (options.masking === true) { maskingStatus = "enabled"; } else if (options.masking === false) { maskingStatus = "disabled"; } else if (typeof options.masking === "object") { maskingStatus = "custom"; } else { maskingStatus = "default-disabled"; } logger.debug("Starting configuration resolution", { appName: options.appName, baseUrl: options.baseUrl, hasApiKey: !!options.apiKey, disableBatch: options.disableBatch, logLevel: resolvedLogLevel, headersCount: Object.keys(options.headers || {}).length, masking: maskingStatus }); let resolvedMasking; if (options.masking === true) { resolvedMasking = { spanMasking: {}, eventMasking: {} }; } else if (options.masking && typeof options.masking === "object") { resolvedMasking = options.masking; } const resolvedConfig = { ...options, appName: process.env["BRIZZ_APP_NAME"] || options.appName || "unknown-app", baseUrl: process.env["BRIZZ_BASE_URL"] || options.baseUrl || "https://telemetry.brizz.dev", headers: { ...options.headers }, apiKey: process.env["BRIZZ_API_KEY"] || options.apiKey, disableBatch: process.env["BRIZZ_DISABLE_BATCH"] === "true" || !!options.disableBatch, logLevel: resolvedLogLevel, masking: resolvedMasking }; if (resolvedConfig.apiKey) { resolvedConfig.headers["Authorization"] = `Bearer ${resolvedConfig.apiKey}`; } if (process.env["BRIZZ_HEADERS"]) { try { const envHeaders = JSON.parse(process.env["BRIZZ_HEADERS"]); Object.assign(resolvedConfig.headers, envHeaders); logger.debug("Added headers from environment variable", { headers: Object.keys(envHeaders) }); } catch (error) { logger.error("Failed to parse BRIZZ_HEADERS environment variable", { error }); throw new Error("Invalid JSON in BRIZZ_HEADERS environment variable"); } } logger.debug("Configuration resolved with environment variables", { appName: resolvedConfig.appName, baseUrl: resolvedConfig.baseUrl, hasApiKey: !!resolvedConfig.apiKey, disableBatch: resolvedConfig.disableBatch, envOverrides: { hasEnvApiKey: !!process.env["BRIZZ_API_KEY"], hasEnvBaseUrl: !!process.env["BRIZZ_BASE_URL"], hasEnvBatch: !!process.env["BRIZZ_DISABLE_BATCH"], hasEnvHeaders: !!process.env["BRIZZ_HEADERS"] } }); return resolvedConfig; } // src/internal/instrumentation/registry.ts var import_instrumentation_anthropic = require("@traceloop/instrumentation-anthropic"); var import_instrumentation_bedrock = require("@traceloop/instrumentation-bedrock"); var import_instrumentation_chromadb = require("@traceloop/instrumentation-chromadb"); var import_instrumentation_cohere = require("@traceloop/instrumentation-cohere"); var import_instrumentation_langchain = require("@traceloop/instrumentation-langchain"); var import_instrumentation_llamaindex = require("@traceloop/instrumentation-llamaindex"); var import_instrumentation_openai = require("@traceloop/instrumentation-openai"); var import_instrumentation_pinecone = require("@traceloop/instrumentation-pinecone"); var import_instrumentation_qdrant = require("@traceloop/instrumentation-qdrant"); var import_instrumentation_together = require("@traceloop/instrumentation-together"); var import_instrumentation_vertexai = require("@traceloop/instrumentation-vertexai"); var InstrumentationRegistry = class _InstrumentationRegistry { static instance; manualModules = null; static getInstance() { if (!_InstrumentationRegistry.instance) { _InstrumentationRegistry.instance = new _InstrumentationRegistry(); } return _InstrumentationRegistry.instance; } /** * Set manual instrumentation modules for Next.js/Webpack environments */ setManualModules(modules) { this.manualModules = modules; logger.info("Manual instrumentation modules configured for Next.js/Webpack compatibility"); } /** * Helper to load instrumentation with optional manual module */ loadInstrumentation(InstrumentationClass, name, manualModule, exceptionLogger) { try { const inst = new InstrumentationClass({ exceptionLogger: exceptionLogger || ((error) => logger.error(`Exception in instrumentation: ${String(error)}`)) }); if (manualModule) { inst.manuallyInstrument(manualModule); logger.debug(`Manual instrumentation enabled for ${name}`); } return inst; } catch (error) { logger.error(`Failed to load ${name} instrumentation: ${String(error)}`); return null; } } /** * Get manual instrumentations only. * Auto-instrumentations are handled at import time. */ getManualInstrumentations() { if (!this.manualModules || Object.keys(this.manualModules).length === 0) { logger.debug("No manual instrumentation modules configured"); return []; } const instrumentations = []; const exceptionLogger = (error) => { logger.error(`Exception in manual instrumentation: ${String(error)}`); }; this.loadManualInstrumentations(instrumentations, exceptionLogger); logger.info(`Loaded ${instrumentations.length} manual instrumentations`); return instrumentations; } /** * Load manual instrumentations only (with modules provided by user). * @private */ loadManualInstrumentations(instrumentations, exceptionLogger) { const instrumentationConfigs = [ { class: import_instrumentation_openai.OpenAIInstrumentation, name: "OpenAI", module: this.manualModules?.openAI }, { class: import_instrumentation_anthropic.AnthropicInstrumentation, name: "Anthropic", module: this.manualModules?.anthropic }, { class: import_instrumentation_cohere.CohereInstrumentation, name: "Cohere", module: this.manualModules?.cohere }, { class: import_instrumentation_vertexai.VertexAIInstrumentation, name: "Vertex AI", module: this.manualModules?.google_vertexai }, { class: import_instrumentation_bedrock.BedrockInstrumentation, name: "Bedrock", module: this.manualModules?.bedrock }, { class: import_instrumentation_pinecone.PineconeInstrumentation, name: "Pinecone", module: this.manualModules?.pinecone }, { class: import_instrumentation_langchain.LangChainInstrumentation, name: "LangChain", module: this.manualModules?.langchain }, { class: import_instrumentation_llamaindex.LlamaIndexInstrumentation, name: "LlamaIndex", module: this.manualModules?.llamaindex }, { class: import_instrumentation_chromadb.ChromaDBInstrumentation, name: "ChromaDB", module: this.manualModules?.chromadb }, { class: import_instrumentation_qdrant.QdrantInstrumentation, name: "Qdrant", module: this.manualModules?.qdrant }, { class: import_instrumentation_together.TogetherInstrumentation, name: "Together", module: this.manualModules?.together } ]; for (const config of instrumentationConfigs) { if (config.module) { const instrumentation = this.loadInstrumentation( config.class, config.name, config.module, exceptionLogger ); if (instrumentation) { instrumentations.push(instrumentation); } } } } }; // src/internal/log/logging.ts var import_api_logs = require("@opentelemetry/api-logs"); var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http"); var import_resources = require("@opentelemetry/resources"); var import_sdk_logs2 = require("@opentelemetry/sdk-logs"); // src/internal/log/processors/log-processor.ts var import_api3 = require("@opentelemetry/api"); var import_sdk_logs = require("@opentelemetry/sdk-logs"); // src/internal/masking/patterns.ts var DEFAULT_PII_PATTERNS = [ // Email addresses { name: "email_addresses", pattern: String.raw`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b` }, // Phone numbers (US format) { name: "us_phone_numbers", 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]|$)` }, // Social Security Numbers { name: "ssn", pattern: String.raw`\b(?!000|666|9\d{2})\d{3}[-\s]?(?!00)\d{2}[-\s]?(?!0000)\d{4}\b` }, // Credit card numbers { name: "credit_cards", 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` }, { name: "credit_cards_with_separators", 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` }, // IP addresses (IPv4) { name: "ipv4_addresses", 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` }, // API keys/tokens { name: "generic_api_keys", 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` }, { name: "openai_keys", pattern: String.raw`\bsk[-_][a-zA-Z0-9]{20,}\b` }, { name: "base64_secrets", pattern: String.raw`\b[A-Za-z0-9+/]{64,}={0,2}\b` }, // AWS Keys { name: "aws_access_keys", pattern: String.raw`\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\b` }, { name: "aws_secret_keys", 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` }, // GitHub tokens { name: "github_tokens", pattern: String.raw`\bghp_[a-zA-Z0-9]{36}\b` }, // Slack tokens { name: "slack_tokens", pattern: String.raw`\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}\b` }, // Stripe keys { name: "stripe_keys", pattern: String.raw`\b(?:sk|pk)_(?:test|live)_[a-zA-Z0-9]{24,}\b` }, // JWT tokens { name: "jwt_tokens", pattern: String.raw`\beyJ[A-Za-z0-9_-]{2,}\\.[A-Za-z0-9_-]{2,}\\.[A-Za-z0-9_-]{2,}\b` }, // MAC addresses { name: "mac_addresses", pattern: String.raw`\b(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\b` }, // IPv6 addresses { name: "ipv6_addresses", pattern: String.raw`\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b` }, // Medical records { name: "medical_record_numbers", pattern: String.raw`\b(?:[Mm][Rr][Nn])[-\s]?\d{6,10}\b` }, // Bitcoin addresses { name: "bitcoin_addresses", pattern: String.raw`\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b` }, // Ethereum addresses { name: "ethereum_addresses", pattern: String.raw`\b0x[a-fA-F0-9]{40}(?![a-fA-F0-9])\b` }, // UUIDs { name: "uuids", 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` }, // Database connection strings { name: "database_connections", 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` }, // Private keys { name: "rsa_private_keys", pattern: "-----BEGIN (?:RSA )?PRIVATE KEY-----" }, { name: "pgp_private_keys", pattern: "-----BEGIN PGP PRIVATE KEY BLOCK-----" }, { name: "certificates", pattern: "-----BEGIN CERTIFICATE-----" }, // Date of birth patterns { name: "date_of_birth_us", pattern: String.raw`\b(?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12]\\d|3[01])[-/](?:19|20)\\d{2}\b` }, { name: "date_of_birth_iso", pattern: String.raw`\b(?:19|20)\\d{2}[-/](?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12]\\d|3[01])\b` }, // US Identification Numbers { name: "us_passport_numbers", pattern: String.raw`\b[A-Z]?\\d{6,9}\b` }, { name: "drivers_license", 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` }, { name: "bank_account_numbers", pattern: String.raw`\b\\d{10,17}\b` }, { name: "aba_routing_numbers", 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` }, { name: "health_insurance_numbers", pattern: String.raw`\b\\d{10}[A-Z]\b` }, { name: "employee_ids", pattern: String.raw`\b(?:[Ee][Mm][Pp]|[Ee][Mm][Pp][Ll][Oo][Yy][Ee][Ee]|[Ee])[-\s]?\\d{5,8}\b` }, { name: "tax_ein", pattern: String.raw`\b\\d{2}-\\d{7}\b` }, { name: "medicare_beneficiary_id", 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` }, { name: "national_provider_id", pattern: String.raw`\b1\\d{9}\b` }, { name: "dea_numbers", pattern: String.raw`\b[A-Z]{2}\\d{7}\b` }, { name: "itin", pattern: String.raw`\b9\\d{2}(?:[ \\-]?)[7,8]\\d(?:[ \\-]?)\\d{4}\b` }, // Vehicle and Location { name: "vin_numbers", pattern: String.raw`\b[A-HJ-NPR-Z0-9]{17}\b` }, { name: "coordinates", pattern: String.raw`\b[-+]?(?:[0-8]?\\d(?:\\.\\d+)?|90(?:\\.0+)?),\\s*[-+]?(?:1[0-7]\\d(?:\\.\\d+)?|180(?:\\.0+)?|[0-9]?\\d(?:\\.\\d+)?)\b` }, { name: "us_license_plates", 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` }, { name: "us_zip_codes", pattern: String.raw`\b(\\d{5}-\\d{4}|\\d{5})\b` }, { name: "us_street_addresses", 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` }, // International Banking { name: "iban", pattern: String.raw`\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b` }, { name: "swift_bic", pattern: String.raw`\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?\b` }, // Additional API Keys and Tokens { name: "google_oauth", pattern: String.raw`\bya29\\.[a-zA-Z0-9_-]{60,}\b` }, { name: "firebase_tokens", pattern: String.raw`\bAAAA[A-Za-z0-9_-]{100,}\b` }, { name: "google_app_ids", pattern: String.raw`\b[0-9]+-\w+\.apps\.googleusercontent\.com\b` }, { name: "facebook_secrets", 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]` }, { name: "github_keys", 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]` }, { name: "heroku_keys", 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\\-]` }, { name: "slack_api_keys", 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]` }, { name: "slack_api_tokens", pattern: String.raw`\b(xox[pb](?:-[a-zA-Z0-9]+){4,})\b` }, // Extended Private Keys and Certificates { name: "dsa_private_keys", pattern: String.raw`-----BEGIN DSA PRIVATE KEY-----(?:[a-zA-Z0-9\+\=\/"']|\s)+?-----END DSA PRIVATE KEY-----` }, { name: "ec_private_keys", pattern: String.raw`-----BEGIN (?:EC|ECDSA) PRIVATE KEY-----(?:[a-zA-Z0-9\+\=\/"']|\s)+?-----END (?:EC|ECDSA) PRIVATE KEY-----` }, { name: "encrypted_private_keys", pattern: String.raw`-----BEGIN ENCRYPTED PRIVATE KEY-----(?:.|\s)+?-----END ENCRYPTED PRIVATE KEY-----` }, { name: "ssl_certificates", pattern: String.raw`-----BEGIN CERTIFICATE-----(?:.|\n)+?\s-----END CERTIFICATE-----` }, { name: "ssh_dss_public", pattern: String.raw`\bssh-dss [0-9A-Za-z+/]+[=]{2}\b` }, { name: "ssh_rsa_public", pattern: String.raw`\bssh-rsa AAAA[0-9A-Za-z+/]+[=]{0,3} [^@]+@[^@]+\b` }, { name: "putty_ssh_keys", pattern: String.raw`PuTTY-User-Key-File-2: ssh-(?:rsa|dss)\s*Encryption: none(?:.|\s?)*?Private-MAC:` }, // International Phone Numbers { name: "france_phone_numbers", pattern: String.raw`\b([0O]?[1lI][1lI])?[3E][3E][0O]?[\\dOIlZEASB]{9}\b` }, { name: "german_phone_numbers", pattern: String.raw`\b[\d\w]\d{2}[\d\w]{6}\d[\d\w]\b` }, { name: "uk_phone_numbers", pattern: String.raw`\b([0O]?[1lI][1lI])?[4A][4A][\\dOIlZEASB]{10,11}\b` }, // International IDs { name: "uk_drivers_license", pattern: String.raw`\b[A-Z]{5}\d{6}[A-Z]{2}\d{1}[A-Z]{2}\b` }, { name: "uk_passport", pattern: String.raw`\b\\d{10}GB[RP]\\d{7}[UMF]{1}\\d{9}\b` }, { name: "argentina_dni", pattern: String.raw`\b\\d{2}\\.\\d{3}\\.\\d{3}\b` }, { name: "australia_tfn", pattern: String.raw`\b[Tt][Ff][Nn](:|:\\s|\\s|)(\\d{8,9})\b` }, { name: "canada_passport", pattern: String.raw`\b[\\w]{2}[\\d]{6}\b` }, { name: "croatia_vat", pattern: String.raw`\bHR\\d{11}\b` }, { name: "czech_vat", pattern: String.raw`\bCZ\\d{8,10}\b` }, { name: "denmark_personal_id", pattern: String.raw`\b(?:\\d{10}|\\d{6}[-\\s]\\d{4})\b` }, { name: "france_national_id", pattern: String.raw`\b\\d{12}\b` }, { name: "france_ssn", pattern: String.raw`\b(?:\\d{13}|\\d{13}\\s\\d{2})\b` }, { name: "france_passport", pattern: String.raw`\b\\d{2}11\\d{5}\b` }, { name: "california_drivers_license", pattern: String.raw`\b[A-Z]{1}\\d{7}\b` }, // Medical and Healthcare { name: "hipaa_ndc", pattern: String.raw`\b\\d{4,5}-\\d{3,4}-\\d{1,2}\b` }, // Security and Network { name: "cve_numbers", pattern: String.raw`\b[Cc][Vv][Ee]-\\d{4}-\\d{4,7}\b` }, { name: "microsoft_oauth", pattern: String.raw`https://login\.microsoftonline\.com/common/oauth2/v2\.0/token|https://login\.windows\.net/common/oauth2/token` }, { name: "postgres_connections", pattern: String.raw`\b(?:[Pp][Oo][Ss][Tt][Gg][Rr][Ee][Ss]|[Pp][Gg][Ss][Qq][Ll])\\:\\/\\/` }, { name: "box_links", pattern: String.raw`https://app\.box\.com/[s|l]/\S+` }, { name: "dropbox_links", pattern: String.raw`https://www\.dropbox\.com/(?:s|l)/\S+` }, // Credit Card Variants { name: "amex_cards", pattern: String.raw`\b3[47][0-9]{13}\b` }, { name: "visa_cards", pattern: String.raw`\b4[0-9]{12}(?:[0-9]{3})?\b` }, { name: "discover_cards", pattern: String.raw`\b65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}\b` }, { name: "enhanced_credit_cards", 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` }, // Bank Routing Numbers (US specific) { name: "bbva_routing_ca", pattern: String.raw`\\b321170538\\b` }, { name: "boa_routing_ca", pattern: String.raw`\\b(?:121|026)00(?:0|9)(?:358|593)\\b` }, { name: "chase_routing_ca", pattern: String.raw`\\b322271627\\b` }, { name: "citibank_routing_ca", pattern: String.raw`\\b32(?:11|22)71(?:18|72)4\\b` }, { name: "usbank_routing_ca", pattern: String.raw`\\b12(?:1122676|2235821)\\b` }, { name: "united_bank_routing_ca", pattern: String.raw`\\b122243350\\b` }, { name: "wells_fargo_routing_ca", pattern: String.raw`\\b121042882\\b` }, // Unrealistic alphanumeric identifiers { name: "generic_non_usual", pattern: String.raw`(?:^|\s)(?=[A-Za-z0-9_\)\*\=@]*[A-Za-z])(?=[A-Za-z0-9_\)\*\=@]*[0-9])([A-Za-z0-9_\)\*\=@]{5,})(?=\s|$)` } ]; // src/internal/masking/utils.ts function isValidPatternName(name) { return /^[a-zA-Z0-9_]+$/.test(name); } function isLikelyReDoSPattern(pattern) { const dangerousPatterns = [ // Nested quantifiers like (a+)+, (a*)+, (a+)* /\([^)]*[+*]\)[+*]/, // Alternation with overlapping groups like (a|a)* /\([^)]*\|[^)]*\)[+*]/, // Complex backtracking patterns - but more specific /\([^)]*[+*][^)]*[+*][^)]*\)[+*]/ ]; return dangerousPatterns.some((dangerous) => dangerous.test(pattern)); } function getGroupedPattern(patternEntry) { let name = patternEntry.name; if (!name || name === "") { name = `pattern_${Math.random().toString(16).slice(2)}`; } if (!isValidPatternName(name)) { throw new Error( `Pattern name '${name}' must only contain alphanumeric characters and underscores` ); } if (isLikelyReDoSPattern(patternEntry.pattern)) { throw new Error(`Potentially dangerous ReDoS pattern detected: '${patternEntry.pattern}'`); } try { new RegExp(patternEntry.pattern); } catch (error) { throw new Error(`Invalid regex pattern '${patternEntry.pattern}': ${String(error)}`); } return `(?<${name}>${patternEntry.pattern})`; } function isIPatternEntry(obj) { return typeof obj === "object" && obj !== null && typeof obj.pattern === "string" && (obj.name === void 0 || typeof obj.name === "string"); } function isIEventMaskingRule(obj) { return typeof obj === "object" && obj !== null && typeof obj.mode === "string" && Array.isArray(obj.patterns) && obj.patterns.every( (p) => isIPatternEntry(p) || typeof p === "string" ) && (obj.attributePattern === void 0 || typeof obj.attributePattern === "string"); } function convertPatternsToPatternEntries(patterns) { const patternEntries = []; for (const pattern of patterns) { if (typeof pattern === "string") { patternEntries.push({ pattern, name: "" }); } else if (isIPatternEntry(pattern)) { patternEntries.push(pattern); } else { throw new Error("Patterns must be either strings or PatternEntry instances"); } } return patternEntries; } function compilePatternEntries(patternEntries) { const patternGroups = []; for (const patternEntry of patternEntries) { try { patternGroups.push(getGroupedPattern(patternEntry)); } catch (error) { logger.warn(`Invalid pattern '${patternEntry.name}': ${patternEntry.pattern}`, error); continue; } } if (patternGroups.length === 0) { return null; } try { return new RegExp(patternGroups.join("|")); } catch (error) { logger.warn("Failed to compile pattern entries into regex", error); return null; } } function getCompiledAttributeNamePattern(rule) { if (!rule.attributePattern) { logger.debug("No attribute pattern provided, using default .*"); return /.*/; } let compiledPatternString = rule.attributePattern; if (isIEventMaskingRule(rule) && rule.eventNamePattern) { compiledPatternString = `(${compiledPatternString})|(${rule.eventNamePattern})`; } return new RegExp(compiledPatternString); } function shouldContinueExecution(startTime, iterations, timeoutMs) { if (Date.now() - startTime > timeoutMs) { logger.warn("Regex execution timed out - potential ReDoS pattern detected"); return false; } const maxIterations = 1e3; if (iterations > maxIterations) { logger.warn("Regex execution exceeded maximum iterations - potential ReDoS pattern detected"); return false; } return true; } function createGlobalPattern(pattern) { return new RegExp( pattern.source, pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g" ); } function executeRegexWithTimeout(pattern, value, timeoutMs = 100) { const matches = []; const globalPattern = createGlobalPattern(pattern); const startTime = Date.now(); let match; let iterations = 0; while ((match = globalPattern.exec(value)) !== null) { if (!shouldContinueExecution(startTime, ++iterations, timeoutMs)) { break; } matches.push(match); if (match[0].length === 0) { globalPattern.lastIndex = match.index + 1; } } return matches; } function maskStringByPattern(value, pattern, mode = "full") { const matches = []; try { const regexMatches = executeRegexWithTimeout(pattern, value); for (const match of regexMatches) { matches.push({ start: match.index, end: match.index + match[0].length, text: match[0], groups: match.groups }); } } catch (error) { logger.warn("Regex execution failed, skipping masking", error); return value; } for (const matchInfo of matches.reverse()) { let patternName = "unknown"; if (matchInfo.groups) { for (const [groupName, groupValue] of Object.entries(matchInfo.groups)) { if (groupValue !== void 0) { if (groupName.includes("_") && /\d$/.test(groupName)) { const parts = groupName.split("_"); patternName = parts[0] || groupName; } else { patternName = groupName; } break; } } } logger.info(`Masking detected: pattern '${patternName}' found match in value`); const masked = mode === "partial" ? matchInfo.text[0] + "*****" : "*****"; value = value.slice(0, matchInfo.start) + masked + value.slice(matchInfo.end); } return value; } function maskStringByPatternBasedRule(value, rule) { const patternEntries = convertPatternsToPatternEntries(rule.patterns); if (patternEntries.length === 0) { logger.warn("No patterns provided for masking rule, returning original value"); return value; } const mode = rule.mode; if (!patternEntries || patternEntries.length === 0) { return mode === "partial" && value ? value[0] + "*****" : "*****"; } const compiledPatterns = compilePatternEntries(patternEntries); if (!compiledPatterns) { return value; } return maskStringByPattern(value, compiledPatterns, mode); } function maskValue(value, rule) { if (typeof value === "string") { return maskStringByPatternBasedRule(value, rule); } else if (typeof value === "boolean" || typeof value === "number") { return maskStringByPatternBasedRule(String(value), rule); } else if (Array.isArray(value)) { return value.map((v) => maskStringByPatternBasedRule(String(v), rule)); } else if (value !== null && typeof value === "object") { const result = {}; for (const [k, v] of Object.entries(value)) { result[k] = maskValue(v, rule); } return result; } else { throw new Error(`Unsupported value type for masking: ${typeof value}`); } } function maskAttributes(attributes, rules, outputOriginalValue = false) { if (!attributes || Object.keys(attributes).length === 0) { return {}; } const maskedAttributes = { ...attributes }; for (const rule of rules) { const compiledAttributeNamePattern = getCompiledAttributeNamePattern(rule); const attributesToMask = compiledAttributeNamePattern ? Object.keys(maskedAttributes).filter((attr) => compiledAttributeNamePattern.test(attr)) : Object.keys(maskedAttributes); for (const attribute of attributesToMask) { const value = maskedAttributes[attribute]; if (value === null || value === void 0) { continue; } if (outputOriginalValue) { logger.debug(`Masking attribute '${attribute}' with original value`, { value }); } maskedAttributes[attribute] = maskValue(value, rule); } } return maskedAttributes; } // src/internal/semantic-conventions.ts var import_api2 = require("@opentelemetry/api"); var BRIZZ = "brizz"; var PROPERTIES = "properties"; var PROPERTIES_CONTEXT_KEY = (0, import_api2.createContextKey)(PROPERTIES); // src/internal/log/processors/log-processor.ts var DEFAULT_LOG_MASKING_RULES = [ { mode: "partial", attributePattern: "event.name", patterns: DEFAULT_PII_PATTERNS } ]; var BrizzSimpleLogRecordProcessor = class extends import_sdk_logs.SimpleLogRecordProcessor { config; constructor(exporter, config) { super(exporter); this.config = config; } onEmit(logRecord) { const maskingConfig = this.config.masking?.eventMasking; if (maskingConfig) { maskLog(logRecord, maskingConfig); } const associationProperties = import_api3.context.active().getValue(PROPERTIES_CONTEXT_KEY); if (associationProperties) { for (const [key, value] of Object.entries(associationProperties)) { logRecord.setAttribute(`${BRIZZ}.${key}`, value); } } super.onEmit(logRecord); } }; var BrizzBatchLogRecordProcessor = class extends import_sdk_logs.BatchLogRecordProcessor { config; constructor(exporter, config) { super(exporter); this.config = config; } onEmit(logRecord) { const maskingConfig = this.config.masking?.eventMasking; if (maskingConfig) { maskLog(logRecord, maskingConfig); } const associationProperties = import_api3.context.active().getValue(PROPERTIES_CONTEXT_KEY); if (associationProperties) { for (const [key, value] of Object.entries(associationProperties)) { logRecord.setAttribute(`${BRIZZ}.${key}`, value); } } super.onEmit(logRecord); } }; function maskLog(logRecord, config) { if (!logRecord.attributes) { return logRecord; } let rules = config.rules || []; if (!config.disableDefaultRules) { rules = [...DEFAULT_LOG_MASKING_RULES, ...rules]; } try { if (logRecord.attributes && Object.keys(logRecord.attributes).length > 0) { const attributesRecord = {}; for (const [key, value] of Object.entries(logRecord.attributes)) { attributesRecord[key] = value; } const maskedAttributes = maskAttributes(attributesRecord, rules); if (maskedAttributes) { const newAttributes = {}; for (const [key, value] of Object.entries(maskedAttributes)) { newAttributes[key] = value; } logRecord.setAttributes(newAttributes); } } if (config.maskBody && logRecord.body !== void 0 && logRecord.body !== null) { let maskedBody = logRecord.body; for (const rule of rules) { maskedBody = maskValue(maskedBody, rule); } logRecord.setBody(maskedBody); } return logRecord; } catch (error) { logger.error("Error masking log record:", error); return logRecord; } } // src/internal/log/logging.ts var LoggingModule = class _LoggingModule { static instance = null; logExporter = null; logProcessor = null; loggerProvider = null; static getInstance() { if (!_LoggingModule.instance) { throw new Error("Brizz must be initialized before accessing LoggingModule"); } return _LoggingModule.instance; } /** * Initialize the logging module with the provided configuration */ setup(config) { logger.info("Setting up logging module"); this.initLogExporter(config); this.initLogProcessor(config); this.initLoggerProvider(config); _LoggingModule.instance = this; logger.info("Logging module setup completed"); } /** * Initialize the log exporter */ initLogExporter(config) { if (this.logExporter) { logger.debug("Log exporter already initialized, skipping re-initialization"); return; } if (config.customLogExporter) { logger.debug("Using custom log exporter"); this.logExporter = config.customLogExporter; logger.debug("Custom log exporter initialized successfully"); return; } const logsUrl = config.baseUrl.replace(/\/$/, "") + "/v1/logs"; logger.debug("Initializing default OTLP log exporter", { url: logsUrl }); const headers = { ...config.headers }; this.logExporter = new import_exporter_logs_otlp_http.OTLPLogExporter({ url: logsUrl, headers }); logger.debug("OTLP log exporter initialized successfully"); } /** * Initialize the log processor with masking support */ initLogProcessor(config) { if (this.logProcessor) { logger.debug("Log processor already initialized, skipping re-initialization"); return; } if (!this.logExporter) { throw new Error("Log exporter must be initialized before processor"); } logger.debug("Initializing log processor", { disableBatch: config.disableBatch, hasMasking: !!config.masking?.eventMasking }); this.logProcessor = config.disableBatch ? new BrizzSimpleLogRecordProcessor(this.logExporter, config) : new BrizzBatchLogRecordProcessor(this.logExporter, config); logger.debug("Log processor initialized successfully"); } /** * Initialize the logger provider */ initLoggerProvider(config) { if (this.loggerProvider) { logger.debug("Logger provider already initialized, skipping re-initialization"); return; } if (!this.logProcessor) { throw new Error("Log processor must be initialized before logger provider"); } logger.debug("Creating resource with service name", { serviceName: config.appName }); const resource = (0, import_resources.resourceFromAttributes)({ "service.name": config.appName }); logger.debug("Creating logger provider with resource"); this.loggerProvider = new import_sdk_logs2.LoggerProvider({ resource, processors: [this.logProcessor] }); logger.debug("Logger provider initialization completed"); } /** * Emit a custom event to the telemetry pipeline */ emitEvent(name, attributes, body, severityNumber = import_api_logs.SeverityNumber.INFO) { logger.debug("Attempting to emit event", { name, hasAttributes: !!attributes, attributesCount: attributes ? Object.keys(attributes).length : 0, hasBody: !!body, severityNumber }); if (!this.loggerProvider) { logger.error("Cannot emit event: Logger provider not initialized"); throw new Error("Logging module not initialized"); } const logAttributes = { "event.name": name }; if (attributes) { Object.assign(logAttributes, attributes); logger.debug("Combined log attributes", { attributes: Object.keys(logAttributes) }); } logger.debug("Getting logger instance for brizz_events"); const eventLogger = this.loggerProvider.getLogger("brizz.events"); logger.debug("Emitting log record with eventName", { eventName: name }); try { eventLogger.emit({ eventName: name, attributes: logAttributes, severityNumber, body }); logger.debug("Event successfully emitted", { eventName: name }); } catch (error) { logger.error(`Failed to emit event '${name}'`, { error, eventName: name }); logger.error("Log record that failed", { eventName: name, hasAttributes: logAttributes, severityNumber, hasBody: body }); throw error instanceof Error ? error : new Error(String(error)); } } /** * Check if the module is initialized */ isInitialized() { return this.loggerProvider !== null; } /** * Get the logger provider */ getLoggerProvider() { return this.loggerProvider; } /** * Shutdown the logging module */ async shutdown() { logger.debug("Shutting down logging module"); if (this.loggerProvider) { await this.loggerProvider.shutdown(); this.loggerProvider = null; } this.logProcessor = null; this.logExporter = null; if (_LoggingModule.instance === this) { _LoggingModule.instance = null; } logger.debug("Logging module shutdown completed"); } }; // src/internal/metric/metrics.ts var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http"); var import_sdk_metrics = require("@opentelemetry/sdk-metrics"); var MetricsModule = class _MetricsModule { static instance = null; metricsExporter = null; metricsReader = null; static getInstance() { if (!_MetricsModule.instance) { throw new Error("Brizz must be initialized before accessing MetricsModule"); } return _MetricsModule.instance; } /** * Initialize the metrics module with the provided configuration */ setup(config) { logger.info("Setting up metrics module"); this.initMetricsReader(config); _MetricsModule.instance = this; logger.info("Metrics module setup completed"); } /** * Initialize the metrics exporter */ initMetricsExporter(config) { if (this.metricsExporter) { logger.debug("Metrics exporter already initialized, skipping re-initialization"); return; } const metricsUrl = config.baseUrl.replace(/\/$/, "") + "/v1/metrics"; logger.debug("Initializing metrics exporter", { url: metricsUrl }); this.metricsExporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({ url: metricsUrl, headers: config.headers }); logger.debug("Metrics exporter initialized successfully"); } /** * Initialize the metrics reader */ initMetricsReader(config) { if (this.metricsReader) { logger.debug("Metrics reader already initialized, skipping re-initialization"); return; } if (config.customMetricReader) { logger.debug("Using custom metric reader"); this.metricsReader = config.customMetricReader; logger.debug("Custom metric reader initialized successfully"); return; } logger.debug( "Using default metrics flow - creating OTLP exporter and PeriodicExportingMetricReader" ); this.initMetricsExporter(config); if (!this.metricsExporter) { throw new Error("Failed to initialize metrics exporter"); } this.metricsReader = new import_sdk_metrics.PeriodicExportingMetricReader({ exporter: this.metricsExporter }); logger.debug("Default metrics reader initialized successfully"); } /** * Get the metrics exporter */ getMetricsExporter() { if (!this.metricsExporter) { throw new Error("Metrics module not initialized"); } return this.metricsExporter; } /** * Get the metrics reader */ getMetricsReader() { if (!this.metricsReader) { throw new Error("Metrics module not initialized"); } return this.metricsReader; } /** * Shutdown the metrics module */ shutdown() { logger.debug("Shutting down metrics module"); this.metricsReader = null; this.metricsExporter = null; if (_MetricsModule.instance === this) { _MetricsModule.instance = null; } logger.debug("Metrics module shutdown completed"); } }; function getMetricsReader() { return MetricsModule.getInstance().getMetricsReader(); } // src/internal/trace/tracing.ts var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http"); // src/internal/trace/processors/span-processor.ts var import_api4 = require("@opentelemetry/api"); var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base"); // src/internal/trace/transformations/vercel-ai.ts var import_ai_semantic_conventions = require("@traceloop/ai-semantic-conventions"); var AI_GENERATE_TEXT_DO_GENERATE = "ai.generateText.doGenerate"; var AI_STREAM_TEXT_DO_STREAM = "ai.streamText.doStream"; var HANDLED_SPAN_NAMES = { [AI_GENERATE_TEXT_DO_GENERATE]: "gen_ai.chat", [AI_STREAM_TEXT_DO_STREAM]: "gen_ai.chat", "ai.streamText": "ai.streamText", "ai.toolCall": (span) => { const toolName = span.attributes["ai.toolCall.name"]; return `${String(toolName ?? "unknown")}.tool`; } }; var AI_RESPONSE_TEXT = "ai.response.text"; var AI_PROMPT_MESSAGES = "ai.prompt.messages"; var AI_USAGE_PROMPT_TOKENS = "ai.usage.promptTokens"; var AI_USAGE_COMPLETION_TOKENS = "ai.usage.completionTokens"; var AI_MODEL_PROVIDER = "ai.model.provider"; var transformAiSdkSpanName = (span) => { if (span.name in HANDLED_SPAN_NAMES) { const handler = HANDLED_SPAN_NAMES[span.name]; if (typeof handler === "function") { span.name = handler(span); } else if (handler) { span.name = handler; } } }; var transformResponseText = (attributes) => { if (AI_RESPONSE_TEXT in attributes) { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_COMPLETIONS}.0.content`] = attributes[AI_RESPONSE_TEXT]; attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_COMPLETIONS}.0.role`] = "assistant"; delete attributes[AI_RESPONSE_TEXT]; } }; var transformPromptMessages = (attributes) => { if (AI_PROMPT_MESSAGES in attributes) { try { const messages = JSON.parse(attributes[AI_PROMPT_MESSAGES]); for (const [index, msg] of messages.entries()) { const message = msg; logger.debug("Transforming prompt message", { msg: message, type: typeof message.content }); if (typeof message.content === "string") { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_PROMPTS}.${index}.content`] = message.content; } else { if (Array.isArray(message.content) && message.content.length > 0) { const lastContent = message.content.at(-1); if (lastContent?.text) { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_PROMPTS}.${index}.content`] = lastContent.text; } } else { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_PROMPTS}.${index}.content`] = JSON.stringify( message.content ); } } attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_PROMPTS}.${index}.role`] = message.role; } delete attributes[AI_PROMPT_MESSAGES]; } catch (error) { logger.debug("Skipping prompt messages transformation because of JSON parsing error", { e: error }); } } }; var transformPromptTokens = (attributes) => { if (AI_USAGE_PROMPT_TOKENS in attributes) { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_USAGE_PROMPT_TOKENS}`] = attributes[AI_USAGE_PROMPT_TOKENS]; delete attributes[AI_USAGE_PROMPT_TOKENS]; } }; var transformCompletionTokens = (attributes) => { if (AI_USAGE_COMPLETION_TOKENS in attributes) { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_USAGE_COMPLETION_TOKENS}`] = attributes[AI_USAGE_COMPLETION_TOKENS]; delete attributes[AI_USAGE_COMPLETION_TOKENS]; } }; var calculateTotalTokens = (attributes) => { const promptTokens = attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_USAGE_PROMPT_TOKENS}`]; const completionTokens = attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_USAGE_COMPLETION_TOKENS}`]; if (promptTokens && completionTokens) { attributes[`${import_ai_semantic_conventions.SpanAttributes.LLM_USAGE_TOTAL_TOKENS}`] = Number(promptTokens) + Number(completionTokens); } }; var transformVendor = (attributes) => { if (AI_MODEL_PROVIDER in attributes) { const vendor = attributes[AI_MODEL_PROVIDER]; attributes[import_ai_semantic_conventions.SpanAttributes.LLM_SYSTEM] = vendor && vendor.startsWith("openai") ? "OpenAI" : vendor; delete attributes[AI_MODEL_PROVIDER]; } }; var transformAiSdkAttributes = (attributes) => { transformResponseText(attributes); transformPromptMessages(attributes); transformPromptTokens(attributes); transformCompletionTokens(attributes); calculateTotalTokens(attributes); transformVendor(attributes); }; var shouldHandleSpan = (span) => { return span.name in HANDLED_SPAN_NAMES; }; var transformAiSdkSpan = (span) => { if (!shouldHandleSpan(span)) { logger.debug("Skipping span transformation", { spanName: span.name }); return; } for (const key in span.attributes) { if (Number.isNaN(span.attributes[key])) { span.attributes[key] = 0; } } logger.debug("Transforming AI SDK span", { spanName: span.name, spanContext: span.spanContext(), attributes: span.attributes }); transformAiSdkSpanName(span); transformAiSdkAttributes(span.attributes); }; // src/internal/trace/processors/span-processor.ts var DEFAULT_MASKING_RULES = [ { mode: "partial", attributePattern: "gen_ai.prompt", patterns: DEFAULT_PII_PATTERNS }, { mode: "partial", attributePattern: "gen_ai.completion", patterns: DEFAULT_PII_PATTERNS } ]; var BrizzSimpleSpanProcessor = class extends import_sdk_trace_base.SimpleSpanProcessor { config; constructor(spanExporter, config) { super(spanExporter); this.config = config; } // Will work with the following code: // const span = tracer.startSpan('sensitive-operation',{attributes:{ // 'user.password': 'secret123', // 'user.email': 'user@example.com', // }}); // // Won't work because onStart is called before attributes are set: // span.setAttributes({ // 'user.password': 'secret123', // 'user.email': 'user@example.com' // }); onStart(span, parentContex