enhanced-adot-node-autoinstrumentation
Version:
This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.
117 lines • 7.32 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.AttributePropagatingSpanProcessor = void 0;
const api_1 = require("@opentelemetry/api");
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
const aws_attribute_keys_1 = require("./aws-attribute-keys");
const aws_span_processing_util_1 = require("./aws-span-processing-util");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
/**
* AttributePropagatingSpanProcessor handles the propagation of attributes from parent spans to
* child spans, specified in {@link attributesKeysToPropagate}. AttributePropagatingSpanProcessor
* also propagates configurable data from parent spans to child spans, as a new attribute specified
* by {@link propagationDataKey}. Propagated data can be configured via the {@link propagationDataExtractor}.
* Span data propagation only starts from local root server/consumer spans, but from there will
* be propagated to any descendant spans. If the span is a CONSUMER PROCESS with the parent also
* a CONSUMER, it will set attribute AWS_CONSUMER_PARENT_SPAN_KIND as CONSUMER to indicate that
* dependency metrics should not be generated for this span.
*/
class AttributePropagatingSpanProcessor {
constructor(propagationDataExtractor, propagationDataKey, attributesKeysToPropagate) {
this.propagationDataExtractor = propagationDataExtractor;
this.propagationDataKey = propagationDataKey;
this.attributesKeysToPropagate = attributesKeysToPropagate;
}
static create(propagationDataExtractor, propagationDataKey, attributesKeysToPropagate) {
return new AttributePropagatingSpanProcessor(propagationDataExtractor, propagationDataKey, attributesKeysToPropagate);
}
onStart(span, parentContext) {
// Divergence from Java/Python
// Workaround implemented in TypeScript. Calculation of isLocalRoot is not possible
// in `AwsSpanProcessingUtil.isLocalRoot` because the parent context is not accessible
// from a span. Therefore we pre-calculate its value here as an attribute.
aws_span_processing_util_1.AwsSpanProcessingUtil.setIsLocalRootInformation(span, parentContext);
const parentSpan = api_1.trace.getSpan(parentContext);
let parentReadableSpan = undefined;
// In Python and Java, the check is "parentSpan is an instance of ReadableSpan" is not possible
// in TypeScript because the check is not allowed for TypeScript interfaces (such as ReadableSpan).
// This is because JavaScript doesn't support interfaces, which is what TypeScript will compile to.
// `Span` is the only class that implements ReadableSpan, so check for instance of Span.
if (parentSpan instanceof sdk_trace_base_1.Span) {
parentReadableSpan = parentSpan;
// Add the AWS_SDK_DESCENDANT attribute to the immediate child spans of AWS SDK span.
// This attribute helps the backend differentiate between SDK spans and their immediate
// children.
// It's assumed that the HTTP spans are immediate children of the AWS SDK span
// TODO: we should have a contract test to check the immediate children are HTTP span
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isAwsSDKSpan(parentReadableSpan)) {
span.setAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_SDK_DESCENDANT, 'true');
}
if (api_1.SpanKind.INTERNAL === parentReadableSpan.kind) {
for (const keyToPropagate of this.attributesKeysToPropagate) {
const valueToPropagate = parentReadableSpan.attributes[keyToPropagate];
if (valueToPropagate !== undefined) {
span.setAttribute(keyToPropagate, valueToPropagate);
}
}
}
// We cannot guarantee that messaging.operation is set onStart, it could be set after the fact.
// To work around this, add the AWS_CONSUMER_PARENT_SPAN_KIND attribute if parent and child are
// both CONSUMER then check later if a metric should be generated.
if (this.isConsumerKind(span) && this.isConsumerKind(parentReadableSpan)) {
span.setAttribute(aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_CONSUMER_PARENT_SPAN_KIND, api_1.SpanKind[parentReadableSpan.kind]);
}
// If parent span contains "cloud.resource_id" or "faas.id" but not in child span, child span will be
// propagated with one of these attribute from parent. "cloud.resource_id" takes priority if it exists
const parentResourceId = aws_span_processing_util_1.AwsSpanProcessingUtil.getResourceId(parentSpan);
const resourceId = aws_span_processing_util_1.AwsSpanProcessingUtil.getResourceId(span);
if (!resourceId && parentResourceId) {
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isKeyPresent(parentSpan, aws_span_processing_util_1.AwsSpanProcessingUtil.CLOUD_RESOURCE_ID)) {
span.setAttribute(aws_span_processing_util_1.AwsSpanProcessingUtil.CLOUD_RESOURCE_ID, parentResourceId);
}
else {
span.setAttribute(semantic_conventions_1.SEMRESATTRS_FAAS_ID, parentResourceId);
}
}
}
let propagationData = undefined;
if (aws_span_processing_util_1.AwsSpanProcessingUtil.isLocalRoot(span)) {
if (!this.isServerKind(span)) {
propagationData = this.propagationDataExtractor(span);
}
}
else if (parentReadableSpan !== undefined && this.isServerKind(parentReadableSpan)) {
// In TypeScript, perform `parentReadableSpan !== undefined` check
// This should be done in Python and Java as well, but is not as of now
// If parentReadableSpan is not defined, the first `if statement` should occur,
// so that is why it is not a problem for Java/Python...
propagationData = this.propagationDataExtractor(parentReadableSpan);
}
else {
// In TypeScript, perform `parentReadableSpan?` check (returns undefined if undefined)
// This should be done in Python and Java as well, but is not as of now
propagationData = parentReadableSpan === null || parentReadableSpan === void 0 ? void 0 : parentReadableSpan.attributes[this.propagationDataKey];
}
if (propagationData !== undefined) {
span.setAttribute(this.propagationDataKey, propagationData);
}
}
isConsumerKind(span) {
return api_1.SpanKind.CONSUMER === span.kind;
}
isServerKind(span) {
return api_1.SpanKind.SERVER === span.kind;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onEnd(span) { }
shutdown() {
return this.forceFlush();
}
forceFlush() {
return Promise.resolve();
}
}
exports.AttributePropagatingSpanProcessor = AttributePropagatingSpanProcessor;
//# sourceMappingURL=attribute-propagating-span-processor.js.map