@zendesk/retrace
Version:
define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API
121 lines • 6.28 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureSpanHasInheritedAttributes = ensureSpanHasInheritedAttributes;
exports.createParentSpanResolver = createParentSpanResolver;
const constants_1 = require("./constants");
const matchSpan_1 = require("./matchSpan");
const spanTypes_1 = require("./spanTypes");
const TickParentResolver_1 = require("./TickParentResolver");
function ensureSpanHasInheritedAttributes(span, heritableSpanAttributes) {
let allRequestedAttributesInheritedFromLineage = true;
for (const [key, value] of Object.entries(span.attributes)) {
const inheritanceType = value === constants_1.INHERIT_FROM_PARENT
? 'span-request'
: heritableSpanAttributes?.includes(key)
? 'always-requested'
: undefined;
if (!inheritanceType) {
// eslint-disable-next-line no-continue
continue;
}
allRequestedAttributesInheritedFromLineage = false;
let ancestorSpan = span;
const lineageRequiringAttributes = [];
// eslint-disable-next-line no-cond-assign
while ((ancestorSpan = ancestorSpan[spanTypes_1.PARENT_SPAN])) {
if (ancestorSpan.attributes[key] === constants_1.INHERIT_FROM_PARENT ||
inheritanceType === 'always-requested') {
// parent also requests inheritance, so we need to keep going
lineageRequiringAttributes.push(ancestorSpan);
}
else if (ancestorSpan.attributes[key] !== undefined) {
// found an ancestor with the attribute, let's assign it
// eslint-disable-next-line no-param-reassign
span.attributes[key] = ancestorSpan.attributes[key];
// let's also assign it to all the intermediary ancestors that requested inheritance
// as a performance optimization (less work to do later)
for (const ancestor of lineageRequiringAttributes) {
ancestor.attributes[key] = ancestorSpan.attributes[key];
}
allRequestedAttributesInheritedFromLineage = true;
break;
}
}
}
return allRequestedAttributesInheritedFromLineage;
}
function createParentSpanResolver(span, parentSpanMatcher, heritableSpanAttributes) {
let shouldAttemptToResolveParentAgain = true;
let inheritedRequestedAttributes = false;
return (context, recursive) => {
const getParentSpan = () => {
if (span[spanTypes_1.PARENT_SPAN] || !parentSpanMatcher) {
// short-circuit if we have the parent span, or if there is nowhere to search for it
return span[spanTypes_1.PARENT_SPAN];
}
if (!shouldAttemptToResolveParentAgain) {
return undefined;
}
const tickSource = parentSpanMatcher.search === 'span-created-tick' ||
!context.traceContext
? context.thisSpanAndAnnotation.span[TickParentResolver_1.TICK_META]
: parentSpanMatcher.search === 'span-ended-tick'
? context.thisSpanAndAnnotation.span[TickParentResolver_1.TICK_META_END]
: undefined;
if (tickSource?.spansInCurrentTick.tickCompleted) {
// do not attempt to resolve parent span again - if we haven't found in this iteration,
// we will not find it, because now more data will be added to the tick
shouldAttemptToResolveParentAgain = false;
}
const spanAndAnnotations = tickSource
? tickSource?.spansInCurrentTick.map((sp) => context.traceContext?.recordedItems.get(sp.id) ?? { span: sp }) ?? []
: // note: parentSpanMatcher.search === 'entire-recording' only works if the traceContext is available
[...context.traceContext.recordedItems.values()];
const parentSpanMatchFn = typeof parentSpanMatcher.match === 'object'
? (0, matchSpan_1.fromDefinition)(parentSpanMatcher.match)
: parentSpanMatcher.match;
// memoize the matcher function to avoid re-creating it on every call
// eslint-disable-next-line no-param-reassign
parentSpanMatcher.match = parentSpanMatchFn;
const thisSpanIndex = parentSpanMatcher.search === 'entire-recording'
? spanAndAnnotations.findIndex((spanAndAnnotation) => spanAndAnnotation.span.id === span.id)
: tickSource?.thisSpanInCurrentTickIndex ?? -1;
if (thisSpanIndex === -1) {
// invalid
return undefined;
}
const found = (0, matchSpan_1.findMatchingSpan)(parentSpanMatchFn, spanAndAnnotations, context.traceContext, {
...(parentSpanMatcher.searchDirection === 'after-self' && {
nthMatch: 0,
lowestIndexToConsider: thisSpanIndex + 1,
}),
...(parentSpanMatcher.searchDirection === 'before-self' && {
nthMatch: -1,
highestIndexToConsider: thisSpanIndex - 1,
}),
});
if (found) {
// cache the found parent span on the span itself
// so we don't have to search for it again
// eslint-disable-next-line no-param-reassign
span[spanTypes_1.PARENT_SPAN] = found.span;
return found.span;
}
return undefined;
};
const parentSpan = getParentSpan();
if (parentSpan) {
if (recursive) {
parentSpan.getParentSpan?.({
traceContext: context.traceContext,
thisSpanAndAnnotation: context.traceContext?.recordedItems.get(parentSpan.id) ?? { span: parentSpan },
}, recursive);
}
if (!inheritedRequestedAttributes) {
inheritedRequestedAttributes = ensureSpanHasInheritedAttributes(span, heritableSpanAttributes);
}
}
return parentSpan;
};
}
//# sourceMappingURL=createParentSpanResolver.js.map