@sentry/core
Version:
Base implementation for all Sentry JavaScript SDKs
413 lines (409 loc) • 16.5 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const index = require('../asyncContext/index.js');
const carrier = require('../carrier.js');
const currentScopes = require('../currentScopes.js');
const debugBuild = require('../debug-build.js');
const attributes = require('@sentry/conventions/attributes');
const semanticAttributes = require('../semanticAttributes.js');
const baggage = require('../utils/baggage.js');
const debugLogger = require('../utils/debug-logger.js');
const handleCallbackErrors = require('../utils/handleCallbackErrors.js');
const errorSpanAttribution = require('../utils/errorSpanAttribution.js');
const hasSpansEnabled = require('../utils/hasSpansEnabled.js');
const shouldIgnoreSpan = require('../utils/should-ignore-span.js');
const hasSpanStreamingEnabled = require('./spans/hasSpanStreamingEnabled.js');
const parseSampleRate = require('../utils/parseSampleRate.js');
const propagationContext = require('../utils/propagationContext.js');
const randomSafeContext = require('../utils/randomSafeContext.js');
const spanOnScope = require('../utils/spanOnScope.js');
const spanUtils = require('../utils/spanUtils.js');
const tracing = require('../utils/tracing.js');
const dynamicSamplingContext = require('./dynamicSamplingContext.js');
const logSpans = require('./logSpans.js');
const sampling = require('./sampling.js');
const sentryNonRecordingSpan = require('./sentryNonRecordingSpan.js');
const sentrySpan = require('./sentrySpan.js');
const spanstatus = require('./spanstatus.js');
const utils = require('./utils.js');
const constants = require('./constants.js');
function startSpan(options, callback) {
const spanArguments = parseSentrySpanArguments(options);
const { forceTransaction, parentSpan: customParentSpan, scope: customScope } = options;
const customForkedScope = customScope?.clone();
return currentScopes.withScope(customForkedScope, () => {
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const scope = currentScopes.getCurrentScope();
const parentSpan = getParentSpan(customScope ?? scope, customParentSpan);
const client = currentScopes.getClient();
const missingRequiredParent = options.onlyIfParent && !parentSpan;
const activeSpan = missingRequiredParent ? startMissingRequiredParentSpan(scope, client) : createChildOrRootSpan({
parentSpan,
spanArguments,
forceTransaction,
scope
});
const makeSpanActive = !spanIsIgnored(activeSpan) || !parentSpan;
return runCallback(
activeSpan,
makeSpanActive,
() => callback(activeSpan),
() => activeSpan.end()
);
});
});
}
function startSpanManual(options, callback) {
const spanArguments = parseSentrySpanArguments(options);
const { forceTransaction, parentSpan: customParentSpan, scope: customScope } = options;
const customForkedScope = customScope?.clone();
return currentScopes.withScope(customForkedScope, () => {
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const scope = currentScopes.getCurrentScope();
const parentSpan = getParentSpan(customScope ?? scope, customParentSpan);
const missingRequiredParent = options.onlyIfParent && !parentSpan;
const activeSpan = missingRequiredParent ? startMissingRequiredParentSpan(scope, currentScopes.getClient()) : createChildOrRootSpan({
parentSpan,
spanArguments,
forceTransaction,
scope
});
const makeSpanActive = !spanIsIgnored(activeSpan) || !parentSpan;
return runCallback(activeSpan, makeSpanActive, () => callback(activeSpan, () => activeSpan.end()));
});
});
}
function startInactiveSpan(options) {
const spanArguments = parseSentrySpanArguments(options);
const { forceTransaction, parentSpan: customParentSpan, scope: customScope } = options;
const wrapper = customScope ? (callback) => currentScopes.withScope(customScope, callback) : customParentSpan !== void 0 ? (callback) => withActiveSpan(customParentSpan, callback) : (callback) => callback();
return wrapper(() => {
const scope = currentScopes.getCurrentScope();
const parentSpan = getParentSpan(customScope ?? scope, customParentSpan);
const client = currentScopes.getClient();
const missingRequiredParent = options.onlyIfParent && !parentSpan;
if (missingRequiredParent) {
return startMissingRequiredParentSpan(scope, client);
}
return createChildOrRootSpan({
parentSpan,
spanArguments,
forceTransaction,
scope
});
});
}
const continueTrace = (options, callback) => {
const carrier$1 = carrier.getMainCarrier();
const acs = index.getAsyncContextStrategy(carrier$1);
if (acs.continueTrace) {
return acs.continueTrace(options, callback);
}
const { sentryTrace, baggage: baggage$1 } = options;
const client = currentScopes.getClient();
const incomingDsc = baggage.baggageHeaderToDynamicSamplingContext(baggage$1);
if (client && !tracing.shouldContinueTrace(client, incomingDsc?.org_id)) {
return startNewTrace(callback);
}
return currentScopes.withScope((scope) => {
const propagationContext = tracing.propagationContextFromHeaders(sentryTrace, baggage$1);
scope.setPropagationContext(propagationContext);
return withActiveSpan(null, callback);
});
};
function withActiveSpan(span, callback) {
const acs = getAcs();
if (acs.withActiveSpan) {
return acs.withActiveSpan(span, callback);
}
return currentScopes.withScope((scope) => {
spanOnScope._setSpanForScope(scope, span || void 0);
return callback(scope);
});
}
function suppressTracing(callback) {
const acs = getAcs();
if (acs.suppressTracing) {
return acs.suppressTracing(callback);
}
return currentScopes.withScope((scope) => {
scope.setSDKProcessingMetadata({ [constants.SUPPRESS_TRACING_KEY]: true });
return callback();
});
}
function isTracingSuppressed(scope = currentScopes.getCurrentScope()) {
const acs = getAcs();
if (acs.isTracingSuppressed) {
return acs.isTracingSuppressed(scope);
}
return scope.getScopeData().sdkProcessingMetadata[constants.SUPPRESS_TRACING_KEY] === true;
}
function startNewTrace(callback) {
const acs = getAcs();
if (acs.startNewTrace) {
return acs.startNewTrace(callback);
}
return withActiveSpan(null, () => {
return currentScopes.withScope((scope) => {
scope.setPropagationContext({
traceId: propagationContext.generateTraceId(),
sampleRand: randomSafeContext.safeMathRandom()
});
debugBuild.DEBUG_BUILD && debugLogger.debug.log(`Starting a new trace with id ${scope.getPropagationContext().traceId}`);
return callback();
});
});
}
function startMissingRequiredParentSpan(scope, client) {
client?.recordDroppedEvent("no_parent_span", "span");
const span = new sentryNonRecordingSpan.SentryNonRecordingSpan({ traceId: scope.getPropagationContext().traceId });
utils.setCapturedScopesOnSpan(span, scope, currentScopes.getIsolationScope());
return span;
}
function createChildOrRootSpan({
parentSpan: resolvedParentSpan,
spanArguments,
forceTransaction,
scope: currentScope
}) {
const isolationScope = currentScopes.getIsolationScope();
const spanScope = {
scope: currentScope,
parentSpan: resolvedParentSpan
};
currentScopes.getClient()?.emit("prepareSpanScope", spanScope);
const { scope, parentSpan } = spanScope;
if (!hasSpansEnabled.hasSpansEnabled()) {
const scopePropagationContext = scope.getPropagationContext();
const traceId = parentSpan ? parentSpan.spanContext().traceId : scopePropagationContext.traceId;
const span2 = new sentryNonRecordingSpan.SentryNonRecordingSpan({ traceId });
if (parentSpan && !forceTransaction) {
spanUtils.addChildSpanToSpan(parentSpan, span2);
}
utils.setCapturedScopesOnSpan(span2, scope, isolationScope);
return span2;
}
const client = currentScopes.getClient();
if (_shouldIgnoreStreamedSpan(client, spanArguments)) {
if (!isTracingSuppressed(scope)) {
client?.recordDroppedEvent("ignored", "span");
}
const ignoredSpan = new sentryNonRecordingSpan.SentryNonRecordingSpan({
dropReason: "ignored",
traceId: parentSpan?.spanContext().traceId ?? scope.getPropagationContext().traceId
});
if (parentSpan && !forceTransaction) {
spanUtils.addChildSpanToSpan(parentSpan, ignoredSpan);
}
utils.setCapturedScopesOnSpan(ignoredSpan, scope, isolationScope);
return ignoredSpan;
}
let span;
if (parentSpan && !forceTransaction) {
span = _startChildSpan(parentSpan, scope, spanArguments, isolationScope);
spanUtils.addChildSpanToSpan(parentSpan, span);
} else if (parentSpan) {
const dsc = dynamicSamplingContext.getDynamicSamplingContextFromSpan(parentSpan);
const { traceId, spanId: parentSpanId } = parentSpan.spanContext();
const parentSampled = spanUtils.spanIsSampled(parentSpan);
span = _startRootSpan(
{
traceId,
parentSpanId,
...spanArguments
},
scope,
isolationScope,
parentSampled
);
dynamicSamplingContext.freezeDscOnSpan(span, dsc);
} else {
const { traceId, dsc, parentSpanId, sampled: parentSampled, sampleRand } = scope.getPropagationContext();
span = _startRootSpan(
{
traceId,
parentSpanId,
...spanArguments
},
scope,
isolationScope,
parentSampled
);
if (dsc) {
const dscWithSampleRand = Object.keys(dsc).length === 0 && sampleRand !== void 0 ? { sample_rand: sampleRand.toString() } : dsc;
dynamicSamplingContext.freezeDscOnSpan(span, dscWithSampleRand);
}
}
logSpans.logSpanStart(span);
return span;
}
function parseSentrySpanArguments(options) {
const initialCtx = {
// TODO(standalone): remove once the static (transaction) trace lifecycle is dropped.
// oxlint-disable-next-line typescript/no-deprecated
isStandalone: options.experimental?.standalone,
...options
};
if (options.op) {
initialCtx.attributes = {
[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_OP]: options.op,
...options.attributes
};
}
if (options.startTime) {
const ctx = { ...initialCtx };
ctx.startTimestamp = spanUtils.spanTimeInputToSeconds(options.startTime);
delete ctx.startTime;
return ctx;
}
return initialCtx;
}
function getAcs() {
const carrier$1 = carrier.getMainCarrier();
return index.getAsyncContextStrategy(carrier$1);
}
function _startRootSpan(spanArguments, scope, isolationScope, parentSampled) {
const client = currentScopes.getClient();
const options = client?.getOptions() || {};
const { name = "" } = spanArguments;
const mutableSpanSamplingData = { spanAttributes: { ...spanArguments.attributes }, spanName: name, parentSampled };
client?.emit("beforeSampling", mutableSpanSamplingData, { decision: false });
const finalParentSampled = mutableSpanSamplingData.parentSampled ?? parentSampled;
const finalAttributes = mutableSpanSamplingData.spanAttributes;
const currentPropagationContext = scope.getPropagationContext();
const _isTracingSuppressed = isTracingSuppressed(scope);
const [sampled, sampleRate, localSampleRateWasApplied, dropReason] = _isTracingSuppressed ? [false] : sampling.sampleSpan(
options,
{
name,
parentSampled: finalParentSampled,
attributes: finalAttributes,
normalizedRequest: isolationScope.getScopeData().sdkProcessingMetadata.normalizedRequest,
parentSampleRate: parseSampleRate.parseSampleRate(currentPropagationContext.dsc?.sample_rate)
},
currentPropagationContext.sampleRand
);
const rootSpan = new sentrySpan.SentrySpan({
...spanArguments,
attributes: {
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "custom",
[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate !== void 0 && localSampleRateWasApplied ? sampleRate : void 0,
...finalAttributes
},
sampled
});
if (!sampled && client && !_isTracingSuppressed) {
debugBuild.DEBUG_BUILD && debugLogger.debug.log("[Tracing] Discarding root span because its trace was not chosen to be sampled.");
client.recordDroppedEvent(dropReason || "sample_rate", hasSpanStreamingEnabled.hasSpanStreamingEnabled(client) ? "span" : "transaction");
}
utils.setCapturedScopesOnSpan(rootSpan, scope, isolationScope);
if (client) {
client.emit("spanStart", rootSpan);
}
return rootSpan;
}
function _startChildSpan(parentSpan, scope, spanArguments, isolationScope) {
const { spanId, traceId } = parentSpan.spanContext();
const _isTracingSuppressed = isTracingSuppressed(scope);
const sampled = _isTracingSuppressed ? false : spanUtils.spanIsSampled(parentSpan);
const childSpan = sampled ? new sentrySpan.SentrySpan({
...spanArguments,
parentSpanId: spanId,
traceId,
sampled
}) : new sentryNonRecordingSpan.SentryNonRecordingSpan({ traceId });
spanUtils.addChildSpanToSpan(parentSpan, childSpan);
utils.setCapturedScopesOnSpan(childSpan, scope, isolationScope);
const client = currentScopes.getClient();
if (!client) {
return childSpan;
}
if (hasSpanStreamingEnabled.hasSpanStreamingEnabled(client) && sentryNonRecordingSpan.spanIsNonRecordingSpan(childSpan)) {
if (sentryNonRecordingSpan.spanIsNonRecordingSpan(parentSpan) && parentSpan.dropReason) {
childSpan.dropReason = parentSpan.dropReason;
client.recordDroppedEvent(parentSpan.dropReason, "span");
} else if (!_isTracingSuppressed) {
childSpan.dropReason = "sample_rate";
client.recordDroppedEvent("sample_rate", "span");
}
}
client.emit("spanStart", childSpan);
return childSpan;
}
function getParentSpan(scope, customParentSpan) {
if (customParentSpan) {
return customParentSpan;
}
if (customParentSpan === null) {
return void 0;
}
const span = spanUtils.getActiveSpan(scope);
if (!span) {
return void 0;
}
const client = currentScopes.getClient();
const options = client ? client.getOptions() : {};
if (options.parentSpanIsAlwaysRootSpan) {
return spanUtils.getRootSpan(span);
}
return span;
}
function getActiveSpanWrapper(parentSpan) {
return parentSpan !== void 0 ? (callback) => {
return withActiveSpan(parentSpan, callback);
} : (callback) => callback();
}
function _shouldIgnoreStreamedSpan(client, spanArguments) {
const ignoreSpans = client?.getOptions().ignoreSpans;
if (!client || !hasSpanStreamingEnabled.hasSpanStreamingEnabled(client) || !ignoreSpans?.length) {
return false;
}
return shouldIgnoreSpan.shouldIgnoreSpan(
{
description: spanArguments.name || "",
op: spanArguments.attributes?.[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_OP] || spanArguments.op,
attributes: spanArguments.attributes
},
ignoreSpans
);
}
function spanIsIgnored(span) {
return sentryNonRecordingSpan.spanIsNonRecordingSpan(span) && span.dropReason === "ignored";
}
function runCallback(span, makeSpanActive, callback, finallyCallback) {
const wrapper = makeSpanActive ? (callback2) => {
return withActiveSpan(span, () => {
const scope = currentScopes.getCurrentScope();
const creationScope = utils.getCapturedScopesOnSpan(span).scope;
if (creationScope) {
scope.setPropagationContext(creationScope.getPropagationContext());
}
utils.setCapturedScopesOnSpan(span, scope, currentScopes.getIsolationScope());
return callback2();
});
} : (callback2) => callback2();
return wrapper(
() => handleCallbackErrors.handleCallbackErrors(
() => callback(),
(error) => {
errorSpanAttribution.recordEscapedErrorSpan(error, span);
const { status } = spanUtils.spanToStaticSpanJSON(span);
if (span.isRecording() && status === "ok") {
span.setStatus({ code: spanstatus.SPAN_STATUS_ERROR, message: "internal_error" });
}
},
finallyCallback
)
);
}
exports.continueTrace = continueTrace;
exports.isTracingSuppressed = isTracingSuppressed;
exports.spanIsIgnored = spanIsIgnored;
exports.startInactiveSpan = startInactiveSpan;
exports.startNewTrace = startNewTrace;
exports.startSpan = startSpan;
exports.startSpanManual = startSpanManual;
exports.suppressTracing = suppressTracing;
exports.withActiveSpan = withActiveSpan;
//# sourceMappingURL=trace.js.map