UNPKG

@azure/monitor-opentelemetry

Version:
718 lines 33.8 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import os from "node:os"; import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; import { SpanKind, SpanStatusCode, ValueType, context } from "@opentelemetry/api"; import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base"; import { KnownCollectionConfigurationErrorType, KnownTelemetryType, } from "../../generated/index.js"; import { getLogDocument, getSdkVersion, getSpanData, getLogData, getSpanDocument, getTransmissionTime, isRequestData, getSpanExceptionColumns, isExceptionData, isDependencyData, } from "./utils.js"; import { QuickpulseMetricExporter } from "./export/exporter.js"; import { QuickpulseSender } from "./export/sender.js"; import { ConnectionStringParser } from "../../utils/connectionStringParser.js"; import { DEFAULT_LIVEMETRICS_ENDPOINT } from "../../types.js"; import { QuickPulseOpenTelemetryMetricNames } from "./types.js"; import { hrTimeToMilliseconds, suppressTracing } from "@opentelemetry/core"; import { getInstance } from "../../utils/statsbeat.js"; import { Filter } from "./filtering/filter.js"; import { Validator } from "./filtering/validator.js"; import { CollectionConfigurationErrorTracker } from "./filtering/collectionConfigurationErrorTracker.js"; import { Projection } from "./filtering/projection.js"; import { TelemetryTypeError, UnexpectedFilterCreateError, DuplicateMetricIdError, MetricFailureToCreateError, } from "./filtering/quickpulseErrors.js"; import { SEMATTRS_EXCEPTION_TYPE } from "@opentelemetry/semantic-conventions"; import { getPhysicalMemory, getProcessorTimeNormalized } from "../utils.js"; import { getCloudRole, getCloudRoleInstance } from "../utils.js"; import { Logger } from "../../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 */ export class LiveMetrics { config; meterProvider; metricReader; meter; requestDurationGauge; dependencyDurationGauge; requestRateGauge; requestFailedRateGauge; dependencyRateGauge; dependencyFailedRateGauge; processPhysicalBytesGauge; percentProcessorTimeNormalizedGauge; exceptionsRateGauge; documents = []; pingInterval; postInterval; quickpulseExporter; pingSender; isCollectingData; 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(); // For tracking of duplicate metric ids in the same configuration. seenMetricIds = new Set(); validDerivedMetrics = new Map(); derivedMetricProjection = new Projection(); validator = new Validator(); filter = new 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 RandomIdGenerator(); const streamId = idGenerator.generateTraceId(); const machineName = os.hostname(); const instance = getCloudRoleInstance(this.config.resource); const roleName = getCloudRole(this.config.resource); const version = 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.parse(this.config.azureMonitorExporterOptions.connectionString || process.env["APPLICATIONINSIGHTS_CONNECTION_STRING"]); this.pingSender = new QuickpulseSender({ endpointUrl: parsedConnectionString.liveendpoint || DEFAULT_LIVEMETRICS_ENDPOINT, instrumentationKey: parsedConnectionString.instrumentationkey || "", credential: this.config.azureMonitorExporterOptions.credential, credentialScopes: parsedConnectionString.aadaudience || this.config.azureMonitorExporterOptions.credentialScopes, }); const exporterOptions = { endpointUrl: parsedConnectionString.liveendpoint || 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 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(); } shutdown() { this.meterProvider?.shutdown(); } async goQuickpulse() { if (!this.isCollectingData) { // If not collecting, Ping try { const params = { transmissionTime: getTransmissionTime(), monitoringDataPoint: this.baseMonitoringDataPoint, configurationEtag: this.etag, }; await context.with(suppressTracing(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 }); } } // eslint-disable-next-line @typescript-eslint/require-await 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) { this.postInterval = FALLBACK_INTERVAL; this.deactivateMetrics(); 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 = ""; 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) { 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 PeriodicExportingMetricReader(metricReaderOptions); const meterProviderConfig = { resource: this.config.resource, readers: [this.metricReader], }; this.meterProvider = new MeterProvider(meterProviderConfig); this.meter = this.meterProvider.getMeter("AzureMonitorLiveMetricsMeter"); this.requestDurationGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.REQUEST_DURATION, { valueType: ValueType.DOUBLE, }); this.requestRateGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.REQUEST_RATE, { valueType: ValueType.DOUBLE, }); this.requestFailedRateGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.REQUEST_FAILURE_RATE, { valueType: ValueType.DOUBLE, }); this.dependencyDurationGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.DEPENDENCY_DURATION, { valueType: ValueType.DOUBLE, }); this.dependencyRateGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.DEPENDENCY_RATE, { valueType: ValueType.DOUBLE, }); this.dependencyFailedRateGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.DEPENDENCY_FAILURE_RATE, { valueType: ValueType.DOUBLE, }); this.processPhysicalBytesGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.PHYSICAL_BYTES, { valueType: ValueType.INT, }); this.percentProcessorTimeNormalizedGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.PROCESSOR_TIME_NORMALIZED, { valueType: ValueType.DOUBLE, }); this.exceptionsRateGauge = this.meter.createObservableGauge(QuickPulseOpenTelemetryMetricNames.EXCEPTION_RATE, { valueType: 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 */ deactivateMetrics() { this.documents = []; this.validDocumentFilterConjuctionGroupInfos.clear(); this.errorTracker.clearRunTimeErrors(); this.errorTracker.clearValidationTimeErrors(); this.validDerivedMetrics.clear(); this.derivedMetricProjection.clearProjectionMaps(); this.seenMetricIds.clear(); this.meterProvider?.shutdown(); this.meterProvider = undefined; } /** * 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 = getSpanData(span); let documentConfiguration; let derivedMetricInfos; if (isRequestData(columns)) { documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get(KnownTelemetryType.Request) || new Map(); derivedMetricInfos = this.validDerivedMetrics.get(KnownTelemetryType.Request) || []; } else { documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get(KnownTelemetryType.Dependency) || new Map(); derivedMetricInfos = this.validDerivedMetrics.get(KnownTelemetryType.Dependency) || []; } this.applyDocumentFilters(documentConfiguration, columns); this.checkMetricFilterAndCreateProjection(derivedMetricInfos, columns); const durationMs = hrTimeToMilliseconds(span.duration); const success = span.status.code !== SpanStatusCode.ERROR; if (span.kind === SpanKind.SERVER || span.kind === 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 = getSpanExceptionColumns(event.attributes, span.attributes); documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get(KnownTelemetryType.Exception) || new Map(); this.applyDocumentFilters(documentConfiguration, exceptionColumns, event.attributes[SEMATTRS_EXCEPTION_TYPE]); derivedMetricInfos = this.validDerivedMetrics.get(KnownTelemetryType.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 = getLogData(logRecord); let derivedMetricInfos; let documentConfiguration; if (isExceptionData(columns)) { documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get(KnownTelemetryType.Exception) || new Map(); this.applyDocumentFilters(documentConfiguration, columns, logRecord.attributes[SEMATTRS_EXCEPTION_TYPE]); derivedMetricInfos = this.validDerivedMetrics.get(KnownTelemetryType.Exception) || []; this.totalExceptionCount++; } else { // trace documentConfiguration = this.validDocumentFilterConjuctionGroupInfos.get(KnownTelemetryType.Trace) || new Map(); this.applyDocumentFilters(documentConfiguration, columns); derivedMetricInfos = this.validDerivedMetrics.get(KnownTelemetryType.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 = getPhysicalMemory(); observableResult.observe(rss); } getProcessorTimeNormalized(observableResult) { if (process && process.hrtime) { const cpuUsagePercent = getProcessorTimeNormalized(this.lastHrTime, this.lastCpuUsage); observableResult.observe(cpuUsagePercent); this.lastHrTime = process.hrtime.bigint(); this.lastCpuUsage = process.cpuUsage(); } else { 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: "", message: "", fullException: "", data: [], }; if (error instanceof TelemetryTypeError) { configError.collectionConfigurationErrorType = "DocumentTelemetryTypeUnsupported"; } else if (error instanceof UnexpectedFilterCreateError) { configError.collectionConfigurationErrorType = KnownCollectionConfigurationErrorType.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 (isRequestData(data) || isDependencyData(data)) { document = getSpanDocument(data); } else if (isExceptionData(data) && exceptionType) { document = getLogDocument(data, exceptionType); } else { document = 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 DuplicateMetricIdError(`Duplicate Metric Id: ${derivedMetricInfo.id}`); } this.derivedMetricProjection.initDerivedMetricProjection(derivedMetricInfo); } catch (error) { const configError = { collectionConfigurationErrorType: "", message: "", fullException: "", data: [], }; if (error instanceof TelemetryTypeError) { configError.collectionConfigurationErrorType = KnownCollectionConfigurationErrorType.MetricTelemetryTypeUnsupported; } else if (error instanceof UnexpectedFilterCreateError) { configError.collectionConfigurationErrorType = KnownCollectionConfigurationErrorType.MetricFailureToCreateFilterUnexpected; } else if (error instanceof DuplicateMetricIdError) { configError.collectionConfigurationErrorType = KnownCollectionConfigurationErrorType.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: "", message: "", fullException: "", data: [], }; if (error instanceof MetricFailureToCreateError) { configError.collectionConfigurationErrorType = KnownCollectionConfigurationErrorType.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); } } } }); } } //# sourceMappingURL=liveMetrics.js.map