UNPKG

@azure/monitor-opentelemetry

Version:
774 lines 37.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LiveMetrics = void 0; const tslib_1 = require("tslib"); // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. const node_os_1 = tslib_1.__importDefault(require("node:os")); const sdk_metrics_1 = require("@opentelemetry/sdk-metrics"); const api_1 = require("@opentelemetry/api"); const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base"); const utils_js_1 = require("./utils.js"); const exporter_js_1 = require("./export/exporter.js"); const sender_js_1 = require("./export/sender.js"); const connectionStringParser_js_1 = require("../../utils/connectionStringParser.js"); const types_js_1 = require("../../types.js"); const types_js_2 = require("./types.js"); const core_1 = require("@opentelemetry/core"); const statsbeat_js_1 = require("../../utils/statsbeat.js"); const filter_js_1 = require("./filtering/filter.js"); const validator_js_1 = require("./filtering/validator.js"); const collectionConfigurationErrorTracker_js_1 = require("./filtering/collectionConfigurationErrorTracker.js"); const projection_js_1 = require("./filtering/projection.js"); const quickpulseErrors_js_1 = require("./filtering/quickpulseErrors.js"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const utils_js_2 = require("../utils.js"); const utils_js_3 = require("../utils.js"); const logger_js_1 = require("../../shared/logging/logger.js"); const POST_INTERVAL = 1000; const MAX_POST_WAIT_TIME = 20000; const PING_INTERVAL = 5000; const MAX_PING_WAIT_TIME = 60000; const FALLBACK_INTERVAL = 60000; /** * Azure Monitor Live Metrics * @internal */ class LiveMetrics { config; meterProvider; metricReader; meter; requestDurationGauge; dependencyDurationGauge; requestRateGauge; requestFailedRateGauge; dependencyRateGauge; dependencyFailedRateGauge; processPhysicalBytesGauge; percentProcessorTimeNormalizedGauge; exceptionsRateGauge; documents = []; pingInterval; postInterval; quickpulseExporter; pingSender; isCollectingData; isDeactivating = false; deactivatingPromise; lastSuccessTime = Date.now(); handle; // Monitoring data point with common properties baseMonitoringDataPoint; totalRequestCount = 0; totalFailedRequestCount = 0; totalDependencyCount = 0; totalFailedDependencyCount = 0; totalExceptionCount = 0; requestDuration = 0; dependencyDuration = 0; lastRequestDuration = { count: 0, duration: 0, time: 0, }; lastRequestRate = { count: 0, time: 0 }; lastFailedRequestRate = { count: 0, time: 0 }; lastDependencyDuration = { count: 0, duration: 0, time: 0, }; lastDependencyRate = { count: 0, time: 0 }; lastFailedDependencyRate = { count: 0, time: 0 }; lastExceptionRate = { count: 0, time: 0 }; lastCpuUsage; lastHrTime; statsbeatOptionsUpdated = false; etag = ""; errorTracker = new collectionConfigurationErrorTracker_js_1.CollectionConfigurationErrorTracker(); // For tracking of duplicate metric ids in the same configuration. seenMetricIds = new Set(); validDerivedMetrics = new Map(); derivedMetricProjection = new projection_js_1.Projection(); validator = new validator_js_1.Validator(); filter = new filter_js_1.Filter(); // type: Map<telemetryType, Map<id, FilterConjunctionGroupInfo[]>> validDocumentFilterConjuctionGroupInfos = new Map(); /** * Initializes a new instance of the StandardMetrics class. * @param config - Distro configuration. * @param options - Standard Metrics options. */ constructor(config) { this.config = config; const idGenerator = new sdk_trace_base_1.RandomIdGenerator(); const streamId = idGenerator.generateTraceId(); const machineName = node_os_1.default.hostname(); const instance = (0, utils_js_3.getCloudRoleInstance)(this.config.resource); const roleName = (0, utils_js_3.getCloudRole)(this.config.resource); const version = (0, utils_js_1.getSdkVersion)(); this.baseMonitoringDataPoint = { version: version, invariantVersion: 5, // 5 means we support live metrics filtering of metrics and documents instance: instance, roleName: roleName, machineName: machineName, streamId: streamId, performanceCollectionSupported: true, isWebApp: process.env["WEBSITE_SITE_NAME"] ? true : false, }; const parsedConnectionString = connectionStringParser_js_1.ConnectionStringParser.parse(this.config.azureMonitorExporterOptions.connectionString || process.env["APPLICATIONINSIGHTS_CONNECTION_STRING"]); this.pingSender = new sender_js_1.QuickpulseSender({ endpointUrl: parsedConnectionString.liveendpoint || types_js_1.DEFAULT_LIVEMETRICS_ENDPOINT, instrumentationKey: parsedConnectionString.instrumentationkey || "", credential: this.config.azureMonitorExporterOptions.credential, credentialScopes: parsedConnectionString.aadaudience || this.config.azureMonitorExporterOptions.credentialScopes, }); const exporterOptions = { endpointUrl: parsedConnectionString.liveendpoint || types_js_1.DEFAULT_LIVEMETRICS_ENDPOINT, instrumentationKey: parsedConnectionString.instrumentationkey || "", credential: this.config.azureMonitorExporterOptions.credential, credentialScopes: parsedConnectionString.aadaudience || this.config.azureMonitorExporterOptions.credentialScopes, // eslint-disable-next-line @typescript-eslint/no-misused-promises postCallback: this.quickPulseDone.bind(this), getDocumentsFn: this.getDocuments.bind(this), getErrorsFn: this.getErrors.bind(this), getDerivedMetricValuesFn: this.getDerivedMetricValues.bind(this), baseMonitoringDataPoint: this.baseMonitoringDataPoint, }; this.quickpulseExporter = new exporter_js_1.QuickpulseMetricExporter(exporterOptions); this.isCollectingData = false; this.pingInterval = PING_INTERVAL; // Default this.postInterval = POST_INTERVAL; // eslint-disable-next-line @typescript-eslint/no-misused-promises this.handle = setTimeout(this.goQuickpulse.bind(this), this.pingInterval); this.handle.unref(); // Don't block apps from terminating this.lastCpuUsage = process.cpuUsage(); this.lastHrTime = process.hrtime.bigint(); } async shutdown() { // Force collecting=false before tearing down so the shutdown's final // force-flush export, if it fails, cannot trigger the deactivate/ // reactivate fallback in quickPulseDone(). Delegate to deactivateMetrics() // which manages the in-flight deactivation promise so callers (including // a concurrent failure-fallback in quickPulseDone) all observe the same // completion and shutdown waits for any in-flight deactivation to finish. this.isCollectingData = false; await this.deactivateMetrics(); } async goQuickpulse() { if (!this.isCollectingData) { // If not collecting, Ping try { const params = { transmissionTime: (0, utils_js_1.getTransmissionTime)(), monitoringDataPoint: this.baseMonitoringDataPoint, configurationEtag: this.etag, }; await api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), async () => { const response = await this.pingSender.isSubscribed(params); this.quickPulseDone(response); }); } catch (error) { this.quickPulseDone(undefined); } // eslint-disable-next-line @typescript-eslint/no-misused-promises this.handle = setTimeout(this.goQuickpulse.bind(this), this.pingInterval); this.handle.unref(); } if (this.isCollectingData) { this.activateMetrics({ collectionInterval: this.postInterval }); } } async quickPulseDone(response) { if (!response) { if (!this.isCollectingData) { if (Date.now() - this.lastSuccessTime >= MAX_PING_WAIT_TIME) { this.pingInterval = FALLBACK_INTERVAL; } } else { if (Date.now() - this.lastSuccessTime >= MAX_POST_WAIT_TIME) { // Re-entrancy guard: MeterProvider.shutdown() triggers a final // force-flush export which, on failure, re-invokes this callback // synchronously from inside the shutdown's export call. If a // deactivation is already in flight, bail out — awaiting the // in-flight promise here would deadlock the exporter's callback. if (this.isDeactivating) { return; } this.postInterval = FALLBACK_INTERVAL; try { await this.deactivateMetrics(); } catch (error) { // The exporter invokes postCallback without awaiting it, so a // rejection here would surface as an unhandled rejection. Swallow // and log so the failure path stays contained. logger_js_1.Logger.getInstance().warn("Failed to deactivate Live Metrics during failure fallback", error); } // Reset the success-time baseline so the FALLBACK_INTERVAL backoff // can take effect before we consider deactivating again. this.lastSuccessTime = Date.now(); // Re-check after the await: shutdown() may have run concurrently and // flipped isCollectingData to false. Don't restart collection in // that case — doing so would re-create the meterProvider after // shutdown has begun. if (this.isCollectingData) { this.activateMetrics({ collectionInterval: this.postInterval }); } } } } else { this.postInterval = POST_INTERVAL; // Update using response if needed this.lastSuccessTime = Date.now(); this.isCollectingData = response.xMsQpsSubscribed && response.xMsQpsSubscribed === "true" ? true : false; if (response.xMsQpsConfigurationEtag && this.etag !== response.xMsQpsConfigurationEtag) { this.updateConfiguration(response); } // If collecting was stoped if (!this.isCollectingData && this.meterProvider) { this.etag = ""; await this.deactivateMetrics(); // eslint-disable-next-line @typescript-eslint/no-misused-promises this.handle = setTimeout(this.goQuickpulse.bind(this), this.pingInterval); this.handle.unref(); } const endpointRedirect = response.xMsQpsServiceEndpointRedirectV2; if (endpointRedirect) { this.pingSender.handlePermanentRedirect(endpointRedirect); this.quickpulseExporter.getSender().handlePermanentRedirect(endpointRedirect); } const pollingInterval = response.xMsQpsServicePollingIntervalHint; if (pollingInterval) { this.pingInterval = Number(pollingInterval); } else { this.pingInterval = PING_INTERVAL; } } } // Activate live metrics collection activateMetrics(options) { if (this.meterProvider) { return; } // Turn on live metrics active collection for statsbeat if (!this.statsbeatOptionsUpdated) { (0, statsbeat_js_1.getInstance)().setStatsbeatFeatures({}, { liveMetrics: true }); this.statsbeatOptionsUpdated = true; } this.totalDependencyCount = 0; this.totalExceptionCount = 0; this.totalFailedDependencyCount = 0; this.totalFailedRequestCount = 0; this.totalRequestCount = 0; this.requestDuration = 0; this.dependencyDuration = 0; this.lastRequestDuration = { count: 0, duration: 0, time: 0 }; this.lastRequestRate = { count: 0, time: 0 }; this.lastFailedRequestRate = { count: 0, time: 0 }; this.lastDependencyDuration = { count: 0, duration: 0, time: 0 }; this.lastDependencyRate = { count: 0, time: 0 }; this.lastFailedDependencyRate = { count: 0, time: 0 }; this.lastExceptionRate = { count: 0, time: 0 }; const metricReaderOptions = { exporter: this.quickpulseExporter, exportIntervalMillis: options?.collectionInterval, }; this.metricReader = new sdk_metrics_1.PeriodicExportingMetricReader(metricReaderOptions); const meterProviderConfig = { resource: this.config.resource, readers: [this.metricReader], }; this.meterProvider = new sdk_metrics_1.MeterProvider(meterProviderConfig); this.meter = this.meterProvider.getMeter("AzureMonitorLiveMetricsMeter"); this.requestDurationGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.REQUEST_DURATION, { valueType: api_1.ValueType.DOUBLE, }); this.requestRateGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.REQUEST_RATE, { valueType: api_1.ValueType.DOUBLE, }); this.requestFailedRateGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.REQUEST_FAILURE_RATE, { valueType: api_1.ValueType.DOUBLE, }); this.dependencyDurationGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.DEPENDENCY_DURATION, { valueType: api_1.ValueType.DOUBLE, }); this.dependencyRateGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.DEPENDENCY_RATE, { valueType: api_1.ValueType.DOUBLE, }); this.dependencyFailedRateGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.DEPENDENCY_FAILURE_RATE, { valueType: api_1.ValueType.DOUBLE, }); this.processPhysicalBytesGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.PHYSICAL_BYTES, { valueType: api_1.ValueType.INT, }); this.percentProcessorTimeNormalizedGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.PROCESSOR_TIME_NORMALIZED, { valueType: api_1.ValueType.DOUBLE, }); this.exceptionsRateGauge = this.meter.createObservableGauge(types_js_2.QuickPulseOpenTelemetryMetricNames.EXCEPTION_RATE, { valueType: api_1.ValueType.DOUBLE, }); this.requestDurationGauge.addCallback(this.getRequestDuration.bind(this)); this.requestRateGauge.addCallback(this.getRequestRate.bind(this)); this.requestFailedRateGauge.addCallback(this.getRequestFailedRate.bind(this)); this.dependencyDurationGauge.addCallback(this.getDependencyDuration.bind(this)); this.dependencyRateGauge.addCallback(this.getDependencyRate.bind(this)); this.dependencyFailedRateGauge.addCallback(this.getDependencyFailedRate.bind(this)); this.exceptionsRateGauge.addCallback(this.getExceptionRate.bind(this)); this.processPhysicalBytesGauge.addCallback(this.getPhysicalMemory.bind(this)); this.percentProcessorTimeNormalizedGauge.addCallback(this.getProcessorTimeNormalized.bind(this)); } /** * Deactivate metric collection */ async deactivateMetrics() { // Coalesce concurrent deactivations: callers (shutdown(), the // failure-fallback in quickPulseDone(), and the "unsubscribed" branch in // goQuickpulse()) can all race. Return the in-flight promise so every // caller awaits the same completion and the underlying meterProvider // shutdown only runs once. if (this.deactivatingPromise) { return this.deactivatingPromise; } this.isDeactivating = true; this.deactivatingPromise = (async () => { try { this.documents = []; this.validDocumentFilterConjuctionGroupInfos.clear(); this.errorTracker.clearRunTimeErrors(); this.errorTracker.clearValidationTimeErrors(); this.validDerivedMetrics.clear(); this.derivedMetricProjection.clearProjectionMaps(); this.seenMetricIds.clear(); // Capture and clear the reference before awaiting shutdown so any // re-entrant calls triggered by the shutdown's final force-flush // export observe meterProvider as undefined and exit early. const provider = this.meterProvider; this.meterProvider = undefined; await provider?.shutdown(); } finally { this.isDeactivating = false; this.deactivatingPromise = undefined; } })(); return this.deactivatingPromise; } /** * Force flush Meter Provider. */ async flush() { await this.meterProvider?.forceFlush(); } /** *Get OpenTelemetry MeterProvider */ getMeterProvider() { return this.meterProvider; } getDocuments() { const result = this.documents; this.documents = []; return result; } getErrors() { const result = this.errorTracker.getErrors(); this.errorTracker.clearRunTimeErrors(); return result; } getDerivedMetricValues() { return this.derivedMetricProjection.getMetricValues(); } addDocument(document) { if (document) { // Limit risk of memory leak by limiting doc length to something manageable if (this.documents.length > 20) { this.documents.shift(); // Remove oldest document } this.documents.push(document); } } /** * Record Span metrics * @internal */ recordSpan(span) { if (this.isCollectingData) { const columns = (0, utils_js_1.getSpanData)(span); let documentConfiguration; let derivedMetricInfos; if ((0, utils_js_1.isRequestData)(columns)) { documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get("Request") || new Map(); derivedMetricInfos = this.validDerivedMetrics.get("Request") || []; } else { documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get("Dependency") || new Map(); derivedMetricInfos = this.validDerivedMetrics.get("Dependency") || []; } this.applyDocumentFilters(documentConfiguration, columns); this.checkMetricFilterAndCreateProjection(derivedMetricInfos, columns); const durationMs = (0, core_1.hrTimeToMilliseconds)(span.duration); const success = span.status.code !== api_1.SpanStatusCode.ERROR; if (span.kind === api_1.SpanKind.SERVER || span.kind === api_1.SpanKind.CONSUMER) { this.totalRequestCount++; this.requestDuration += durationMs; if (!success) { this.totalFailedRequestCount++; } } else { this.totalDependencyCount++; this.dependencyDuration += durationMs; if (!success) { this.totalFailedDependencyCount++; } } if (span.events) { span.events.forEach((event) => { event.attributes = event.attributes || {}; if (event.name === "exception") { const exceptionColumns = (0, utils_js_1.getSpanExceptionColumns)(event.attributes, span.attributes); documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get("Exception") || new Map(); this.applyDocumentFilters(documentConfiguration, exceptionColumns, event.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_TYPE]); derivedMetricInfos = this.validDerivedMetrics.get("Exception") || []; this.checkMetricFilterAndCreateProjection(derivedMetricInfos, exceptionColumns); this.totalExceptionCount++; } }); } } } /** * Record LogRecord metrics, add attribute so data is not aggregated again in ingestion * @internal */ recordLog(logRecord) { if (this.isCollectingData) { const columns = (0, utils_js_1.getLogData)(logRecord); let derivedMetricInfos; let documentConfiguration; if ((0, utils_js_1.isExceptionData)(columns)) { documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get("Exception") || new Map(); this.applyDocumentFilters(documentConfiguration, columns, logRecord.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_TYPE]); derivedMetricInfos = this.validDerivedMetrics.get("Exception") || []; this.totalExceptionCount++; } else { // trace documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get("Trace") || new Map(); this.applyDocumentFilters(documentConfiguration, columns); derivedMetricInfos = this.validDerivedMetrics.get("Trace") || []; } this.checkMetricFilterAndCreateProjection(derivedMetricInfos, columns); } } getRequestDuration(observableResult) { const currentTime = +new Date(); const requestInterval = this.totalRequestCount - this.lastRequestDuration.count || 0; const durationInterval = this.requestDuration - this.lastRequestDuration.duration || 0; const elapsedMs = currentTime - this.lastRequestDuration.time; if (elapsedMs > 0) { const averageExecutionTime = durationInterval / requestInterval || 0; // default to 0 in case no requests in this interval observableResult.observe(averageExecutionTime); } this.lastRequestDuration = { count: this.totalRequestCount, duration: this.requestDuration, time: currentTime, }; } getRequestRate(observableResult) { const currentTime = +new Date(); const intervalRequests = this.totalRequestCount - this.lastRequestRate.count || 0; const elapsedMs = currentTime - this.lastRequestRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const dataPerSec = intervalRequests / elapsedSeconds; observableResult.observe(dataPerSec); } this.lastRequestRate = { count: this.totalRequestCount, time: currentTime, }; } getRequestFailedRate(observableResult) { const currentTime = +new Date(); const intervalRequests = this.totalFailedRequestCount - this.lastFailedRequestRate.count || 0; const elapsedMs = currentTime - this.lastFailedRequestRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const dataPerSec = intervalRequests / elapsedSeconds; observableResult.observe(dataPerSec); } this.lastFailedRequestRate = { count: this.totalFailedRequestCount, time: currentTime, }; } getDependencyDuration(observableResult) { const currentTime = +new Date(); const dependencyInterval = this.totalDependencyCount - this.lastDependencyDuration.count || 0; const durationInterval = this.dependencyDuration - this.lastDependencyDuration.duration || 0; const elapsedMs = currentTime - this.lastDependencyDuration.time; if (elapsedMs > 0) { const averageExecutionTime = durationInterval / dependencyInterval || 0; // default to 0 in case no dependencies in this interval observableResult.observe(averageExecutionTime); } this.lastDependencyDuration = { count: this.totalDependencyCount, duration: this.dependencyDuration, time: currentTime, }; } getDependencyRate(observableResult) { const currentTime = +new Date(); const intervalData = this.totalDependencyCount - this.lastDependencyRate.count || 0; const elapsedMs = currentTime - this.lastDependencyRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const dataPerSec = intervalData / elapsedSeconds; observableResult.observe(dataPerSec); } this.lastDependencyRate = { count: this.totalDependencyCount, time: currentTime, }; } getDependencyFailedRate(observableResult) { const currentTime = +new Date(); const intervalData = this.totalFailedDependencyCount - this.lastFailedDependencyRate.count || 0; const elapsedMs = currentTime - this.lastFailedDependencyRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const dataPerSec = intervalData / elapsedSeconds; observableResult.observe(dataPerSec); } this.lastFailedDependencyRate = { count: this.totalFailedDependencyCount, time: currentTime, }; } getExceptionRate(observableResult) { const currentTime = +new Date(); const intervalData = this.totalExceptionCount - this.lastExceptionRate.count || 0; const elapsedMs = currentTime - this.lastExceptionRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const dataPerSec = intervalData / elapsedSeconds; observableResult.observe(dataPerSec); } this.lastExceptionRate = { count: this.totalExceptionCount, time: currentTime, }; } getPhysicalMemory(observableResult) { const rss = (0, utils_js_2.getPhysicalMemory)(); observableResult.observe(rss); } getProcessorTimeNormalized(observableResult) { if (process && process.hrtime) { const cpuUsagePercent = (0, utils_js_2.getProcessorTimeNormalized)(this.lastHrTime, this.lastCpuUsage); observableResult.observe(cpuUsagePercent); this.lastHrTime = process.hrtime.bigint(); this.lastCpuUsage = process.cpuUsage(); } else { logger_js_1.Logger.getInstance().debug("Getting Normalized Processor Time Failed. No process available."); } } updateConfiguration(response) { this.etag = response.xMsQpsConfigurationEtag || ""; this.quickpulseExporter.setEtag(this.etag); this.errorTracker.clearValidationTimeErrors(); this.validDocumentFilterConjuctionGroupInfos.clear(); this.validDerivedMetrics.clear(); this.derivedMetricProjection.clearProjectionMaps(); this.seenMetricIds.clear(); this.parseDocumentFilterConfiguration(response); this.parseMetricFilterConfiguration(response); } parseDocumentFilterConfiguration(response) { if (!response?.documentStreams || typeof response.documentStreams.forEach !== "function") { return; } response.documentStreams.forEach((documentStreamInfo) => { documentStreamInfo.documentFilterGroups.forEach((documentFilterGroupInfo) => { try { this.validator.validateTelemetryType(documentFilterGroupInfo.telemetryType); this.validator.validateDocumentFilters(documentFilterGroupInfo); this.filter.renameExceptionFieldNamesForFiltering(documentFilterGroupInfo.filters); if (!this.validDocumentFilterConjuctionGroupInfos.has(documentFilterGroupInfo.telemetryType)) { this.validDocumentFilterConjuctionGroupInfos.set(documentFilterGroupInfo.telemetryType, new Map()); } const innerMap = this.validDocumentFilterConjuctionGroupInfos.get(documentFilterGroupInfo.telemetryType); if (!innerMap?.has(documentStreamInfo.id)) { innerMap?.set(documentStreamInfo.id, [documentFilterGroupInfo.filters]); } else { innerMap.get(documentStreamInfo.id)?.push(documentFilterGroupInfo.filters); } } catch (error) { const configError = { collectionConfigurationErrorType: "Unknown", message: "", fullException: "", data: [], }; if (error instanceof quickpulseErrors_js_1.TelemetryTypeError) { configError.collectionConfigurationErrorType = "CollectionConfigurationFailureToCreateUnexpected"; } else if (error instanceof quickpulseErrors_js_1.UnexpectedFilterCreateError) { configError.collectionConfigurationErrorType = "DocumentStreamFailureToCreateFilterUnexpected"; } if (error instanceof Error) { configError.message = error.message; configError.fullException = error.stack || ""; } const data = []; data.push({ key: "DocumentStreamInfoId", value: documentStreamInfo.id }); data.push({ key: "ETag", value: this.etag }); configError.data = data; this.errorTracker.addValidationError(configError); } }); }); } applyDocumentFilters(documentConfiguration, data, exceptionType) { const streamIds = new Set(); documentConfiguration.forEach((filterConjunctionGroupInfoList, streamId) => { filterConjunctionGroupInfoList.forEach((filterConjunctionGroupInfo) => { // by going though each filterConjuctionGroupInfo, we are implicitly -OR-ing // different filterConjunctionGroupInfo within documentStreamInfo. If there are multiple // documentStreamInfos, this logic will -OR- the filtering results of each documentStreamInfo. if (this.filter.checkFilterConjunctionGroup(filterConjunctionGroupInfo, data)) { streamIds.add(streamId); } }); }); // Emit a document when a telemetry data matches a particular filtering configuration, // or when filtering configuration is empty. if (streamIds.size > 0 || documentConfiguration.size === 0) { let document; if ((0, utils_js_1.isRequestData)(data) || (0, utils_js_1.isDependencyData)(data)) { document = (0, utils_js_1.getSpanDocument)(data); } else if ((0, utils_js_1.isExceptionData)(data) && exceptionType) { document = (0, utils_js_1.getLogDocument)(data, exceptionType); } else { document = (0, utils_js_1.getLogDocument)(data); } document.documentStreamIds = [...streamIds]; this.addDocument(document); } } parseMetricFilterConfiguration(response) { if (!response?.documentStreams || typeof response.documentStreams.forEach !== "function") { return; } response.metrics.forEach((derivedMetricInfo) => { try { if (!this.seenMetricIds.has(derivedMetricInfo.id)) { this.seenMetricIds.add(derivedMetricInfo.id); this.validator.validateTelemetryType(derivedMetricInfo.telemetryType); this.validator.checkCustomMetricProjection(derivedMetricInfo); this.validator.validateMetricFilters(derivedMetricInfo); derivedMetricInfo.filterGroups.forEach((filterConjunctionGroupInfo) => { this.filter.renameExceptionFieldNamesForFiltering(filterConjunctionGroupInfo); }); if (this.validDerivedMetrics.has(derivedMetricInfo.telemetryType)) { this.validDerivedMetrics.get(derivedMetricInfo.telemetryType)?.push(derivedMetricInfo); } else { this.validDerivedMetrics.set(derivedMetricInfo.telemetryType, [derivedMetricInfo]); } } else { throw new quickpulseErrors_js_1.DuplicateMetricIdError(`Duplicate Metric Id: ${derivedMetricInfo.id}`); } this.derivedMetricProjection.initDerivedMetricProjection(derivedMetricInfo); } catch (error) { const configError = { collectionConfigurationErrorType: "Unknown", message: "", fullException: "", data: [], }; if (error instanceof quickpulseErrors_js_1.TelemetryTypeError) { configError.collectionConfigurationErrorType = "MetricTelemetryTypeUnsupported"; } else if (error instanceof quickpulseErrors_js_1.UnexpectedFilterCreateError) { configError.collectionConfigurationErrorType = "MetricFailureToCreateFilterUnexpected"; } else if (error instanceof quickpulseErrors_js_1.DuplicateMetricIdError) { configError.collectionConfigurationErrorType = "MetricDuplicateIds"; } if (error instanceof Error) { configError.message = error.message; configError.fullException = error.stack || ""; } const data = []; data.push({ key: "MetricId", value: derivedMetricInfo.id }); data.push({ key: "ETag", value: this.etag }); configError.data = data; this.errorTracker.addValidationError(configError); } }); } checkMetricFilterAndCreateProjection(derivedMetricInfoList, data) { derivedMetricInfoList.forEach((derivedMetricInfo) => { if (this.filter.checkMetricFilters(derivedMetricInfo, data)) { try { this.derivedMetricProjection.calculateProjection(derivedMetricInfo, data); } catch (error) { const configError = { collectionConfigurationErrorType: "Unknown", message: "", fullException: "", data: [], }; if (error instanceof quickpulseErrors_js_1.MetricFailureToCreateError) { configError.collectionConfigurationErrorType = "MetricFailureToCreate"; if (error instanceof Error) { configError.message = error.message; configError.fullException = error.stack || ""; } const errorData = []; errorData.push({ key: "MetricId", value: derivedMetricInfo.id }); errorData.push({ key: "ETag", value: this.etag }); configError.data = errorData; this.errorTracker.addRunTimeError(configError); } } } }); } } exports.LiveMetrics = LiveMetrics; //# sourceMappingURL=liveMetrics.js.map