@azure/monitor-opentelemetry
Version:
Azure Monitor OpenTelemetry (Node.js)
564 lines • 23.8 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSdkVersion = getSdkVersion;
exports.getSdkVersionType = getSdkVersionType;
exports.setSdkPrefix = setSdkPrefix;
exports.resourceMetricsToQuickpulseDataPoint = resourceMetricsToQuickpulseDataPoint;
exports.getSpanData = getSpanData;
exports.getSpanExceptionColumns = getSpanExceptionColumns;
exports.getLogData = getLogData;
exports.getLogDocument = getLogDocument;
exports.isRequestData = isRequestData;
exports.isDependencyData = isDependencyData;
exports.isTraceData = isTraceData;
exports.isExceptionData = isExceptionData;
exports.getSpanDocument = getSpanDocument;
exports.getTransmissionTime = getTransmissionTime;
exports.getMsFromFilterTimestampString = getMsFromFilterTimestampString;
exports.getPeerIp = getPeerIp;
exports.getUserAgent = getUserAgent;
exports.getHttpUrl = getHttpUrl;
exports.getHttpMethod = getHttpMethod;
exports.getHttpStatusCode = getHttpStatusCode;
exports.getHttpScheme = getHttpScheme;
exports.getHttpTarget = getHttpTarget;
exports.getHttpHost = getHttpHost;
exports.getNetPeerName = getNetPeerName;
exports.getNetHostPort = getNetHostPort;
exports.getNetPeerPort = getNetPeerPort;
const index_js_1 = require("../../generated/index.js");
const api_1 = require("@opentelemetry/api");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const core_1 = require("@opentelemetry/core");
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
const types_js_1 = require("../../types.js");
const types_js_2 = require("./types.js");
const common_js_1 = require("../../utils/common.js");
const common_js_2 = require("../../utils/common.js");
const utils_js_1 = require("../utils.js");
const index_js_2 = require("../../shared/logging/index.js");
/** Get the internal SDK version */
function getSdkVersion() {
var _a;
const { nodeVersion } = process.versions;
const opentelemetryVersion = core_1.SDK_INFO[semantic_conventions_1.SEMRESATTRS_TELEMETRY_SDK_VERSION];
const version = getSdkVersionType();
const internalSdkVersion = `${(_a = process.env[types_js_1.AZURE_MONITOR_PREFIX]) !== null && _a !== void 0 ? _a : ""}node${nodeVersion}:otel${opentelemetryVersion}:${version}`;
return internalSdkVersion;
}
/** Get the internal SDK version type */
function getSdkVersionType() {
if (process.env[types_js_1.APPLICATION_INSIGHTS_SHIM_VERSION]) {
return `sha${process.env[types_js_1.APPLICATION_INSIGHTS_SHIM_VERSION]}`;
}
else {
return `dst${types_js_1.AZURE_MONITOR_OPENTELEMETRY_VERSION}`;
}
}
// eslint-disable-next-line tsdoc/syntax
/** Set the version prefix to a string in the format "{ResourceProvider}{OS}m_ */
function setSdkPrefix() {
if (!process.env[types_js_1.AZURE_MONITOR_PREFIX]) {
const prefixAttachType = process.env[types_js_1.AZURE_MONITOR_AUTO_ATTACH] === "true"
? types_js_1.AttachTypePrefix.INTEGRATED_AUTO
: types_js_1.AttachTypePrefix.MANUAL;
process.env[types_js_1.AZURE_MONITOR_PREFIX] =
`${(0, common_js_2.getResourceProvider)()}${(0, common_js_1.getOsPrefix)()}${prefixAttachType}_`;
}
}
function resourceMetricsToQuickpulseDataPoint(metrics, baseMonitoringDataPoint, documents, errors, derivedMetricValues) {
const metricPoints = [];
metrics.scopeMetrics.forEach((scopeMetric) => {
scopeMetric.metrics.forEach((metric) => {
metric.dataPoints.forEach((dataPoint) => {
const metricPoint = {
weight: 1,
name: "",
value: 0,
};
// Update name to expected value in Quickpulse, needed because those names are invalid in OTel
switch (metric.descriptor.name) {
case types_js_2.QuickPulseOpenTelemetryMetricNames.PHYSICAL_BYTES:
metricPoint.name = types_js_2.QuickPulseMetricNames.PHYSICAL_BYTES;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.DEPENDENCY_DURATION:
metricPoint.name = types_js_2.QuickPulseMetricNames.DEPENDENCY_DURATION;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.DEPENDENCY_FAILURE_RATE:
metricPoint.name = types_js_2.QuickPulseMetricNames.DEPENDENCY_FAILURE_RATE;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.DEPENDENCY_RATE:
metricPoint.name = types_js_2.QuickPulseMetricNames.DEPENDENCY_RATE;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.EXCEPTION_RATE:
metricPoint.name = types_js_2.QuickPulseMetricNames.EXCEPTION_RATE;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.PROCESSOR_TIME_NORMALIZED:
metricPoint.name = types_js_2.QuickPulseMetricNames.PROCESSOR_TIME_NORMALIZED;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.REQUEST_DURATION:
metricPoint.name = types_js_2.QuickPulseMetricNames.REQUEST_DURATION;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.REQUEST_FAILURE_RATE:
metricPoint.name = types_js_2.QuickPulseMetricNames.REQUEST_FAILURE_RATE;
break;
case types_js_2.QuickPulseOpenTelemetryMetricNames.REQUEST_RATE:
metricPoint.name = types_js_2.QuickPulseMetricNames.REQUEST_RATE;
break;
default:
metricPoint.name = metric.descriptor.name;
}
if (metric.dataPointType === sdk_metrics_1.DataPointType.SUM ||
metric.dataPointType === sdk_metrics_1.DataPointType.GAUGE) {
metricPoint.value = dataPoint.value;
}
else {
metricPoint.value = dataPoint.value.sum || 0;
}
metricPoints.push(metricPoint);
// TODO: remove the metric points with the old metric names after
// UI side has done their changes to support the new names.
if (metricPoint.name === types_js_2.QuickPulseMetricNames.PHYSICAL_BYTES ||
metricPoint.name === types_js_2.QuickPulseMetricNames.PROCESSOR_TIME_NORMALIZED) {
const oldMetricPoint = {
weight: 1,
name: metricPoint.name === types_js_2.QuickPulseMetricNames.PHYSICAL_BYTES
? types_js_2.QuickPulseMetricNames.COMMITTED_BYTES
: types_js_2.QuickPulseMetricNames.PROCESSOR_TIME,
value: dataPoint.value,
};
metricPoints.push(oldMetricPoint);
}
});
});
});
derivedMetricValues.forEach((value, id) => {
const metricPoint = {
weight: 1,
name: id,
value: value,
};
metricPoints.push(metricPoint);
});
const quickpulseDataPoint = Object.assign(Object.assign({}, baseMonitoringDataPoint), { timestamp: new Date(), metrics: metricPoints, documents: documents, collectionConfigurationErrors: errors });
return [quickpulseDataPoint];
}
function getIso8601Duration(milliseconds) {
const seconds = milliseconds / 1000;
return `PT${seconds}S`;
}
function getSpanData(span) {
if (span.kind === api_1.SpanKind.SERVER || span.kind === api_1.SpanKind.CONSUMER) {
// request
return getRequestData(span);
}
else {
// dependency
return getDependencyData(span);
}
}
function getSpanExceptionColumns(eventAttributes, spanAttributes) {
const exceptionData = {
Message: eventAttributes[semantic_conventions_1.SEMATTRS_EXCEPTION_MESSAGE],
StackTrace: eventAttributes[semantic_conventions_1.SEMATTRS_EXCEPTION_STACKTRACE],
CustomDimensions: createCustomDimsFromAttributes(spanAttributes),
};
return exceptionData;
}
// A slightly modified version of createRequestData from spanUtils in exporter
function getRequestData(span) {
const requestData = {
Url: "",
Duration: (0, core_1.hrTimeToMilliseconds)(span.duration),
ResponseCode: 0,
Success: false,
Name: span.name || "",
CustomDimensions: createCustomDimsFromAttributes(span.attributes),
};
const httpMethod = getHttpMethod(span.attributes);
const grpcStatusCode = span.attributes[semantic_conventions_1.SEMATTRS_RPC_GRPC_STATUS_CODE];
if (httpMethod) {
requestData.Url = getUrl(span.attributes);
if (URL.canParse(requestData.Url)) {
const urlObj = new URL(requestData.Url);
requestData.Name = `${httpMethod} ${urlObj.pathname}`;
}
else {
index_js_2.Logger.getInstance().info("Request data sent to live metrics has no valid URL field.");
}
const httpStatusCode = getHttpStatusCode(span.attributes);
if (httpStatusCode) {
requestData.ResponseCode = Number(httpStatusCode);
}
}
else if (grpcStatusCode) {
requestData.ResponseCode = Number(grpcStatusCode);
}
requestData.Success = span.status.code !== api_1.SpanStatusCode.ERROR && requestData.ResponseCode < 400;
return requestData;
}
// A slightly modified version of createDependencyData from spanUtils in exporter
function getDependencyData(span) {
const dependencyData = {
Target: "",
Duration: (0, core_1.hrTimeToMilliseconds)(span.duration),
Success: span.status.code !== api_1.SpanStatusCode.ERROR,
Name: span.name,
ResultCode: 0,
Type: "",
Data: "",
CustomDimensions: createCustomDimsFromAttributes(span.attributes),
};
if (span.kind === api_1.SpanKind.PRODUCER) {
dependencyData.Type = types_js_2.DependencyTypes.QueueMessage;
}
if (span.kind === api_1.SpanKind.INTERNAL && span.parentSpanId) {
dependencyData.Type = types_js_2.DependencyTypes.InProc;
}
const httpMethod = getHttpMethod(span.attributes);
const dbSystem = span.attributes[semantic_conventions_1.SEMATTRS_DB_SYSTEM];
const rpcSystem = span.attributes[semantic_conventions_1.SEMATTRS_RPC_SYSTEM];
// HTTP Dependency
if (httpMethod) {
const httpUrl = getHttpUrl(span.attributes);
if (httpUrl) {
if (URL.canParse(String(httpUrl))) {
const dependencyUrl = new URL(String(httpUrl));
dependencyData.Name = `${httpMethod} ${dependencyUrl.pathname}`;
}
else {
index_js_2.Logger.getInstance().info("Dependency data sent to live metrics has no valid URL field.");
}
}
dependencyData.Type = types_js_2.DependencyTypes.Http;
dependencyData.Data = getUrl(span.attributes);
const httpStatusCode = getHttpStatusCode(span.attributes);
if (httpStatusCode) {
dependencyData.ResultCode = Number(httpStatusCode);
}
let target = (0, utils_js_1.getDependencyTarget)(span.attributes);
if (target) {
try {
// Remove default port
const portRegex = new RegExp(/(https?)(:\/\/.*)(:\d+)(\S*)/);
const res = portRegex.exec(target);
if (res !== null) {
const protocol = res[1];
const port = res[3];
if ((protocol === "https" && port === ":443") ||
(protocol === "http" && port === ":80")) {
// Drop port
target = res[1] + res[2] + res[4];
}
}
}
catch (ex) {
/* no-op */
}
dependencyData.Target = `${target}`;
}
}
// DB Dependency
else if (dbSystem) {
// TODO: Remove special logic when Azure UX supports OpenTelemetry dbSystem
if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_MYSQL) {
dependencyData.Type = types_js_2.DependencyTypes.mysql;
}
else if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_POSTGRESQL) {
dependencyData.Type = types_js_2.DependencyTypes.postgresql;
}
else if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_MONGODB) {
dependencyData.Type = types_js_2.DependencyTypes.mongodb;
}
else if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_REDIS) {
dependencyData.Type = types_js_2.DependencyTypes.redis;
}
else if ((0, utils_js_1.isSqlDB)(String(dbSystem))) {
dependencyData.Type = types_js_2.DependencyTypes.Sql;
}
else {
dependencyData.Type = String(dbSystem);
}
const dbStatement = span.attributes[semantic_conventions_1.SEMATTRS_DB_STATEMENT];
const dbOperation = span.attributes[semantic_conventions_1.SEMATTRS_DB_OPERATION];
if (dbStatement) {
dependencyData.Data = String(dbStatement);
}
else if (dbOperation) {
dependencyData.Data = String(dbOperation);
}
const target = (0, utils_js_1.getDependencyTarget)(span.attributes);
const dbName = span.attributes[semantic_conventions_1.SEMATTRS_DB_NAME];
if (target) {
dependencyData.Target = dbName ? `${target}|${dbName}` : `${target}`;
}
else {
dependencyData.Target = dbName ? `${dbName}` : `${dbSystem}`;
}
}
// grpc Dependency
else if (rpcSystem) {
if (rpcSystem === types_js_2.DependencyTypes.Wcf) {
dependencyData.Type = types_js_2.DependencyTypes.Wcf;
}
else {
dependencyData.Type = types_js_2.DependencyTypes.Grpc;
}
const grpcStatusCode = span.attributes[semantic_conventions_1.SEMATTRS_RPC_GRPC_STATUS_CODE];
if (grpcStatusCode) {
dependencyData.ResultCode = Number(grpcStatusCode);
}
const target = (0, utils_js_1.getDependencyTarget)(span.attributes);
if (target) {
dependencyData.Target = `${target}`;
}
else if (rpcSystem) {
dependencyData.Target = String(rpcSystem);
}
}
return dependencyData;
}
function getLogData(log) {
const customDims = createCustomDimsFromAttributes(log.attributes);
if ((0, utils_js_1.isExceptionTelemetry)(log)) {
return {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
Message: String(log.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_MESSAGE]),
// eslint-disable-next-line @typescript-eslint/no-base-to-string
StackTrace: String(log.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_STACKTRACE]),
CustomDimensions: customDims,
};
}
else {
return {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
Message: String(log.body),
CustomDimensions: customDims,
};
}
}
function getLogDocument(data, exceptionType) {
if (isExceptionData(data) && exceptionType) {
return {
documentType: index_js_1.KnownDocumentType.Exception,
exceptionMessage: data.Message,
exceptionType: exceptionType,
properties: mapToKeyValuePairList(data.CustomDimensions),
};
}
else {
// trace
return {
documentType: index_js_1.KnownDocumentType.Trace,
message: data.Message,
properties: mapToKeyValuePairList(data.CustomDimensions),
};
}
}
function isRequestData(data) {
return data.Url !== undefined;
}
function isDependencyData(data) {
return data.Target !== undefined;
}
function isTraceData(data) {
return data.Message !== undefined && data.StackTrace === undefined;
}
function isExceptionData(data) {
return data.StackTrace !== undefined;
}
function getSpanDocument(telemetryData) {
let document = {
documentType: index_js_1.KnownDocumentType.Request,
};
if (isRequestData(telemetryData)) {
document = {
documentType: index_js_1.KnownDocumentType.Request,
name: telemetryData.Name,
url: telemetryData.Url,
responseCode: String(telemetryData.ResponseCode),
duration: getIso8601Duration(telemetryData.Duration),
};
}
else if (isDependencyData(telemetryData)) {
document = {
documentType: index_js_1.KnownDocumentType.RemoteDependency,
name: telemetryData.Name,
commandName: telemetryData.Data,
resultCode: String(telemetryData.ResultCode),
duration: getIso8601Duration(telemetryData.Duration),
};
}
document.properties = mapToKeyValuePairList(telemetryData.CustomDimensions);
return document;
}
function createCustomDimsFromAttributes(attributes) {
const customDims = new Map();
if (attributes) {
for (const key of Object.keys(attributes)) {
if (!(key.startsWith("_MS.") ||
types_js_2.legacySemanticValues.includes(key) ||
types_js_2.httpSemanticValues.includes(key))) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
customDims.set(key, String(attributes[key]));
}
}
}
return customDims;
}
function mapToKeyValuePairList(map) {
const list = [];
map.forEach((value, key) => {
list.push({ key, value });
});
return list;
}
function getUrl(attributes) {
if (!attributes) {
return "";
}
const httpMethod = getHttpMethod(attributes);
if (httpMethod) {
const httpUrl = getHttpUrl(attributes);
if (httpUrl) {
return String(httpUrl);
}
else {
const httpScheme = getHttpScheme(attributes);
const httpTarget = getHttpTarget(attributes);
if (httpScheme && httpTarget) {
const httpHost = getHttpHost(attributes);
if (httpHost) {
return `${httpScheme}://${httpHost}${httpTarget}`;
}
else {
const netPeerPort = getNetPeerPort(attributes);
if (netPeerPort) {
const netPeerName = getNetPeerName(attributes);
if (netPeerName) {
return `${httpScheme}://${netPeerName}:${netPeerPort}${httpTarget}`;
}
else {
const netPeerIp = getPeerIp(attributes);
if (netPeerIp) {
return `${httpScheme}://${netPeerIp}:${netPeerPort}${httpTarget}`;
}
}
}
}
}
}
}
return "";
}
/**
* UTC time the request was made. Expressed as the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight on January 1, 0001. This is used for clock skew calculations, so the value can never be stale (cached).
*
* @example
* 8/5/2020 10:15:00 PM UTC =\> 637322625000000000
* 8/5/2020 10:15:01 PM UTC =\> 637322625010000000
*/
function getTransmissionTime() {
return (Date.now() + 62135596800000) * 10000;
}
function getMsFromFilterTimestampString(timestamp) {
// The service side will return a timestamp in the following format:
// [days].[hours]:[minutes]:[seconds]
// the seconds may be a whole number or something like 7.89. 7.89 seconds translates to 7890 ms.
// writing this method because date.getmilliseconds() returns incorrect result on large timestamps.
// examples: "14.6:56:7.89" = 1234567890 ms, "0.0:0:0.2" = 200 ms
const parts = timestamp.split(":");
if (parts.length !== 3) {
return NaN;
}
const seconds = parseFloat(parts[2]);
const minutes = parseFloat(parts[1]);
const firstPart = parts[0].split(".");
if (firstPart.length !== 2) {
return NaN;
}
const hours = parseFloat(firstPart[1]);
const days = parseFloat(firstPart[0]);
if (isNaN(days) || isNaN(hours) || isNaN(minutes) || isNaN(seconds)) {
return NaN;
}
return seconds * 1000 + minutes * 60000 + hours * 3600000 + days * 86400000;
}
function getPeerIp(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_NETWORK_PEER_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_NET_PEER_IP]);
}
return;
}
function getUserAgent(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_USER_AGENT_ORIGINAL] || attributes[semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT]);
}
return;
}
function getHttpUrl(attributes) {
// Stable sem conv only supports populating url from `url.full`
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_URL_FULL] || attributes[semantic_conventions_1.SEMATTRS_HTTP_URL] || "");
}
return "";
}
function getHttpMethod(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD] || attributes[semantic_conventions_1.SEMATTRS_HTTP_METHOD]);
}
return;
}
function getHttpStatusCode(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE] || attributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE]);
}
return;
}
function getHttpScheme(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_URL_SCHEME] || attributes[semantic_conventions_1.SEMATTRS_HTTP_SCHEME]);
}
return;
}
function getHttpTarget(attributes) {
if (attributes) {
if (attributes[semantic_conventions_1.ATTR_URL_PATH]) {
return String(attributes[semantic_conventions_1.ATTR_URL_PATH]);
}
if (attributes[semantic_conventions_1.ATTR_URL_QUERY]) {
return String(attributes[semantic_conventions_1.ATTR_URL_QUERY]);
}
return String(attributes[semantic_conventions_1.SEMATTRS_HTTP_TARGET] || "");
}
return "";
}
function getHttpHost(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_SERVER_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_HTTP_HOST]);
}
return;
}
function getNetPeerName(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_CLIENT_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_NET_PEER_NAME] || "");
}
return "";
}
function getNetHostPort(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_SERVER_PORT] || attributes[semantic_conventions_1.SEMATTRS_NET_HOST_PORT] || "");
}
return "";
}
function getNetPeerPort(attributes) {
if (attributes) {
return String(attributes[semantic_conventions_1.ATTR_CLIENT_PORT] ||
attributes[semantic_conventions_1.ATTR_SERVER_PORT] ||
attributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT]);
}
return;
}
//# sourceMappingURL=utils.js.map
;