@azure/monitor-opentelemetry
Version:
Azure Monitor OpenTelemetry (Node.js)
718 lines • 33.8 kB
JavaScript
// 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