UNPKG

@azure/monitor-opentelemetry-exporter

Version:

Application Insights exporter for the OpenTelemetry JavaScript (Node.js) SDK

521 lines 21.5 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", { value: true }); exports.readableSpanToEnvelope = readableSpanToEnvelope; exports.spanEventsToEnvelopes = spanEventsToEnvelopes; exports.getPeerIp = getPeerIp; exports.getLocationIp = getLocationIp; exports.getHttpClientIp = getHttpClientIp; 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.getNetPeerPort = getNetPeerPort; const core_1 = require("@opentelemetry/core"); const api_1 = require("@opentelemetry/api"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const common_js_1 = require("./common.js"); const types_js_1 = require("../types.js"); const eventhub_js_1 = require("./eventhub.js"); const applicationinsights_js_1 = require("./constants/applicationinsights.js"); const azAttributes_js_1 = require("./constants/span/azAttributes.js"); const index_js_1 = require("../generated/index.js"); const breezeUtils_js_1 = require("./breezeUtils.js"); function createTagsFromSpan(span) { var _a; const tags = (0, common_js_1.createTagsFromResource)(span.resource); tags[index_js_1.KnownContextTagKeys.AiOperationId] = span.spanContext().traceId; if ((_a = span.parentSpanContext) === null || _a === void 0 ? void 0 : _a.spanId) { tags[index_js_1.KnownContextTagKeys.AiOperationParentId] = span.parentSpanContext.spanId; } const endUserId = span.attributes[semantic_conventions_1.SEMATTRS_ENDUSER_ID]; if (endUserId) { tags[index_js_1.KnownContextTagKeys.AiUserId] = String(endUserId); } const httpUserAgent = getUserAgent(span.attributes); if (httpUserAgent) { // TODO: Not exposed in Swagger, need to update def tags["ai.user.userAgent"] = String(httpUserAgent); } if ((0, common_js_1.isSyntheticSource)(span.attributes)) { tags[index_js_1.KnownContextTagKeys.AiOperationSyntheticSource] = "True"; } if (span.kind === api_1.SpanKind.SERVER) { const httpMethod = getHttpMethod(span.attributes); getLocationIp(tags, span.attributes); if (httpMethod) { const httpRoute = span.attributes[semantic_conventions_1.ATTR_HTTP_ROUTE]; const httpUrl = getHttpUrl(span.attributes); tags[index_js_1.KnownContextTagKeys.AiOperationName] = span.name; // Default if (httpRoute) { // AiOperationName max length is 1024 // https://github.com/MohanGsk/ApplicationInsights-Home/blob/master/EndpointSpecs/Schemas/Bond/ContextTagKeys.bond tags[index_js_1.KnownContextTagKeys.AiOperationName] = String(`${httpMethod} ${httpRoute}`).substring(0, types_js_1.MaxPropertyLengths.TEN_BIT); } else if (httpUrl) { try { const url = new URL(String(httpUrl)); tags[index_js_1.KnownContextTagKeys.AiOperationName] = String(`${httpMethod} ${url.pathname}`).substring(0, types_js_1.MaxPropertyLengths.TEN_BIT); } catch (_b) { /* no-op */ } } } else { tags[index_js_1.KnownContextTagKeys.AiOperationName] = span.name; } } else { if (span.attributes[index_js_1.KnownContextTagKeys.AiOperationName]) { tags[index_js_1.KnownContextTagKeys.AiOperationName] = span.attributes[index_js_1.KnownContextTagKeys.AiOperationName]; } } // TODO: Location IP TBD for non server spans return tags; } function createPropertiesFromSpanAttributes(attributes) { const properties = {}; if (attributes) { for (const key of Object.keys(attributes)) { // Avoid duplication ignoring fields already mapped. if ( // We need to not ignore the _MS.ProcessedByMetricExtractors key as it's used to identify standard metrics !((key.startsWith("_MS.") && !types_js_1.internalMicrosoftAttributes.includes(key)) || key.startsWith("microsoft.") || types_js_1.legacySemanticValues.includes(key) || types_js_1.httpSemanticValues.includes(key) || key === index_js_1.KnownContextTagKeys.AiOperationName)) { properties[key] = (0, common_js_1.serializeAttribute)(attributes[key]); } } } return properties; } function createPropertiesFromSpan(span) { const properties = createPropertiesFromSpanAttributes(span.attributes); const measurements = {}; const links = span.links.map((link) => ({ operation_Id: link.context.traceId, id: link.context.spanId, })); if (links.length > 0) { properties[applicationinsights_js_1.MS_LINKS] = JSON.stringify(links); } return [properties, measurements]; } function createDependencyData(span) { var _a; const remoteDependencyData = { name: span.name, // Default id: `${span.spanContext().spanId}`, success: ((_a = span.status) === null || _a === void 0 ? void 0 : _a.code) !== api_1.SpanStatusCode.ERROR, resultCode: "0", type: "Dependency", duration: (0, breezeUtils_js_1.msToTimeSpan)((0, core_1.hrTimeToMilliseconds)(span.duration)), version: 2, }; if (span.kind === api_1.SpanKind.PRODUCER) { remoteDependencyData.type = applicationinsights_js_1.DependencyTypes.QueueMessage; } if (span.kind === api_1.SpanKind.INTERNAL && span.parentSpanContext) { remoteDependencyData.type = applicationinsights_js_1.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) { try { const dependencyUrl = new URL(String(httpUrl)); remoteDependencyData.name = `${httpMethod} ${dependencyUrl.pathname}`; } catch (_b) { /* no-op */ } } remoteDependencyData.type = applicationinsights_js_1.DependencyTypes.Http; remoteDependencyData.data = (0, common_js_1.getUrl)(span.attributes); const httpStatusCode = getHttpStatusCode(span.attributes); if (httpStatusCode) { remoteDependencyData.resultCode = String(httpStatusCode); } let target = (0, common_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 (_c) { /* no-op */ } remoteDependencyData.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) { remoteDependencyData.type = "mysql"; } else if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_POSTGRESQL) { remoteDependencyData.type = "postgresql"; } else if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_MONGODB) { remoteDependencyData.type = "mongodb"; } else if (String(dbSystem) === semantic_conventions_1.DBSYSTEMVALUES_REDIS) { remoteDependencyData.type = "redis"; } else if ((0, common_js_1.isSqlDB)(String(dbSystem))) { remoteDependencyData.type = "SQL"; } else { remoteDependencyData.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) { remoteDependencyData.data = String(dbStatement); } else if (dbOperation) { remoteDependencyData.data = String(dbOperation); } const target = (0, common_js_1.getDependencyTarget)(span.attributes); const dbName = span.attributes[semantic_conventions_1.SEMATTRS_DB_NAME]; if (target) { remoteDependencyData.target = dbName ? `${target}|${dbName}` : `${target}`; } else { remoteDependencyData.target = dbName ? `${dbName}` : `${dbSystem}`; } } // grpc Dependency else if (rpcSystem) { if (rpcSystem === applicationinsights_js_1.DependencyTypes.Wcf) { remoteDependencyData.type = applicationinsights_js_1.DependencyTypes.Wcf; } else { remoteDependencyData.type = applicationinsights_js_1.DependencyTypes.Grpc; } const grpcStatusCode = span.attributes[semantic_conventions_1.SEMATTRS_RPC_GRPC_STATUS_CODE]; if (grpcStatusCode) { remoteDependencyData.resultCode = String(grpcStatusCode); } const target = (0, common_js_1.getDependencyTarget)(span.attributes); if (target) { remoteDependencyData.target = `${target}`; } else if (rpcSystem) { remoteDependencyData.target = String(rpcSystem); } } return remoteDependencyData; } function createRequestData(span) { const requestData = { id: `${span.spanContext().spanId}`, success: span.status.code !== api_1.SpanStatusCode.ERROR && (Number(getHttpStatusCode(span.attributes)) || 0) < 400, responseCode: "0", duration: (0, breezeUtils_js_1.msToTimeSpan)((0, core_1.hrTimeToMilliseconds)(span.duration)), version: 2, source: undefined, }; const httpMethod = getHttpMethod(span.attributes); const grpcStatusCode = span.attributes[semantic_conventions_1.SEMATTRS_RPC_GRPC_STATUS_CODE]; if (httpMethod) { requestData.url = (0, common_js_1.getUrl)(span.attributes); const httpStatusCode = getHttpStatusCode(span.attributes); if (httpStatusCode) { requestData.responseCode = String(httpStatusCode); } } else if (grpcStatusCode) { requestData.responseCode = String(grpcStatusCode); } return requestData; } /** * Span to Azure envelope parsing. * @internal */ function readableSpanToEnvelope(span, ikey) { let name; let baseType; let baseData; const time = (0, common_js_1.hrTimeToDate)(span.startTime); const instrumentationKey = ikey; const tags = createTagsFromSpan(span); const [properties, measurements] = createPropertiesFromSpan(span); switch (span.kind) { case api_1.SpanKind.CLIENT: case api_1.SpanKind.PRODUCER: case api_1.SpanKind.INTERNAL: name = "Microsoft.ApplicationInsights.RemoteDependency"; baseType = "RemoteDependencyData"; baseData = createDependencyData(span); break; case api_1.SpanKind.SERVER: case api_1.SpanKind.CONSUMER: name = "Microsoft.ApplicationInsights.Request"; baseType = "RequestData"; baseData = createRequestData(span); baseData.name = tags[index_js_1.KnownContextTagKeys.AiOperationName]; break; default: // never api_1.diag.error(`Unsupported span kind ${span.kind}`); throw new Error(`Unsupported span kind ${span.kind}`); } let sampleRate = 100; if (span.attributes[applicationinsights_js_1.AzureMonitorSampleRate]) { sampleRate = Number(span.attributes[applicationinsights_js_1.AzureMonitorSampleRate]); } // Azure SDK if (span.attributes[azAttributes_js_1.AzNamespace]) { if (span.kind === api_1.SpanKind.INTERNAL) { baseData.type = `${applicationinsights_js_1.DependencyTypes.InProc} | ${span.attributes[azAttributes_js_1.AzNamespace]}`; } if (span.attributes[azAttributes_js_1.AzNamespace] === azAttributes_js_1.MicrosoftEventHub) { (0, eventhub_js_1.parseEventHubSpan)(span, baseData); } } // Truncate properties if (baseData.id) { baseData.id = baseData.id.substring(0, types_js_1.MaxPropertyLengths.NINE_BIT); } if (baseData.name) { baseData.name = baseData.name.substring(0, types_js_1.MaxPropertyLengths.TEN_BIT); } if (baseData.resultCode) { baseData.resultCode = String(baseData.resultCode).substring(0, types_js_1.MaxPropertyLengths.TEN_BIT); } if (baseData.data) { baseData.data = String(baseData.data).substring(0, types_js_1.MaxPropertyLengths.THIRTEEN_BIT); } if (baseData.type) { baseData.type = String(baseData.type).substring(0, types_js_1.MaxPropertyLengths.TEN_BIT); } if (baseData.target) { baseData.target = String(baseData.target).substring(0, types_js_1.MaxPropertyLengths.TEN_BIT); } if (baseData.properties) { for (const key of Object.keys(baseData.properties)) { baseData.properties[key] = baseData.properties[key].substring(0, types_js_1.MaxPropertyLengths.THIRTEEN_BIT); } } return { name, sampleRate, time, instrumentationKey, tags, version: 1, data: { baseType, baseData: Object.assign(Object.assign({}, baseData), { properties, measurements }), }, }; } /** * Span Events to Azure envelopes parsing. * @internal */ function spanEventsToEnvelopes(span, ikey) { const envelopes = []; if (span.events) { span.events.forEach((event) => { let baseType; const time = (0, common_js_1.hrTimeToDate)(event.time); let name = ""; let baseData; const properties = createPropertiesFromSpanAttributes(event.attributes); const tags = (0, common_js_1.createTagsFromResource)(span.resource); tags[index_js_1.KnownContextTagKeys.AiOperationId] = span.spanContext().traceId; const spanId = span.spanContext().spanId; if (spanId) { tags[index_js_1.KnownContextTagKeys.AiOperationParentId] = spanId; } // Only generate exception telemetry for incoming requests if (event.name === "exception") { name = "Microsoft.ApplicationInsights.Exception"; baseType = "ExceptionData"; let typeName = ""; let message = "Exception"; let stack = ""; let hasFullStack = false; if (event.attributes) { typeName = String(event.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_TYPE]); stack = String(event.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_STACKTRACE]); if (stack) { hasFullStack = true; } const exceptionMsg = event.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_MESSAGE]; if (exceptionMsg) { message = String(exceptionMsg); } const escaped = event.attributes[semantic_conventions_1.SEMATTRS_EXCEPTION_ESCAPED]; if (escaped !== undefined) { properties[semantic_conventions_1.SEMATTRS_EXCEPTION_ESCAPED] = String(escaped); } } const exceptionDetails = { typeName: typeName, message: message, stack: stack, hasFullStack: hasFullStack, }; const exceptionData = { exceptions: [exceptionDetails], version: 2, properties: properties, }; baseData = exceptionData; } else { name = "Microsoft.ApplicationInsights.Message"; baseType = "MessageData"; const messageData = { message: event.name, version: 2, properties: properties, }; baseData = messageData; } let sampleRate = 100; if (span.attributes[applicationinsights_js_1.AzureMonitorSampleRate]) { sampleRate = Number(span.attributes[applicationinsights_js_1.AzureMonitorSampleRate]); } // Truncate properties if (baseData.message) { baseData.message = String(baseData.message).substring(0, types_js_1.MaxPropertyLengths.FIFTEEN_BIT); } if (baseData.properties) { for (const key of Object.keys(baseData.properties)) { baseData.properties[key] = baseData.properties[key].substring(0, types_js_1.MaxPropertyLengths.THIRTEEN_BIT); } } const env = { name: name, time: time, instrumentationKey: ikey, version: 1, sampleRate: sampleRate, data: { baseType: baseType, baseData: baseData, }, tags: tags, }; envelopes.push(env); }); } return envelopes; } function getPeerIp(attributes) { if (attributes) { return attributes[semantic_conventions_1.ATTR_NETWORK_PEER_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_NET_PEER_IP]; } return; } function getLocationIp(tags, attributes) { if (attributes) { const httpClientIp = getHttpClientIp(attributes); const netPeerIp = getPeerIp(attributes); if (httpClientIp) { tags[index_js_1.KnownContextTagKeys.AiLocationIp] = String(httpClientIp); } else if (netPeerIp) { tags[index_js_1.KnownContextTagKeys.AiLocationIp] = String(netPeerIp); } } } function getHttpClientIp(attributes) { if (attributes) { return attributes[semantic_conventions_1.ATTR_CLIENT_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_HTTP_CLIENT_IP]; } return; } function getUserAgent(attributes) { if (attributes) { return 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 attributes[semantic_conventions_1.ATTR_URL_FULL] || attributes[semantic_conventions_1.SEMATTRS_HTTP_URL]; } return; } function getHttpMethod(attributes) { if (attributes) { return attributes[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD] || attributes[semantic_conventions_1.SEMATTRS_HTTP_METHOD]; } return; } function getHttpStatusCode(attributes) { if (attributes) { return attributes[semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE] || attributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE]; } return; } function getHttpScheme(attributes) { if (attributes) { return 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 attributes[semantic_conventions_1.ATTR_URL_PATH]; } if (attributes[semantic_conventions_1.ATTR_URL_QUERY]) { return attributes[semantic_conventions_1.ATTR_URL_QUERY]; } return attributes[semantic_conventions_1.SEMATTRS_HTTP_TARGET]; } return; } function getHttpHost(attributes) { if (attributes) { return attributes[semantic_conventions_1.ATTR_SERVER_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_HTTP_HOST]; } return; } function getNetPeerName(attributes) { if (attributes) { return attributes[semantic_conventions_1.ATTR_CLIENT_ADDRESS] || attributes[semantic_conventions_1.SEMATTRS_NET_PEER_NAME]; } return; } function getNetPeerPort(attributes) { if (attributes) { return (attributes[semantic_conventions_1.ATTR_CLIENT_PORT] || attributes[semantic_conventions_1.ATTR_SERVER_PORT] || attributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT]); } return; } //# sourceMappingURL=spanUtils.js.map