@aws/aws-distro-opentelemetry-node-autoinstrumentation
Version:
This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.
714 lines • 47 kB
JavaScript
;
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.AwsMetricAttributeGenerator = void 0;
const api_1 = require("@opentelemetry/api");
const resources_1 = require("@opentelemetry/resources");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const aws_attribute_keys_1 = require("./aws-attribute-keys");
const aws_span_processing_util_1 = require("./aws-span-processing-util");
const metric_attribute_generator_1 = require("./metric-attribute-generator");
const regional_resource_arn_parser_1 = require("./regional-resource-arn-parser");
const sqs_url_parser_1 = require("./sqs-url-parser");
const aws_opentelemetry_configurator_1 = require("./aws-opentelemetry-configurator");
// Does not exist in @opentelemetry/semantic-conventions
const _SERVER_SOCKET_ADDRESS = 'server.socket.address';
const _SERVER_SOCKET_PORT = 'server.socket.port';
const _NET_SOCK_PEER_ADDR = 'net.sock.peer.addr';
const _NET_SOCK_PEER_PORT = 'net.sock.peer.port';
// Alternatively, `import { SemanticAttributes } from '@opentelemetry/instrumentation-undici/build/src/enums/SemanticAttributes';`
// SemanticAttributes.SERVER_ADDRESS
// SemanticAttributes.SERVER_PORT
const _SERVER_ADDRESS = 'server.address';
const _SERVER_PORT = 'server.port';
// Alternatively, `import { AttributeNames } from '@opentelemetry/instrumentation-graphql/build/src/enums/AttributeNames';`
// AttributeNames.OPERATION_TYPE
const _GRAPHQL_OPERATION_TYPE = 'graphql.operation.type';
// Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present.
const GRAPHQL = 'graphql';
// Constants for Lambda operations
const LAMBDA_INVOKE_OPERATION = 'Invoke';
// Normalized remote service names for supported AWS services
const NORMALIZED_DYNAMO_DB_SERVICE_NAME = 'AWS::DynamoDB';
const NORMALIZED_KINESIS_SERVICE_NAME = 'AWS::Kinesis';
const NORMALIZED_S3_SERVICE_NAME = 'AWS::S3';
const NORMALIZED_SQS_SERVICE_NAME = 'AWS::SQS';
const NORMALIZED_SNS_SERVICE_NAME = 'AWS::SNS';
const NORMALIZED_SECRETSMANAGER_SERVICE_NAME = 'AWS::SecretsManager';
const NORMALIZED_STEPFUNCTIONS_SERVICE_NAME = 'AWS::StepFunctions';
const NORMALIZED_LAMBDA_SERVICE_NAME = 'AWS::Lambda';
const NORMALIZED_BEDROCK_SERVICE_NAME = 'AWS::Bedrock';
const NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME = 'AWS::BedrockRuntime';
const DB_CONNECTION_RESOURCE_TYPE = 'DB::Connection';
// As per https://opentelemetry.io/docs/specs/semconv/resource/#service, if service name is not specified, SDK defaults
// the service name to unknown_service:<process name> or just unknown_service.
// - https://github.com/open-telemetry/opentelemetry-js/blob/b2778e1b2ff7b038cebf371f1eb9f808fd98107f/packages/opentelemetry-resources/src/platform/node/default-service-name.ts#L16.
// - `defaultServiceName()` returns `unknown_service:${process.argv0}`
const OTEL_UNKNOWN_SERVICE = (0, resources_1.defaultServiceName)();
/**
* AwsMetricAttributeGenerator generates very specific metric attributes based on low-cardinality
* span and resource attributes. If such attributes are not present, we fallback to default values.
*
* <p>The goal of these particular metric attributes is to get metrics for incoming and outgoing
* traffic for a service. Namely, {@link SpanKind.SERVER} and {@link SpanKind.CONSUMER} spans
* represent "incoming" traffic, {@link SpanKind.CLIENT} and {@link SpanKind.PRODUCER} spans
* represent "outgoing" traffic, and {@link SpanKind.INTERNAL} spans are ignored.
*/
class AwsMetricAttributeGenerator {
// This method is used by the AwsSpanMetricsProcessor to generate service and dependency metrics
generateMetricAttributeMapFromSpan(span, resource) {
const attributesMap = {};
if (aws_span_processing_util_1.AwsSpanProcessingUtil.shouldGenerateServiceMetricAttributes(span)) {
attributesMap[metric_attribute_generator_1.SERVICE_METRIC] = this.generateServiceMetricAttributes(span, resource);
}
if (aws_span_processing_util_1.AwsSpanProcessingUtil.shouldGenerateDependencyMetricAttributes(span)) {
attributesMap[metric_attribute_generator_1.DEPENDENCY_METRIC] = this.generateDependencyMetricAttributes(span, resource);
}
return attributesMap;
}
generateServiceMetricAttributes(span, resource) {
const attributes = {};
AwsMetricAttributeGenerator.setService(resource, span, attributes);
AwsMetricAttributeGenerator.setIngressOperation(span, attributes);
AwsMetricAttributeGenerator.setSpanKindForService(span, attributes);
return attributes;
}
generateDependencyMetricAttributes(span, resource) {
const attributes = {};
AwsMetricAttributeGenerator.setService(resource, span, attributes);
AwsMetricAttributeGenerator.setEgressOperation(span, attributes);
AwsMetricAttributeGenerator.setRemoteServiceAndOperation(span, attributes);
const isRemoteResourceIdentifierPresent = AwsMetricAttributeGenerator.setRemoteResourceTypeAndIdentifier(span, attributes);
AwsMetricAttributeGenerator.setRemoteEnvironment(span, attributes);
if (isRemoteResourceIdentifierPresent) {
const isAccountIdAndRegionPresent = AwsMetricAttributeGenerator.setRemoteResourceAccountIdAndRegion(span, attributes);
if (!isAccountIdAndRegionPresent) {
AwsMetricAttributeGenerator.setRemoteResourceAccessKeyAndRegion(span, attributes);
}
}
AwsMetricAttributeGenerator.setSpanKindForDependency(span, attributes);
AwsMetricAttributeGenerator.setRemoteDbUser(span, attributes);
return attributes;
}
/** Service is always derived from {@link SEMRESATTRS_SERVICE_NAME} */
static setService(resource, span, attributes) {
let service = resource.attributes[semantic_conventions_1.SEMRESATTRS_SERVICE_NAME];
// In practice the service name is never undefined, but we can be defensive here.
if (service === undefined || service === OTEL_UNKNOWN_SERVICE) {
AwsMetricAttributeGenerator.logUnknownAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LOCAL_SERVICE, span);
service = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_SERVICE;
}
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LOCAL_SERVICE] = service;
}
/**
* Ingress operation (i.e. operation for Server and Consumer spans) will be generated from
* "http.method + http.target/with the first API path parameter" if the default span name equals
* null, UnknownOperation or http.method value.
*/
static setIngressOperation(span, attributes) {
const operation = aws_span_processing_util_1.AwsSpanProcessingUtil.getIngressOperation(span);
if (operation === aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_OPERATION) {
AwsMetricAttributeGenerator.logUnknownAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LOCAL_OPERATION, span);
}
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LOCAL_OPERATION] = operation;
}
/**
* Egress operation (i.e. operation for Client and Producer spans) is always derived from a
* special span attribute, {@link AWS_ATTRIBUTE_KEYS.AWS_LOCAL_OPERATION}. This attribute is
* generated with a separate SpanProcessor, {@link AttributePropagatingSpanProcessor}
*/
static setEgressOperation(span, attributes) {
let operation = aws_span_processing_util_1.AwsSpanProcessingUtil.getEgressOperation(span);
if (operation === undefined) {
AwsMetricAttributeGenerator.logUnknownAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LOCAL_OPERATION, span);
operation = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_OPERATION;
}
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LOCAL_OPERATION] = operation;
}
/**
* Remote attributes (only for Client and Producer spans) are generated based on low-cardinality
* span attributes, in priority order.
*
* <p>The first priority is the AWS Remote attributes, which are generated from manually
* instrumented span attributes, and are clear indications of customer intent. If AWS Remote
* attributes are not present, the next highest priority span attribute is Peer Service, which is
* also a reliable indicator of customer intent. If this is set, it will override
* AWS_REMOTE_SERVICE identified from any other span attribute, other than AWS Remote attributes.
*
* <p>After this, we look for the following low-cardinality span attributes that can be used to
* determine the remote metric attributes:
*
* <ul>
* <li>RPC
* <li>DB
* <li>FAAS
* <li>Messaging
* <li>GraphQL - Special case, if {@link _GRAPHQL_OPERATION_TYPE} is present,
* we use it for RemoteOperation and set RemoteService to {@link GRAPHQL}.
* </ul>
*
* <p>In each case, these span attributes were selected from the OpenTelemetry trace semantic
* convention specifications as they adhere to the three following criteria:
*
* <ul>
* <li>Attributes are meaningfully indicative of remote service/operation names.
* <li>Attributes are defined in the specification to be low cardinality, usually with a low-
* cardinality list of values.
* <li>Attributes are confirmed to have low-cardinality values, based on code analysis.
* </ul>
*
* if the selected attributes are still producing the UnknownRemoteService or
* UnknownRemoteOperation, `net.peer.name`, `net.peer.port`, `server.address`, `server.port`,
* `net.peer.sock.addr`, `net.peer.sock.port`, `http.url` and `url.full` will be used to derive
* the RemoteService. And `http.method`, `http.request.method`, `http.url` and `url.full` will be
* used to derive the RemoteOperation.
*/
static setRemoteServiceAndOperation(span, attributes) {
let remoteService = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE;
let remoteOperation = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_SERVICE) ||
aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_OPERATION)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_SERVICE);
remoteOperation = AwsMetricAttributeGenerator.getRemoteOperation(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_OPERATION);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_RPC_SERVICE) ||
aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_RPC_METHOD)) {
remoteService = AwsMetricAttributeGenerator.normalizeRemoteServiceName(span, AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_RPC_SERVICE));
remoteOperation = AwsMetricAttributeGenerator.getRemoteOperation(span, semantic_conventions_1.SEMATTRS_RPC_METHOD);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isDBSpan(span)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_DB_SYSTEM);
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_DB_OPERATION)) {
remoteOperation = AwsMetricAttributeGenerator.getRemoteOperation(span, semantic_conventions_1.SEMATTRS_DB_OPERATION);
}
else {
remoteOperation = AwsMetricAttributeGenerator.getDBStatementRemoteOperation(span, semantic_conventions_1.SEMATTRS_DB_STATEMENT);
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_FAAS_INVOKED_NAME) ||
aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_FAAS_TRIGGER)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_FAAS_INVOKED_NAME);
remoteOperation = AwsMetricAttributeGenerator.getRemoteOperation(span, semantic_conventions_1.SEMATTRS_FAAS_TRIGGER);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_MESSAGING_SYSTEM) ||
aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_MESSAGING_OPERATION)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_MESSAGING_SYSTEM);
remoteOperation = AwsMetricAttributeGenerator.getRemoteOperation(span, semantic_conventions_1.SEMATTRS_MESSAGING_OPERATION);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _GRAPHQL_OPERATION_TYPE)) {
remoteService = GRAPHQL;
remoteOperation = AwsMetricAttributeGenerator.getRemoteOperation(span, _GRAPHQL_OPERATION_TYPE);
}
// Peer service takes priority as RemoteService over everything but AWS Remote.
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_PEER_SERVICE) &&
!aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_SERVICE)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_PEER_SERVICE);
}
// try to derive RemoteService and RemoteOperation from the other related attributes
if (remoteService === aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE) {
remoteService = AwsMetricAttributeGenerator.generateRemoteService(span);
}
if (remoteOperation === aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION) {
remoteOperation = AwsMetricAttributeGenerator.generateRemoteOperation(span);
}
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_SERVICE] = remoteService;
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_OPERATION] = remoteOperation;
}
/**
* When the remote call operation is undetermined for http use cases, will try to extract the
* remote operation name from http url string
*/
static generateRemoteOperation(span) {
let remoteOperation = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
const httpUrlKey = aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_HTTP_URL)
? semantic_conventions_1.SEMATTRS_HTTP_URL
: aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.ATTR_URL_FULL)
? semantic_conventions_1.ATTR_URL_FULL
: undefined;
if (httpUrlKey !== undefined) {
const httpUrl = span.attributes[httpUrlKey];
try {
let url;
if (typeof httpUrl === 'string') {
url = new URL(httpUrl);
remoteOperation = aws_span_processing_util_1.AwsSpanProcessingUtil.extractAPIPathValue(url.pathname);
}
}
catch (e) {
api_1.diag.verbose(`invalid URL attribute: ${httpUrl}`);
}
}
const httpMethodKey = aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_HTTP_METHOD)
? semantic_conventions_1.SEMATTRS_HTTP_METHOD
: aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD)
? semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD
: undefined;
if (httpMethodKey !== undefined) {
const httpMethod = span.attributes[httpMethodKey];
if (typeof httpMethod === 'string') {
remoteOperation = httpMethod + ' ' + remoteOperation;
}
}
if (remoteOperation === aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION) {
AwsMetricAttributeGenerator.logUnknownAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_OPERATION, span);
}
return remoteOperation;
}
static generateRemoteService(span) {
let remoteService = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE;
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_NET_PEER_NAME)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_NET_PEER_NAME);
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_NET_PEER_PORT)) {
const port = span.attributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT];
remoteService += ':' + port;
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _NET_SOCK_PEER_ADDR)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, _NET_SOCK_PEER_ADDR);
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _NET_SOCK_PEER_PORT)) {
const port = span.attributes[_NET_SOCK_PEER_PORT];
remoteService += ':' + port;
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_HTTP_URL)) {
const urlStr = span.attributes[semantic_conventions_1.SEMATTRS_HTTP_URL];
try {
const url = new URL(urlStr);
if (url.hostname !== '') {
remoteService = url.hostname;
if (url.port !== '') {
remoteService += ':' + url.port;
}
}
}
catch (e) {
api_1.diag.verbose(`invalid URL attribute: ${urlStr}`);
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _SERVER_ADDRESS)) {
remoteService = AwsMetricAttributeGenerator.getRemoteService(span, _SERVER_ADDRESS);
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _SERVER_PORT)) {
const port = span.attributes[_SERVER_PORT];
remoteService += ':' + port;
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.ATTR_URL_FULL)) {
const urlStr = span.attributes[semantic_conventions_1.ATTR_URL_FULL];
try {
const url = new URL(urlStr);
if (url.hostname !== '') {
remoteService = url.hostname;
if (url.port !== '') {
remoteService += ':' + url.port;
}
}
}
catch (e) {
api_1.diag.verbose(`invalid URL attribute: ${urlStr}`);
}
}
else {
AwsMetricAttributeGenerator.logUnknownAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_SERVICE, span);
}
return remoteService;
}
/**
* If the span is an AWS SDK span, normalize the name to align with <a
* href="https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html">AWS
* Cloud Control resource format</a> as much as possible, with special attention to services we
* can detect remote resource information for. Long term, we would like to normalize service name
* in the upstream.
*
* For Bedrock, Bedrock Agent, and Bedrock Agent Runtime, we can align with AWS Cloud Control and use
* AWS::Bedrock for RemoteService. For BedrockRuntime, we are using AWS::BedrockRuntime
* as the associated remote resource (Model) is not listed in Cloud Control.
*/
static normalizeRemoteServiceName(span, serviceName) {
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isAwsSDKSpan(span)) {
const awsSdkServiceMapping = {
BedrockAgent: NORMALIZED_BEDROCK_SERVICE_NAME,
BedrockAgentRuntime: NORMALIZED_BEDROCK_SERVICE_NAME,
BedrockRuntime: NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME,
SecretsManager: NORMALIZED_SECRETSMANAGER_SERVICE_NAME,
SFN: NORMALIZED_STEPFUNCTIONS_SERVICE_NAME,
Lambda: NORMALIZED_LAMBDA_SERVICE_NAME,
};
// Special handling for Lambda invoke operations
if (AwsMetricAttributeGenerator.isLambdaInvokeOperation(span)) {
const lambdaFunctionName = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_FUNCTION_NAME];
// If Lambda name is not present, use UnknownRemoteService
// This is intentional - we want to clearly indicate when the Lambda function name
// is missing rather than falling back to a generic service name
return lambdaFunctionName ? String(lambdaFunctionName) : aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE;
}
return awsSdkServiceMapping[serviceName] || 'AWS::' + serviceName;
}
return serviceName;
}
/**
* Remote resource attributes {@link AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_TYPE} and
* {@link AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_IDENTIFIER} are used to store information about the
* resource associated with the remote invocation, such as S3 bucket name, etc. We should only
* ever set both type and identifier or neither. If any identifier value contains | or ^ , they
* will be replaced with ^| or ^^.
*
* <p>AWS resources type and identifier adhere to <a
* href="https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/supported-resources.html">AWS
* Cloud Control resource format</a>.
*/
static setRemoteResourceTypeAndIdentifier(span, attributes) {
let remoteResourceType;
let remoteResourceIdentifier;
let cloudFormationIdentifier;
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isAwsSDKSpan(span)) {
const awsTableNames = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES];
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES) &&
Array.isArray(awsTableNames) &&
awsTableNames.length === 1) {
remoteResourceType = NORMALIZED_DYNAMO_DB_SERVICE_NAME + '::Table';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(awsTableNames[0]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_ARN)) {
remoteResourceType = NORMALIZED_DYNAMO_DB_SERVICE_NAME + '::Table';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(regional_resource_arn_parser_1.RegionalResourceArnParser.extractDynamoDbTableNameFromArn(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_ARN]));
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_NAME)) {
remoteResourceType = NORMALIZED_KINESIS_SERVICE_NAME + '::Stream';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_NAME]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_ARN)) {
remoteResourceType = NORMALIZED_KINESIS_SERVICE_NAME + '::Stream';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(regional_resource_arn_parser_1.RegionalResourceArnParser.extractKinesisStreamNameFromArn(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_ARN]));
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_S3_BUCKET)) {
remoteResourceType = NORMALIZED_S3_SERVICE_NAME + '::Bucket';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_S3_BUCKET]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN)) {
const snsArn = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN];
remoteResourceType = NORMALIZED_SNS_SERVICE_NAME + '::Topic';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(regional_resource_arn_parser_1.RegionalResourceArnParser.extractResourceNameFromArn(snsArn));
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(snsArn);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN)) {
const secretsArn = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN];
remoteResourceType = NORMALIZED_SECRETSMANAGER_SERVICE_NAME + '::Secret';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(regional_resource_arn_parser_1.RegionalResourceArnParser.extractResourceNameFromArn(secretsArn));
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(secretsArn);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN)) {
const stateMachineArn = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN];
remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::StateMachine';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(regional_resource_arn_parser_1.RegionalResourceArnParser.extractResourceNameFromArn(stateMachineArn));
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(stateMachineArn);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN)) {
const activityArn = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN];
remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::Activity';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(regional_resource_arn_parser_1.RegionalResourceArnParser.extractResourceNameFromArn(activityArn));
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(activityArn);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_FUNCTION_NAME)) {
// For non-Invoke Lambda operations, treat Lambda as a resource,
// see normalizeRemoteServiceName for more information.
if (!AwsMetricAttributeGenerator.isLambdaInvokeOperation(span)) {
remoteResourceType = NORMALIZED_LAMBDA_SERVICE_NAME + '::Function';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_FUNCTION_NAME]);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_FUNCTION_ARN]);
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID)) {
remoteResourceType = NORMALIZED_LAMBDA_SERVICE_NAME + '::EventSourceMapping';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME)) {
remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL)) {
const sqsQueueUrl = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL]);
remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue';
remoteResourceIdentifier = sqs_url_parser_1.SqsUrlParser.getQueueName(sqsQueueUrl);
cloudFormationIdentifier = sqsQueueUrl;
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Agent';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::DataSource';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]);
cloudFormationIdentifier = `${AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID])}|${remoteResourceIdentifier}`;
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Guardrail';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::KnowledgeBase';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_span_processing_util_1.AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Model';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_span_processing_util_1.AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]);
}
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isDBSpan(span)) {
remoteResourceType = DB_CONNECTION_RESOURCE_TYPE;
remoteResourceIdentifier = AwsMetricAttributeGenerator.getDbConnection(span);
}
if (cloudFormationIdentifier === undefined) {
cloudFormationIdentifier = remoteResourceIdentifier;
}
if (remoteResourceType !== undefined && remoteResourceIdentifier !== undefined) {
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_TYPE] = remoteResourceType;
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_IDENTIFIER] = remoteResourceIdentifier;
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER] = cloudFormationIdentifier;
return true;
}
return false;
}
/**
* Remote environment is used to identify the environment of downstream services. Currently only
* set to "lambda:default" for Lambda Invoke operations when aws-api system is detected.
*/
static setRemoteEnvironment(span, attributes) {
var _a;
// We want to treat downstream Lambdas as a service rather than a resource because
// Application Signals topology map gets disconnected due to conflicting Lambda Entity
// definitions
// Additional context can be found in
// https://github.com/aws-observability/aws-otel-python-instrumentation/pull/319
if (AwsMetricAttributeGenerator.isLambdaInvokeOperation(span)) {
let remoteEnvironment = (_a = process.env[aws_opentelemetry_configurator_1.LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT]) === null || _a === void 0 ? void 0 : _a.trim();
if (!remoteEnvironment) {
remoteEnvironment = 'default';
}
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_ENVIRONMENT] = `lambda:${remoteEnvironment}`;
}
}
static setRemoteResourceAccountIdAndRegion(span, attributes) {
const ARN_ATTRIBUTES = [
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_FUNCTION_ARN,
aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN,
];
let remoteResourceAccountId = undefined;
let remoteResourceRegion = undefined;
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL)) {
const sqsQueueUrl = AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL]);
remoteResourceAccountId = sqs_url_parser_1.SqsUrlParser.getAccountId(sqsQueueUrl);
remoteResourceRegion = sqs_url_parser_1.SqsUrlParser.getRegion(sqsQueueUrl);
}
else {
for (const attributeKey of ARN_ATTRIBUTES) {
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, attributeKey)) {
const arn = span.attributes[attributeKey];
remoteResourceAccountId = regional_resource_arn_parser_1.RegionalResourceArnParser.getAccountId(arn);
remoteResourceRegion = regional_resource_arn_parser_1.RegionalResourceArnParser.getRegion(arn);
break;
}
}
}
if (remoteResourceAccountId !== undefined && remoteResourceRegion !== undefined) {
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_ACCOUNT_ID] = remoteResourceAccountId;
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_REGION] = remoteResourceRegion;
return true;
}
return false;
}
static setRemoteResourceAccessKeyAndRegion(span, attributes) {
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_AUTH_ACCOUNT_ACCESS_KEY)) {
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_ACCOUNT_ACCESS_KEY] =
span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_AUTH_ACCOUNT_ACCESS_KEY];
}
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_AUTH_REGION)) {
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_REGION] = span.attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_AUTH_REGION];
}
}
/**
* RemoteResourceIdentifier is populated with rule <code>
* ^[{db.name}|]?{address}[|{port}]?
* </code>
*
* <pre>
* {address} attribute is retrieved in priority order:
* - {@link _SERVER_ADDRESS},
* - {@link SEMATTRS_NET_PEER_NAME},
* - {@link _SERVER_SOCKET_ADDRESS}
* - {@link SEMATTRS_DB_CONNECTION_STRING}-Hostname
* </pre>
*
* <pre>
* {port} attribute is retrieved in priority order:
* - {@link _SERVER_PORT},
* - {@link SEMATTRS_NET_PEER_PORT},
* - {@link _SERVER_SOCKET_PORT}
* - {@link SEMATTRS_DB_CONNECTION_STRING}-Port
* </pre>
*
* If address is not present, neither RemoteResourceType nor RemoteResourceIdentifier will be
* provided.
*/
static getDbConnection(span) {
const dbName = span.attributes[semantic_conventions_1.SEMATTRS_DB_NAME];
let dbConnection;
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _SERVER_ADDRESS)) {
const serverAddress = span.attributes[_SERVER_ADDRESS];
const serverPort = span.attributes[_SERVER_PORT];
dbConnection = AwsMetricAttributeGenerator.buildDbConnection(serverAddress, serverPort);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_NET_PEER_NAME)) {
const networkPeerAddress = span.attributes[semantic_conventions_1.SEMATTRS_NET_PEER_NAME];
const networkPeerPort = span.attributes[semantic_conventions_1.SEMATTRS_NET_PEER_PORT];
dbConnection = AwsMetricAttributeGenerator.buildDbConnection(networkPeerAddress, networkPeerPort);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, _SERVER_SOCKET_ADDRESS)) {
const serverSocketAddress = span.attributes[_SERVER_SOCKET_ADDRESS];
const serverSocketPort = span.attributes[_SERVER_SOCKET_PORT];
dbConnection = AwsMetricAttributeGenerator.buildDbConnection(serverSocketAddress, serverSocketPort);
}
else if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_DB_CONNECTION_STRING)) {
const connectionString = span.attributes[semantic_conventions_1.SEMATTRS_DB_CONNECTION_STRING];
dbConnection = AwsMetricAttributeGenerator.buildDbConnectionString(connectionString);
}
// return empty resource identifier if db server is not found
if (dbConnection !== undefined && dbName !== undefined) {
return AwsMetricAttributeGenerator.escapeDelimiters(dbName) + '|' + dbConnection;
}
return dbConnection;
}
static buildDbConnection(address, port) {
if (typeof address !== 'string') {
return undefined;
}
return AwsMetricAttributeGenerator.escapeDelimiters(address) + (port !== undefined ? '|' + port : '');
}
static buildDbConnectionString(connectionString) {
if (typeof connectionString !== 'string') {
return undefined;
}
let uri;
let address;
let port;
try {
// Divergence from Java/Python
// `jdbc:<dababase>://` isn't handled well with `new URL()`
// uri.host and uri.port will be empty strings
// examples:
// - jdbc:postgresql://host:port/database?properties
// - jdbc:mysql://localhost:3306
// - abc:def:ghi://host:3306
// Try with a dummy schema without `:`, since we do not care about the schema
const schemeEndIndex = connectionString.indexOf('://');
if (schemeEndIndex === -1) {
uri = new URL(connectionString);
}
else {
uri = new URL('dummyschema' + connectionString.substring(schemeEndIndex));
}
address = uri.hostname;
port = uri.port;
}
catch (error) {
api_1.diag.verbose(`invalid DB ConnectionString: ${connectionString}`);
return undefined;
}
if (address === '') {
return undefined;
}
return AwsMetricAttributeGenerator.escapeDelimiters(address) + (port !== '' ? '|' + port : '');
}
static escapeDelimiters(input) {
if (typeof input !== 'string') {
return undefined;
}
// Divergence from Java/Python
// `replaceAll(a,b)` is not available, and `replace(a,b)` only replaces the first occurrence
// `split(a).join(b)` is not equivalent for all (a,b), but works with `a = '^'` or a = '|'`.
// Implementing some regex is also possible
// e.g. let re = new RegExp(String.raw`\s${variable}\s`, "g");
return input.split('^').join('^^').split('|').join('^|');
}
/**
* Check if the span represents a Lambda Invoke operation.
*/
static isLambdaInvokeOperation(span) {
if (!aws_span_processing_util_1.AwsSpanProcessingUtil.isAwsSDKSpan(span)) {
return false;
}
const rpcService = AwsMetricAttributeGenerator.getRemoteService(span, semantic_conventions_1.SEMATTRS_RPC_SERVICE);
return rpcService === 'Lambda' && span.attributes[semantic_conventions_1.SEMATTRS_RPC_METHOD] === LAMBDA_INVOKE_OPERATION;
}
/** Span kind is needed for differentiating metrics in the EMF exporter */
static setSpanKindForService(span, attributes) {
let spanKind = api_1.SpanKind[span.kind];
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isLocalRoot(span)) {
spanKind = aws_span_processing_util_1.AwsSpanProcessingUtil.LOCAL_ROOT;
}
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SPAN_KIND] = spanKind;
}
static setSpanKindForDependency(span, attributes) {
const spanKind = api_1.SpanKind[span.kind];
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SPAN_KIND] = spanKind;
}
static setRemoteDbUser(span, attributes) {
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isDBSpan(span) && aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(span, semantic_conventions_1.SEMATTRS_DB_USER)) {
attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_DB_USER] = span.attributes[semantic_conventions_1.SEMATTRS_DB_USER];
}
}
static getRemoteService(span, remoteServiceKey) {
let remoteService = span.attributes[remoteServiceKey];
if (typeof remoteService !== 'string') {
remoteService = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE;
}
return remoteService;
}
static getRemoteOperation(span, remoteOperationKey) {
let remoteOperation = span.attributes[remoteOperationKey];
if (typeof remoteOperation !== 'string') {
remoteOperation = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
}
return remoteOperation;
}
/**
* If no db.operation attribute provided in the span, we use db.statement to compute a valid
* remote operation in a best-effort manner. To do this, we take the first substring of the
* statement and compare to a regex list of known SQL keywords. The substring length is determined
* by the longest known SQL keywords.
*/
static getDBStatementRemoteOperation(span, remoteOperationKey) {
let remoteOperation = span.attributes[remoteOperationKey];
if (typeof remoteOperation !== 'string') {
remoteOperation = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
}
// Remove all whitespace and newline characters from the beginning of remote_operation
// and retrieve the first MAX_KEYWORD_LENGTH characters
remoteOperation = remoteOperation.trimStart();
if (remoteOperation.length > aws_span_processing_util_1.AwsSpanProcessingUtil.MAX_KEYWORD_LENGTH) {
remoteOperation = remoteOperation.substring(0, aws_span_processing_util_1.AwsSpanProcessingUtil.MAX_KEYWORD_LENGTH);
}
const matcher = remoteOperation
.toUpperCase()
.match(aws_span_processing_util_1.AwsSpanProcessingUtil.SQL_DIALECT_PATTERN);
if (matcher == null || matcher.length === 0) {
remoteOperation = aws_span_processing_util_1.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION;
}
else {
remoteOperation = matcher[0];
}
return remoteOperation;
}
static logUnknownAttribute(attributeKey, span) {
api_1.diag.verbose(`No valid ${attributeKey} value found for ${api_1.SpanKind[span.kind]} span ${span.spanContext().spanId}`);
}
}
exports.AwsMetricAttributeGenerator = AwsMetricAttributeGenerator;
//# sourceMappingURL=aws-metric-attribute-generator.js.map