@sentry/core
Version:
Base implementation for all Sentry JavaScript SDKs
408 lines (404 loc) • 15.6 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 semanticAttributes = require('../semanticAttributes.js');
const baggage = require('../utils/baggage.js');
const debugLogger = require('../utils/debug-logger.js');
const handleCallbackErrors = require('../utils/handleCallbackErrors.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 SUPPRESS_TRACING_KEY = "__SENTRY_SUPPRESS_TRACING__";
function startSpan(options, callback) {
const acs = getAcs();
if (acs.startSpan) {
return acs.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(scope, customParentSpan);
const client = currentScopes.getClient();
const missingRequiredParent = options.onlyIfParent && !parentSpan;
const activeSpan = missingRequiredParent ? new sentryNonRecordingSpan.SentryNonRecordingSpan() : createChildOrRootSpan({
parentSpan,
spanArguments,
forceTransaction,
scope
});
if (missingRequiredParent) {
client?.recordDroppedEvent("no_parent_span", "span");
}
if (!_isIgnoredSpan(activeSpan) || !parentSpan) {
spanOnScope._setSpanForScope(scope, activeSpan);
}
return handleCallbackErrors.handleCallbackErrors(
() => callback(activeSpan),
() => {
const { status } = spanUtils.spanToJSON(activeSpan);
if (activeSpan.isRecording() && (!status || status === "ok")) {
activeSpan.setStatus({ code: spanstatus.SPAN_STATUS_ERROR, message: "internal_error" });
}
},
() => {
activeSpan.end();
}
);
});
});
}
function startSpanManual(options, callback) {
const acs = getAcs();
if (acs.startSpanManual) {
return acs.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(scope, customParentSpan);
const missingRequiredParent = options.onlyIfParent && !parentSpan;
const activeSpan = missingRequiredParent ? new sentryNonRecordingSpan.SentryNonRecordingSpan() : createChildOrRootSpan({
parentSpan,
spanArguments,
forceTransaction,
scope
});
if (missingRequiredParent) {
currentScopes.getClient()?.recordDroppedEvent("no_parent_span", "span");
}
if (!_isIgnoredSpan(activeSpan) || !parentSpan) {
spanOnScope._setSpanForScope(scope, activeSpan);
}
return handleCallbackErrors.handleCallbackErrors(
// We pass the `finish` function to the callback, so the user can finish the span manually
// this is mainly here for historic purposes because previously, we instructed users to call
// `finish` instead of `span.end()` to also clean up the scope. Nowadays, calling `span.end()`
// or `finish` has the same effect and we simply leave it here to avoid breaking user code.
() => callback(activeSpan, () => activeSpan.end()),
() => {
const { status } = spanUtils.spanToJSON(activeSpan);
if (activeSpan.isRecording() && (!status || status === "ok")) {
activeSpan.setStatus({ code: spanstatus.SPAN_STATUS_ERROR, message: "internal_error" });
}
}
);
});
});
}
function startInactiveSpan(options) {
const acs = getAcs();
if (acs.startInactiveSpan) {
return acs.startInactiveSpan(options);
}
const spanArguments = parseSentrySpanArguments(options);
const { forceTransaction, parentSpan: customParentSpan } = options;
const wrapper = options.scope ? (callback) => currentScopes.withScope(options.scope, callback) : customParentSpan !== void 0 ? (callback) => withActiveSpan(customParentSpan, callback) : (callback) => callback();
return wrapper(() => {
const scope = currentScopes.getCurrentScope();
const parentSpan = getParentSpan(scope, customParentSpan);
const client = currentScopes.getClient();
const missingRequiredParent = options.onlyIfParent && !parentSpan;
if (missingRequiredParent) {
client?.recordDroppedEvent("no_parent_span", "span");
return new sentryNonRecordingSpan.SentryNonRecordingSpan();
}
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);
spanOnScope._setSpanForScope(scope, void 0);
return 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({ [SUPPRESS_TRACING_KEY]: true });
const res = callback();
scope.setSDKProcessingMetadata({ [SUPPRESS_TRACING_KEY]: void 0 });
return res;
});
}
function startNewTrace(callback) {
const acs = getAcs();
if (acs.startNewTrace) {
return acs.startNewTrace(callback);
}
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 withActiveSpan(null, callback);
});
}
function createChildOrRootSpan({
parentSpan,
spanArguments,
forceTransaction,
scope
}) {
if (!hasSpansEnabled.hasSpansEnabled()) {
const span2 = new sentryNonRecordingSpan.SentryNonRecordingSpan();
if (forceTransaction || !parentSpan) {
const dsc = {
sampled: "false",
sample_rate: "0",
transaction: spanArguments.name,
...dynamicSamplingContext.getDynamicSamplingContextFromSpan(span2)
};
dynamicSamplingContext.freezeDscOnSpan(span2, dsc);
}
return span2;
}
const client = currentScopes.getClient();
if (_shouldIgnoreStreamedSpan(client, spanArguments)) {
if (!_isTracingSuppressed(scope)) {
client?.recordDroppedEvent("ignored", "span");
}
return new sentryNonRecordingSpan.SentryNonRecordingSpan({
dropReason: "ignored",
traceId: parentSpan?.spanContext().traceId ?? scope.getPropagationContext().traceId
});
}
const isolationScope = currentScopes.getIsolationScope();
let span;
if (parentSpan && !forceTransaction) {
span = _startChildSpan(parentSpan, scope, spanArguments);
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,
parentSampled
);
dynamicSamplingContext.freezeDscOnSpan(span, dsc);
} else {
const {
traceId,
dsc,
parentSpanId,
sampled: parentSampled
} = {
...isolationScope.getPropagationContext(),
...scope.getPropagationContext()
};
span = _startRootSpan(
{
traceId,
parentSpanId,
...spanArguments
},
scope,
parentSampled
);
if (dsc) {
dynamicSamplingContext.freezeDscOnSpan(span, dsc);
}
}
logSpans.logSpanStart(span);
utils.setCapturedScopesOnSpan(span, scope, isolationScope);
return span;
}
function parseSentrySpanArguments(options) {
const exp = options.experimental || {};
const initialCtx = {
isStandalone: exp.standalone,
...options
};
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, 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] = isTracingSuppressed ? [false] : sampling.sampleSpan(
options,
{
name,
parentSampled: finalParentSampled,
attributes: finalAttributes,
parentSampleRate: parseSampleRate.parseSampleRate(currentPropagationContext.dsc?.sample_rate)
},
currentPropagationContext.sampleRand
);
const rootSpan = new sentrySpan.SentrySpan({
...spanArguments,
attributes: {
[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_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("sample_rate", hasSpanStreamingEnabled.hasSpanStreamingEnabled(client) ? "span" : "transaction");
}
if (client) {
client.emit("spanStart", rootSpan);
}
return rootSpan;
}
function _startChildSpan(parentSpan, scope, spanArguments) {
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);
const client = currentScopes.getClient();
if (!client) {
return childSpan;
}
if (hasSpanStreamingEnabled.hasSpanStreamingEnabled(client) && childSpan instanceof sentryNonRecordingSpan.SentryNonRecordingSpan) {
if (parentSpan instanceof sentryNonRecordingSpan.SentryNonRecordingSpan && 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);
if (spanArguments.endTimestamp) {
client.emit("spanEnd", childSpan);
client.emit("afterSpanEnd", childSpan);
}
return childSpan;
}
function getParentSpan(scope, customParentSpan) {
if (customParentSpan) {
return customParentSpan;
}
if (customParentSpan === null) {
return void 0;
}
const span = spanOnScope._getSpanForScope(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 _isIgnoredSpan(span) {
return span instanceof sentryNonRecordingSpan.SentryNonRecordingSpan && span.dropReason === "ignored";
}
function _isTracingSuppressed(scope) {
return scope.getScopeData().sdkProcessingMetadata[SUPPRESS_TRACING_KEY] === true;
}
exports.SUPPRESS_TRACING_KEY = SUPPRESS_TRACING_KEY;
exports.continueTrace = continueTrace;
exports.startInactiveSpan = startInactiveSpan;
exports.startNewTrace = startNewTrace;
exports.startSpan = startSpan;
exports.startSpanManual = startSpanManual;
exports.suppressTracing = suppressTracing;
exports.withActiveSpan = withActiveSpan;
//# sourceMappingURL=trace.js.map