🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@sentry/opentelemetry

Package Overview
Dependencies
Maintainers
1
Versions
312
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sentry/opentelemetry - npm Package Compare versions

Comparing version
10.61.0
to
10.62.0
+1702
build/cjs/resource-DuZKnQnC.js
const api = require('@opentelemetry/api');
const core = require('@sentry/core');
const attributes = require('@sentry/conventions/attributes');
const core$1 = require('@opentelemetry/core');
const sdkTraceBase = require('@opentelemetry/sdk-trace-base');
const SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE = "sentry.parentIsRemote";
const SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION = "sentry.graphql.operation";
function getParentSpanId(span) {
if ("parentSpanId" in span) {
return span.parentSpanId;
} else if ("parentSpanContext" in span) {
return span.parentSpanContext?.spanId;
}
return void 0;
}
function spanHasAttributes(span) {
const castSpan = span;
return !!castSpan.attributes && typeof castSpan.attributes === "object";
}
function spanHasKind(span) {
const castSpan = span;
return typeof castSpan.kind === "number";
}
function spanHasStatus(span) {
const castSpan = span;
return !!castSpan.status;
}
function spanHasName(span) {
const castSpan = span;
return !!castSpan.name;
}
function spanHasParentId(span) {
const castSpan = span;
return !!getParentSpanId(castSpan);
}
function spanHasEvents(span) {
const castSpan = span;
return Array.isArray(castSpan.events);
}
function getRequestSpanData(span) {
if (!spanHasAttributes(span)) {
return {};
}
const maybeUrlAttribute = span.attributes[attributes.URL_FULL] || span.attributes[attributes.HTTP_URL];
const data = {
url: maybeUrlAttribute,
// eslint-disable-next-line typescript/no-deprecated
"http.method": span.attributes[attributes.HTTP_REQUEST_METHOD] || span.attributes[attributes.HTTP_METHOD]
};
if (!data["http.method"] && data.url) {
data["http.method"] = "GET";
}
try {
if (typeof maybeUrlAttribute === "string") {
const url = core.parseUrl(maybeUrlAttribute);
data.url = core.getSanitizedUrlString(url);
if (url.search) {
data["http.query"] = url.search;
}
if (url.hash) {
data["http.fragment"] = url.hash;
}
}
} catch {
}
return data;
}
function wrapClientClass(ClientClass) {
class OpenTelemetryClient extends ClientClass {
constructor(...args) {
super(...args);
}
/** Get the OTEL tracer. */
get tracer() {
if (this._tracer) {
return this._tracer;
}
const name = "@sentry/opentelemetry";
const version = core.SDK_VERSION;
const tracer = api.trace.getTracer(name, version);
this._tracer = tracer;
return tracer;
}
/**
* @inheritDoc
*/
async flush(timeout) {
const provider = this.traceProvider;
await provider?.forceFlush();
return super.flush(timeout);
}
}
return OpenTelemetryClient;
}
function getSpanKind(span) {
if (spanHasKind(span)) {
return span.kind;
}
return api.SpanKind.INTERNAL;
}
const SENTRY_TRACE_HEADER = "sentry-trace";
const SENTRY_BAGGAGE_HEADER = "baggage";
const SENTRY_TRACE_STATE_DSC = "sentry.dsc";
const SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING = "sentry.sampled_not_recording";
const SENTRY_TRACE_STATE_URL = "sentry.url";
const SENTRY_TRACE_STATE_SAMPLE_RAND = "sentry.sample_rand";
const SENTRY_TRACE_STATE_SAMPLE_RATE = "sentry.sample_rate";
const SENTRY_TRACE_STATE_CHILD_IGNORED = "sentry.ignored";
const SENTRY_TRACE_STATE_SEGMENT_IGNORED = "sentry.segment_ignored";
const SENTRY_SCOPES_CONTEXT_KEY = api.createContextKey("sentry_scopes");
const SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY = api.createContextKey("sentry_fork_isolation_scope");
const SENTRY_FORK_SET_SCOPE_CONTEXT_KEY = api.createContextKey("sentry_fork_set_scope");
const SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY = api.createContextKey("sentry_fork_set_isolation_scope");
const SCOPE_CONTEXT_FIELD = "_scopeContext";
function getScopesFromContext(context) {
return context.getValue(SENTRY_SCOPES_CONTEXT_KEY);
}
function setScopesOnContext(context, scopes) {
return context.setValue(SENTRY_SCOPES_CONTEXT_KEY, scopes);
}
function setContextOnScope(scope, context) {
core.addNonEnumerableProperty(scope, SCOPE_CONTEXT_FIELD, core.makeWeakRef(context));
}
function getContextFromScope(scope) {
return core.derefWeakRef(scope[SCOPE_CONTEXT_FIELD]);
}
function isSentryRequestSpan(span) {
if (!spanHasAttributes(span)) {
return false;
}
const { attributes: attributes$1 } = span;
const httpUrl = attributes$1[attributes.HTTP_URL] || attributes$1[attributes.URL_FULL];
if (!httpUrl) {
return false;
}
return core.isSentryRequestUrl(httpUrl.toString(), core.getClient());
}
function getSamplingDecision(spanContext) {
const { traceFlags, traceState } = spanContext;
const sampledNotRecording = traceState ? traceState.get(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING) === "1" : false;
if (traceFlags === api.TraceFlags.SAMPLED) {
return true;
}
if (sampledNotRecording) {
return false;
}
const dscString = traceState ? traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? core.baggageHeaderToDynamicSamplingContext(dscString) : void 0;
if (dsc?.sampled === "true") {
return true;
}
if (dsc?.sampled === "false") {
return false;
}
return void 0;
}
function inferSpanData(spanName, attributes$1, kind) {
const httpMethod = attributes$1[attributes.HTTP_REQUEST_METHOD] || attributes$1[attributes.HTTP_METHOD];
if (httpMethod) {
return descriptionForHttpMethod({ attributes: attributes$1, name: spanName, kind }, httpMethod);
}
const dbSystem = attributes$1[attributes.DB_SYSTEM_NAME] || attributes$1[attributes.DB_SYSTEM];
const opIsCache = typeof attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] === "string" && attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_OP].startsWith("cache.");
if (dbSystem && !opIsCache) {
return descriptionForDbSystem({ attributes: attributes$1, name: spanName });
}
const customSourceOrRoute = attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom" ? "custom" : "route";
const rpcService = attributes$1[attributes.RPC_SERVICE];
if (rpcService) {
return {
...getUserUpdatedNameAndSource(spanName, attributes$1, "route"),
op: "rpc"
};
}
const messagingSystem = attributes$1[attributes.MESSAGING_SYSTEM];
if (messagingSystem) {
return {
...getUserUpdatedNameAndSource(spanName, attributes$1, customSourceOrRoute),
op: "message"
};
}
const faasTrigger = attributes$1[attributes.FAAS_TRIGGER];
if (faasTrigger) {
return {
...getUserUpdatedNameAndSource(spanName, attributes$1, customSourceOrRoute),
op: faasTrigger.toString()
};
}
return { op: void 0, description: spanName, source: "custom" };
}
function parseSpanDescription(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const name = spanHasName(span) ? span.name : "<unknown>";
const kind = getSpanKind(span);
return inferSpanData(name, attributes, kind);
}
function descriptionForDbSystem({ attributes: attributes$1, name }) {
const userDefinedName = attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (typeof userDefinedName === "string") {
return {
op: "db",
description: userDefinedName,
source: attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || "custom"
};
}
if (attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom") {
return { op: "db", description: name, source: "custom" };
}
const statement = attributes$1[attributes.DB_STATEMENT];
const description = statement ? statement.toString() : name;
return { op: "db", description, source: "task" };
}
function descriptionForHttpMethod({ name, kind, attributes }, httpMethod) {
const opParts = ["http"];
switch (kind) {
case api.SpanKind.CLIENT:
opParts.push("client");
break;
case api.SpanKind.SERVER:
opParts.push("server");
break;
}
if (attributes["sentry.http.prefetch"]) {
opParts.push("prefetch");
}
const { urlPath, url, query, fragment, hasRoute } = getSanitizedUrl(attributes, kind);
if (!urlPath) {
return { ...getUserUpdatedNameAndSource(name, attributes), op: opParts.join(".") };
}
const graphqlOperationsAttribute = attributes[SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION];
const baseDescription = `${httpMethod} ${urlPath}`;
const inferredDescription = graphqlOperationsAttribute ? `${baseDescription} (${getGraphqlOperationNamesFromAttribute(graphqlOperationsAttribute)})` : baseDescription;
const inferredSource = hasRoute || urlPath === "/" ? "route" : "url";
const data = {};
if (url) {
data.url = url;
}
if (query) {
data["http.query"] = query;
}
if (fragment) {
data["http.fragment"] = fragment;
}
const isClientOrServerKind = kind === api.SpanKind.CLIENT || kind === api.SpanKind.SERVER;
const origin = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] || "manual";
const isManualSpan = !`${origin}`.startsWith("auto");
const alreadyHasCustomSource = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom";
const customSpanName = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
const useInferredDescription = !alreadyHasCustomSource && customSpanName == null && (isClientOrServerKind || !isManualSpan);
const { description, source } = useInferredDescription ? { description: inferredDescription, source: inferredSource } : getUserUpdatedNameAndSource(name, attributes);
return {
op: opParts.join("."),
description,
source,
data
};
}
function getGraphqlOperationNamesFromAttribute(attr) {
if (Array.isArray(attr)) {
const sorted = attr.slice().sort();
if (sorted.length <= 5) {
return sorted.join(", ");
} else {
return `${sorted.slice(0, 5).join(", ")}, +${sorted.length - 5}`;
}
}
return `${attr}`;
}
function getSanitizedUrl(attributes$1, kind) {
const httpTarget = attributes$1[attributes.HTTP_TARGET];
const httpUrl = attributes$1[attributes.HTTP_URL] || attributes$1[attributes.URL_FULL];
const httpRoute = attributes$1[attributes.HTTP_ROUTE];
const parsedUrl = typeof httpUrl === "string" ? core.parseUrl(httpUrl) : void 0;
const url = parsedUrl ? core.getSanitizedUrlString(parsedUrl) : void 0;
const query = parsedUrl?.search || void 0;
const fragment = parsedUrl?.hash || void 0;
if (typeof httpRoute === "string") {
return { urlPath: httpRoute, url, query, fragment, hasRoute: true };
}
if (kind === api.SpanKind.SERVER && typeof httpTarget === "string") {
return { urlPath: core.stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
if (parsedUrl) {
return { urlPath: url, url, query, fragment, hasRoute: false };
}
if (typeof httpTarget === "string") {
return { urlPath: core.stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
return { urlPath: void 0, url, query, fragment, hasRoute: false };
}
function getUserUpdatedNameAndSource(originalName, attributes, fallbackSource = "custom") {
const source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || fallbackSource;
const description = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (description && typeof description === "string") {
return {
description,
source
};
}
return { description: originalName, source };
}
function enhanceDscWithOpenTelemetryRootSpanName(client) {
client.on("createDsc", (dsc, rootSpan) => {
if (!rootSpan) {
return;
}
const jsonSpan = core.spanToJSON(rootSpan);
const attributes = jsonSpan.data;
const source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
const { description } = spanHasName(rootSpan) ? parseSpanDescription(rootSpan) : { description: void 0 };
if (source !== "url" && description) {
dsc.transaction = description;
}
if (core.hasSpansEnabled()) {
const sampled = getSamplingDecision(rootSpan.spanContext());
dsc.sampled = sampled == void 0 ? void 0 : String(sampled);
}
});
}
function getActiveSpan() {
return api.trace.getActiveSpan();
}
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
class TraceState {
constructor() {
this._internalState = /* @__PURE__ */ new Map();
}
/** @inheritDoc */
set(key, value) {
const next = this._clone();
if (next._internalState.has(key)) {
next._internalState.delete(key);
}
next._internalState.set(key, value);
return next;
}
/** @inheritDoc */
unset(key) {
const next = this._clone();
next._internalState.delete(key);
return next;
}
/** @inheritDoc */
get(key) {
return this._internalState.get(key);
}
/** @inheritDoc */
serialize() {
return Array.from(this._internalState.keys()).reverse().map((key) => `${key}=${this._internalState.get(key)}`).join(",");
}
_clone() {
const next = new TraceState();
next._internalState = new Map(this._internalState);
return next;
}
}
function makeTraceState({
dsc,
sampled
}) {
const dscString = dsc ? core.dynamicSamplingContextToSentryBaggageHeader(dsc) : void 0;
const traceStateBase = new TraceState();
const traceStateWithDsc = dscString ? traceStateBase.set(SENTRY_TRACE_STATE_DSC, dscString) : traceStateBase;
return sampled === false ? traceStateWithDsc.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") : traceStateWithDsc;
}
const setupElements = /* @__PURE__ */ new Set();
function openTelemetrySetupCheck() {
return Array.from(setupElements);
}
function setIsSetup(element) {
setupElements.add(element);
}
class SentryPropagator extends core$1.W3CBaggagePropagator {
constructor() {
super();
setIsSetup("SentryPropagator");
this._urlMatchesTargetsMap = new core.LRUMap(100);
}
/**
* @inheritDoc
*/
inject(context2, carrier, setter) {
if (core$1.isTracingSuppressed(context2)) {
DEBUG_BUILD && core.debug.log("[Tracing] Not injecting trace data for url because tracing is suppressed.");
return;
}
const activeSpan = api.trace.getSpan(context2);
const url = activeSpan && getCurrentURL(activeSpan);
const { tracePropagationTargets, propagateTraceparent } = core.getClient()?.getOptions() || {};
if (!core.shouldPropagateTraceForUrl(url, tracePropagationTargets, this._urlMatchesTargetsMap)) {
DEBUG_BUILD && core.debug.log("[Tracing] Not injecting trace data for url because it does not match tracePropagationTargets:", url);
return;
}
const existingBaggageHeader = getExistingBaggage(carrier);
const existingSentryTraceHeader = getExistingSentryTrace(carrier);
let baggage = api.propagation.getBaggage(context2) || api.propagation.createBaggage({});
const { dynamicSamplingContext, traceId, spanId, sampled } = getInjectionData(context2);
if (existingBaggageHeader) {
const baggageEntries = core.parseBaggageHeader(existingBaggageHeader);
if (baggageEntries) {
Object.entries(baggageEntries).forEach(([key, value]) => {
if (!existingSentryTraceHeader && key.startsWith(core.SENTRY_BAGGAGE_KEY_PREFIX)) {
return;
}
baggage = baggage.setEntry(key, { value });
});
}
}
if (!existingSentryTraceHeader && dynamicSamplingContext) {
baggage = Object.entries(dynamicSamplingContext).reduce((b, [dscKey, dscValue]) => {
if (dscValue) {
return b.setEntry(`${core.SENTRY_BAGGAGE_KEY_PREFIX}${dscKey}`, { value: dscValue });
}
return b;
}, baggage);
}
if (!existingSentryTraceHeader && traceId && traceId !== api.INVALID_TRACEID) {
setter.set(carrier, SENTRY_TRACE_HEADER, core.generateSentryTraceHeader(traceId, spanId, sampled));
if (propagateTraceparent) {
setter.set(carrier, "traceparent", core.generateTraceparentHeader(traceId, spanId, sampled));
}
}
super.inject(api.propagation.setBaggage(context2, baggage), carrier, setter);
}
/**
* @inheritDoc
*/
extract(context2, carrier, getter) {
const maybeSentryTraceHeader = getter.get(carrier, SENTRY_TRACE_HEADER);
const baggage = getter.get(carrier, SENTRY_BAGGAGE_HEADER);
const sentryTrace = maybeSentryTraceHeader ? Array.isArray(maybeSentryTraceHeader) ? maybeSentryTraceHeader[0] : maybeSentryTraceHeader : void 0;
return ensureScopesOnContext(getContextWithRemoteActiveSpan(context2, { sentryTrace, baggage }));
}
/**
* @inheritDoc
*/
fields() {
return [SENTRY_TRACE_HEADER, SENTRY_BAGGAGE_HEADER, "traceparent"];
}
}
function getInjectionData(context2, options = {}) {
const span = api.trace.getSpan(context2);
if (span?.spanContext().isRemote) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = core.getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: void 0,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
if (span) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = core.getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: spanContext.spanId,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
const scope = options.scope || getScopesFromContext(context2)?.scope || core.getCurrentScope();
const client = options.client || core.getClient();
const propagationContext = scope.getPropagationContext();
const dynamicSamplingContext = client ? core.getDynamicSamplingContextFromScope(client, scope) : void 0;
return {
dynamicSamplingContext,
traceId: propagationContext.traceId,
spanId: propagationContext.propagationSpanId,
sampled: propagationContext.sampled
};
}
function getContextWithRemoteActiveSpan(ctx, { sentryTrace, baggage }) {
const propagationContext = core.propagationContextFromHeaders(sentryTrace, baggage);
const { traceId, parentSpanId, sampled, dsc } = propagationContext;
const client = core.getClient();
const incomingDsc = core.baggageHeaderToDynamicSamplingContext(baggage);
if (!parentSpanId || client && !core.shouldContinueTrace(client, incomingDsc?.org_id)) {
return ctx;
}
const spanContext = generateRemoteSpanContext({
traceId,
spanId: parentSpanId,
sampled,
dsc
});
return api.trace.setSpanContext(ctx, spanContext);
}
function continueTraceAsRemoteSpan(ctx, options, callback) {
const ctxWithSpanContext = ensureScopesOnContext(getContextWithRemoteActiveSpan(ctx, options));
return api.context.with(ctxWithSpanContext, callback);
}
function ensureScopesOnContext(ctx) {
const scopes = getScopesFromContext(ctx);
const newScopes = {
// If we have no scope here, this is most likely either the root context or a context manually derived from it
// In this case, we want to fork the current scope, to ensure we do not pollute the root scope
scope: scopes ? scopes.scope : core.getCurrentScope().clone(),
isolationScope: scopes ? scopes.isolationScope : core.getIsolationScope()
};
return setScopesOnContext(ctx, newScopes);
}
function getExistingBaggage(carrier) {
try {
const baggage = carrier[SENTRY_BAGGAGE_HEADER];
return Array.isArray(baggage) ? baggage.join(",") : baggage;
} catch {
return void 0;
}
}
function getExistingSentryTrace(carrier) {
try {
return carrier[SENTRY_TRACE_HEADER];
} catch {
return void 0;
}
}
function getCurrentURL(span) {
const spanData = core.spanToJSON(span).data;
const urlAttribute = spanData[attributes.HTTP_URL] || spanData[attributes.URL_FULL];
if (typeof urlAttribute === "string") {
return urlAttribute;
}
const urlTraceState = span.spanContext().traceState?.get(SENTRY_TRACE_STATE_URL);
if (urlTraceState) {
return urlTraceState;
}
return void 0;
}
function generateRemoteSpanContext({
spanId,
traceId,
sampled,
dsc
}) {
const traceState = makeTraceState({
dsc,
sampled
});
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE,
traceState
};
return spanContext;
}
function _startSpan(options, callback, autoEnd) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !api.trace.getSpan(activeCtx);
const ctx = missingRequiredParent ? core$1.suppressTracing(activeCtx) : activeCtx;
if (missingRequiredParent) {
core.getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!core.hasSpansEnabled()) {
const suppressedCtx = core$1.isTracingSuppressed(ctx) ? ctx : core$1.suppressTracing(ctx);
return api.context.with(suppressedCtx, () => {
return tracer.startActiveSpan(name, spanOptions, suppressedCtx, (span) => {
patchSpanEnd(span);
return api.context.with(activeCtx, () => {
return core.handleCallbackErrors(
() => callback(span),
() => {
if (core.spanToJSON(span).status === void 0) {
span.setStatus({ code: api.SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
});
}
return tracer.startActiveSpan(name, spanOptions, ctx, (span) => {
patchSpanEnd(span);
return core.handleCallbackErrors(
() => callback(span),
() => {
if (core.spanToJSON(span).status === void 0) {
span.setStatus({ code: api.SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
}
function startSpan(options, callback) {
return _startSpan(options, callback, true);
}
function startSpanManual(options, callback) {
return _startSpan(options, (span) => callback(span, () => span.end()), false);
}
function startInactiveSpan(options) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !api.trace.getSpan(activeCtx);
let ctx = missingRequiredParent ? core$1.suppressTracing(activeCtx) : activeCtx;
if (missingRequiredParent) {
core.getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!core.hasSpansEnabled()) {
ctx = core$1.isTracingSuppressed(ctx) ? ctx : core$1.suppressTracing(ctx);
}
const span = tracer.startSpan(name, spanOptions, ctx);
patchSpanEnd(span);
return span;
});
}
function withActiveSpan(span, callback) {
const newContextWithActiveSpan = span ? api.trace.setSpan(api.context.active(), span) : api.trace.deleteSpan(api.context.active());
return api.context.with(newContextWithActiveSpan, () => callback(core.getCurrentScope()));
}
function getTracer() {
const client = core.getClient();
return client?.tracer || api.trace.getTracer("@sentry/opentelemetry", core.SDK_VERSION);
}
function getSpanOptions(options) {
const { startTime, attributes, kind, op, links } = options;
const fixedStartTime = typeof startTime === "number" ? ensureTimestampInMilliseconds(startTime) : startTime;
return {
attributes: op ? {
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...attributes
} : attributes,
kind,
links,
startTime: fixedStartTime
};
}
function ensureTimestampInMilliseconds(timestamp) {
const isMs = timestamp < 9999999999;
return isMs ? timestamp * 1e3 : timestamp;
}
function patchSpanEnd(span) {
const originalEnd = span.end.bind(span);
span.end = (endTime) => {
return originalEnd(typeof endTime === "number" ? ensureTimestampInMilliseconds(endTime) : endTime);
};
}
function getContext(scope, forceTransaction) {
const ctx = getContextForScope(scope);
const parentSpan = api.trace.getSpan(ctx);
if (!parentSpan) {
return ctx;
}
if (!forceTransaction) {
return ctx;
}
const ctxWithoutSpan = api.trace.deleteSpan(ctx);
const { spanId, traceId } = parentSpan.spanContext();
const sampled = getSamplingDecision(parentSpan.spanContext());
const rootSpan = core.getRootSpan(parentSpan);
const dsc = core.getDynamicSamplingContextFromSpan(rootSpan);
const traceState = makeTraceState({
dsc,
sampled
});
const spanOptions = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE,
traceState
};
const ctxWithSpanContext = api.trace.setSpanContext(ctxWithoutSpan, spanOptions);
return ctxWithSpanContext;
}
function getContextForScope(scope) {
if (scope) {
const ctx = getContextFromScope(scope);
if (ctx) {
return ctx;
}
}
return api.context.active();
}
function continueTrace(options, callback) {
return continueTraceAsRemoteSpan(api.context.active(), options, callback);
}
function startNewTrace(callback) {
const traceId = core.generateTraceId();
const spanId = core.generateSpanId();
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: api.TraceFlags.NONE
};
const ctxWithTrace = api.trace.setSpanContext(api.context.active(), spanContext);
return api.context.with(ctxWithTrace, () => {
core.getCurrentScope().setPropagationContext({
traceId,
sampleRand: core._INTERNAL_safeMathRandom()
});
return callback();
});
}
function getTraceContextForScope(client, scope) {
const ctx = getContextFromScope(scope);
const span = ctx && api.trace.getSpan(ctx);
const traceContext = span ? core.spanToTraceContext(span) : core.getTraceContextFromScope(scope);
const dynamicSamplingContext = span ? core.getDynamicSamplingContextFromSpan(span) : core.getDynamicSamplingContextFromScope(client, scope);
return [dynamicSamplingContext, traceContext];
}
function getActiveSpanWrapper(parentSpan) {
return parentSpan !== void 0 ? (callback) => {
return withActiveSpan(parentSpan, callback);
} : (callback) => callback();
}
function suppressTracing(callback) {
const ctx = core$1.suppressTracing(api.context.active());
return api.context.with(ctx, callback);
}
function setupEventContextTrace(client) {
client.on("preprocessEvent", (event) => {
const span = getActiveSpan();
if (!span || event.type === "transaction") {
return;
}
event.contexts = {
trace: core.spanToTraceContext(span),
...event.contexts
};
const rootSpan = core.getRootSpan(span);
event.sdkProcessingMetadata = {
dynamicSamplingContext: core.getDynamicSamplingContextFromSpan(rootSpan),
...event.sdkProcessingMetadata
};
return event;
});
}
function getTraceData({
span,
scope,
client,
propagateTraceparent
} = {}) {
let ctx = (scope && getContextFromScope(scope)) ?? api.context.active();
if (span) {
const { scope: scope2 } = core.getCapturedScopesOnSpan(span);
ctx = scope2 && getContextFromScope(scope2) || api.trace.setSpan(api.context.active(), span);
}
const { traceId, spanId, sampled, dynamicSamplingContext } = getInjectionData(ctx, { scope, client });
const traceData = {
"sentry-trace": core.generateSentryTraceHeader(traceId, spanId, sampled),
baggage: core.dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext)
};
if (propagateTraceparent) {
traceData.traceparent = core.generateTraceparentHeader(traceId, spanId, sampled);
}
return traceData;
}
function setOpenTelemetryContextAsyncContextStrategy() {
function getScopes() {
const ctx = api.context.active();
const scopes = getScopesFromContext(ctx);
if (scopes) {
return scopes;
}
return {
scope: core.getDefaultCurrentScope(),
isolationScope: core.getDefaultIsolationScope()
};
}
function withScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx, () => {
return callback(getCurrentScope());
});
}
function withSetScope(scope, callback) {
const ctx = getContextFromScope(scope) || api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY, scope), () => {
return callback(scope);
});
}
function withIsolationScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY, true), () => {
return callback(getIsolationScope());
});
}
function withSetIsolationScope(isolationScope, callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY, isolationScope), () => {
return callback(getIsolationScope());
});
}
function getCurrentScope() {
return getScopes().scope;
}
function getIsolationScope() {
return getScopes().isolationScope;
}
core.setAsyncContextStrategy({
withScope,
withSetScope,
withSetIsolationScope,
withIsolationScope,
getCurrentScope,
getIsolationScope,
startSpan,
startSpanManual,
startInactiveSpan,
getActiveSpan,
suppressTracing,
getTraceData,
continueTrace,
startNewTrace,
// The types here don't fully align, because our own `Span` type is narrower
// than the OTEL one - but this is OK for here, as we now we'll only have OTEL spans passed around
withActiveSpan,
getTracingChannelBinding: () => {
try {
const contextManager = api.context._getContextManager();
const lookup = contextManager.getAsyncLocalStorageLookup();
return {
asyncLocalStorage: lookup.asyncLocalStorage,
getStoreWithActiveSpan: (span) => api.trace.setSpan(api.context.active(), span)
};
} catch {
return void 0;
}
}
});
}
function buildContextWithSentryScopes(context, activeContext) {
const span = api.trace.getSpan(context);
let effectiveContext;
if (span?.spanContext().traceState?.get(SENTRY_TRACE_STATE_CHILD_IGNORED) === "1") {
const contextWithoutSpan = api.trace.deleteSpan(context);
const parentSpan = api.trace.getSpan(activeContext);
effectiveContext = parentSpan ? api.trace.setSpan(contextWithoutSpan, parentSpan) : contextWithoutSpan;
} else {
effectiveContext = context;
}
const currentScopes = getScopesFromContext(effectiveContext);
const currentScope = currentScopes?.scope || core.getCurrentScope();
const currentIsolationScope = currentScopes?.isolationScope || core.getIsolationScope();
const shouldForkIsolationScope = effectiveContext.getValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY) === true;
const scope = effectiveContext.getValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY);
const isolationScope = effectiveContext.getValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
const newCurrentScope = scope || currentScope.clone();
const newIsolationScope = isolationScope || (shouldForkIsolationScope ? currentIsolationScope.clone() : currentIsolationScope);
const scopes = { scope: newCurrentScope, isolationScope: newIsolationScope };
const ctx1 = setScopesOnContext(effectiveContext, scopes);
const ctx2 = ctx1.deleteValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
setContextOnScope(newCurrentScope, ctx2);
return ctx2;
}
function wrapContextManagerClass(ContextManagerClass) {
class SentryContextManager extends ContextManagerClass {
constructor(...args) {
super(...args);
setIsSetup("SentryContextManager");
}
/**
* Overwrite with() of the original AsyncLocalStorageContextManager
* to ensure we also create new scopes per context.
*/
with(context, fn, thisArg, ...args) {
const ctx2 = buildContextWithSentryScopes(context, this.active());
return super.with(ctx2, fn, thisArg, ...args);
}
/**
* Gets underlying AsyncLocalStorage and symbol to allow lookup of scope.
*/
getAsyncLocalStorageLookup() {
return {
// @ts-expect-error This is on the base class, but not part of the interface
asyncLocalStorage: this._asyncLocalStorage,
contextSymbol: SENTRY_SCOPES_CONTEXT_KEY
};
}
}
return SentryContextManager;
}
function groupSpansWithParents(spans) {
const nodeMap = /* @__PURE__ */ new Map();
for (const span of spans) {
createOrUpdateSpanNodeAndRefs(nodeMap, span);
}
return Array.from(nodeMap, function([_id, spanNode]) {
return spanNode;
});
}
function getLocalParentId(span) {
const parentIsRemote = span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE] === true;
return !parentIsRemote ? getParentSpanId(span) : void 0;
}
function createOrUpdateSpanNodeAndRefs(nodeMap, span) {
const id = span.spanContext().spanId;
const parentId = getLocalParentId(span);
if (!parentId) {
createOrUpdateNode(nodeMap, { id, span, children: [] });
return;
}
const parentNode = createOrGetParentNode(nodeMap, parentId);
const node = createOrUpdateNode(nodeMap, { id, span, parentNode, children: [] });
parentNode.children.push(node);
}
function createOrGetParentNode(nodeMap, id) {
const existing = nodeMap.get(id);
if (existing) {
return existing;
}
return createOrUpdateNode(nodeMap, { id, children: [] });
}
function createOrUpdateNode(nodeMap, spanNode) {
const existing = nodeMap.get(spanNode.id);
if (existing?.span) {
return existing;
}
if (existing && !existing.span) {
existing.span = spanNode.span;
existing.parentNode = spanNode.parentNode;
return existing;
}
nodeMap.set(spanNode.id, spanNode);
return spanNode;
}
const canonicalGrpcErrorCodesMap = {
"1": "cancelled",
"2": "unknown_error",
"3": "invalid_argument",
"4": "deadline_exceeded",
"5": "not_found",
"6": "already_exists",
"7": "permission_denied",
"8": "resource_exhausted",
"9": "failed_precondition",
"10": "aborted",
"11": "out_of_range",
"12": "unimplemented",
"13": "internal_error",
"14": "unavailable",
"15": "data_loss",
"16": "unauthenticated"
};
const isStatusErrorMessageValid = (message) => {
return Object.values(canonicalGrpcErrorCodesMap).includes(message);
};
function mapStatus(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const status = spanHasStatus(span) ? span.status : void 0;
if (status) {
if (status.code === api.SpanStatusCode.OK) {
return { code: core.SPAN_STATUS_OK };
} else if (status.code === api.SpanStatusCode.ERROR) {
if (typeof status.message === "undefined") {
const inferredStatus2 = inferStatusFromAttributes(attributes);
if (inferredStatus2) {
return inferredStatus2;
}
}
if (status.message && isStatusErrorMessageValid(status.message)) {
return { code: core.SPAN_STATUS_ERROR, message: status.message };
} else {
return { code: core.SPAN_STATUS_ERROR, message: "internal_error" };
}
}
}
const inferredStatus = inferStatusFromAttributes(attributes);
if (inferredStatus) {
return inferredStatus;
}
if (status?.code === api.SpanStatusCode.UNSET) {
return { code: core.SPAN_STATUS_OK };
} else {
return { code: core.SPAN_STATUS_ERROR, message: "unknown_error" };
}
}
function inferStatusFromAttributes(attributes$1) {
const httpCodeAttribute = attributes$1[attributes.HTTP_RESPONSE_STATUS_CODE] || attributes$1[attributes.HTTP_STATUS_CODE];
const grpcCodeAttribute = attributes$1[attributes.RPC_GRPC_STATUS_CODE];
const numberHttpCode = typeof httpCodeAttribute === "number" ? httpCodeAttribute : typeof httpCodeAttribute === "string" ? parseInt(httpCodeAttribute) : void 0;
if (typeof numberHttpCode === "number") {
return core.getSpanStatusFromHttpCode(numberHttpCode);
}
if (typeof grpcCodeAttribute === "string") {
return { code: core.SPAN_STATUS_ERROR, message: canonicalGrpcErrorCodesMap[grpcCodeAttribute] || "unknown_error" };
}
return void 0;
}
const MAX_SPAN_COUNT = 1e3;
const DEFAULT_TIMEOUT = 300;
const SENT_SPANS_MAX_SIZE = 1e4;
class SentrySpanExporter {
constructor(options) {
this._finishedSpanBucketSize = options?.timeout || DEFAULT_TIMEOUT;
this._finishedSpanBuckets = new Array(this._finishedSpanBucketSize).fill(void 0);
this._lastCleanupTimestampInS = Math.floor(core._INTERNAL_safeDateNow() / 1e3);
this._spansToBucketEntry = /* @__PURE__ */ new WeakMap();
this._sentSpans = new core.LRUMap(SENT_SPANS_MAX_SIZE);
this._debouncedFlush = core.debounce(this.flush.bind(this), 1, { maxWait: 100 });
}
/**
* Export a single span.
* This is called by the span processor whenever a span is ended.
*/
export(span) {
const currentTimestampInS = Math.floor(core._INTERNAL_safeDateNow() / 1e3);
if (this._lastCleanupTimestampInS !== currentTimestampInS) {
let droppedSpanCount = 0;
this._finishedSpanBuckets.forEach((bucket, i) => {
if (bucket && bucket.timestampInS <= currentTimestampInS - this._finishedSpanBucketSize) {
droppedSpanCount += bucket.spans.size;
this._finishedSpanBuckets[i] = void 0;
}
});
if (droppedSpanCount > 0) {
DEBUG_BUILD && core.debug.log(
`SpanExporter dropped ${droppedSpanCount} spans because they were pending for more than ${this._finishedSpanBucketSize} seconds.`
);
}
this._lastCleanupTimestampInS = currentTimestampInS;
}
const currentBucketIndex = currentTimestampInS % this._finishedSpanBucketSize;
const currentBucket = this._finishedSpanBuckets[currentBucketIndex] || {
timestampInS: currentTimestampInS,
spans: /* @__PURE__ */ new Set()
};
this._finishedSpanBuckets[currentBucketIndex] = currentBucket;
currentBucket.spans.add(span);
this._spansToBucketEntry.set(span, currentBucket);
const localParentId = getLocalParentId(span);
if (!localParentId || this._sentSpans.get(localParentId)) {
this._debouncedFlush();
}
}
/**
* Try to flush any pending spans immediately.
* This is called internally by the exporter (via _debouncedFlush),
* but can also be triggered externally if we force-flush.
*/
flush() {
const finishedSpans = this._finishedSpanBuckets.flatMap((bucket) => bucket ? Array.from(bucket.spans) : []);
const sentSpans = this._maybeSend(finishedSpans);
const sentSpanCount = sentSpans.size;
const remainingOpenSpanCount = finishedSpans.length - sentSpanCount;
DEBUG_BUILD && core.debug.log(
`SpanExporter exported ${sentSpanCount} spans, ${remainingOpenSpanCount} spans are waiting for their parent spans to finish`
);
for (const span of sentSpans) {
this._sentSpans.set(span.spanContext().spanId, 1);
const bucketEntry = this._spansToBucketEntry.get(span);
if (bucketEntry) {
bucketEntry.spans.delete(span);
}
}
this._debouncedFlush.cancel();
}
/**
* Clear the exporter.
* This is called when the span processor is shut down.
*/
clear() {
this._finishedSpanBuckets = this._finishedSpanBuckets.fill(void 0);
this._sentSpans.clear();
this._debouncedFlush.cancel();
}
/**
* Send the given spans, but only if they are part of a finished transaction.
*
* Returns the sent spans.
* Spans remain unsent when their parent span is not yet finished.
* This will happen regularly, as child spans are generally finished before their parents.
* But it _could_ also happen because, for whatever reason, a parent span was lost.
* In this case, we'll eventually need to clean this up.
*/
_maybeSend(spans) {
const grouped = groupSpansWithParents(spans);
const sentSpans = /* @__PURE__ */ new Set();
const rootNodes = this._getCompletedRootNodes(grouped);
for (const root of rootNodes) {
const span = root.span;
sentSpans.add(span);
const transactionEvent = createTransactionForOtelSpan(span);
if (root.parentNode && this._sentSpans.get(root.parentNode.id)) {
const traceData = transactionEvent.contexts?.trace?.data;
if (traceData) {
traceData["sentry.parent_span_already_sent"] = true;
}
}
const spans2 = transactionEvent.spans || [];
let hasGenAiSpans = false;
for (const child of root.children) {
if (createAndFinishSpanForOtelSpan(child, spans2, sentSpans)) {
hasGenAiSpans = true;
}
}
transactionEvent.spans = spans2.length > MAX_SPAN_COUNT ? spans2.sort((a, b) => a.start_timestamp - b.start_timestamp).slice(0, MAX_SPAN_COUNT) : spans2;
if (hasGenAiSpans) {
transactionEvent.sdkProcessingMetadata = {
...transactionEvent.sdkProcessingMetadata,
hasGenAiSpans: true
};
}
const measurements = core.timedEventsToMeasurements(span.events);
if (measurements) {
transactionEvent.measurements = measurements;
}
core.captureEvent(transactionEvent);
}
return sentSpans;
}
/** Check if a node is a completed root node or a node whose parent has already been sent */
_nodeIsCompletedRootNodeOrHasSentParent(node) {
return !!node.span && (!node.parentNode || !!this._sentSpans.get(node.parentNode.id));
}
/** Get all completed root nodes from a list of nodes */
_getCompletedRootNodes(nodes) {
return nodes.filter((node) => this._nodeIsCompletedRootNodeOrHasSentParent(node));
}
}
function parseSpan(span) {
const attributes = span.attributes;
const origin = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];
const op = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP];
const source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
return { origin, op, source };
}
function createTransactionForOtelSpan(span) {
const { op, description, data, origin = "manual", source } = getSpanData(span);
const capturedSpanScopes = core.getCapturedScopesOnSpan(span);
const sampleRate = span.attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
const attributes$1 = {
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate,
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
...data,
...removeSentryAttributes(span.attributes)
};
const { links } = span;
const { traceId: trace_id, spanId: span_id } = span.spanContext();
const parent_span_id = getParentSpanId(span);
const status = mapStatus(span);
const traceContext = {
parent_span_id,
span_id,
trace_id,
data: attributes$1,
origin,
op,
status: core.getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
links: core.convertSpanLinksForEnvelope(links)
};
const statusCode = attributes$1[attributes.HTTP_RESPONSE_STATUS_CODE];
const responseContext = typeof statusCode === "number" ? { response: { status_code: statusCode } } : void 0;
const transactionEvent = {
contexts: {
trace: traceContext,
otel: {
resource: span.resource.attributes
},
...responseContext
},
spans: [],
start_timestamp: core.spanTimeInputToSeconds(span.startTime),
timestamp: core.spanTimeInputToSeconds(span.endTime),
transaction: description,
type: "transaction",
sdkProcessingMetadata: {
capturedSpanScope: capturedSpanScopes.scope,
capturedSpanIsolationScope: capturedSpanScopes.isolationScope,
sampleRate,
dynamicSamplingContext: core.getDynamicSamplingContextFromSpan(span)
},
...source && {
transaction_info: {
source
}
}
};
return transactionEvent;
}
function createAndFinishSpanForOtelSpan(node, spans, sentSpans) {
const span = node.span;
if (span) {
sentSpans.add(span);
}
const shouldDrop = !span;
if (shouldDrop) {
let hasGenAiSpans2 = false;
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans2 = true;
}
});
return hasGenAiSpans2;
}
const span_id = span.spanContext().spanId;
const trace_id = span.spanContext().traceId;
const parentSpanId = getParentSpanId(span);
const { attributes, startTime, endTime, links } = span;
const { op, description, data, origin = "manual" } = getSpanData(span);
const allData = {
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...removeSentryAttributes(attributes),
...data
};
const status = mapStatus(span);
const spanJSON = {
span_id,
trace_id,
data: allData,
description,
parent_span_id: parentSpanId,
start_timestamp: core.spanTimeInputToSeconds(startTime),
// This is [0,0] by default in OTEL, in which case we want to interpret this as no end time
timestamp: core.spanTimeInputToSeconds(endTime) || void 0,
status: core.getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
op,
origin,
measurements: core.timedEventsToMeasurements(span.events),
links: core.convertSpanLinksForEnvelope(links)
};
spans.push(spanJSON);
let hasGenAiSpans = !!op?.startsWith("gen_ai.");
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans = true;
}
});
return hasGenAiSpans;
}
function getSpanData(span) {
const { op: definedOp, source: definedSource, origin } = parseSpan(span);
const { op: inferredOp, description, source: inferredSource, data: inferredData } = parseSpanDescription(span);
const op = definedOp || inferredOp;
const source = definedSource || inferredSource;
const data = { ...inferredData, ...getData(span) };
return {
op,
description,
source,
origin,
data
};
}
function removeSentryAttributes(data) {
const cleanedData = { ...data };
delete cleanedData[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE];
delete cleanedData[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
return cleanedData;
}
function getData(span) {
const attributes$1 = span.attributes;
const data = {};
if (span.kind !== api.SpanKind.INTERNAL) {
data["otel.kind"] = api.SpanKind[span.kind];
}
const maybeHttpStatusCodeAttribute = attributes$1[attributes.HTTP_STATUS_CODE];
if (maybeHttpStatusCodeAttribute) {
data[attributes.HTTP_RESPONSE_STATUS_CODE] = maybeHttpStatusCodeAttribute;
}
const requestData = getRequestSpanData(span);
if (requestData.url) {
data.url = requestData.url;
}
if (requestData["http.query"]) {
data["http.query"] = requestData["http.query"].slice(1);
}
if (requestData["http.fragment"]) {
data["http.fragment"] = requestData["http.fragment"].slice(1);
}
return data;
}
class SentrySpanProcessor {
constructor(options) {
this._unsubscribePreprocessSpan = void 0;
setIsSetup("SentrySpanProcessor");
this._exporter = new SentrySpanExporter(options);
this._client = options?.client ?? core.getClient();
if (this._client && core.hasSpanStreamingEnabled(this._client)) {
this._unsubscribePreprocessSpan = this._client.on("preprocessSpan", backfillStreamedSpanDataFromOtel);
}
}
/**
* @inheritDoc
*/
async forceFlush() {
this._exporter.flush();
}
/**
* @inheritDoc
*/
async shutdown() {
this._unsubscribePreprocessSpan?.();
this._exporter.clear();
}
/**
* @inheritDoc
*/
onStart(span, parentContext) {
const parentSpan = api.trace.getSpan(parentContext);
let scopes = getScopesFromContext(parentContext);
if (parentSpan && !parentSpan.spanContext().isRemote) {
core.addChildSpanToSpan(parentSpan, span);
}
if (parentSpan?.spanContext().isRemote) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE, true);
}
if (parentContext === api.ROOT_CONTEXT) {
scopes = {
scope: core.getDefaultCurrentScope(),
isolationScope: core.getDefaultIsolationScope()
};
}
if (scopes) {
core.setCapturedScopesOnSpan(span, scopes.scope, scopes.isolationScope);
}
core.logSpanStart(span);
this._client?.emit("spanStart", span);
}
/** @inheritDoc */
onEnd(span) {
core.logSpanEnd(span);
this._client?.emit("spanEnd", span);
if (this._client && core.hasSpanStreamingEnabled(this._client)) {
this._client.emit("afterSpanEnd", span);
} else {
this._exporter.export(span);
}
}
}
function backfillStreamedSpanDataFromOtel(spanJSON, hint) {
const attributes = spanJSON.attributes;
if (!attributes) {
return;
}
const kind = hint?.spanKind ?? core.SPAN_KIND.INTERNAL;
const { op, description, source, data } = inferSpanData(spanJSON.name, attributes, kind);
spanJSON.name = description;
core.safeSetSpanJSONAttributes(spanJSON, {
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
...data
});
if (kind !== core.SPAN_KIND.INTERNAL) {
core.safeSetSpanJSONAttributes(spanJSON, {
"otel.kind": core.spanKindToName(kind)
});
}
}
class SentrySampler {
constructor(client) {
this._client = client;
this._isSpanStreaming = core.hasSpanStreamingEnabled(client);
setIsSetup("SentrySampler");
}
/** @inheritDoc */
shouldSample(context, traceId, spanName, spanKind, spanAttributes, _links) {
const options = this._client.getOptions();
const { ignoreSpans } = options;
const parentSpan = getValidSpan(context);
const parentContext = parentSpan?.spanContext();
if (!core.hasSpansEnabled(options)) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const maybeSpanHttpMethod = spanAttributes[attributes.HTTP_METHOD] || spanAttributes[attributes.HTTP_REQUEST_METHOD];
if (spanKind === api.SpanKind.CLIENT && maybeSpanHttpMethod && (!parentSpan || parentContext?.isRemote)) {
if (!this._isSpanStreaming) {
this._client.recordDroppedEvent("no_parent_span", "span");
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
}
const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : void 0;
const isRootSpan = !parentSpan || parentContext?.isRemote;
if (!isRootSpan) {
if (this._isSpanStreaming) {
if (parentSampled) {
if (ignoreSpans?.length) {
const { description: inferredChildName, op: childOp } = inferSpanData(spanName, spanAttributes, spanKind);
if (core.shouldIgnoreSpan(
{
description: inferredChildName,
op: spanAttributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? childOp,
attributes: spanAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredChildSpan: true
});
}
}
}
if (!parentSampled) {
const parentSegmentIgnored = parentContext?.traceState?.get(SENTRY_TRACE_STATE_SEGMENT_IGNORED) === "1";
this._client.recordDroppedEvent(parentSegmentIgnored ? "ignored" : "sample_rate", "span");
}
}
return wrapSamplingDecision({
decision: parentSampled ? sdkTraceBase.SamplingDecision.RECORD_AND_SAMPLED : sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes
});
}
const {
description: inferredSpanName,
data: inferredAttributes,
op
} = inferSpanData(spanName, spanAttributes, spanKind);
const mergedAttributes = {
...inferredAttributes,
...spanAttributes
};
if (op) {
mergedAttributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] = op;
}
if (this._isSpanStreaming && ignoreSpans?.length && core.shouldIgnoreSpan(
{
description: inferredSpanName,
op: mergedAttributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? op,
attributes: mergedAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredSegmentSpan: true
});
}
const mutableSamplingDecision = { decision: true };
this._client.emit(
"beforeSampling",
{
spanAttributes: mergedAttributes,
spanName: inferredSpanName,
parentSampled,
parentContext
},
mutableSamplingDecision
);
if (!mutableSamplingDecision.decision) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const { isolationScope } = getScopesFromContext(context) ?? {};
const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? core.baggageHeaderToDynamicSamplingContext(dscString) : void 0;
const sampleRand = core.parseSampleRate(dsc?.sample_rand) ?? core._INTERNAL_safeMathRandom();
const [sampled, sampleRate, localSampleRateWasApplied] = core.sampleSpan(
options,
{
name: inferredSpanName,
attributes: mergedAttributes,
normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest,
parentSampled,
parentSampleRate: core.parseSampleRate(dsc?.sample_rate)
},
sampleRand
);
const method = `${maybeSpanHttpMethod}`.toUpperCase();
if (method === "OPTIONS" || method === "HEAD") {
DEBUG_BUILD && core.debug.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
return wrapSamplingDecision({
decision: sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: 0
// we don't want to sample anything in the downstream trace either
});
}
if (!sampled && // We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace)
parentSampled === void 0) {
DEBUG_BUILD && core.debug.log("[Tracing] Discarding root span because its trace was not chosen to be sampled.");
this._client.recordDroppedEvent("sample_rate", this._isSpanStreaming ? "span" : "transaction");
}
return {
...wrapSamplingDecision({
decision: sampled ? sdkTraceBase.SamplingDecision.RECORD_AND_SAMPLED : sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : void 0
}),
attributes: {
// We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry
[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : void 0
}
};
}
/** Returns the sampler name or short description with the configuration. */
toString() {
return "SentrySampler";
}
}
function getParentSampled(parentSpan, traceId, spanName) {
const parentContext = parentSpan.spanContext();
if (api.isSpanContextValid(parentContext) && parentContext.traceId === traceId) {
if (parentContext.isRemote) {
const parentSampled2 = getSamplingDecision(parentSpan.spanContext());
DEBUG_BUILD && core.debug.log(`[Tracing] Inheriting remote parent's sampled decision for ${spanName}: ${parentSampled2}`);
return parentSampled2;
}
const parentSampled = getSamplingDecision(parentContext);
DEBUG_BUILD && core.debug.log(`[Tracing] Inheriting parent's sampled decision for ${spanName}: ${parentSampled}`);
return parentSampled;
}
return void 0;
}
function wrapSamplingDecision({
decision,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate,
ignoredChildSpan,
ignoredSegmentSpan
}) {
let traceState = getBaseTraceState(context, spanAttributes);
if (downstreamTraceSampleRate !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RATE, `${downstreamTraceSampleRate}`);
}
if (sampleRand !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RAND, `${sampleRand}`);
}
if (ignoredChildSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_CHILD_IGNORED, "1");
}
if (ignoredSegmentSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_SEGMENT_IGNORED, "1");
}
if (decision == void 0) {
return { decision: sdkTraceBase.SamplingDecision.NOT_RECORD, traceState };
}
if (decision === sdkTraceBase.SamplingDecision.NOT_RECORD) {
return { decision, traceState: traceState.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") };
}
return { decision, traceState };
}
function getBaseTraceState(context, spanAttributes) {
const parentSpan = api.trace.getSpan(context);
const parentContext = parentSpan?.spanContext();
let traceState = parentContext?.traceState || new TraceState();
const url = spanAttributes[attributes.HTTP_URL] || spanAttributes[attributes.URL_FULL];
if (url && typeof url === "string") {
traceState = traceState.set(SENTRY_TRACE_STATE_URL, url);
}
return traceState;
}
function getValidSpan(context) {
const span = api.trace.getSpan(context);
return span && api.isSpanContextValid(span.spanContext()) ? span : void 0;
}
const ATTR_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language";
const ATTR_TELEMETRY_SDK_NAME = "telemetry.sdk.name";
const ATTR_TELEMETRY_SDK_VERSION = "telemetry.sdk.version";
const SEMRESATTRS_SERVICE_NAMESPACE = "service.namespace";
class SentryResource {
constructor(attributes) {
this._attributes = attributes;
}
get attributes() {
return this._attributes;
}
merge(other) {
if (!other) {
return this;
}
return new SentryResource({ ...this._attributes, ...other.attributes });
}
getRawAttributes() {
return Object.entries(this._attributes);
}
}
function parseOtelResourceAttributes(raw) {
if (!raw) {
return {};
}
const result = {};
for (const pair of raw.split(",")) {
const eq = pair.indexOf("=");
if (eq === -1) {
continue;
}
const key = pair.substring(0, eq).trim();
const value = pair.substring(eq + 1).trim();
if (key) {
try {
result[key] = decodeURIComponent(value);
} catch {
result[key] = value;
}
}
}
return result;
}
function getSentryResource(serviceNameFallback) {
const env = typeof process !== "undefined" ? process.env : {};
const otelServiceName = env.OTEL_SERVICE_NAME;
const otelResourceAttrs = parseOtelResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
return new SentryResource({
// Lowest priority: Sentry defaults
// eslint-disable-next-line typescript/no-deprecated
[SEMRESATTRS_SERVICE_NAMESPACE]: "sentry",
[attributes.SERVICE_NAME]: serviceNameFallback,
// OTEL_RESOURCE_ATTRIBUTES overrides defaults (including service.name and service.namespace)
...otelResourceAttrs,
// OTEL_SERVICE_NAME explicitly overrides service.name
...otelServiceName ? { [attributes.SERVICE_NAME]: otelServiceName } : {},
// Highest priority: Sentry SDK telemetry attrs (cannot be overridden by env vars)
[attributes.SERVICE_VERSION]: core.SDK_VERSION,
[ATTR_TELEMETRY_SDK_LANGUAGE]: core$1.SDK_INFO[ATTR_TELEMETRY_SDK_LANGUAGE],
[ATTR_TELEMETRY_SDK_NAME]: core$1.SDK_INFO[ATTR_TELEMETRY_SDK_NAME],
[ATTR_TELEMETRY_SDK_VERSION]: core$1.SDK_INFO[ATTR_TELEMETRY_SDK_VERSION]
});
}
exports.SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION = SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION;
exports.SENTRY_SCOPES_CONTEXT_KEY = SENTRY_SCOPES_CONTEXT_KEY;
exports.SentryPropagator = SentryPropagator;
exports.SentrySampler = SentrySampler;
exports.SentrySpanProcessor = SentrySpanProcessor;
exports.buildContextWithSentryScopes = buildContextWithSentryScopes;
exports.continueTrace = continueTrace;
exports.enhanceDscWithOpenTelemetryRootSpanName = enhanceDscWithOpenTelemetryRootSpanName;
exports.getActiveSpan = getActiveSpan;
exports.getRequestSpanData = getRequestSpanData;
exports.getScopesFromContext = getScopesFromContext;
exports.getSentryResource = getSentryResource;
exports.getSpanKind = getSpanKind;
exports.getTraceContextForScope = getTraceContextForScope;
exports.isSentryRequestSpan = isSentryRequestSpan;
exports.openTelemetrySetupCheck = openTelemetrySetupCheck;
exports.setIsSetup = setIsSetup;
exports.setOpenTelemetryContextAsyncContextStrategy = setOpenTelemetryContextAsyncContextStrategy;
exports.setupEventContextTrace = setupEventContextTrace;
exports.spanHasAttributes = spanHasAttributes;
exports.spanHasEvents = spanHasEvents;
exports.spanHasKind = spanHasKind;
exports.spanHasName = spanHasName;
exports.spanHasParentId = spanHasParentId;
exports.spanHasStatus = spanHasStatus;
exports.startInactiveSpan = startInactiveSpan;
exports.startSpan = startSpan;
exports.startSpanManual = startSpanManual;
exports.suppressTracing = suppressTracing;
exports.withActiveSpan = withActiveSpan;
exports.wrapClientClass = wrapClientClass;
exports.wrapContextManagerClass = wrapContextManagerClass;
exports.wrapSamplingDecision = wrapSamplingDecision;
//# sourceMappingURL=resource-DuZKnQnC.js.map

Sorry, the diff of this file is too big to display

import * as api from '@opentelemetry/api';
import { trace, SpanKind, createContextKey, TraceFlags, propagation, INVALID_TRACEID, context, SpanStatusCode, ROOT_CONTEXT, isSpanContextValid } from '@opentelemetry/api';
import { parseUrl, getSanitizedUrlString, SDK_VERSION, derefWeakRef, addNonEnumerableProperty, makeWeakRef, isSentryRequestUrl, getClient, baggageHeaderToDynamicSamplingContext, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME, stripUrlQueryAndFragment, spanToJSON, hasSpansEnabled, dynamicSamplingContextToSentryBaggageHeader, LRUMap, debug, shouldPropagateTraceForUrl, parseBaggageHeader, SENTRY_BAGGAGE_KEY_PREFIX, generateSentryTraceHeader, generateTraceparentHeader, getDynamicSamplingContextFromSpan, getCurrentScope, getDynamicSamplingContextFromScope, getIsolationScope, propagationContextFromHeaders, shouldContinueTrace, spanToTraceContext, getTraceContextFromScope, generateTraceId, generateSpanId, _INTERNAL_safeMathRandom, getRootSpan, handleCallbackErrors, getCapturedScopesOnSpan, setAsyncContextStrategy, getDefaultIsolationScope, getDefaultCurrentScope, SPAN_STATUS_OK, SPAN_STATUS_ERROR, getSpanStatusFromHttpCode, _INTERNAL_safeDateNow, debounce, timedEventsToMeasurements, captureEvent, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, convertSpanLinksForEnvelope, getStatusMessage, spanTimeInputToSeconds, hasSpanStreamingEnabled, addChildSpanToSpan, setCapturedScopesOnSpan, logSpanStart, logSpanEnd, SPAN_KIND, safeSetSpanJSONAttributes, spanKindToName, shouldIgnoreSpan, parseSampleRate, sampleSpan } from '@sentry/core';
import { URL_FULL, HTTP_URL, HTTP_REQUEST_METHOD, HTTP_METHOD, DB_SYSTEM_NAME, DB_SYSTEM, RPC_SERVICE, MESSAGING_SYSTEM, FAAS_TRIGGER, DB_STATEMENT, HTTP_TARGET, HTTP_ROUTE, HTTP_RESPONSE_STATUS_CODE, HTTP_STATUS_CODE, RPC_GRPC_STATUS_CODE, SERVICE_VERSION, SERVICE_NAME } from '@sentry/conventions/attributes';
import { W3CBaggagePropagator, isTracingSuppressed, suppressTracing as suppressTracing$1, SDK_INFO } from '@opentelemetry/core';
import { SamplingDecision } from '@opentelemetry/sdk-trace-base';
const SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE = "sentry.parentIsRemote";
const SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION = "sentry.graphql.operation";
function getParentSpanId(span) {
if ("parentSpanId" in span) {
return span.parentSpanId;
} else if ("parentSpanContext" in span) {
return span.parentSpanContext?.spanId;
}
return void 0;
}
function spanHasAttributes(span) {
const castSpan = span;
return !!castSpan.attributes && typeof castSpan.attributes === "object";
}
function spanHasKind(span) {
const castSpan = span;
return typeof castSpan.kind === "number";
}
function spanHasStatus(span) {
const castSpan = span;
return !!castSpan.status;
}
function spanHasName(span) {
const castSpan = span;
return !!castSpan.name;
}
function spanHasParentId(span) {
const castSpan = span;
return !!getParentSpanId(castSpan);
}
function spanHasEvents(span) {
const castSpan = span;
return Array.isArray(castSpan.events);
}
function getRequestSpanData(span) {
if (!spanHasAttributes(span)) {
return {};
}
const maybeUrlAttribute = span.attributes[URL_FULL] || span.attributes[HTTP_URL];
const data = {
url: maybeUrlAttribute,
// eslint-disable-next-line typescript/no-deprecated
"http.method": span.attributes[HTTP_REQUEST_METHOD] || span.attributes[HTTP_METHOD]
};
if (!data["http.method"] && data.url) {
data["http.method"] = "GET";
}
try {
if (typeof maybeUrlAttribute === "string") {
const url = parseUrl(maybeUrlAttribute);
data.url = getSanitizedUrlString(url);
if (url.search) {
data["http.query"] = url.search;
}
if (url.hash) {
data["http.fragment"] = url.hash;
}
}
} catch {
}
return data;
}
function wrapClientClass(ClientClass) {
class OpenTelemetryClient extends ClientClass {
constructor(...args) {
super(...args);
}
/** Get the OTEL tracer. */
get tracer() {
if (this._tracer) {
return this._tracer;
}
const name = "@sentry/opentelemetry";
const version = SDK_VERSION;
const tracer = trace.getTracer(name, version);
this._tracer = tracer;
return tracer;
}
/**
* @inheritDoc
*/
async flush(timeout) {
const provider = this.traceProvider;
await provider?.forceFlush();
return super.flush(timeout);
}
}
return OpenTelemetryClient;
}
function getSpanKind(span) {
if (spanHasKind(span)) {
return span.kind;
}
return SpanKind.INTERNAL;
}
const SENTRY_TRACE_HEADER = "sentry-trace";
const SENTRY_BAGGAGE_HEADER = "baggage";
const SENTRY_TRACE_STATE_DSC = "sentry.dsc";
const SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING = "sentry.sampled_not_recording";
const SENTRY_TRACE_STATE_URL = "sentry.url";
const SENTRY_TRACE_STATE_SAMPLE_RAND = "sentry.sample_rand";
const SENTRY_TRACE_STATE_SAMPLE_RATE = "sentry.sample_rate";
const SENTRY_TRACE_STATE_CHILD_IGNORED = "sentry.ignored";
const SENTRY_TRACE_STATE_SEGMENT_IGNORED = "sentry.segment_ignored";
const SENTRY_SCOPES_CONTEXT_KEY = createContextKey("sentry_scopes");
const SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY = createContextKey("sentry_fork_isolation_scope");
const SENTRY_FORK_SET_SCOPE_CONTEXT_KEY = createContextKey("sentry_fork_set_scope");
const SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY = createContextKey("sentry_fork_set_isolation_scope");
const SCOPE_CONTEXT_FIELD = "_scopeContext";
function getScopesFromContext(context) {
return context.getValue(SENTRY_SCOPES_CONTEXT_KEY);
}
function setScopesOnContext(context, scopes) {
return context.setValue(SENTRY_SCOPES_CONTEXT_KEY, scopes);
}
function setContextOnScope(scope, context) {
addNonEnumerableProperty(scope, SCOPE_CONTEXT_FIELD, makeWeakRef(context));
}
function getContextFromScope(scope) {
return derefWeakRef(scope[SCOPE_CONTEXT_FIELD]);
}
function isSentryRequestSpan(span) {
if (!spanHasAttributes(span)) {
return false;
}
const { attributes } = span;
const httpUrl = attributes[HTTP_URL] || attributes[URL_FULL];
if (!httpUrl) {
return false;
}
return isSentryRequestUrl(httpUrl.toString(), getClient());
}
function getSamplingDecision(spanContext) {
const { traceFlags, traceState } = spanContext;
const sampledNotRecording = traceState ? traceState.get(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING) === "1" : false;
if (traceFlags === TraceFlags.SAMPLED) {
return true;
}
if (sampledNotRecording) {
return false;
}
const dscString = traceState ? traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : void 0;
if (dsc?.sampled === "true") {
return true;
}
if (dsc?.sampled === "false") {
return false;
}
return void 0;
}
function inferSpanData(spanName, attributes, kind) {
const httpMethod = attributes[HTTP_REQUEST_METHOD] || attributes[HTTP_METHOD];
if (httpMethod) {
return descriptionForHttpMethod({ attributes, name: spanName, kind }, httpMethod);
}
const dbSystem = attributes[DB_SYSTEM_NAME] || attributes[DB_SYSTEM];
const opIsCache = typeof attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] === "string" && attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP].startsWith("cache.");
if (dbSystem && !opIsCache) {
return descriptionForDbSystem({ attributes, name: spanName });
}
const customSourceOrRoute = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom" ? "custom" : "route";
const rpcService = attributes[RPC_SERVICE];
if (rpcService) {
return {
...getUserUpdatedNameAndSource(spanName, attributes, "route"),
op: "rpc"
};
}
const messagingSystem = attributes[MESSAGING_SYSTEM];
if (messagingSystem) {
return {
...getUserUpdatedNameAndSource(spanName, attributes, customSourceOrRoute),
op: "message"
};
}
const faasTrigger = attributes[FAAS_TRIGGER];
if (faasTrigger) {
return {
...getUserUpdatedNameAndSource(spanName, attributes, customSourceOrRoute),
op: faasTrigger.toString()
};
}
return { op: void 0, description: spanName, source: "custom" };
}
function parseSpanDescription(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const name = spanHasName(span) ? span.name : "<unknown>";
const kind = getSpanKind(span);
return inferSpanData(name, attributes, kind);
}
function descriptionForDbSystem({ attributes, name }) {
const userDefinedName = attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (typeof userDefinedName === "string") {
return {
op: "db",
description: userDefinedName,
source: attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || "custom"
};
}
if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom") {
return { op: "db", description: name, source: "custom" };
}
const statement = attributes[DB_STATEMENT];
const description = statement ? statement.toString() : name;
return { op: "db", description, source: "task" };
}
function descriptionForHttpMethod({ name, kind, attributes }, httpMethod) {
const opParts = ["http"];
switch (kind) {
case SpanKind.CLIENT:
opParts.push("client");
break;
case SpanKind.SERVER:
opParts.push("server");
break;
}
if (attributes["sentry.http.prefetch"]) {
opParts.push("prefetch");
}
const { urlPath, url, query, fragment, hasRoute } = getSanitizedUrl(attributes, kind);
if (!urlPath) {
return { ...getUserUpdatedNameAndSource(name, attributes), op: opParts.join(".") };
}
const graphqlOperationsAttribute = attributes[SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION];
const baseDescription = `${httpMethod} ${urlPath}`;
const inferredDescription = graphqlOperationsAttribute ? `${baseDescription} (${getGraphqlOperationNamesFromAttribute(graphqlOperationsAttribute)})` : baseDescription;
const inferredSource = hasRoute || urlPath === "/" ? "route" : "url";
const data = {};
if (url) {
data.url = url;
}
if (query) {
data["http.query"] = query;
}
if (fragment) {
data["http.fragment"] = fragment;
}
const isClientOrServerKind = kind === SpanKind.CLIENT || kind === SpanKind.SERVER;
const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] || "manual";
const isManualSpan = !`${origin}`.startsWith("auto");
const alreadyHasCustomSource = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom";
const customSpanName = attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
const useInferredDescription = !alreadyHasCustomSource && customSpanName == null && (isClientOrServerKind || !isManualSpan);
const { description, source } = useInferredDescription ? { description: inferredDescription, source: inferredSource } : getUserUpdatedNameAndSource(name, attributes);
return {
op: opParts.join("."),
description,
source,
data
};
}
function getGraphqlOperationNamesFromAttribute(attr) {
if (Array.isArray(attr)) {
const sorted = attr.slice().sort();
if (sorted.length <= 5) {
return sorted.join(", ");
} else {
return `${sorted.slice(0, 5).join(", ")}, +${sorted.length - 5}`;
}
}
return `${attr}`;
}
function getSanitizedUrl(attributes, kind) {
const httpTarget = attributes[HTTP_TARGET];
const httpUrl = attributes[HTTP_URL] || attributes[URL_FULL];
const httpRoute = attributes[HTTP_ROUTE];
const parsedUrl = typeof httpUrl === "string" ? parseUrl(httpUrl) : void 0;
const url = parsedUrl ? getSanitizedUrlString(parsedUrl) : void 0;
const query = parsedUrl?.search || void 0;
const fragment = parsedUrl?.hash || void 0;
if (typeof httpRoute === "string") {
return { urlPath: httpRoute, url, query, fragment, hasRoute: true };
}
if (kind === SpanKind.SERVER && typeof httpTarget === "string") {
return { urlPath: stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
if (parsedUrl) {
return { urlPath: url, url, query, fragment, hasRoute: false };
}
if (typeof httpTarget === "string") {
return { urlPath: stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
return { urlPath: void 0, url, query, fragment, hasRoute: false };
}
function getUserUpdatedNameAndSource(originalName, attributes, fallbackSource = "custom") {
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || fallbackSource;
const description = attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (description && typeof description === "string") {
return {
description,
source
};
}
return { description: originalName, source };
}
function enhanceDscWithOpenTelemetryRootSpanName(client) {
client.on("createDsc", (dsc, rootSpan) => {
if (!rootSpan) {
return;
}
const jsonSpan = spanToJSON(rootSpan);
const attributes = jsonSpan.data;
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
const { description } = spanHasName(rootSpan) ? parseSpanDescription(rootSpan) : { description: void 0 };
if (source !== "url" && description) {
dsc.transaction = description;
}
if (hasSpansEnabled()) {
const sampled = getSamplingDecision(rootSpan.spanContext());
dsc.sampled = sampled == void 0 ? void 0 : String(sampled);
}
});
}
function getActiveSpan() {
return trace.getActiveSpan();
}
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
class TraceState {
constructor() {
this._internalState = /* @__PURE__ */ new Map();
}
/** @inheritDoc */
set(key, value) {
const next = this._clone();
if (next._internalState.has(key)) {
next._internalState.delete(key);
}
next._internalState.set(key, value);
return next;
}
/** @inheritDoc */
unset(key) {
const next = this._clone();
next._internalState.delete(key);
return next;
}
/** @inheritDoc */
get(key) {
return this._internalState.get(key);
}
/** @inheritDoc */
serialize() {
return Array.from(this._internalState.keys()).reverse().map((key) => `${key}=${this._internalState.get(key)}`).join(",");
}
_clone() {
const next = new TraceState();
next._internalState = new Map(this._internalState);
return next;
}
}
function makeTraceState({
dsc,
sampled
}) {
const dscString = dsc ? dynamicSamplingContextToSentryBaggageHeader(dsc) : void 0;
const traceStateBase = new TraceState();
const traceStateWithDsc = dscString ? traceStateBase.set(SENTRY_TRACE_STATE_DSC, dscString) : traceStateBase;
return sampled === false ? traceStateWithDsc.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") : traceStateWithDsc;
}
const setupElements = /* @__PURE__ */ new Set();
function openTelemetrySetupCheck() {
return Array.from(setupElements);
}
function setIsSetup(element) {
setupElements.add(element);
}
class SentryPropagator extends W3CBaggagePropagator {
constructor() {
super();
setIsSetup("SentryPropagator");
this._urlMatchesTargetsMap = new LRUMap(100);
}
/**
* @inheritDoc
*/
inject(context2, carrier, setter) {
if (isTracingSuppressed(context2)) {
DEBUG_BUILD && debug.log("[Tracing] Not injecting trace data for url because tracing is suppressed.");
return;
}
const activeSpan = trace.getSpan(context2);
const url = activeSpan && getCurrentURL(activeSpan);
const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {};
if (!shouldPropagateTraceForUrl(url, tracePropagationTargets, this._urlMatchesTargetsMap)) {
DEBUG_BUILD && debug.log("[Tracing] Not injecting trace data for url because it does not match tracePropagationTargets:", url);
return;
}
const existingBaggageHeader = getExistingBaggage(carrier);
const existingSentryTraceHeader = getExistingSentryTrace(carrier);
let baggage = propagation.getBaggage(context2) || propagation.createBaggage({});
const { dynamicSamplingContext, traceId, spanId, sampled } = getInjectionData(context2);
if (existingBaggageHeader) {
const baggageEntries = parseBaggageHeader(existingBaggageHeader);
if (baggageEntries) {
Object.entries(baggageEntries).forEach(([key, value]) => {
if (!existingSentryTraceHeader && key.startsWith(SENTRY_BAGGAGE_KEY_PREFIX)) {
return;
}
baggage = baggage.setEntry(key, { value });
});
}
}
if (!existingSentryTraceHeader && dynamicSamplingContext) {
baggage = Object.entries(dynamicSamplingContext).reduce((b, [dscKey, dscValue]) => {
if (dscValue) {
return b.setEntry(`${SENTRY_BAGGAGE_KEY_PREFIX}${dscKey}`, { value: dscValue });
}
return b;
}, baggage);
}
if (!existingSentryTraceHeader && traceId && traceId !== INVALID_TRACEID) {
setter.set(carrier, SENTRY_TRACE_HEADER, generateSentryTraceHeader(traceId, spanId, sampled));
if (propagateTraceparent) {
setter.set(carrier, "traceparent", generateTraceparentHeader(traceId, spanId, sampled));
}
}
super.inject(propagation.setBaggage(context2, baggage), carrier, setter);
}
/**
* @inheritDoc
*/
extract(context2, carrier, getter) {
const maybeSentryTraceHeader = getter.get(carrier, SENTRY_TRACE_HEADER);
const baggage = getter.get(carrier, SENTRY_BAGGAGE_HEADER);
const sentryTrace = maybeSentryTraceHeader ? Array.isArray(maybeSentryTraceHeader) ? maybeSentryTraceHeader[0] : maybeSentryTraceHeader : void 0;
return ensureScopesOnContext(getContextWithRemoteActiveSpan(context2, { sentryTrace, baggage }));
}
/**
* @inheritDoc
*/
fields() {
return [SENTRY_TRACE_HEADER, SENTRY_BAGGAGE_HEADER, "traceparent"];
}
}
function getInjectionData(context2, options = {}) {
const span = trace.getSpan(context2);
if (span?.spanContext().isRemote) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: void 0,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
if (span) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: spanContext.spanId,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
const scope = options.scope || getScopesFromContext(context2)?.scope || getCurrentScope();
const client = options.client || getClient();
const propagationContext = scope.getPropagationContext();
const dynamicSamplingContext = client ? getDynamicSamplingContextFromScope(client, scope) : void 0;
return {
dynamicSamplingContext,
traceId: propagationContext.traceId,
spanId: propagationContext.propagationSpanId,
sampled: propagationContext.sampled
};
}
function getContextWithRemoteActiveSpan(ctx, { sentryTrace, baggage }) {
const propagationContext = propagationContextFromHeaders(sentryTrace, baggage);
const { traceId, parentSpanId, sampled, dsc } = propagationContext;
const client = getClient();
const incomingDsc = baggageHeaderToDynamicSamplingContext(baggage);
if (!parentSpanId || client && !shouldContinueTrace(client, incomingDsc?.org_id)) {
return ctx;
}
const spanContext = generateRemoteSpanContext({
traceId,
spanId: parentSpanId,
sampled,
dsc
});
return trace.setSpanContext(ctx, spanContext);
}
function continueTraceAsRemoteSpan(ctx, options, callback) {
const ctxWithSpanContext = ensureScopesOnContext(getContextWithRemoteActiveSpan(ctx, options));
return context.with(ctxWithSpanContext, callback);
}
function ensureScopesOnContext(ctx) {
const scopes = getScopesFromContext(ctx);
const newScopes = {
// If we have no scope here, this is most likely either the root context or a context manually derived from it
// In this case, we want to fork the current scope, to ensure we do not pollute the root scope
scope: scopes ? scopes.scope : getCurrentScope().clone(),
isolationScope: scopes ? scopes.isolationScope : getIsolationScope()
};
return setScopesOnContext(ctx, newScopes);
}
function getExistingBaggage(carrier) {
try {
const baggage = carrier[SENTRY_BAGGAGE_HEADER];
return Array.isArray(baggage) ? baggage.join(",") : baggage;
} catch {
return void 0;
}
}
function getExistingSentryTrace(carrier) {
try {
return carrier[SENTRY_TRACE_HEADER];
} catch {
return void 0;
}
}
function getCurrentURL(span) {
const spanData = spanToJSON(span).data;
const urlAttribute = spanData[HTTP_URL] || spanData[URL_FULL];
if (typeof urlAttribute === "string") {
return urlAttribute;
}
const urlTraceState = span.spanContext().traceState?.get(SENTRY_TRACE_STATE_URL);
if (urlTraceState) {
return urlTraceState;
}
return void 0;
}
function generateRemoteSpanContext({
spanId,
traceId,
sampled,
dsc
}) {
const traceState = makeTraceState({
dsc,
sampled
});
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,
traceState
};
return spanContext;
}
function _startSpan(options, callback, autoEnd) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx);
const ctx = missingRequiredParent ? suppressTracing$1(activeCtx) : activeCtx;
if (missingRequiredParent) {
getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!hasSpansEnabled()) {
const suppressedCtx = isTracingSuppressed(ctx) ? ctx : suppressTracing$1(ctx);
return context.with(suppressedCtx, () => {
return tracer.startActiveSpan(name, spanOptions, suppressedCtx, (span) => {
patchSpanEnd(span);
return context.with(activeCtx, () => {
return handleCallbackErrors(
() => callback(span),
() => {
if (spanToJSON(span).status === void 0) {
span.setStatus({ code: SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
});
}
return tracer.startActiveSpan(name, spanOptions, ctx, (span) => {
patchSpanEnd(span);
return handleCallbackErrors(
() => callback(span),
() => {
if (spanToJSON(span).status === void 0) {
span.setStatus({ code: SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
}
function startSpan(options, callback) {
return _startSpan(options, callback, true);
}
function startSpanManual(options, callback) {
return _startSpan(options, (span) => callback(span, () => span.end()), false);
}
function startInactiveSpan(options) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx);
let ctx = missingRequiredParent ? suppressTracing$1(activeCtx) : activeCtx;
if (missingRequiredParent) {
getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!hasSpansEnabled()) {
ctx = isTracingSuppressed(ctx) ? ctx : suppressTracing$1(ctx);
}
const span = tracer.startSpan(name, spanOptions, ctx);
patchSpanEnd(span);
return span;
});
}
function withActiveSpan(span, callback) {
const newContextWithActiveSpan = span ? trace.setSpan(context.active(), span) : trace.deleteSpan(context.active());
return context.with(newContextWithActiveSpan, () => callback(getCurrentScope()));
}
function getTracer() {
const client = getClient();
return client?.tracer || trace.getTracer("@sentry/opentelemetry", SDK_VERSION);
}
function getSpanOptions(options) {
const { startTime, attributes, kind, op, links } = options;
const fixedStartTime = typeof startTime === "number" ? ensureTimestampInMilliseconds(startTime) : startTime;
return {
attributes: op ? {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...attributes
} : attributes,
kind,
links,
startTime: fixedStartTime
};
}
function ensureTimestampInMilliseconds(timestamp) {
const isMs = timestamp < 9999999999;
return isMs ? timestamp * 1e3 : timestamp;
}
function patchSpanEnd(span) {
const originalEnd = span.end.bind(span);
span.end = (endTime) => {
return originalEnd(typeof endTime === "number" ? ensureTimestampInMilliseconds(endTime) : endTime);
};
}
function getContext(scope, forceTransaction) {
const ctx = getContextForScope(scope);
const parentSpan = trace.getSpan(ctx);
if (!parentSpan) {
return ctx;
}
if (!forceTransaction) {
return ctx;
}
const ctxWithoutSpan = trace.deleteSpan(ctx);
const { spanId, traceId } = parentSpan.spanContext();
const sampled = getSamplingDecision(parentSpan.spanContext());
const rootSpan = getRootSpan(parentSpan);
const dsc = getDynamicSamplingContextFromSpan(rootSpan);
const traceState = makeTraceState({
dsc,
sampled
});
const spanOptions = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,
traceState
};
const ctxWithSpanContext = trace.setSpanContext(ctxWithoutSpan, spanOptions);
return ctxWithSpanContext;
}
function getContextForScope(scope) {
if (scope) {
const ctx = getContextFromScope(scope);
if (ctx) {
return ctx;
}
}
return context.active();
}
function continueTrace(options, callback) {
return continueTraceAsRemoteSpan(context.active(), options, callback);
}
function startNewTrace(callback) {
const traceId = generateTraceId();
const spanId = generateSpanId();
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: TraceFlags.NONE
};
const ctxWithTrace = trace.setSpanContext(context.active(), spanContext);
return context.with(ctxWithTrace, () => {
getCurrentScope().setPropagationContext({
traceId,
sampleRand: _INTERNAL_safeMathRandom()
});
return callback();
});
}
function getTraceContextForScope(client, scope) {
const ctx = getContextFromScope(scope);
const span = ctx && trace.getSpan(ctx);
const traceContext = span ? spanToTraceContext(span) : getTraceContextFromScope(scope);
const dynamicSamplingContext = span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromScope(client, scope);
return [dynamicSamplingContext, traceContext];
}
function getActiveSpanWrapper(parentSpan) {
return parentSpan !== void 0 ? (callback) => {
return withActiveSpan(parentSpan, callback);
} : (callback) => callback();
}
function suppressTracing(callback) {
const ctx = suppressTracing$1(context.active());
return context.with(ctx, callback);
}
function setupEventContextTrace(client) {
client.on("preprocessEvent", (event) => {
const span = getActiveSpan();
if (!span || event.type === "transaction") {
return;
}
event.contexts = {
trace: spanToTraceContext(span),
...event.contexts
};
const rootSpan = getRootSpan(span);
event.sdkProcessingMetadata = {
dynamicSamplingContext: getDynamicSamplingContextFromSpan(rootSpan),
...event.sdkProcessingMetadata
};
return event;
});
}
function getTraceData({
span,
scope,
client,
propagateTraceparent
} = {}) {
let ctx = (scope && getContextFromScope(scope)) ?? api.context.active();
if (span) {
const { scope: scope2 } = getCapturedScopesOnSpan(span);
ctx = scope2 && getContextFromScope(scope2) || api.trace.setSpan(api.context.active(), span);
}
const { traceId, spanId, sampled, dynamicSamplingContext } = getInjectionData(ctx, { scope, client });
const traceData = {
"sentry-trace": generateSentryTraceHeader(traceId, spanId, sampled),
baggage: dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext)
};
if (propagateTraceparent) {
traceData.traceparent = generateTraceparentHeader(traceId, spanId, sampled);
}
return traceData;
}
function setOpenTelemetryContextAsyncContextStrategy() {
function getScopes() {
const ctx = api.context.active();
const scopes = getScopesFromContext(ctx);
if (scopes) {
return scopes;
}
return {
scope: getDefaultCurrentScope(),
isolationScope: getDefaultIsolationScope()
};
}
function withScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx, () => {
return callback(getCurrentScope());
});
}
function withSetScope(scope, callback) {
const ctx = getContextFromScope(scope) || api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY, scope), () => {
return callback(scope);
});
}
function withIsolationScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY, true), () => {
return callback(getIsolationScope());
});
}
function withSetIsolationScope(isolationScope, callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY, isolationScope), () => {
return callback(getIsolationScope());
});
}
function getCurrentScope() {
return getScopes().scope;
}
function getIsolationScope() {
return getScopes().isolationScope;
}
setAsyncContextStrategy({
withScope,
withSetScope,
withSetIsolationScope,
withIsolationScope,
getCurrentScope,
getIsolationScope,
startSpan,
startSpanManual,
startInactiveSpan,
getActiveSpan,
suppressTracing,
getTraceData,
continueTrace,
startNewTrace,
// The types here don't fully align, because our own `Span` type is narrower
// than the OTEL one - but this is OK for here, as we now we'll only have OTEL spans passed around
withActiveSpan,
getTracingChannelBinding: () => {
try {
const contextManager = api.context._getContextManager();
const lookup = contextManager.getAsyncLocalStorageLookup();
return {
asyncLocalStorage: lookup.asyncLocalStorage,
getStoreWithActiveSpan: (span) => api.trace.setSpan(api.context.active(), span)
};
} catch {
return void 0;
}
}
});
}
function buildContextWithSentryScopes(context, activeContext) {
const span = trace.getSpan(context);
let effectiveContext;
if (span?.spanContext().traceState?.get(SENTRY_TRACE_STATE_CHILD_IGNORED) === "1") {
const contextWithoutSpan = trace.deleteSpan(context);
const parentSpan = trace.getSpan(activeContext);
effectiveContext = parentSpan ? trace.setSpan(contextWithoutSpan, parentSpan) : contextWithoutSpan;
} else {
effectiveContext = context;
}
const currentScopes = getScopesFromContext(effectiveContext);
const currentScope = currentScopes?.scope || getCurrentScope();
const currentIsolationScope = currentScopes?.isolationScope || getIsolationScope();
const shouldForkIsolationScope = effectiveContext.getValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY) === true;
const scope = effectiveContext.getValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY);
const isolationScope = effectiveContext.getValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
const newCurrentScope = scope || currentScope.clone();
const newIsolationScope = isolationScope || (shouldForkIsolationScope ? currentIsolationScope.clone() : currentIsolationScope);
const scopes = { scope: newCurrentScope, isolationScope: newIsolationScope };
const ctx1 = setScopesOnContext(effectiveContext, scopes);
const ctx2 = ctx1.deleteValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
setContextOnScope(newCurrentScope, ctx2);
return ctx2;
}
function wrapContextManagerClass(ContextManagerClass) {
class SentryContextManager extends ContextManagerClass {
constructor(...args) {
super(...args);
setIsSetup("SentryContextManager");
}
/**
* Overwrite with() of the original AsyncLocalStorageContextManager
* to ensure we also create new scopes per context.
*/
with(context, fn, thisArg, ...args) {
const ctx2 = buildContextWithSentryScopes(context, this.active());
return super.with(ctx2, fn, thisArg, ...args);
}
/**
* Gets underlying AsyncLocalStorage and symbol to allow lookup of scope.
*/
getAsyncLocalStorageLookup() {
return {
// @ts-expect-error This is on the base class, but not part of the interface
asyncLocalStorage: this._asyncLocalStorage,
contextSymbol: SENTRY_SCOPES_CONTEXT_KEY
};
}
}
return SentryContextManager;
}
function groupSpansWithParents(spans) {
const nodeMap = /* @__PURE__ */ new Map();
for (const span of spans) {
createOrUpdateSpanNodeAndRefs(nodeMap, span);
}
return Array.from(nodeMap, function([_id, spanNode]) {
return spanNode;
});
}
function getLocalParentId(span) {
const parentIsRemote = span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE] === true;
return !parentIsRemote ? getParentSpanId(span) : void 0;
}
function createOrUpdateSpanNodeAndRefs(nodeMap, span) {
const id = span.spanContext().spanId;
const parentId = getLocalParentId(span);
if (!parentId) {
createOrUpdateNode(nodeMap, { id, span, children: [] });
return;
}
const parentNode = createOrGetParentNode(nodeMap, parentId);
const node = createOrUpdateNode(nodeMap, { id, span, parentNode, children: [] });
parentNode.children.push(node);
}
function createOrGetParentNode(nodeMap, id) {
const existing = nodeMap.get(id);
if (existing) {
return existing;
}
return createOrUpdateNode(nodeMap, { id, children: [] });
}
function createOrUpdateNode(nodeMap, spanNode) {
const existing = nodeMap.get(spanNode.id);
if (existing?.span) {
return existing;
}
if (existing && !existing.span) {
existing.span = spanNode.span;
existing.parentNode = spanNode.parentNode;
return existing;
}
nodeMap.set(spanNode.id, spanNode);
return spanNode;
}
const canonicalGrpcErrorCodesMap = {
"1": "cancelled",
"2": "unknown_error",
"3": "invalid_argument",
"4": "deadline_exceeded",
"5": "not_found",
"6": "already_exists",
"7": "permission_denied",
"8": "resource_exhausted",
"9": "failed_precondition",
"10": "aborted",
"11": "out_of_range",
"12": "unimplemented",
"13": "internal_error",
"14": "unavailable",
"15": "data_loss",
"16": "unauthenticated"
};
const isStatusErrorMessageValid = (message) => {
return Object.values(canonicalGrpcErrorCodesMap).includes(message);
};
function mapStatus(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const status = spanHasStatus(span) ? span.status : void 0;
if (status) {
if (status.code === SpanStatusCode.OK) {
return { code: SPAN_STATUS_OK };
} else if (status.code === SpanStatusCode.ERROR) {
if (typeof status.message === "undefined") {
const inferredStatus2 = inferStatusFromAttributes(attributes);
if (inferredStatus2) {
return inferredStatus2;
}
}
if (status.message && isStatusErrorMessageValid(status.message)) {
return { code: SPAN_STATUS_ERROR, message: status.message };
} else {
return { code: SPAN_STATUS_ERROR, message: "internal_error" };
}
}
}
const inferredStatus = inferStatusFromAttributes(attributes);
if (inferredStatus) {
return inferredStatus;
}
if (status?.code === SpanStatusCode.UNSET) {
return { code: SPAN_STATUS_OK };
} else {
return { code: SPAN_STATUS_ERROR, message: "unknown_error" };
}
}
function inferStatusFromAttributes(attributes) {
const httpCodeAttribute = attributes[HTTP_RESPONSE_STATUS_CODE] || attributes[HTTP_STATUS_CODE];
const grpcCodeAttribute = attributes[RPC_GRPC_STATUS_CODE];
const numberHttpCode = typeof httpCodeAttribute === "number" ? httpCodeAttribute : typeof httpCodeAttribute === "string" ? parseInt(httpCodeAttribute) : void 0;
if (typeof numberHttpCode === "number") {
return getSpanStatusFromHttpCode(numberHttpCode);
}
if (typeof grpcCodeAttribute === "string") {
return { code: SPAN_STATUS_ERROR, message: canonicalGrpcErrorCodesMap[grpcCodeAttribute] || "unknown_error" };
}
return void 0;
}
const MAX_SPAN_COUNT = 1e3;
const DEFAULT_TIMEOUT = 300;
const SENT_SPANS_MAX_SIZE = 1e4;
class SentrySpanExporter {
constructor(options) {
this._finishedSpanBucketSize = options?.timeout || DEFAULT_TIMEOUT;
this._finishedSpanBuckets = new Array(this._finishedSpanBucketSize).fill(void 0);
this._lastCleanupTimestampInS = Math.floor(_INTERNAL_safeDateNow() / 1e3);
this._spansToBucketEntry = /* @__PURE__ */ new WeakMap();
this._sentSpans = new LRUMap(SENT_SPANS_MAX_SIZE);
this._debouncedFlush = debounce(this.flush.bind(this), 1, { maxWait: 100 });
}
/**
* Export a single span.
* This is called by the span processor whenever a span is ended.
*/
export(span) {
const currentTimestampInS = Math.floor(_INTERNAL_safeDateNow() / 1e3);
if (this._lastCleanupTimestampInS !== currentTimestampInS) {
let droppedSpanCount = 0;
this._finishedSpanBuckets.forEach((bucket, i) => {
if (bucket && bucket.timestampInS <= currentTimestampInS - this._finishedSpanBucketSize) {
droppedSpanCount += bucket.spans.size;
this._finishedSpanBuckets[i] = void 0;
}
});
if (droppedSpanCount > 0) {
DEBUG_BUILD && debug.log(
`SpanExporter dropped ${droppedSpanCount} spans because they were pending for more than ${this._finishedSpanBucketSize} seconds.`
);
}
this._lastCleanupTimestampInS = currentTimestampInS;
}
const currentBucketIndex = currentTimestampInS % this._finishedSpanBucketSize;
const currentBucket = this._finishedSpanBuckets[currentBucketIndex] || {
timestampInS: currentTimestampInS,
spans: /* @__PURE__ */ new Set()
};
this._finishedSpanBuckets[currentBucketIndex] = currentBucket;
currentBucket.spans.add(span);
this._spansToBucketEntry.set(span, currentBucket);
const localParentId = getLocalParentId(span);
if (!localParentId || this._sentSpans.get(localParentId)) {
this._debouncedFlush();
}
}
/**
* Try to flush any pending spans immediately.
* This is called internally by the exporter (via _debouncedFlush),
* but can also be triggered externally if we force-flush.
*/
flush() {
const finishedSpans = this._finishedSpanBuckets.flatMap((bucket) => bucket ? Array.from(bucket.spans) : []);
const sentSpans = this._maybeSend(finishedSpans);
const sentSpanCount = sentSpans.size;
const remainingOpenSpanCount = finishedSpans.length - sentSpanCount;
DEBUG_BUILD && debug.log(
`SpanExporter exported ${sentSpanCount} spans, ${remainingOpenSpanCount} spans are waiting for their parent spans to finish`
);
for (const span of sentSpans) {
this._sentSpans.set(span.spanContext().spanId, 1);
const bucketEntry = this._spansToBucketEntry.get(span);
if (bucketEntry) {
bucketEntry.spans.delete(span);
}
}
this._debouncedFlush.cancel();
}
/**
* Clear the exporter.
* This is called when the span processor is shut down.
*/
clear() {
this._finishedSpanBuckets = this._finishedSpanBuckets.fill(void 0);
this._sentSpans.clear();
this._debouncedFlush.cancel();
}
/**
* Send the given spans, but only if they are part of a finished transaction.
*
* Returns the sent spans.
* Spans remain unsent when their parent span is not yet finished.
* This will happen regularly, as child spans are generally finished before their parents.
* But it _could_ also happen because, for whatever reason, a parent span was lost.
* In this case, we'll eventually need to clean this up.
*/
_maybeSend(spans) {
const grouped = groupSpansWithParents(spans);
const sentSpans = /* @__PURE__ */ new Set();
const rootNodes = this._getCompletedRootNodes(grouped);
for (const root of rootNodes) {
const span = root.span;
sentSpans.add(span);
const transactionEvent = createTransactionForOtelSpan(span);
if (root.parentNode && this._sentSpans.get(root.parentNode.id)) {
const traceData = transactionEvent.contexts?.trace?.data;
if (traceData) {
traceData["sentry.parent_span_already_sent"] = true;
}
}
const spans2 = transactionEvent.spans || [];
let hasGenAiSpans = false;
for (const child of root.children) {
if (createAndFinishSpanForOtelSpan(child, spans2, sentSpans)) {
hasGenAiSpans = true;
}
}
transactionEvent.spans = spans2.length > MAX_SPAN_COUNT ? spans2.sort((a, b) => a.start_timestamp - b.start_timestamp).slice(0, MAX_SPAN_COUNT) : spans2;
if (hasGenAiSpans) {
transactionEvent.sdkProcessingMetadata = {
...transactionEvent.sdkProcessingMetadata,
hasGenAiSpans: true
};
}
const measurements = timedEventsToMeasurements(span.events);
if (measurements) {
transactionEvent.measurements = measurements;
}
captureEvent(transactionEvent);
}
return sentSpans;
}
/** Check if a node is a completed root node or a node whose parent has already been sent */
_nodeIsCompletedRootNodeOrHasSentParent(node) {
return !!node.span && (!node.parentNode || !!this._sentSpans.get(node.parentNode.id));
}
/** Get all completed root nodes from a list of nodes */
_getCompletedRootNodes(nodes) {
return nodes.filter((node) => this._nodeIsCompletedRootNodeOrHasSentParent(node));
}
}
function parseSpan(span) {
const attributes = span.attributes;
const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];
const op = attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP];
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
return { origin, op, source };
}
function createTransactionForOtelSpan(span) {
const { op, description, data, origin = "manual", source } = getSpanData(span);
const capturedSpanScopes = getCapturedScopesOnSpan(span);
const sampleRate = span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
const attributes = {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate,
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
...data,
...removeSentryAttributes(span.attributes)
};
const { links } = span;
const { traceId: trace_id, spanId: span_id } = span.spanContext();
const parent_span_id = getParentSpanId(span);
const status = mapStatus(span);
const traceContext = {
parent_span_id,
span_id,
trace_id,
data: attributes,
origin,
op,
status: getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
links: convertSpanLinksForEnvelope(links)
};
const statusCode = attributes[HTTP_RESPONSE_STATUS_CODE];
const responseContext = typeof statusCode === "number" ? { response: { status_code: statusCode } } : void 0;
const transactionEvent = {
contexts: {
trace: traceContext,
otel: {
resource: span.resource.attributes
},
...responseContext
},
spans: [],
start_timestamp: spanTimeInputToSeconds(span.startTime),
timestamp: spanTimeInputToSeconds(span.endTime),
transaction: description,
type: "transaction",
sdkProcessingMetadata: {
capturedSpanScope: capturedSpanScopes.scope,
capturedSpanIsolationScope: capturedSpanScopes.isolationScope,
sampleRate,
dynamicSamplingContext: getDynamicSamplingContextFromSpan(span)
},
...source && {
transaction_info: {
source
}
}
};
return transactionEvent;
}
function createAndFinishSpanForOtelSpan(node, spans, sentSpans) {
const span = node.span;
if (span) {
sentSpans.add(span);
}
const shouldDrop = !span;
if (shouldDrop) {
let hasGenAiSpans2 = false;
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans2 = true;
}
});
return hasGenAiSpans2;
}
const span_id = span.spanContext().spanId;
const trace_id = span.spanContext().traceId;
const parentSpanId = getParentSpanId(span);
const { attributes, startTime, endTime, links } = span;
const { op, description, data, origin = "manual" } = getSpanData(span);
const allData = {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...removeSentryAttributes(attributes),
...data
};
const status = mapStatus(span);
const spanJSON = {
span_id,
trace_id,
data: allData,
description,
parent_span_id: parentSpanId,
start_timestamp: spanTimeInputToSeconds(startTime),
// This is [0,0] by default in OTEL, in which case we want to interpret this as no end time
timestamp: spanTimeInputToSeconds(endTime) || void 0,
status: getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
op,
origin,
measurements: timedEventsToMeasurements(span.events),
links: convertSpanLinksForEnvelope(links)
};
spans.push(spanJSON);
let hasGenAiSpans = !!op?.startsWith("gen_ai.");
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans = true;
}
});
return hasGenAiSpans;
}
function getSpanData(span) {
const { op: definedOp, source: definedSource, origin } = parseSpan(span);
const { op: inferredOp, description, source: inferredSource, data: inferredData } = parseSpanDescription(span);
const op = definedOp || inferredOp;
const source = definedSource || inferredSource;
const data = { ...inferredData, ...getData(span) };
return {
op,
description,
source,
origin,
data
};
}
function removeSentryAttributes(data) {
const cleanedData = { ...data };
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE];
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
return cleanedData;
}
function getData(span) {
const attributes = span.attributes;
const data = {};
if (span.kind !== SpanKind.INTERNAL) {
data["otel.kind"] = SpanKind[span.kind];
}
const maybeHttpStatusCodeAttribute = attributes[HTTP_STATUS_CODE];
if (maybeHttpStatusCodeAttribute) {
data[HTTP_RESPONSE_STATUS_CODE] = maybeHttpStatusCodeAttribute;
}
const requestData = getRequestSpanData(span);
if (requestData.url) {
data.url = requestData.url;
}
if (requestData["http.query"]) {
data["http.query"] = requestData["http.query"].slice(1);
}
if (requestData["http.fragment"]) {
data["http.fragment"] = requestData["http.fragment"].slice(1);
}
return data;
}
class SentrySpanProcessor {
constructor(options) {
this._unsubscribePreprocessSpan = void 0;
setIsSetup("SentrySpanProcessor");
this._exporter = new SentrySpanExporter(options);
this._client = options?.client ?? getClient();
if (this._client && hasSpanStreamingEnabled(this._client)) {
this._unsubscribePreprocessSpan = this._client.on("preprocessSpan", backfillStreamedSpanDataFromOtel);
}
}
/**
* @inheritDoc
*/
async forceFlush() {
this._exporter.flush();
}
/**
* @inheritDoc
*/
async shutdown() {
this._unsubscribePreprocessSpan?.();
this._exporter.clear();
}
/**
* @inheritDoc
*/
onStart(span, parentContext) {
const parentSpan = trace.getSpan(parentContext);
let scopes = getScopesFromContext(parentContext);
if (parentSpan && !parentSpan.spanContext().isRemote) {
addChildSpanToSpan(parentSpan, span);
}
if (parentSpan?.spanContext().isRemote) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE, true);
}
if (parentContext === ROOT_CONTEXT) {
scopes = {
scope: getDefaultCurrentScope(),
isolationScope: getDefaultIsolationScope()
};
}
if (scopes) {
setCapturedScopesOnSpan(span, scopes.scope, scopes.isolationScope);
}
logSpanStart(span);
this._client?.emit("spanStart", span);
}
/** @inheritDoc */
onEnd(span) {
logSpanEnd(span);
this._client?.emit("spanEnd", span);
if (this._client && hasSpanStreamingEnabled(this._client)) {
this._client.emit("afterSpanEnd", span);
} else {
this._exporter.export(span);
}
}
}
function backfillStreamedSpanDataFromOtel(spanJSON, hint) {
const attributes = spanJSON.attributes;
if (!attributes) {
return;
}
const kind = hint?.spanKind ?? SPAN_KIND.INTERNAL;
const { op, description, source, data } = inferSpanData(spanJSON.name, attributes, kind);
spanJSON.name = description;
safeSetSpanJSONAttributes(spanJSON, {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
...data
});
if (kind !== SPAN_KIND.INTERNAL) {
safeSetSpanJSONAttributes(spanJSON, {
"otel.kind": spanKindToName(kind)
});
}
}
class SentrySampler {
constructor(client) {
this._client = client;
this._isSpanStreaming = hasSpanStreamingEnabled(client);
setIsSetup("SentrySampler");
}
/** @inheritDoc */
shouldSample(context, traceId, spanName, spanKind, spanAttributes, _links) {
const options = this._client.getOptions();
const { ignoreSpans } = options;
const parentSpan = getValidSpan(context);
const parentContext = parentSpan?.spanContext();
if (!hasSpansEnabled(options)) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const maybeSpanHttpMethod = spanAttributes[HTTP_METHOD] || spanAttributes[HTTP_REQUEST_METHOD];
if (spanKind === SpanKind.CLIENT && maybeSpanHttpMethod && (!parentSpan || parentContext?.isRemote)) {
if (!this._isSpanStreaming) {
this._client.recordDroppedEvent("no_parent_span", "span");
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
}
const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : void 0;
const isRootSpan = !parentSpan || parentContext?.isRemote;
if (!isRootSpan) {
if (this._isSpanStreaming) {
if (parentSampled) {
if (ignoreSpans?.length) {
const { description: inferredChildName, op: childOp } = inferSpanData(spanName, spanAttributes, spanKind);
if (shouldIgnoreSpan(
{
description: inferredChildName,
op: spanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? childOp,
attributes: spanAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredChildSpan: true
});
}
}
}
if (!parentSampled) {
const parentSegmentIgnored = parentContext?.traceState?.get(SENTRY_TRACE_STATE_SEGMENT_IGNORED) === "1";
this._client.recordDroppedEvent(parentSegmentIgnored ? "ignored" : "sample_rate", "span");
}
}
return wrapSamplingDecision({
decision: parentSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
context,
spanAttributes
});
}
const {
description: inferredSpanName,
data: inferredAttributes,
op
} = inferSpanData(spanName, spanAttributes, spanKind);
const mergedAttributes = {
...inferredAttributes,
...spanAttributes
};
if (op) {
mergedAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] = op;
}
if (this._isSpanStreaming && ignoreSpans?.length && shouldIgnoreSpan(
{
description: inferredSpanName,
op: mergedAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? op,
attributes: mergedAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredSegmentSpan: true
});
}
const mutableSamplingDecision = { decision: true };
this._client.emit(
"beforeSampling",
{
spanAttributes: mergedAttributes,
spanName: inferredSpanName,
parentSampled,
parentContext
},
mutableSamplingDecision
);
if (!mutableSamplingDecision.decision) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const { isolationScope } = getScopesFromContext(context) ?? {};
const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : void 0;
const sampleRand = parseSampleRate(dsc?.sample_rand) ?? _INTERNAL_safeMathRandom();
const [sampled, sampleRate, localSampleRateWasApplied] = sampleSpan(
options,
{
name: inferredSpanName,
attributes: mergedAttributes,
normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest,
parentSampled,
parentSampleRate: parseSampleRate(dsc?.sample_rate)
},
sampleRand
);
const method = `${maybeSpanHttpMethod}`.toUpperCase();
if (method === "OPTIONS" || method === "HEAD") {
DEBUG_BUILD && debug.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
return wrapSamplingDecision({
decision: SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: 0
// we don't want to sample anything in the downstream trace either
});
}
if (!sampled && // We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace)
parentSampled === void 0) {
DEBUG_BUILD && debug.log("[Tracing] Discarding root span because its trace was not chosen to be sampled.");
this._client.recordDroppedEvent("sample_rate", this._isSpanStreaming ? "span" : "transaction");
}
return {
...wrapSamplingDecision({
decision: sampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : void 0
}),
attributes: {
// We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : void 0
}
};
}
/** Returns the sampler name or short description with the configuration. */
toString() {
return "SentrySampler";
}
}
function getParentSampled(parentSpan, traceId, spanName) {
const parentContext = parentSpan.spanContext();
if (isSpanContextValid(parentContext) && parentContext.traceId === traceId) {
if (parentContext.isRemote) {
const parentSampled2 = getSamplingDecision(parentSpan.spanContext());
DEBUG_BUILD && debug.log(`[Tracing] Inheriting remote parent's sampled decision for ${spanName}: ${parentSampled2}`);
return parentSampled2;
}
const parentSampled = getSamplingDecision(parentContext);
DEBUG_BUILD && debug.log(`[Tracing] Inheriting parent's sampled decision for ${spanName}: ${parentSampled}`);
return parentSampled;
}
return void 0;
}
function wrapSamplingDecision({
decision,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate,
ignoredChildSpan,
ignoredSegmentSpan
}) {
let traceState = getBaseTraceState(context, spanAttributes);
if (downstreamTraceSampleRate !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RATE, `${downstreamTraceSampleRate}`);
}
if (sampleRand !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RAND, `${sampleRand}`);
}
if (ignoredChildSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_CHILD_IGNORED, "1");
}
if (ignoredSegmentSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_SEGMENT_IGNORED, "1");
}
if (decision == void 0) {
return { decision: SamplingDecision.NOT_RECORD, traceState };
}
if (decision === SamplingDecision.NOT_RECORD) {
return { decision, traceState: traceState.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") };
}
return { decision, traceState };
}
function getBaseTraceState(context, spanAttributes) {
const parentSpan = trace.getSpan(context);
const parentContext = parentSpan?.spanContext();
let traceState = parentContext?.traceState || new TraceState();
const url = spanAttributes[HTTP_URL] || spanAttributes[URL_FULL];
if (url && typeof url === "string") {
traceState = traceState.set(SENTRY_TRACE_STATE_URL, url);
}
return traceState;
}
function getValidSpan(context) {
const span = trace.getSpan(context);
return span && isSpanContextValid(span.spanContext()) ? span : void 0;
}
const ATTR_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language";
const ATTR_TELEMETRY_SDK_NAME = "telemetry.sdk.name";
const ATTR_TELEMETRY_SDK_VERSION = "telemetry.sdk.version";
const SEMRESATTRS_SERVICE_NAMESPACE = "service.namespace";
class SentryResource {
constructor(attributes) {
this._attributes = attributes;
}
get attributes() {
return this._attributes;
}
merge(other) {
if (!other) {
return this;
}
return new SentryResource({ ...this._attributes, ...other.attributes });
}
getRawAttributes() {
return Object.entries(this._attributes);
}
}
function parseOtelResourceAttributes(raw) {
if (!raw) {
return {};
}
const result = {};
for (const pair of raw.split(",")) {
const eq = pair.indexOf("=");
if (eq === -1) {
continue;
}
const key = pair.substring(0, eq).trim();
const value = pair.substring(eq + 1).trim();
if (key) {
try {
result[key] = decodeURIComponent(value);
} catch {
result[key] = value;
}
}
}
return result;
}
function getSentryResource(serviceNameFallback) {
const env = typeof process !== "undefined" ? process.env : {};
const otelServiceName = env.OTEL_SERVICE_NAME;
const otelResourceAttrs = parseOtelResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
return new SentryResource({
// Lowest priority: Sentry defaults
// eslint-disable-next-line typescript/no-deprecated
[SEMRESATTRS_SERVICE_NAMESPACE]: "sentry",
[SERVICE_NAME]: serviceNameFallback,
// OTEL_RESOURCE_ATTRIBUTES overrides defaults (including service.name and service.namespace)
...otelResourceAttrs,
// OTEL_SERVICE_NAME explicitly overrides service.name
...otelServiceName ? { [SERVICE_NAME]: otelServiceName } : {},
// Highest priority: Sentry SDK telemetry attrs (cannot be overridden by env vars)
[SERVICE_VERSION]: SDK_VERSION,
[ATTR_TELEMETRY_SDK_LANGUAGE]: SDK_INFO[ATTR_TELEMETRY_SDK_LANGUAGE],
[ATTR_TELEMETRY_SDK_NAME]: SDK_INFO[ATTR_TELEMETRY_SDK_NAME],
[ATTR_TELEMETRY_SDK_VERSION]: SDK_INFO[ATTR_TELEMETRY_SDK_VERSION]
});
}
export { startSpanManual as A, suppressTracing as B, withActiveSpan as C, wrapClientClass as D, wrapContextManagerClass as E, wrapSamplingDecision as F, SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION as S, SENTRY_SCOPES_CONTEXT_KEY as a, SentryPropagator as b, SentrySampler as c, SentrySpanProcessor as d, buildContextWithSentryScopes as e, continueTrace as f, enhanceDscWithOpenTelemetryRootSpanName as g, getActiveSpan as h, getRequestSpanData as i, getScopesFromContext as j, getSentryResource as k, getSpanKind as l, getTraceContextForScope as m, isSentryRequestSpan as n, openTelemetrySetupCheck as o, setOpenTelemetryContextAsyncContextStrategy as p, setupEventContextTrace as q, spanHasAttributes as r, setIsSetup as s, spanHasEvents as t, spanHasKind as u, spanHasName as v, spanHasParentId as w, spanHasStatus as x, startInactiveSpan as y, startSpan as z };
//# sourceMappingURL=resource-DazDAxCp.js.map

Sorry, the diff of this file is too big to display

+1
-1
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const core = require('@sentry/core');
const resource = require('./resource-PpJQa_6P.js');
const resource = require('./resource-DuZKnQnC.js');
require('@opentelemetry/api');

@@ -6,0 +6,0 @@ require('@sentry/conventions/attributes');

Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const resource = require('./resource-PpJQa_6P.js');
const resource = require('./resource-DuZKnQnC.js');
const core = require('@sentry/core');

@@ -5,0 +5,0 @@ const api = require('@opentelemetry/api');

import { consoleSandbox } from '@sentry/core';
export { getClient, getDynamicSamplingContextFromSpan, shouldPropagateTraceForUrl, withStreamedSpan } from '@sentry/core';
export { S as SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, b as SentryPropagator, c as SentrySampler, d as SentrySpanProcessor, f as continueTrace, g as enhanceDscWithOpenTelemetryRootSpanName, h as getActiveSpan, i as getRequestSpanData, j as getScopesFromContext, k as getSentryResource, l as getSpanKind, m as getTraceContextForScope, n as isSentryRequestSpan, o as openTelemetrySetupCheck, p as setOpenTelemetryContextAsyncContextStrategy, q as setupEventContextTrace, r as spanHasAttributes, t as spanHasEvents, u as spanHasKind, v as spanHasName, w as spanHasParentId, x as spanHasStatus, y as startInactiveSpan, z as startSpan, A as startSpanManual, B as suppressTracing, C as withActiveSpan, D as wrapClientClass, E as wrapContextManagerClass, F as wrapSamplingDecision } from './resource-CB9ITx8O.js';
export { S as SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, b as SentryPropagator, c as SentrySampler, d as SentrySpanProcessor, f as continueTrace, g as enhanceDscWithOpenTelemetryRootSpanName, h as getActiveSpan, i as getRequestSpanData, j as getScopesFromContext, k as getSentryResource, l as getSpanKind, m as getTraceContextForScope, n as isSentryRequestSpan, o as openTelemetrySetupCheck, p as setOpenTelemetryContextAsyncContextStrategy, q as setupEventContextTrace, r as spanHasAttributes, t as spanHasEvents, u as spanHasKind, v as spanHasName, w as spanHasParentId, x as spanHasStatus, y as startInactiveSpan, z as startSpan, A as startSpanManual, B as suppressTracing, C as withActiveSpan, D as wrapClientClass, E as wrapContextManagerClass, F as wrapSamplingDecision } from './resource-DazDAxCp.js';
import '@opentelemetry/api';

@@ -5,0 +5,0 @@ import '@sentry/conventions/attributes';

@@ -1,3 +0,3 @@

import { s as setIsSetup, e as buildContextWithSentryScopes, a as SENTRY_SCOPES_CONTEXT_KEY } from './resource-CB9ITx8O.js';
export { S as SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, b as SentryPropagator, c as SentrySampler, d as SentrySpanProcessor, f as continueTrace, g as enhanceDscWithOpenTelemetryRootSpanName, h as getActiveSpan, i as getRequestSpanData, j as getScopesFromContext, k as getSentryResource, l as getSpanKind, m as getTraceContextForScope, n as isSentryRequestSpan, o as openTelemetrySetupCheck, p as setOpenTelemetryContextAsyncContextStrategy, q as setupEventContextTrace, r as spanHasAttributes, t as spanHasEvents, u as spanHasKind, v as spanHasName, w as spanHasParentId, x as spanHasStatus, y as startInactiveSpan, z as startSpan, A as startSpanManual, B as suppressTracing, C as withActiveSpan, D as wrapClientClass, E as wrapContextManagerClass, F as wrapSamplingDecision } from './resource-CB9ITx8O.js';
import { s as setIsSetup, e as buildContextWithSentryScopes, a as SENTRY_SCOPES_CONTEXT_KEY } from './resource-DazDAxCp.js';
export { S as SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION, b as SentryPropagator, c as SentrySampler, d as SentrySpanProcessor, f as continueTrace, g as enhanceDscWithOpenTelemetryRootSpanName, h as getActiveSpan, i as getRequestSpanData, j as getScopesFromContext, k as getSentryResource, l as getSpanKind, m as getTraceContextForScope, n as isSentryRequestSpan, o as openTelemetrySetupCheck, p as setOpenTelemetryContextAsyncContextStrategy, q as setupEventContextTrace, r as spanHasAttributes, t as spanHasEvents, u as spanHasKind, v as spanHasName, w as spanHasParentId, x as spanHasStatus, y as startInactiveSpan, z as startSpan, A as startSpanManual, B as suppressTracing, C as withActiveSpan, D as wrapClientClass, E as wrapContextManagerClass, F as wrapSamplingDecision } from './resource-DazDAxCp.js';
export { getClient, getDynamicSamplingContextFromSpan, shouldPropagateTraceForUrl, withStreamedSpan } from '@sentry/core';

@@ -4,0 +4,0 @@ import { ROOT_CONTEXT } from '@opentelemetry/api';

@@ -1,1 +0,1 @@

{"type":"module","version":"10.61.0","sideEffects":false}
{"type":"module","version":"10.62.0","sideEffects":false}

@@ -1,1 +0,1 @@

{"version":3,"file":"spanProcessor.d.ts","sourceRoot":"","sources":["../../src/spanProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,IAAI,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACjH,OAAO,KAAK,EAAE,MAAM,EAAoC,MAAM,cAAc,CAAC;AAmB7E;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAChE,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,0BAA0B,CAAuC;gBAEtD,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAclE;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtC;;OAEG;IACI,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI;IAmCxD,kBAAkB;IACX,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,GAAG,IAAI;CAW9C"}
{"version":3,"file":"spanProcessor.d.ts","sourceRoot":"","sources":["../../src/spanProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,IAAI,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACjH,OAAO,KAAK,EAAE,MAAM,EAAoC,MAAM,cAAc,CAAC;AAqB7E;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAChE,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,0BAA0B,CAAuC;gBAEtD,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAclE;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtC;;OAEG;IACI,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI;IAmCxD,kBAAkB;IACX,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,GAAG,IAAI;CAW9C"}
{
"name": "@sentry/opentelemetry",
"version": "10.61.0",
"version": "10.62.0",
"description": "Official Sentry utilities for OpenTelemetry",

@@ -59,3 +59,3 @@ "repository": "git://github.com/getsentry/sentry-javascript.git",

"@sentry/conventions": "^0.12.0",
"@sentry/core": "10.61.0"
"@sentry/core": "10.62.0"
},

@@ -62,0 +62,0 @@ "peerDependencies": {

const api = require('@opentelemetry/api');
const core = require('@sentry/core');
const attributes = require('@sentry/conventions/attributes');
const core$1 = require('@opentelemetry/core');
const sdkTraceBase = require('@opentelemetry/sdk-trace-base');
const SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE = "sentry.parentIsRemote";
const SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION = "sentry.graphql.operation";
function getParentSpanId(span) {
if ("parentSpanId" in span) {
return span.parentSpanId;
} else if ("parentSpanContext" in span) {
return span.parentSpanContext?.spanId;
}
return void 0;
}
function spanHasAttributes(span) {
const castSpan = span;
return !!castSpan.attributes && typeof castSpan.attributes === "object";
}
function spanHasKind(span) {
const castSpan = span;
return typeof castSpan.kind === "number";
}
function spanHasStatus(span) {
const castSpan = span;
return !!castSpan.status;
}
function spanHasName(span) {
const castSpan = span;
return !!castSpan.name;
}
function spanHasParentId(span) {
const castSpan = span;
return !!getParentSpanId(castSpan);
}
function spanHasEvents(span) {
const castSpan = span;
return Array.isArray(castSpan.events);
}
function getRequestSpanData(span) {
if (!spanHasAttributes(span)) {
return {};
}
const maybeUrlAttribute = span.attributes[attributes.URL_FULL] || span.attributes[attributes.HTTP_URL];
const data = {
url: maybeUrlAttribute,
// eslint-disable-next-line typescript/no-deprecated
"http.method": span.attributes[attributes.HTTP_REQUEST_METHOD] || span.attributes[attributes.HTTP_METHOD]
};
if (!data["http.method"] && data.url) {
data["http.method"] = "GET";
}
try {
if (typeof maybeUrlAttribute === "string") {
const url = core.parseUrl(maybeUrlAttribute);
data.url = core.getSanitizedUrlString(url);
if (url.search) {
data["http.query"] = url.search;
}
if (url.hash) {
data["http.fragment"] = url.hash;
}
}
} catch {
}
return data;
}
function wrapClientClass(ClientClass) {
class OpenTelemetryClient extends ClientClass {
constructor(...args) {
super(...args);
}
/** Get the OTEL tracer. */
get tracer() {
if (this._tracer) {
return this._tracer;
}
const name = "@sentry/opentelemetry";
const version = core.SDK_VERSION;
const tracer = api.trace.getTracer(name, version);
this._tracer = tracer;
return tracer;
}
/**
* @inheritDoc
*/
async flush(timeout) {
const provider = this.traceProvider;
await provider?.forceFlush();
return super.flush(timeout);
}
}
return OpenTelemetryClient;
}
function getSpanKind(span) {
if (spanHasKind(span)) {
return span.kind;
}
return api.SpanKind.INTERNAL;
}
const SENTRY_TRACE_HEADER = "sentry-trace";
const SENTRY_BAGGAGE_HEADER = "baggage";
const SENTRY_TRACE_STATE_DSC = "sentry.dsc";
const SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING = "sentry.sampled_not_recording";
const SENTRY_TRACE_STATE_URL = "sentry.url";
const SENTRY_TRACE_STATE_SAMPLE_RAND = "sentry.sample_rand";
const SENTRY_TRACE_STATE_SAMPLE_RATE = "sentry.sample_rate";
const SENTRY_TRACE_STATE_CHILD_IGNORED = "sentry.ignored";
const SENTRY_TRACE_STATE_SEGMENT_IGNORED = "sentry.segment_ignored";
const SENTRY_SCOPES_CONTEXT_KEY = api.createContextKey("sentry_scopes");
const SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY = api.createContextKey("sentry_fork_isolation_scope");
const SENTRY_FORK_SET_SCOPE_CONTEXT_KEY = api.createContextKey("sentry_fork_set_scope");
const SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY = api.createContextKey("sentry_fork_set_isolation_scope");
const SCOPE_CONTEXT_FIELD = "_scopeContext";
function getScopesFromContext(context) {
return context.getValue(SENTRY_SCOPES_CONTEXT_KEY);
}
function setScopesOnContext(context, scopes) {
return context.setValue(SENTRY_SCOPES_CONTEXT_KEY, scopes);
}
function setContextOnScope(scope, context) {
core.addNonEnumerableProperty(scope, SCOPE_CONTEXT_FIELD, core.makeWeakRef(context));
}
function getContextFromScope(scope) {
return core.derefWeakRef(scope[SCOPE_CONTEXT_FIELD]);
}
function isSentryRequestSpan(span) {
if (!spanHasAttributes(span)) {
return false;
}
const { attributes: attributes$1 } = span;
const httpUrl = attributes$1[attributes.HTTP_URL] || attributes$1[attributes.URL_FULL];
if (!httpUrl) {
return false;
}
return core.isSentryRequestUrl(httpUrl.toString(), core.getClient());
}
function getSamplingDecision(spanContext) {
const { traceFlags, traceState } = spanContext;
const sampledNotRecording = traceState ? traceState.get(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING) === "1" : false;
if (traceFlags === api.TraceFlags.SAMPLED) {
return true;
}
if (sampledNotRecording) {
return false;
}
const dscString = traceState ? traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? core.baggageHeaderToDynamicSamplingContext(dscString) : void 0;
if (dsc?.sampled === "true") {
return true;
}
if (dsc?.sampled === "false") {
return false;
}
return void 0;
}
function inferSpanData(spanName, attributes$1, kind) {
const httpMethod = attributes$1[attributes.HTTP_REQUEST_METHOD] || attributes$1[attributes.HTTP_METHOD];
if (httpMethod) {
return descriptionForHttpMethod({ attributes: attributes$1, name: spanName, kind }, httpMethod);
}
const dbSystem = attributes$1[attributes.DB_SYSTEM_NAME] || attributes$1[attributes.DB_SYSTEM];
const opIsCache = typeof attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] === "string" && attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_OP].startsWith("cache.");
if (dbSystem && !opIsCache) {
return descriptionForDbSystem({ attributes: attributes$1, name: spanName });
}
const customSourceOrRoute = attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom" ? "custom" : "route";
const rpcService = attributes$1[attributes.RPC_SERVICE];
if (rpcService) {
return {
...getUserUpdatedNameAndSource(spanName, attributes$1, "route"),
op: "rpc"
};
}
const messagingSystem = attributes$1[attributes.MESSAGING_SYSTEM];
if (messagingSystem) {
return {
...getUserUpdatedNameAndSource(spanName, attributes$1, customSourceOrRoute),
op: "message"
};
}
const faasTrigger = attributes$1[attributes.FAAS_TRIGGER];
if (faasTrigger) {
return {
...getUserUpdatedNameAndSource(spanName, attributes$1, customSourceOrRoute),
op: faasTrigger.toString()
};
}
return { op: void 0, description: spanName, source: "custom" };
}
function parseSpanDescription(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const name = spanHasName(span) ? span.name : "<unknown>";
const kind = getSpanKind(span);
return inferSpanData(name, attributes, kind);
}
function descriptionForDbSystem({ attributes: attributes$1, name }) {
const userDefinedName = attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (typeof userDefinedName === "string") {
return {
op: "db",
description: userDefinedName,
source: attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || "custom"
};
}
if (attributes$1[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom") {
return { op: "db", description: name, source: "custom" };
}
const statement = attributes$1[attributes.DB_STATEMENT];
const description = statement ? statement.toString() : name;
return { op: "db", description, source: "task" };
}
function descriptionForHttpMethod({ name, kind, attributes }, httpMethod) {
const opParts = ["http"];
switch (kind) {
case api.SpanKind.CLIENT:
opParts.push("client");
break;
case api.SpanKind.SERVER:
opParts.push("server");
break;
}
if (attributes["sentry.http.prefetch"]) {
opParts.push("prefetch");
}
const { urlPath, url, query, fragment, hasRoute } = getSanitizedUrl(attributes, kind);
if (!urlPath) {
return { ...getUserUpdatedNameAndSource(name, attributes), op: opParts.join(".") };
}
const graphqlOperationsAttribute = attributes[SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION];
const baseDescription = `${httpMethod} ${urlPath}`;
const inferredDescription = graphqlOperationsAttribute ? `${baseDescription} (${getGraphqlOperationNamesFromAttribute(graphqlOperationsAttribute)})` : baseDescription;
const inferredSource = hasRoute || urlPath === "/" ? "route" : "url";
const data = {};
if (url) {
data.url = url;
}
if (query) {
data["http.query"] = query;
}
if (fragment) {
data["http.fragment"] = fragment;
}
const isClientOrServerKind = kind === api.SpanKind.CLIENT || kind === api.SpanKind.SERVER;
const origin = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] || "manual";
const isManualSpan = !`${origin}`.startsWith("auto");
const alreadyHasCustomSource = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom";
const customSpanName = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
const useInferredDescription = !alreadyHasCustomSource && customSpanName == null && (isClientOrServerKind || !isManualSpan);
const { description, source } = useInferredDescription ? { description: inferredDescription, source: inferredSource } : getUserUpdatedNameAndSource(name, attributes);
return {
op: opParts.join("."),
description,
source,
data
};
}
function getGraphqlOperationNamesFromAttribute(attr) {
if (Array.isArray(attr)) {
const sorted = attr.slice().sort();
if (sorted.length <= 5) {
return sorted.join(", ");
} else {
return `${sorted.slice(0, 5).join(", ")}, +${sorted.length - 5}`;
}
}
return `${attr}`;
}
function getSanitizedUrl(attributes$1, kind) {
const httpTarget = attributes$1[attributes.HTTP_TARGET];
const httpUrl = attributes$1[attributes.HTTP_URL] || attributes$1[attributes.URL_FULL];
const httpRoute = attributes$1[attributes.HTTP_ROUTE];
const parsedUrl = typeof httpUrl === "string" ? core.parseUrl(httpUrl) : void 0;
const url = parsedUrl ? core.getSanitizedUrlString(parsedUrl) : void 0;
const query = parsedUrl?.search || void 0;
const fragment = parsedUrl?.hash || void 0;
if (typeof httpRoute === "string") {
return { urlPath: httpRoute, url, query, fragment, hasRoute: true };
}
if (kind === api.SpanKind.SERVER && typeof httpTarget === "string") {
return { urlPath: core.stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
if (parsedUrl) {
return { urlPath: url, url, query, fragment, hasRoute: false };
}
if (typeof httpTarget === "string") {
return { urlPath: core.stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
return { urlPath: void 0, url, query, fragment, hasRoute: false };
}
function getUserUpdatedNameAndSource(originalName, attributes, fallbackSource = "custom") {
const source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || fallbackSource;
const description = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (description && typeof description === "string") {
return {
description,
source
};
}
return { description: originalName, source };
}
function enhanceDscWithOpenTelemetryRootSpanName(client) {
client.on("createDsc", (dsc, rootSpan) => {
if (!rootSpan) {
return;
}
const jsonSpan = core.spanToJSON(rootSpan);
const attributes = jsonSpan.data;
const source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
const { description } = spanHasName(rootSpan) ? parseSpanDescription(rootSpan) : { description: void 0 };
if (source !== "url" && description) {
dsc.transaction = description;
}
if (core.hasSpansEnabled()) {
const sampled = getSamplingDecision(rootSpan.spanContext());
dsc.sampled = sampled == void 0 ? void 0 : String(sampled);
}
});
}
function getActiveSpan() {
return api.trace.getActiveSpan();
}
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
class TraceState {
constructor() {
this._internalState = /* @__PURE__ */ new Map();
}
/** @inheritDoc */
set(key, value) {
const next = this._clone();
if (next._internalState.has(key)) {
next._internalState.delete(key);
}
next._internalState.set(key, value);
return next;
}
/** @inheritDoc */
unset(key) {
const next = this._clone();
next._internalState.delete(key);
return next;
}
/** @inheritDoc */
get(key) {
return this._internalState.get(key);
}
/** @inheritDoc */
serialize() {
return Array.from(this._internalState.keys()).reverse().map((key) => `${key}=${this._internalState.get(key)}`).join(",");
}
_clone() {
const next = new TraceState();
next._internalState = new Map(this._internalState);
return next;
}
}
function makeTraceState({
dsc,
sampled
}) {
const dscString = dsc ? core.dynamicSamplingContextToSentryBaggageHeader(dsc) : void 0;
const traceStateBase = new TraceState();
const traceStateWithDsc = dscString ? traceStateBase.set(SENTRY_TRACE_STATE_DSC, dscString) : traceStateBase;
return sampled === false ? traceStateWithDsc.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") : traceStateWithDsc;
}
const setupElements = /* @__PURE__ */ new Set();
function openTelemetrySetupCheck() {
return Array.from(setupElements);
}
function setIsSetup(element) {
setupElements.add(element);
}
class SentryPropagator extends core$1.W3CBaggagePropagator {
constructor() {
super();
setIsSetup("SentryPropagator");
this._urlMatchesTargetsMap = new core.LRUMap(100);
}
/**
* @inheritDoc
*/
inject(context2, carrier, setter) {
if (core$1.isTracingSuppressed(context2)) {
DEBUG_BUILD && core.debug.log("[Tracing] Not injecting trace data for url because tracing is suppressed.");
return;
}
const activeSpan = api.trace.getSpan(context2);
const url = activeSpan && getCurrentURL(activeSpan);
const { tracePropagationTargets, propagateTraceparent } = core.getClient()?.getOptions() || {};
if (!core.shouldPropagateTraceForUrl(url, tracePropagationTargets, this._urlMatchesTargetsMap)) {
DEBUG_BUILD && core.debug.log("[Tracing] Not injecting trace data for url because it does not match tracePropagationTargets:", url);
return;
}
const existingBaggageHeader = getExistingBaggage(carrier);
const existingSentryTraceHeader = getExistingSentryTrace(carrier);
let baggage = api.propagation.getBaggage(context2) || api.propagation.createBaggage({});
const { dynamicSamplingContext, traceId, spanId, sampled } = getInjectionData(context2);
if (existingBaggageHeader) {
const baggageEntries = core.parseBaggageHeader(existingBaggageHeader);
if (baggageEntries) {
Object.entries(baggageEntries).forEach(([key, value]) => {
if (!existingSentryTraceHeader && key.startsWith(core.SENTRY_BAGGAGE_KEY_PREFIX)) {
return;
}
baggage = baggage.setEntry(key, { value });
});
}
}
if (!existingSentryTraceHeader && dynamicSamplingContext) {
baggage = Object.entries(dynamicSamplingContext).reduce((b, [dscKey, dscValue]) => {
if (dscValue) {
return b.setEntry(`${core.SENTRY_BAGGAGE_KEY_PREFIX}${dscKey}`, { value: dscValue });
}
return b;
}, baggage);
}
if (!existingSentryTraceHeader && traceId && traceId !== api.INVALID_TRACEID) {
setter.set(carrier, SENTRY_TRACE_HEADER, core.generateSentryTraceHeader(traceId, spanId, sampled));
if (propagateTraceparent) {
setter.set(carrier, "traceparent", core.generateTraceparentHeader(traceId, spanId, sampled));
}
}
super.inject(api.propagation.setBaggage(context2, baggage), carrier, setter);
}
/**
* @inheritDoc
*/
extract(context2, carrier, getter) {
const maybeSentryTraceHeader = getter.get(carrier, SENTRY_TRACE_HEADER);
const baggage = getter.get(carrier, SENTRY_BAGGAGE_HEADER);
const sentryTrace = maybeSentryTraceHeader ? Array.isArray(maybeSentryTraceHeader) ? maybeSentryTraceHeader[0] : maybeSentryTraceHeader : void 0;
return ensureScopesOnContext(getContextWithRemoteActiveSpan(context2, { sentryTrace, baggage }));
}
/**
* @inheritDoc
*/
fields() {
return [SENTRY_TRACE_HEADER, SENTRY_BAGGAGE_HEADER, "traceparent"];
}
}
function getInjectionData(context2, options = {}) {
const span = api.trace.getSpan(context2);
if (span?.spanContext().isRemote) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = core.getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: void 0,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
if (span) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = core.getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: spanContext.spanId,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
const scope = options.scope || getScopesFromContext(context2)?.scope || core.getCurrentScope();
const client = options.client || core.getClient();
const propagationContext = scope.getPropagationContext();
const dynamicSamplingContext = client ? core.getDynamicSamplingContextFromScope(client, scope) : void 0;
return {
dynamicSamplingContext,
traceId: propagationContext.traceId,
spanId: propagationContext.propagationSpanId,
sampled: propagationContext.sampled
};
}
function getContextWithRemoteActiveSpan(ctx, { sentryTrace, baggage }) {
const propagationContext = core.propagationContextFromHeaders(sentryTrace, baggage);
const { traceId, parentSpanId, sampled, dsc } = propagationContext;
const client = core.getClient();
const incomingDsc = core.baggageHeaderToDynamicSamplingContext(baggage);
if (!parentSpanId || client && !core.shouldContinueTrace(client, incomingDsc?.org_id)) {
return ctx;
}
const spanContext = generateRemoteSpanContext({
traceId,
spanId: parentSpanId,
sampled,
dsc
});
return api.trace.setSpanContext(ctx, spanContext);
}
function continueTraceAsRemoteSpan(ctx, options, callback) {
const ctxWithSpanContext = ensureScopesOnContext(getContextWithRemoteActiveSpan(ctx, options));
return api.context.with(ctxWithSpanContext, callback);
}
function ensureScopesOnContext(ctx) {
const scopes = getScopesFromContext(ctx);
const newScopes = {
// If we have no scope here, this is most likely either the root context or a context manually derived from it
// In this case, we want to fork the current scope, to ensure we do not pollute the root scope
scope: scopes ? scopes.scope : core.getCurrentScope().clone(),
isolationScope: scopes ? scopes.isolationScope : core.getIsolationScope()
};
return setScopesOnContext(ctx, newScopes);
}
function getExistingBaggage(carrier) {
try {
const baggage = carrier[SENTRY_BAGGAGE_HEADER];
return Array.isArray(baggage) ? baggage.join(",") : baggage;
} catch {
return void 0;
}
}
function getExistingSentryTrace(carrier) {
try {
return carrier[SENTRY_TRACE_HEADER];
} catch {
return void 0;
}
}
function getCurrentURL(span) {
const spanData = core.spanToJSON(span).data;
const urlAttribute = spanData[attributes.HTTP_URL] || spanData[attributes.URL_FULL];
if (typeof urlAttribute === "string") {
return urlAttribute;
}
const urlTraceState = span.spanContext().traceState?.get(SENTRY_TRACE_STATE_URL);
if (urlTraceState) {
return urlTraceState;
}
return void 0;
}
function generateRemoteSpanContext({
spanId,
traceId,
sampled,
dsc
}) {
const traceState = makeTraceState({
dsc,
sampled
});
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE,
traceState
};
return spanContext;
}
function _startSpan(options, callback, autoEnd) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !api.trace.getSpan(activeCtx);
const ctx = missingRequiredParent ? core$1.suppressTracing(activeCtx) : activeCtx;
if (missingRequiredParent) {
core.getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!core.hasSpansEnabled()) {
const suppressedCtx = core$1.isTracingSuppressed(ctx) ? ctx : core$1.suppressTracing(ctx);
return api.context.with(suppressedCtx, () => {
return tracer.startActiveSpan(name, spanOptions, suppressedCtx, (span) => {
patchSpanEnd(span);
return api.context.with(activeCtx, () => {
return core.handleCallbackErrors(
() => callback(span),
() => {
if (core.spanToJSON(span).status === void 0) {
span.setStatus({ code: api.SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
});
}
return tracer.startActiveSpan(name, spanOptions, ctx, (span) => {
patchSpanEnd(span);
return core.handleCallbackErrors(
() => callback(span),
() => {
if (core.spanToJSON(span).status === void 0) {
span.setStatus({ code: api.SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
}
function startSpan(options, callback) {
return _startSpan(options, callback, true);
}
function startSpanManual(options, callback) {
return _startSpan(options, (span) => callback(span, () => span.end()), false);
}
function startInactiveSpan(options) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !api.trace.getSpan(activeCtx);
let ctx = missingRequiredParent ? core$1.suppressTracing(activeCtx) : activeCtx;
if (missingRequiredParent) {
core.getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!core.hasSpansEnabled()) {
ctx = core$1.isTracingSuppressed(ctx) ? ctx : core$1.suppressTracing(ctx);
}
const span = tracer.startSpan(name, spanOptions, ctx);
patchSpanEnd(span);
return span;
});
}
function withActiveSpan(span, callback) {
const newContextWithActiveSpan = span ? api.trace.setSpan(api.context.active(), span) : api.trace.deleteSpan(api.context.active());
return api.context.with(newContextWithActiveSpan, () => callback(core.getCurrentScope()));
}
function getTracer() {
const client = core.getClient();
return client?.tracer || api.trace.getTracer("@sentry/opentelemetry", core.SDK_VERSION);
}
function getSpanOptions(options) {
const { startTime, attributes, kind, op, links } = options;
const fixedStartTime = typeof startTime === "number" ? ensureTimestampInMilliseconds(startTime) : startTime;
return {
attributes: op ? {
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...attributes
} : attributes,
kind,
links,
startTime: fixedStartTime
};
}
function ensureTimestampInMilliseconds(timestamp) {
const isMs = timestamp < 9999999999;
return isMs ? timestamp * 1e3 : timestamp;
}
function patchSpanEnd(span) {
const originalEnd = span.end.bind(span);
span.end = (endTime) => {
return originalEnd(typeof endTime === "number" ? ensureTimestampInMilliseconds(endTime) : endTime);
};
}
function getContext(scope, forceTransaction) {
const ctx = getContextForScope(scope);
const parentSpan = api.trace.getSpan(ctx);
if (!parentSpan) {
return ctx;
}
if (!forceTransaction) {
return ctx;
}
const ctxWithoutSpan = api.trace.deleteSpan(ctx);
const { spanId, traceId } = parentSpan.spanContext();
const sampled = getSamplingDecision(parentSpan.spanContext());
const rootSpan = core.getRootSpan(parentSpan);
const dsc = core.getDynamicSamplingContextFromSpan(rootSpan);
const traceState = makeTraceState({
dsc,
sampled
});
const spanOptions = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE,
traceState
};
const ctxWithSpanContext = api.trace.setSpanContext(ctxWithoutSpan, spanOptions);
return ctxWithSpanContext;
}
function getContextForScope(scope) {
if (scope) {
const ctx = getContextFromScope(scope);
if (ctx) {
return ctx;
}
}
return api.context.active();
}
function continueTrace(options, callback) {
return continueTraceAsRemoteSpan(api.context.active(), options, callback);
}
function startNewTrace(callback) {
const traceId = core.generateTraceId();
const spanId = core.generateSpanId();
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: api.TraceFlags.NONE
};
const ctxWithTrace = api.trace.setSpanContext(api.context.active(), spanContext);
return api.context.with(ctxWithTrace, () => {
core.getCurrentScope().setPropagationContext({
traceId,
sampleRand: core._INTERNAL_safeMathRandom()
});
return callback();
});
}
function getTraceContextForScope(client, scope) {
const ctx = getContextFromScope(scope);
const span = ctx && api.trace.getSpan(ctx);
const traceContext = span ? core.spanToTraceContext(span) : core.getTraceContextFromScope(scope);
const dynamicSamplingContext = span ? core.getDynamicSamplingContextFromSpan(span) : core.getDynamicSamplingContextFromScope(client, scope);
return [dynamicSamplingContext, traceContext];
}
function getActiveSpanWrapper(parentSpan) {
return parentSpan !== void 0 ? (callback) => {
return withActiveSpan(parentSpan, callback);
} : (callback) => callback();
}
function suppressTracing(callback) {
const ctx = core$1.suppressTracing(api.context.active());
return api.context.with(ctx, callback);
}
function setupEventContextTrace(client) {
client.on("preprocessEvent", (event) => {
const span = getActiveSpan();
if (!span || event.type === "transaction") {
return;
}
event.contexts = {
trace: core.spanToTraceContext(span),
...event.contexts
};
const rootSpan = core.getRootSpan(span);
event.sdkProcessingMetadata = {
dynamicSamplingContext: core.getDynamicSamplingContextFromSpan(rootSpan),
...event.sdkProcessingMetadata
};
return event;
});
}
function getTraceData({
span,
scope,
client,
propagateTraceparent
} = {}) {
let ctx = (scope && getContextFromScope(scope)) ?? api.context.active();
if (span) {
const { scope: scope2 } = core.getCapturedScopesOnSpan(span);
ctx = scope2 && getContextFromScope(scope2) || api.trace.setSpan(api.context.active(), span);
}
const { traceId, spanId, sampled, dynamicSamplingContext } = getInjectionData(ctx, { scope, client });
const traceData = {
"sentry-trace": core.generateSentryTraceHeader(traceId, spanId, sampled),
baggage: core.dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext)
};
if (propagateTraceparent) {
traceData.traceparent = core.generateTraceparentHeader(traceId, spanId, sampled);
}
return traceData;
}
function setOpenTelemetryContextAsyncContextStrategy() {
function getScopes() {
const ctx = api.context.active();
const scopes = getScopesFromContext(ctx);
if (scopes) {
return scopes;
}
return {
scope: core.getDefaultCurrentScope(),
isolationScope: core.getDefaultIsolationScope()
};
}
function withScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx, () => {
return callback(getCurrentScope());
});
}
function withSetScope(scope, callback) {
const ctx = getContextFromScope(scope) || api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY, scope), () => {
return callback(scope);
});
}
function withIsolationScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY, true), () => {
return callback(getIsolationScope());
});
}
function withSetIsolationScope(isolationScope, callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY, isolationScope), () => {
return callback(getIsolationScope());
});
}
function getCurrentScope() {
return getScopes().scope;
}
function getIsolationScope() {
return getScopes().isolationScope;
}
core.setAsyncContextStrategy({
withScope,
withSetScope,
withSetIsolationScope,
withIsolationScope,
getCurrentScope,
getIsolationScope,
startSpan,
startSpanManual,
startInactiveSpan,
getActiveSpan,
suppressTracing,
getTraceData,
continueTrace,
startNewTrace,
// The types here don't fully align, because our own `Span` type is narrower
// than the OTEL one - but this is OK for here, as we now we'll only have OTEL spans passed around
withActiveSpan,
getTracingChannelBinding: () => {
try {
const contextManager = api.context._getContextManager();
const lookup = contextManager.getAsyncLocalStorageLookup();
return {
asyncLocalStorage: lookup.asyncLocalStorage,
getStoreWithActiveSpan: (span) => api.trace.setSpan(api.context.active(), span)
};
} catch {
return void 0;
}
}
});
}
function buildContextWithSentryScopes(context, activeContext) {
const span = api.trace.getSpan(context);
let effectiveContext;
if (span?.spanContext().traceState?.get(SENTRY_TRACE_STATE_CHILD_IGNORED) === "1") {
const contextWithoutSpan = api.trace.deleteSpan(context);
const parentSpan = api.trace.getSpan(activeContext);
effectiveContext = parentSpan ? api.trace.setSpan(contextWithoutSpan, parentSpan) : contextWithoutSpan;
} else {
effectiveContext = context;
}
const currentScopes = getScopesFromContext(effectiveContext);
const currentScope = currentScopes?.scope || core.getCurrentScope();
const currentIsolationScope = currentScopes?.isolationScope || core.getIsolationScope();
const shouldForkIsolationScope = effectiveContext.getValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY) === true;
const scope = effectiveContext.getValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY);
const isolationScope = effectiveContext.getValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
const newCurrentScope = scope || currentScope.clone();
const newIsolationScope = isolationScope || (shouldForkIsolationScope ? currentIsolationScope.clone() : currentIsolationScope);
const scopes = { scope: newCurrentScope, isolationScope: newIsolationScope };
const ctx1 = setScopesOnContext(effectiveContext, scopes);
const ctx2 = ctx1.deleteValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
setContextOnScope(newCurrentScope, ctx2);
return ctx2;
}
function wrapContextManagerClass(ContextManagerClass) {
class SentryContextManager extends ContextManagerClass {
constructor(...args) {
super(...args);
setIsSetup("SentryContextManager");
}
/**
* Overwrite with() of the original AsyncLocalStorageContextManager
* to ensure we also create new scopes per context.
*/
with(context, fn, thisArg, ...args) {
const ctx2 = buildContextWithSentryScopes(context, this.active());
return super.with(ctx2, fn, thisArg, ...args);
}
/**
* Gets underlying AsyncLocalStorage and symbol to allow lookup of scope.
*/
getAsyncLocalStorageLookup() {
return {
// @ts-expect-error This is on the base class, but not part of the interface
asyncLocalStorage: this._asyncLocalStorage,
contextSymbol: SENTRY_SCOPES_CONTEXT_KEY
};
}
}
return SentryContextManager;
}
function groupSpansWithParents(spans) {
const nodeMap = /* @__PURE__ */ new Map();
for (const span of spans) {
createOrUpdateSpanNodeAndRefs(nodeMap, span);
}
return Array.from(nodeMap, function([_id, spanNode]) {
return spanNode;
});
}
function getLocalParentId(span) {
const parentIsRemote = span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE] === true;
return !parentIsRemote ? getParentSpanId(span) : void 0;
}
function createOrUpdateSpanNodeAndRefs(nodeMap, span) {
const id = span.spanContext().spanId;
const parentId = getLocalParentId(span);
if (!parentId) {
createOrUpdateNode(nodeMap, { id, span, children: [] });
return;
}
const parentNode = createOrGetParentNode(nodeMap, parentId);
const node = createOrUpdateNode(nodeMap, { id, span, parentNode, children: [] });
parentNode.children.push(node);
}
function createOrGetParentNode(nodeMap, id) {
const existing = nodeMap.get(id);
if (existing) {
return existing;
}
return createOrUpdateNode(nodeMap, { id, children: [] });
}
function createOrUpdateNode(nodeMap, spanNode) {
const existing = nodeMap.get(spanNode.id);
if (existing?.span) {
return existing;
}
if (existing && !existing.span) {
existing.span = spanNode.span;
existing.parentNode = spanNode.parentNode;
return existing;
}
nodeMap.set(spanNode.id, spanNode);
return spanNode;
}
const canonicalGrpcErrorCodesMap = {
"1": "cancelled",
"2": "unknown_error",
"3": "invalid_argument",
"4": "deadline_exceeded",
"5": "not_found",
"6": "already_exists",
"7": "permission_denied",
"8": "resource_exhausted",
"9": "failed_precondition",
"10": "aborted",
"11": "out_of_range",
"12": "unimplemented",
"13": "internal_error",
"14": "unavailable",
"15": "data_loss",
"16": "unauthenticated"
};
const isStatusErrorMessageValid = (message) => {
return Object.values(canonicalGrpcErrorCodesMap).includes(message);
};
function mapStatus(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const status = spanHasStatus(span) ? span.status : void 0;
if (status) {
if (status.code === api.SpanStatusCode.OK) {
return { code: core.SPAN_STATUS_OK };
} else if (status.code === api.SpanStatusCode.ERROR) {
if (typeof status.message === "undefined") {
const inferredStatus2 = inferStatusFromAttributes(attributes);
if (inferredStatus2) {
return inferredStatus2;
}
}
if (status.message && isStatusErrorMessageValid(status.message)) {
return { code: core.SPAN_STATUS_ERROR, message: status.message };
} else {
return { code: core.SPAN_STATUS_ERROR, message: "internal_error" };
}
}
}
const inferredStatus = inferStatusFromAttributes(attributes);
if (inferredStatus) {
return inferredStatus;
}
if (status?.code === api.SpanStatusCode.UNSET) {
return { code: core.SPAN_STATUS_OK };
} else {
return { code: core.SPAN_STATUS_ERROR, message: "unknown_error" };
}
}
function inferStatusFromAttributes(attributes$1) {
const httpCodeAttribute = attributes$1[attributes.HTTP_RESPONSE_STATUS_CODE] || attributes$1[attributes.HTTP_STATUS_CODE];
const grpcCodeAttribute = attributes$1[attributes.RPC_GRPC_STATUS_CODE];
const numberHttpCode = typeof httpCodeAttribute === "number" ? httpCodeAttribute : typeof httpCodeAttribute === "string" ? parseInt(httpCodeAttribute) : void 0;
if (typeof numberHttpCode === "number") {
return core.getSpanStatusFromHttpCode(numberHttpCode);
}
if (typeof grpcCodeAttribute === "string") {
return { code: core.SPAN_STATUS_ERROR, message: canonicalGrpcErrorCodesMap[grpcCodeAttribute] || "unknown_error" };
}
return void 0;
}
const MAX_SPAN_COUNT = 1e3;
const DEFAULT_TIMEOUT = 300;
const SENT_SPANS_MAX_SIZE = 1e4;
class SentrySpanExporter {
constructor(options) {
this._finishedSpanBucketSize = options?.timeout || DEFAULT_TIMEOUT;
this._finishedSpanBuckets = new Array(this._finishedSpanBucketSize).fill(void 0);
this._lastCleanupTimestampInS = Math.floor(core._INTERNAL_safeDateNow() / 1e3);
this._spansToBucketEntry = /* @__PURE__ */ new WeakMap();
this._sentSpans = new core.LRUMap(SENT_SPANS_MAX_SIZE);
this._debouncedFlush = core.debounce(this.flush.bind(this), 1, { maxWait: 100 });
}
/**
* Export a single span.
* This is called by the span processor whenever a span is ended.
*/
export(span) {
const currentTimestampInS = Math.floor(core._INTERNAL_safeDateNow() / 1e3);
if (this._lastCleanupTimestampInS !== currentTimestampInS) {
let droppedSpanCount = 0;
this._finishedSpanBuckets.forEach((bucket, i) => {
if (bucket && bucket.timestampInS <= currentTimestampInS - this._finishedSpanBucketSize) {
droppedSpanCount += bucket.spans.size;
this._finishedSpanBuckets[i] = void 0;
}
});
if (droppedSpanCount > 0) {
DEBUG_BUILD && core.debug.log(
`SpanExporter dropped ${droppedSpanCount} spans because they were pending for more than ${this._finishedSpanBucketSize} seconds.`
);
}
this._lastCleanupTimestampInS = currentTimestampInS;
}
const currentBucketIndex = currentTimestampInS % this._finishedSpanBucketSize;
const currentBucket = this._finishedSpanBuckets[currentBucketIndex] || {
timestampInS: currentTimestampInS,
spans: /* @__PURE__ */ new Set()
};
this._finishedSpanBuckets[currentBucketIndex] = currentBucket;
currentBucket.spans.add(span);
this._spansToBucketEntry.set(span, currentBucket);
const localParentId = getLocalParentId(span);
if (!localParentId || this._sentSpans.get(localParentId)) {
this._debouncedFlush();
}
}
/**
* Try to flush any pending spans immediately.
* This is called internally by the exporter (via _debouncedFlush),
* but can also be triggered externally if we force-flush.
*/
flush() {
const finishedSpans = this._finishedSpanBuckets.flatMap((bucket) => bucket ? Array.from(bucket.spans) : []);
const sentSpans = this._maybeSend(finishedSpans);
const sentSpanCount = sentSpans.size;
const remainingOpenSpanCount = finishedSpans.length - sentSpanCount;
DEBUG_BUILD && core.debug.log(
`SpanExporter exported ${sentSpanCount} spans, ${remainingOpenSpanCount} spans are waiting for their parent spans to finish`
);
for (const span of sentSpans) {
this._sentSpans.set(span.spanContext().spanId, 1);
const bucketEntry = this._spansToBucketEntry.get(span);
if (bucketEntry) {
bucketEntry.spans.delete(span);
}
}
this._debouncedFlush.cancel();
}
/**
* Clear the exporter.
* This is called when the span processor is shut down.
*/
clear() {
this._finishedSpanBuckets = this._finishedSpanBuckets.fill(void 0);
this._sentSpans.clear();
this._debouncedFlush.cancel();
}
/**
* Send the given spans, but only if they are part of a finished transaction.
*
* Returns the sent spans.
* Spans remain unsent when their parent span is not yet finished.
* This will happen regularly, as child spans are generally finished before their parents.
* But it _could_ also happen because, for whatever reason, a parent span was lost.
* In this case, we'll eventually need to clean this up.
*/
_maybeSend(spans) {
const grouped = groupSpansWithParents(spans);
const sentSpans = /* @__PURE__ */ new Set();
const rootNodes = this._getCompletedRootNodes(grouped);
for (const root of rootNodes) {
const span = root.span;
sentSpans.add(span);
const transactionEvent = createTransactionForOtelSpan(span);
if (root.parentNode && this._sentSpans.get(root.parentNode.id)) {
const traceData = transactionEvent.contexts?.trace?.data;
if (traceData) {
traceData["sentry.parent_span_already_sent"] = true;
}
}
const spans2 = transactionEvent.spans || [];
let hasGenAiSpans = false;
for (const child of root.children) {
if (createAndFinishSpanForOtelSpan(child, spans2, sentSpans)) {
hasGenAiSpans = true;
}
}
transactionEvent.spans = spans2.length > MAX_SPAN_COUNT ? spans2.sort((a, b) => a.start_timestamp - b.start_timestamp).slice(0, MAX_SPAN_COUNT) : spans2;
if (hasGenAiSpans) {
transactionEvent.sdkProcessingMetadata = {
...transactionEvent.sdkProcessingMetadata,
hasGenAiSpans: true
};
}
const measurements = core.timedEventsToMeasurements(span.events);
if (measurements) {
transactionEvent.measurements = measurements;
}
core.captureEvent(transactionEvent);
}
return sentSpans;
}
/** Check if a node is a completed root node or a node whose parent has already been sent */
_nodeIsCompletedRootNodeOrHasSentParent(node) {
return !!node.span && (!node.parentNode || !!this._sentSpans.get(node.parentNode.id));
}
/** Get all completed root nodes from a list of nodes */
_getCompletedRootNodes(nodes) {
return nodes.filter((node) => this._nodeIsCompletedRootNodeOrHasSentParent(node));
}
}
function parseSpan(span) {
const attributes = span.attributes;
const origin = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];
const op = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP];
const source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
return { origin, op, source };
}
function createTransactionForOtelSpan(span) {
const { op, description, data, origin = "manual", source } = getSpanData(span);
const capturedSpanScopes = core.getCapturedScopesOnSpan(span);
const sampleRate = span.attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
const attributes$1 = {
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate,
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
...data,
...removeSentryAttributes(span.attributes)
};
const { links } = span;
const { traceId: trace_id, spanId: span_id } = span.spanContext();
const parent_span_id = getParentSpanId(span);
const status = mapStatus(span);
const traceContext = {
parent_span_id,
span_id,
trace_id,
data: attributes$1,
origin,
op,
status: core.getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
links: core.convertSpanLinksForEnvelope(links)
};
const statusCode = attributes$1[attributes.HTTP_RESPONSE_STATUS_CODE];
const responseContext = typeof statusCode === "number" ? { response: { status_code: statusCode } } : void 0;
const transactionEvent = {
contexts: {
trace: traceContext,
otel: {
resource: span.resource.attributes
},
...responseContext
},
spans: [],
start_timestamp: core.spanTimeInputToSeconds(span.startTime),
timestamp: core.spanTimeInputToSeconds(span.endTime),
transaction: description,
type: "transaction",
sdkProcessingMetadata: {
capturedSpanScope: capturedSpanScopes.scope,
capturedSpanIsolationScope: capturedSpanScopes.isolationScope,
sampleRate,
dynamicSamplingContext: core.getDynamicSamplingContextFromSpan(span)
},
...source && {
transaction_info: {
source
}
}
};
return transactionEvent;
}
function createAndFinishSpanForOtelSpan(node, spans, sentSpans) {
const span = node.span;
if (span) {
sentSpans.add(span);
}
const shouldDrop = !span;
if (shouldDrop) {
let hasGenAiSpans2 = false;
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans2 = true;
}
});
return hasGenAiSpans2;
}
const span_id = span.spanContext().spanId;
const trace_id = span.spanContext().traceId;
const parentSpanId = getParentSpanId(span);
const { attributes, startTime, endTime, links } = span;
const { op, description, data, origin = "manual" } = getSpanData(span);
const allData = {
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...removeSentryAttributes(attributes),
...data
};
const status = mapStatus(span);
const spanJSON = {
span_id,
trace_id,
data: allData,
description,
parent_span_id: parentSpanId,
start_timestamp: core.spanTimeInputToSeconds(startTime),
// This is [0,0] by default in OTEL, in which case we want to interpret this as no end time
timestamp: core.spanTimeInputToSeconds(endTime) || void 0,
status: core.getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
op,
origin,
measurements: core.timedEventsToMeasurements(span.events),
links: core.convertSpanLinksForEnvelope(links)
};
spans.push(spanJSON);
let hasGenAiSpans = !!op?.startsWith("gen_ai.");
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans = true;
}
});
return hasGenAiSpans;
}
function getSpanData(span) {
const { op: definedOp, source: definedSource, origin } = parseSpan(span);
const { op: inferredOp, description, source: inferredSource, data: inferredData } = parseSpanDescription(span);
const op = definedOp || inferredOp;
const source = definedSource || inferredSource;
const data = { ...inferredData, ...getData(span) };
return {
op,
description,
source,
origin,
data
};
}
function removeSentryAttributes(data) {
const cleanedData = { ...data };
delete cleanedData[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE];
delete cleanedData[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
return cleanedData;
}
function getData(span) {
const attributes$1 = span.attributes;
const data = {};
if (span.kind !== api.SpanKind.INTERNAL) {
data["otel.kind"] = api.SpanKind[span.kind];
}
const maybeHttpStatusCodeAttribute = attributes$1[attributes.HTTP_STATUS_CODE];
if (maybeHttpStatusCodeAttribute) {
data[attributes.HTTP_RESPONSE_STATUS_CODE] = maybeHttpStatusCodeAttribute;
}
const requestData = getRequestSpanData(span);
if (requestData.url) {
data.url = requestData.url;
}
if (requestData["http.query"]) {
data["http.query"] = requestData["http.query"].slice(1);
}
if (requestData["http.fragment"]) {
data["http.fragment"] = requestData["http.fragment"].slice(1);
}
return data;
}
class SentrySpanProcessor {
constructor(options) {
this._unsubscribePreprocessSpan = void 0;
setIsSetup("SentrySpanProcessor");
this._exporter = new SentrySpanExporter(options);
this._client = options?.client ?? core.getClient();
if (this._client && core.hasSpanStreamingEnabled(this._client)) {
this._unsubscribePreprocessSpan = this._client.on("preprocessSpan", backfillStreamedSpanDataFromOtel);
}
}
/**
* @inheritDoc
*/
async forceFlush() {
this._exporter.flush();
}
/**
* @inheritDoc
*/
async shutdown() {
this._unsubscribePreprocessSpan?.();
this._exporter.clear();
}
/**
* @inheritDoc
*/
onStart(span, parentContext) {
const parentSpan = api.trace.getSpan(parentContext);
let scopes = getScopesFromContext(parentContext);
if (parentSpan && !parentSpan.spanContext().isRemote) {
core.addChildSpanToSpan(parentSpan, span);
}
if (parentSpan?.spanContext().isRemote) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE, true);
}
if (parentContext === api.ROOT_CONTEXT) {
scopes = {
scope: core.getDefaultCurrentScope(),
isolationScope: core.getDefaultIsolationScope()
};
}
if (scopes) {
core.setCapturedScopesOnSpan(span, scopes.scope, scopes.isolationScope);
}
core.logSpanStart(span);
this._client?.emit("spanStart", span);
}
/** @inheritDoc */
onEnd(span) {
core.logSpanEnd(span);
this._client?.emit("spanEnd", span);
if (this._client && core.hasSpanStreamingEnabled(this._client)) {
this._client.emit("afterSpanEnd", span);
} else {
this._exporter.export(span);
}
}
}
function backfillStreamedSpanDataFromOtel(spanJSON, hint) {
const attributes = spanJSON.attributes;
if (!attributes) {
return;
}
const kind = hint?.spanKind ?? api.SpanKind.INTERNAL;
const { op, description, source, data } = inferSpanData(spanJSON.name, attributes, kind);
spanJSON.name = description;
core.safeSetSpanJSONAttributes(spanJSON, {
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
...data
});
if (kind !== api.SpanKind.INTERNAL) {
core.safeSetSpanJSONAttributes(spanJSON, {
"otel.kind": api.SpanKind[kind]
});
}
}
class SentrySampler {
constructor(client) {
this._client = client;
this._isSpanStreaming = core.hasSpanStreamingEnabled(client);
setIsSetup("SentrySampler");
}
/** @inheritDoc */
shouldSample(context, traceId, spanName, spanKind, spanAttributes, _links) {
const options = this._client.getOptions();
const { ignoreSpans } = options;
const parentSpan = getValidSpan(context);
const parentContext = parentSpan?.spanContext();
if (!core.hasSpansEnabled(options)) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const maybeSpanHttpMethod = spanAttributes[attributes.HTTP_METHOD] || spanAttributes[attributes.HTTP_REQUEST_METHOD];
if (spanKind === api.SpanKind.CLIENT && maybeSpanHttpMethod && (!parentSpan || parentContext?.isRemote)) {
if (!this._isSpanStreaming) {
this._client.recordDroppedEvent("no_parent_span", "span");
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
}
const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : void 0;
const isRootSpan = !parentSpan || parentContext?.isRemote;
if (!isRootSpan) {
if (this._isSpanStreaming) {
if (parentSampled) {
if (ignoreSpans?.length) {
const { description: inferredChildName, op: childOp } = inferSpanData(spanName, spanAttributes, spanKind);
if (core.shouldIgnoreSpan(
{
description: inferredChildName,
op: spanAttributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? childOp,
attributes: spanAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredChildSpan: true
});
}
}
}
if (!parentSampled) {
const parentSegmentIgnored = parentContext?.traceState?.get(SENTRY_TRACE_STATE_SEGMENT_IGNORED) === "1";
this._client.recordDroppedEvent(parentSegmentIgnored ? "ignored" : "sample_rate", "span");
}
}
return wrapSamplingDecision({
decision: parentSampled ? sdkTraceBase.SamplingDecision.RECORD_AND_SAMPLED : sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes
});
}
const {
description: inferredSpanName,
data: inferredAttributes,
op
} = inferSpanData(spanName, spanAttributes, spanKind);
const mergedAttributes = {
...inferredAttributes,
...spanAttributes
};
if (op) {
mergedAttributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] = op;
}
if (this._isSpanStreaming && ignoreSpans?.length && core.shouldIgnoreSpan(
{
description: inferredSpanName,
op: mergedAttributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? op,
attributes: mergedAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredSegmentSpan: true
});
}
const mutableSamplingDecision = { decision: true };
this._client.emit(
"beforeSampling",
{
spanAttributes: mergedAttributes,
spanName: inferredSpanName,
parentSampled,
parentContext
},
mutableSamplingDecision
);
if (!mutableSamplingDecision.decision) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const { isolationScope } = getScopesFromContext(context) ?? {};
const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? core.baggageHeaderToDynamicSamplingContext(dscString) : void 0;
const sampleRand = core.parseSampleRate(dsc?.sample_rand) ?? core._INTERNAL_safeMathRandom();
const [sampled, sampleRate, localSampleRateWasApplied] = core.sampleSpan(
options,
{
name: inferredSpanName,
attributes: mergedAttributes,
normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest,
parentSampled,
parentSampleRate: core.parseSampleRate(dsc?.sample_rate)
},
sampleRand
);
const method = `${maybeSpanHttpMethod}`.toUpperCase();
if (method === "OPTIONS" || method === "HEAD") {
DEBUG_BUILD && core.debug.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
return wrapSamplingDecision({
decision: sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: 0
// we don't want to sample anything in the downstream trace either
});
}
if (!sampled && // We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace)
parentSampled === void 0) {
DEBUG_BUILD && core.debug.log("[Tracing] Discarding root span because its trace was not chosen to be sampled.");
this._client.recordDroppedEvent("sample_rate", this._isSpanStreaming ? "span" : "transaction");
}
return {
...wrapSamplingDecision({
decision: sampled ? sdkTraceBase.SamplingDecision.RECORD_AND_SAMPLED : sdkTraceBase.SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : void 0
}),
attributes: {
// We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry
[core.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : void 0
}
};
}
/** Returns the sampler name or short description with the configuration. */
toString() {
return "SentrySampler";
}
}
function getParentSampled(parentSpan, traceId, spanName) {
const parentContext = parentSpan.spanContext();
if (api.isSpanContextValid(parentContext) && parentContext.traceId === traceId) {
if (parentContext.isRemote) {
const parentSampled2 = getSamplingDecision(parentSpan.spanContext());
DEBUG_BUILD && core.debug.log(`[Tracing] Inheriting remote parent's sampled decision for ${spanName}: ${parentSampled2}`);
return parentSampled2;
}
const parentSampled = getSamplingDecision(parentContext);
DEBUG_BUILD && core.debug.log(`[Tracing] Inheriting parent's sampled decision for ${spanName}: ${parentSampled}`);
return parentSampled;
}
return void 0;
}
function wrapSamplingDecision({
decision,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate,
ignoredChildSpan,
ignoredSegmentSpan
}) {
let traceState = getBaseTraceState(context, spanAttributes);
if (downstreamTraceSampleRate !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RATE, `${downstreamTraceSampleRate}`);
}
if (sampleRand !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RAND, `${sampleRand}`);
}
if (ignoredChildSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_CHILD_IGNORED, "1");
}
if (ignoredSegmentSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_SEGMENT_IGNORED, "1");
}
if (decision == void 0) {
return { decision: sdkTraceBase.SamplingDecision.NOT_RECORD, traceState };
}
if (decision === sdkTraceBase.SamplingDecision.NOT_RECORD) {
return { decision, traceState: traceState.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") };
}
return { decision, traceState };
}
function getBaseTraceState(context, spanAttributes) {
const parentSpan = api.trace.getSpan(context);
const parentContext = parentSpan?.spanContext();
let traceState = parentContext?.traceState || new TraceState();
const url = spanAttributes[attributes.HTTP_URL] || spanAttributes[attributes.URL_FULL];
if (url && typeof url === "string") {
traceState = traceState.set(SENTRY_TRACE_STATE_URL, url);
}
return traceState;
}
function getValidSpan(context) {
const span = api.trace.getSpan(context);
return span && api.isSpanContextValid(span.spanContext()) ? span : void 0;
}
const ATTR_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language";
const ATTR_TELEMETRY_SDK_NAME = "telemetry.sdk.name";
const ATTR_TELEMETRY_SDK_VERSION = "telemetry.sdk.version";
const SEMRESATTRS_SERVICE_NAMESPACE = "service.namespace";
class SentryResource {
constructor(attributes) {
this._attributes = attributes;
}
get attributes() {
return this._attributes;
}
merge(other) {
if (!other) {
return this;
}
return new SentryResource({ ...this._attributes, ...other.attributes });
}
getRawAttributes() {
return Object.entries(this._attributes);
}
}
function parseOtelResourceAttributes(raw) {
if (!raw) {
return {};
}
const result = {};
for (const pair of raw.split(",")) {
const eq = pair.indexOf("=");
if (eq === -1) {
continue;
}
const key = pair.substring(0, eq).trim();
const value = pair.substring(eq + 1).trim();
if (key) {
try {
result[key] = decodeURIComponent(value);
} catch {
result[key] = value;
}
}
}
return result;
}
function getSentryResource(serviceNameFallback) {
const env = typeof process !== "undefined" ? process.env : {};
const otelServiceName = env.OTEL_SERVICE_NAME;
const otelResourceAttrs = parseOtelResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
return new SentryResource({
// Lowest priority: Sentry defaults
// eslint-disable-next-line typescript/no-deprecated
[SEMRESATTRS_SERVICE_NAMESPACE]: "sentry",
[attributes.SERVICE_NAME]: serviceNameFallback,
// OTEL_RESOURCE_ATTRIBUTES overrides defaults (including service.name and service.namespace)
...otelResourceAttrs,
// OTEL_SERVICE_NAME explicitly overrides service.name
...otelServiceName ? { [attributes.SERVICE_NAME]: otelServiceName } : {},
// Highest priority: Sentry SDK telemetry attrs (cannot be overridden by env vars)
[attributes.SERVICE_VERSION]: core.SDK_VERSION,
[ATTR_TELEMETRY_SDK_LANGUAGE]: core$1.SDK_INFO[ATTR_TELEMETRY_SDK_LANGUAGE],
[ATTR_TELEMETRY_SDK_NAME]: core$1.SDK_INFO[ATTR_TELEMETRY_SDK_NAME],
[ATTR_TELEMETRY_SDK_VERSION]: core$1.SDK_INFO[ATTR_TELEMETRY_SDK_VERSION]
});
}
exports.SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION = SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION;
exports.SENTRY_SCOPES_CONTEXT_KEY = SENTRY_SCOPES_CONTEXT_KEY;
exports.SentryPropagator = SentryPropagator;
exports.SentrySampler = SentrySampler;
exports.SentrySpanProcessor = SentrySpanProcessor;
exports.buildContextWithSentryScopes = buildContextWithSentryScopes;
exports.continueTrace = continueTrace;
exports.enhanceDscWithOpenTelemetryRootSpanName = enhanceDscWithOpenTelemetryRootSpanName;
exports.getActiveSpan = getActiveSpan;
exports.getRequestSpanData = getRequestSpanData;
exports.getScopesFromContext = getScopesFromContext;
exports.getSentryResource = getSentryResource;
exports.getSpanKind = getSpanKind;
exports.getTraceContextForScope = getTraceContextForScope;
exports.isSentryRequestSpan = isSentryRequestSpan;
exports.openTelemetrySetupCheck = openTelemetrySetupCheck;
exports.setIsSetup = setIsSetup;
exports.setOpenTelemetryContextAsyncContextStrategy = setOpenTelemetryContextAsyncContextStrategy;
exports.setupEventContextTrace = setupEventContextTrace;
exports.spanHasAttributes = spanHasAttributes;
exports.spanHasEvents = spanHasEvents;
exports.spanHasKind = spanHasKind;
exports.spanHasName = spanHasName;
exports.spanHasParentId = spanHasParentId;
exports.spanHasStatus = spanHasStatus;
exports.startInactiveSpan = startInactiveSpan;
exports.startSpan = startSpan;
exports.startSpanManual = startSpanManual;
exports.suppressTracing = suppressTracing;
exports.withActiveSpan = withActiveSpan;
exports.wrapClientClass = wrapClientClass;
exports.wrapContextManagerClass = wrapContextManagerClass;
exports.wrapSamplingDecision = wrapSamplingDecision;
//# sourceMappingURL=resource-PpJQa_6P.js.map

Sorry, the diff of this file is too big to display

import * as api from '@opentelemetry/api';
import { trace, SpanKind, createContextKey, TraceFlags, propagation, INVALID_TRACEID, context, SpanStatusCode, ROOT_CONTEXT, isSpanContextValid } from '@opentelemetry/api';
import { parseUrl, getSanitizedUrlString, SDK_VERSION, derefWeakRef, addNonEnumerableProperty, makeWeakRef, isSentryRequestUrl, getClient, baggageHeaderToDynamicSamplingContext, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME, stripUrlQueryAndFragment, spanToJSON, hasSpansEnabled, dynamicSamplingContextToSentryBaggageHeader, LRUMap, debug, shouldPropagateTraceForUrl, parseBaggageHeader, SENTRY_BAGGAGE_KEY_PREFIX, generateSentryTraceHeader, generateTraceparentHeader, getDynamicSamplingContextFromSpan, getCurrentScope, getDynamicSamplingContextFromScope, getIsolationScope, propagationContextFromHeaders, shouldContinueTrace, spanToTraceContext, getTraceContextFromScope, generateTraceId, generateSpanId, _INTERNAL_safeMathRandom, getRootSpan, handleCallbackErrors, getCapturedScopesOnSpan, setAsyncContextStrategy, getDefaultIsolationScope, getDefaultCurrentScope, SPAN_STATUS_OK, SPAN_STATUS_ERROR, getSpanStatusFromHttpCode, _INTERNAL_safeDateNow, debounce, timedEventsToMeasurements, captureEvent, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, convertSpanLinksForEnvelope, getStatusMessage, spanTimeInputToSeconds, hasSpanStreamingEnabled, addChildSpanToSpan, setCapturedScopesOnSpan, logSpanStart, logSpanEnd, safeSetSpanJSONAttributes, shouldIgnoreSpan, parseSampleRate, sampleSpan } from '@sentry/core';
import { URL_FULL, HTTP_URL, HTTP_REQUEST_METHOD, HTTP_METHOD, DB_SYSTEM_NAME, DB_SYSTEM, RPC_SERVICE, MESSAGING_SYSTEM, FAAS_TRIGGER, DB_STATEMENT, HTTP_TARGET, HTTP_ROUTE, HTTP_RESPONSE_STATUS_CODE, HTTP_STATUS_CODE, RPC_GRPC_STATUS_CODE, SERVICE_VERSION, SERVICE_NAME } from '@sentry/conventions/attributes';
import { W3CBaggagePropagator, isTracingSuppressed, suppressTracing as suppressTracing$1, SDK_INFO } from '@opentelemetry/core';
import { SamplingDecision } from '@opentelemetry/sdk-trace-base';
const SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE = "sentry.parentIsRemote";
const SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION = "sentry.graphql.operation";
function getParentSpanId(span) {
if ("parentSpanId" in span) {
return span.parentSpanId;
} else if ("parentSpanContext" in span) {
return span.parentSpanContext?.spanId;
}
return void 0;
}
function spanHasAttributes(span) {
const castSpan = span;
return !!castSpan.attributes && typeof castSpan.attributes === "object";
}
function spanHasKind(span) {
const castSpan = span;
return typeof castSpan.kind === "number";
}
function spanHasStatus(span) {
const castSpan = span;
return !!castSpan.status;
}
function spanHasName(span) {
const castSpan = span;
return !!castSpan.name;
}
function spanHasParentId(span) {
const castSpan = span;
return !!getParentSpanId(castSpan);
}
function spanHasEvents(span) {
const castSpan = span;
return Array.isArray(castSpan.events);
}
function getRequestSpanData(span) {
if (!spanHasAttributes(span)) {
return {};
}
const maybeUrlAttribute = span.attributes[URL_FULL] || span.attributes[HTTP_URL];
const data = {
url: maybeUrlAttribute,
// eslint-disable-next-line typescript/no-deprecated
"http.method": span.attributes[HTTP_REQUEST_METHOD] || span.attributes[HTTP_METHOD]
};
if (!data["http.method"] && data.url) {
data["http.method"] = "GET";
}
try {
if (typeof maybeUrlAttribute === "string") {
const url = parseUrl(maybeUrlAttribute);
data.url = getSanitizedUrlString(url);
if (url.search) {
data["http.query"] = url.search;
}
if (url.hash) {
data["http.fragment"] = url.hash;
}
}
} catch {
}
return data;
}
function wrapClientClass(ClientClass) {
class OpenTelemetryClient extends ClientClass {
constructor(...args) {
super(...args);
}
/** Get the OTEL tracer. */
get tracer() {
if (this._tracer) {
return this._tracer;
}
const name = "@sentry/opentelemetry";
const version = SDK_VERSION;
const tracer = trace.getTracer(name, version);
this._tracer = tracer;
return tracer;
}
/**
* @inheritDoc
*/
async flush(timeout) {
const provider = this.traceProvider;
await provider?.forceFlush();
return super.flush(timeout);
}
}
return OpenTelemetryClient;
}
function getSpanKind(span) {
if (spanHasKind(span)) {
return span.kind;
}
return SpanKind.INTERNAL;
}
const SENTRY_TRACE_HEADER = "sentry-trace";
const SENTRY_BAGGAGE_HEADER = "baggage";
const SENTRY_TRACE_STATE_DSC = "sentry.dsc";
const SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING = "sentry.sampled_not_recording";
const SENTRY_TRACE_STATE_URL = "sentry.url";
const SENTRY_TRACE_STATE_SAMPLE_RAND = "sentry.sample_rand";
const SENTRY_TRACE_STATE_SAMPLE_RATE = "sentry.sample_rate";
const SENTRY_TRACE_STATE_CHILD_IGNORED = "sentry.ignored";
const SENTRY_TRACE_STATE_SEGMENT_IGNORED = "sentry.segment_ignored";
const SENTRY_SCOPES_CONTEXT_KEY = createContextKey("sentry_scopes");
const SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY = createContextKey("sentry_fork_isolation_scope");
const SENTRY_FORK_SET_SCOPE_CONTEXT_KEY = createContextKey("sentry_fork_set_scope");
const SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY = createContextKey("sentry_fork_set_isolation_scope");
const SCOPE_CONTEXT_FIELD = "_scopeContext";
function getScopesFromContext(context) {
return context.getValue(SENTRY_SCOPES_CONTEXT_KEY);
}
function setScopesOnContext(context, scopes) {
return context.setValue(SENTRY_SCOPES_CONTEXT_KEY, scopes);
}
function setContextOnScope(scope, context) {
addNonEnumerableProperty(scope, SCOPE_CONTEXT_FIELD, makeWeakRef(context));
}
function getContextFromScope(scope) {
return derefWeakRef(scope[SCOPE_CONTEXT_FIELD]);
}
function isSentryRequestSpan(span) {
if (!spanHasAttributes(span)) {
return false;
}
const { attributes } = span;
const httpUrl = attributes[HTTP_URL] || attributes[URL_FULL];
if (!httpUrl) {
return false;
}
return isSentryRequestUrl(httpUrl.toString(), getClient());
}
function getSamplingDecision(spanContext) {
const { traceFlags, traceState } = spanContext;
const sampledNotRecording = traceState ? traceState.get(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING) === "1" : false;
if (traceFlags === TraceFlags.SAMPLED) {
return true;
}
if (sampledNotRecording) {
return false;
}
const dscString = traceState ? traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : void 0;
if (dsc?.sampled === "true") {
return true;
}
if (dsc?.sampled === "false") {
return false;
}
return void 0;
}
function inferSpanData(spanName, attributes, kind) {
const httpMethod = attributes[HTTP_REQUEST_METHOD] || attributes[HTTP_METHOD];
if (httpMethod) {
return descriptionForHttpMethod({ attributes, name: spanName, kind }, httpMethod);
}
const dbSystem = attributes[DB_SYSTEM_NAME] || attributes[DB_SYSTEM];
const opIsCache = typeof attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] === "string" && attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP].startsWith("cache.");
if (dbSystem && !opIsCache) {
return descriptionForDbSystem({ attributes, name: spanName });
}
const customSourceOrRoute = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom" ? "custom" : "route";
const rpcService = attributes[RPC_SERVICE];
if (rpcService) {
return {
...getUserUpdatedNameAndSource(spanName, attributes, "route"),
op: "rpc"
};
}
const messagingSystem = attributes[MESSAGING_SYSTEM];
if (messagingSystem) {
return {
...getUserUpdatedNameAndSource(spanName, attributes, customSourceOrRoute),
op: "message"
};
}
const faasTrigger = attributes[FAAS_TRIGGER];
if (faasTrigger) {
return {
...getUserUpdatedNameAndSource(spanName, attributes, customSourceOrRoute),
op: faasTrigger.toString()
};
}
return { op: void 0, description: spanName, source: "custom" };
}
function parseSpanDescription(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const name = spanHasName(span) ? span.name : "<unknown>";
const kind = getSpanKind(span);
return inferSpanData(name, attributes, kind);
}
function descriptionForDbSystem({ attributes, name }) {
const userDefinedName = attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (typeof userDefinedName === "string") {
return {
op: "db",
description: userDefinedName,
source: attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || "custom"
};
}
if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom") {
return { op: "db", description: name, source: "custom" };
}
const statement = attributes[DB_STATEMENT];
const description = statement ? statement.toString() : name;
return { op: "db", description, source: "task" };
}
function descriptionForHttpMethod({ name, kind, attributes }, httpMethod) {
const opParts = ["http"];
switch (kind) {
case SpanKind.CLIENT:
opParts.push("client");
break;
case SpanKind.SERVER:
opParts.push("server");
break;
}
if (attributes["sentry.http.prefetch"]) {
opParts.push("prefetch");
}
const { urlPath, url, query, fragment, hasRoute } = getSanitizedUrl(attributes, kind);
if (!urlPath) {
return { ...getUserUpdatedNameAndSource(name, attributes), op: opParts.join(".") };
}
const graphqlOperationsAttribute = attributes[SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION];
const baseDescription = `${httpMethod} ${urlPath}`;
const inferredDescription = graphqlOperationsAttribute ? `${baseDescription} (${getGraphqlOperationNamesFromAttribute(graphqlOperationsAttribute)})` : baseDescription;
const inferredSource = hasRoute || urlPath === "/" ? "route" : "url";
const data = {};
if (url) {
data.url = url;
}
if (query) {
data["http.query"] = query;
}
if (fragment) {
data["http.fragment"] = fragment;
}
const isClientOrServerKind = kind === SpanKind.CLIENT || kind === SpanKind.SERVER;
const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] || "manual";
const isManualSpan = !`${origin}`.startsWith("auto");
const alreadyHasCustomSource = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === "custom";
const customSpanName = attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
const useInferredDescription = !alreadyHasCustomSource && customSpanName == null && (isClientOrServerKind || !isManualSpan);
const { description, source } = useInferredDescription ? { description: inferredDescription, source: inferredSource } : getUserUpdatedNameAndSource(name, attributes);
return {
op: opParts.join("."),
description,
source,
data
};
}
function getGraphqlOperationNamesFromAttribute(attr) {
if (Array.isArray(attr)) {
const sorted = attr.slice().sort();
if (sorted.length <= 5) {
return sorted.join(", ");
} else {
return `${sorted.slice(0, 5).join(", ")}, +${sorted.length - 5}`;
}
}
return `${attr}`;
}
function getSanitizedUrl(attributes, kind) {
const httpTarget = attributes[HTTP_TARGET];
const httpUrl = attributes[HTTP_URL] || attributes[URL_FULL];
const httpRoute = attributes[HTTP_ROUTE];
const parsedUrl = typeof httpUrl === "string" ? parseUrl(httpUrl) : void 0;
const url = parsedUrl ? getSanitizedUrlString(parsedUrl) : void 0;
const query = parsedUrl?.search || void 0;
const fragment = parsedUrl?.hash || void 0;
if (typeof httpRoute === "string") {
return { urlPath: httpRoute, url, query, fragment, hasRoute: true };
}
if (kind === SpanKind.SERVER && typeof httpTarget === "string") {
return { urlPath: stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
if (parsedUrl) {
return { urlPath: url, url, query, fragment, hasRoute: false };
}
if (typeof httpTarget === "string") {
return { urlPath: stripUrlQueryAndFragment(httpTarget), url, query, fragment, hasRoute: false };
}
return { urlPath: void 0, url, query, fragment, hasRoute: false };
}
function getUserUpdatedNameAndSource(originalName, attributes, fallbackSource = "custom") {
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] || fallbackSource;
const description = attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
if (description && typeof description === "string") {
return {
description,
source
};
}
return { description: originalName, source };
}
function enhanceDscWithOpenTelemetryRootSpanName(client) {
client.on("createDsc", (dsc, rootSpan) => {
if (!rootSpan) {
return;
}
const jsonSpan = spanToJSON(rootSpan);
const attributes = jsonSpan.data;
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
const { description } = spanHasName(rootSpan) ? parseSpanDescription(rootSpan) : { description: void 0 };
if (source !== "url" && description) {
dsc.transaction = description;
}
if (hasSpansEnabled()) {
const sampled = getSamplingDecision(rootSpan.spanContext());
dsc.sampled = sampled == void 0 ? void 0 : String(sampled);
}
});
}
function getActiveSpan() {
return trace.getActiveSpan();
}
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
class TraceState {
constructor() {
this._internalState = /* @__PURE__ */ new Map();
}
/** @inheritDoc */
set(key, value) {
const next = this._clone();
if (next._internalState.has(key)) {
next._internalState.delete(key);
}
next._internalState.set(key, value);
return next;
}
/** @inheritDoc */
unset(key) {
const next = this._clone();
next._internalState.delete(key);
return next;
}
/** @inheritDoc */
get(key) {
return this._internalState.get(key);
}
/** @inheritDoc */
serialize() {
return Array.from(this._internalState.keys()).reverse().map((key) => `${key}=${this._internalState.get(key)}`).join(",");
}
_clone() {
const next = new TraceState();
next._internalState = new Map(this._internalState);
return next;
}
}
function makeTraceState({
dsc,
sampled
}) {
const dscString = dsc ? dynamicSamplingContextToSentryBaggageHeader(dsc) : void 0;
const traceStateBase = new TraceState();
const traceStateWithDsc = dscString ? traceStateBase.set(SENTRY_TRACE_STATE_DSC, dscString) : traceStateBase;
return sampled === false ? traceStateWithDsc.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") : traceStateWithDsc;
}
const setupElements = /* @__PURE__ */ new Set();
function openTelemetrySetupCheck() {
return Array.from(setupElements);
}
function setIsSetup(element) {
setupElements.add(element);
}
class SentryPropagator extends W3CBaggagePropagator {
constructor() {
super();
setIsSetup("SentryPropagator");
this._urlMatchesTargetsMap = new LRUMap(100);
}
/**
* @inheritDoc
*/
inject(context2, carrier, setter) {
if (isTracingSuppressed(context2)) {
DEBUG_BUILD && debug.log("[Tracing] Not injecting trace data for url because tracing is suppressed.");
return;
}
const activeSpan = trace.getSpan(context2);
const url = activeSpan && getCurrentURL(activeSpan);
const { tracePropagationTargets, propagateTraceparent } = getClient()?.getOptions() || {};
if (!shouldPropagateTraceForUrl(url, tracePropagationTargets, this._urlMatchesTargetsMap)) {
DEBUG_BUILD && debug.log("[Tracing] Not injecting trace data for url because it does not match tracePropagationTargets:", url);
return;
}
const existingBaggageHeader = getExistingBaggage(carrier);
const existingSentryTraceHeader = getExistingSentryTrace(carrier);
let baggage = propagation.getBaggage(context2) || propagation.createBaggage({});
const { dynamicSamplingContext, traceId, spanId, sampled } = getInjectionData(context2);
if (existingBaggageHeader) {
const baggageEntries = parseBaggageHeader(existingBaggageHeader);
if (baggageEntries) {
Object.entries(baggageEntries).forEach(([key, value]) => {
if (!existingSentryTraceHeader && key.startsWith(SENTRY_BAGGAGE_KEY_PREFIX)) {
return;
}
baggage = baggage.setEntry(key, { value });
});
}
}
if (!existingSentryTraceHeader && dynamicSamplingContext) {
baggage = Object.entries(dynamicSamplingContext).reduce((b, [dscKey, dscValue]) => {
if (dscValue) {
return b.setEntry(`${SENTRY_BAGGAGE_KEY_PREFIX}${dscKey}`, { value: dscValue });
}
return b;
}, baggage);
}
if (!existingSentryTraceHeader && traceId && traceId !== INVALID_TRACEID) {
setter.set(carrier, SENTRY_TRACE_HEADER, generateSentryTraceHeader(traceId, spanId, sampled));
if (propagateTraceparent) {
setter.set(carrier, "traceparent", generateTraceparentHeader(traceId, spanId, sampled));
}
}
super.inject(propagation.setBaggage(context2, baggage), carrier, setter);
}
/**
* @inheritDoc
*/
extract(context2, carrier, getter) {
const maybeSentryTraceHeader = getter.get(carrier, SENTRY_TRACE_HEADER);
const baggage = getter.get(carrier, SENTRY_BAGGAGE_HEADER);
const sentryTrace = maybeSentryTraceHeader ? Array.isArray(maybeSentryTraceHeader) ? maybeSentryTraceHeader[0] : maybeSentryTraceHeader : void 0;
return ensureScopesOnContext(getContextWithRemoteActiveSpan(context2, { sentryTrace, baggage }));
}
/**
* @inheritDoc
*/
fields() {
return [SENTRY_TRACE_HEADER, SENTRY_BAGGAGE_HEADER, "traceparent"];
}
}
function getInjectionData(context2, options = {}) {
const span = trace.getSpan(context2);
if (span?.spanContext().isRemote) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: void 0,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
if (span) {
const spanContext = span.spanContext();
const dynamicSamplingContext2 = getDynamicSamplingContextFromSpan(span);
return {
dynamicSamplingContext: dynamicSamplingContext2,
traceId: spanContext.traceId,
spanId: spanContext.spanId,
sampled: getSamplingDecision(spanContext)
// TODO: Do we need to change something here?
};
}
const scope = options.scope || getScopesFromContext(context2)?.scope || getCurrentScope();
const client = options.client || getClient();
const propagationContext = scope.getPropagationContext();
const dynamicSamplingContext = client ? getDynamicSamplingContextFromScope(client, scope) : void 0;
return {
dynamicSamplingContext,
traceId: propagationContext.traceId,
spanId: propagationContext.propagationSpanId,
sampled: propagationContext.sampled
};
}
function getContextWithRemoteActiveSpan(ctx, { sentryTrace, baggage }) {
const propagationContext = propagationContextFromHeaders(sentryTrace, baggage);
const { traceId, parentSpanId, sampled, dsc } = propagationContext;
const client = getClient();
const incomingDsc = baggageHeaderToDynamicSamplingContext(baggage);
if (!parentSpanId || client && !shouldContinueTrace(client, incomingDsc?.org_id)) {
return ctx;
}
const spanContext = generateRemoteSpanContext({
traceId,
spanId: parentSpanId,
sampled,
dsc
});
return trace.setSpanContext(ctx, spanContext);
}
function continueTraceAsRemoteSpan(ctx, options, callback) {
const ctxWithSpanContext = ensureScopesOnContext(getContextWithRemoteActiveSpan(ctx, options));
return context.with(ctxWithSpanContext, callback);
}
function ensureScopesOnContext(ctx) {
const scopes = getScopesFromContext(ctx);
const newScopes = {
// If we have no scope here, this is most likely either the root context or a context manually derived from it
// In this case, we want to fork the current scope, to ensure we do not pollute the root scope
scope: scopes ? scopes.scope : getCurrentScope().clone(),
isolationScope: scopes ? scopes.isolationScope : getIsolationScope()
};
return setScopesOnContext(ctx, newScopes);
}
function getExistingBaggage(carrier) {
try {
const baggage = carrier[SENTRY_BAGGAGE_HEADER];
return Array.isArray(baggage) ? baggage.join(",") : baggage;
} catch {
return void 0;
}
}
function getExistingSentryTrace(carrier) {
try {
return carrier[SENTRY_TRACE_HEADER];
} catch {
return void 0;
}
}
function getCurrentURL(span) {
const spanData = spanToJSON(span).data;
const urlAttribute = spanData[HTTP_URL] || spanData[URL_FULL];
if (typeof urlAttribute === "string") {
return urlAttribute;
}
const urlTraceState = span.spanContext().traceState?.get(SENTRY_TRACE_STATE_URL);
if (urlTraceState) {
return urlTraceState;
}
return void 0;
}
function generateRemoteSpanContext({
spanId,
traceId,
sampled,
dsc
}) {
const traceState = makeTraceState({
dsc,
sampled
});
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,
traceState
};
return spanContext;
}
function _startSpan(options, callback, autoEnd) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx);
const ctx = missingRequiredParent ? suppressTracing$1(activeCtx) : activeCtx;
if (missingRequiredParent) {
getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!hasSpansEnabled()) {
const suppressedCtx = isTracingSuppressed(ctx) ? ctx : suppressTracing$1(ctx);
return context.with(suppressedCtx, () => {
return tracer.startActiveSpan(name, spanOptions, suppressedCtx, (span) => {
patchSpanEnd(span);
return context.with(activeCtx, () => {
return handleCallbackErrors(
() => callback(span),
() => {
if (spanToJSON(span).status === void 0) {
span.setStatus({ code: SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
});
}
return tracer.startActiveSpan(name, spanOptions, ctx, (span) => {
patchSpanEnd(span);
return handleCallbackErrors(
() => callback(span),
() => {
if (spanToJSON(span).status === void 0) {
span.setStatus({ code: SpanStatusCode.ERROR });
}
},
autoEnd ? () => span.end() : void 0
);
});
});
}
function startSpan(options, callback) {
return _startSpan(options, callback, true);
}
function startSpanManual(options, callback) {
return _startSpan(options, (span) => callback(span, () => span.end()), false);
}
function startInactiveSpan(options) {
const tracer = getTracer();
const { name, parentSpan: customParentSpan } = options;
const wrapper = getActiveSpanWrapper(customParentSpan);
return wrapper(() => {
const activeCtx = getContext(options.scope, options.forceTransaction);
const missingRequiredParent = options.onlyIfParent && !trace.getSpan(activeCtx);
let ctx = missingRequiredParent ? suppressTracing$1(activeCtx) : activeCtx;
if (missingRequiredParent) {
getClient()?.recordDroppedEvent("no_parent_span", "span");
}
const spanOptions = getSpanOptions(options);
if (!hasSpansEnabled()) {
ctx = isTracingSuppressed(ctx) ? ctx : suppressTracing$1(ctx);
}
const span = tracer.startSpan(name, spanOptions, ctx);
patchSpanEnd(span);
return span;
});
}
function withActiveSpan(span, callback) {
const newContextWithActiveSpan = span ? trace.setSpan(context.active(), span) : trace.deleteSpan(context.active());
return context.with(newContextWithActiveSpan, () => callback(getCurrentScope()));
}
function getTracer() {
const client = getClient();
return client?.tracer || trace.getTracer("@sentry/opentelemetry", SDK_VERSION);
}
function getSpanOptions(options) {
const { startTime, attributes, kind, op, links } = options;
const fixedStartTime = typeof startTime === "number" ? ensureTimestampInMilliseconds(startTime) : startTime;
return {
attributes: op ? {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...attributes
} : attributes,
kind,
links,
startTime: fixedStartTime
};
}
function ensureTimestampInMilliseconds(timestamp) {
const isMs = timestamp < 9999999999;
return isMs ? timestamp * 1e3 : timestamp;
}
function patchSpanEnd(span) {
const originalEnd = span.end.bind(span);
span.end = (endTime) => {
return originalEnd(typeof endTime === "number" ? ensureTimestampInMilliseconds(endTime) : endTime);
};
}
function getContext(scope, forceTransaction) {
const ctx = getContextForScope(scope);
const parentSpan = trace.getSpan(ctx);
if (!parentSpan) {
return ctx;
}
if (!forceTransaction) {
return ctx;
}
const ctxWithoutSpan = trace.deleteSpan(ctx);
const { spanId, traceId } = parentSpan.spanContext();
const sampled = getSamplingDecision(parentSpan.spanContext());
const rootSpan = getRootSpan(parentSpan);
const dsc = getDynamicSamplingContextFromSpan(rootSpan);
const traceState = makeTraceState({
dsc,
sampled
});
const spanOptions = {
traceId,
spanId,
isRemote: true,
traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,
traceState
};
const ctxWithSpanContext = trace.setSpanContext(ctxWithoutSpan, spanOptions);
return ctxWithSpanContext;
}
function getContextForScope(scope) {
if (scope) {
const ctx = getContextFromScope(scope);
if (ctx) {
return ctx;
}
}
return context.active();
}
function continueTrace(options, callback) {
return continueTraceAsRemoteSpan(context.active(), options, callback);
}
function startNewTrace(callback) {
const traceId = generateTraceId();
const spanId = generateSpanId();
const spanContext = {
traceId,
spanId,
isRemote: true,
traceFlags: TraceFlags.NONE
};
const ctxWithTrace = trace.setSpanContext(context.active(), spanContext);
return context.with(ctxWithTrace, () => {
getCurrentScope().setPropagationContext({
traceId,
sampleRand: _INTERNAL_safeMathRandom()
});
return callback();
});
}
function getTraceContextForScope(client, scope) {
const ctx = getContextFromScope(scope);
const span = ctx && trace.getSpan(ctx);
const traceContext = span ? spanToTraceContext(span) : getTraceContextFromScope(scope);
const dynamicSamplingContext = span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromScope(client, scope);
return [dynamicSamplingContext, traceContext];
}
function getActiveSpanWrapper(parentSpan) {
return parentSpan !== void 0 ? (callback) => {
return withActiveSpan(parentSpan, callback);
} : (callback) => callback();
}
function suppressTracing(callback) {
const ctx = suppressTracing$1(context.active());
return context.with(ctx, callback);
}
function setupEventContextTrace(client) {
client.on("preprocessEvent", (event) => {
const span = getActiveSpan();
if (!span || event.type === "transaction") {
return;
}
event.contexts = {
trace: spanToTraceContext(span),
...event.contexts
};
const rootSpan = getRootSpan(span);
event.sdkProcessingMetadata = {
dynamicSamplingContext: getDynamicSamplingContextFromSpan(rootSpan),
...event.sdkProcessingMetadata
};
return event;
});
}
function getTraceData({
span,
scope,
client,
propagateTraceparent
} = {}) {
let ctx = (scope && getContextFromScope(scope)) ?? api.context.active();
if (span) {
const { scope: scope2 } = getCapturedScopesOnSpan(span);
ctx = scope2 && getContextFromScope(scope2) || api.trace.setSpan(api.context.active(), span);
}
const { traceId, spanId, sampled, dynamicSamplingContext } = getInjectionData(ctx, { scope, client });
const traceData = {
"sentry-trace": generateSentryTraceHeader(traceId, spanId, sampled),
baggage: dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext)
};
if (propagateTraceparent) {
traceData.traceparent = generateTraceparentHeader(traceId, spanId, sampled);
}
return traceData;
}
function setOpenTelemetryContextAsyncContextStrategy() {
function getScopes() {
const ctx = api.context.active();
const scopes = getScopesFromContext(ctx);
if (scopes) {
return scopes;
}
return {
scope: getDefaultCurrentScope(),
isolationScope: getDefaultIsolationScope()
};
}
function withScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx, () => {
return callback(getCurrentScope());
});
}
function withSetScope(scope, callback) {
const ctx = getContextFromScope(scope) || api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY, scope), () => {
return callback(scope);
});
}
function withIsolationScope(callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY, true), () => {
return callback(getIsolationScope());
});
}
function withSetIsolationScope(isolationScope, callback) {
const ctx = api.context.active();
return api.context.with(ctx.setValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY, isolationScope), () => {
return callback(getIsolationScope());
});
}
function getCurrentScope() {
return getScopes().scope;
}
function getIsolationScope() {
return getScopes().isolationScope;
}
setAsyncContextStrategy({
withScope,
withSetScope,
withSetIsolationScope,
withIsolationScope,
getCurrentScope,
getIsolationScope,
startSpan,
startSpanManual,
startInactiveSpan,
getActiveSpan,
suppressTracing,
getTraceData,
continueTrace,
startNewTrace,
// The types here don't fully align, because our own `Span` type is narrower
// than the OTEL one - but this is OK for here, as we now we'll only have OTEL spans passed around
withActiveSpan,
getTracingChannelBinding: () => {
try {
const contextManager = api.context._getContextManager();
const lookup = contextManager.getAsyncLocalStorageLookup();
return {
asyncLocalStorage: lookup.asyncLocalStorage,
getStoreWithActiveSpan: (span) => api.trace.setSpan(api.context.active(), span)
};
} catch {
return void 0;
}
}
});
}
function buildContextWithSentryScopes(context, activeContext) {
const span = trace.getSpan(context);
let effectiveContext;
if (span?.spanContext().traceState?.get(SENTRY_TRACE_STATE_CHILD_IGNORED) === "1") {
const contextWithoutSpan = trace.deleteSpan(context);
const parentSpan = trace.getSpan(activeContext);
effectiveContext = parentSpan ? trace.setSpan(contextWithoutSpan, parentSpan) : contextWithoutSpan;
} else {
effectiveContext = context;
}
const currentScopes = getScopesFromContext(effectiveContext);
const currentScope = currentScopes?.scope || getCurrentScope();
const currentIsolationScope = currentScopes?.isolationScope || getIsolationScope();
const shouldForkIsolationScope = effectiveContext.getValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY) === true;
const scope = effectiveContext.getValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY);
const isolationScope = effectiveContext.getValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
const newCurrentScope = scope || currentScope.clone();
const newIsolationScope = isolationScope || (shouldForkIsolationScope ? currentIsolationScope.clone() : currentIsolationScope);
const scopes = { scope: newCurrentScope, isolationScope: newIsolationScope };
const ctx1 = setScopesOnContext(effectiveContext, scopes);
const ctx2 = ctx1.deleteValue(SENTRY_FORK_ISOLATION_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_SCOPE_CONTEXT_KEY).deleteValue(SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY);
setContextOnScope(newCurrentScope, ctx2);
return ctx2;
}
function wrapContextManagerClass(ContextManagerClass) {
class SentryContextManager extends ContextManagerClass {
constructor(...args) {
super(...args);
setIsSetup("SentryContextManager");
}
/**
* Overwrite with() of the original AsyncLocalStorageContextManager
* to ensure we also create new scopes per context.
*/
with(context, fn, thisArg, ...args) {
const ctx2 = buildContextWithSentryScopes(context, this.active());
return super.with(ctx2, fn, thisArg, ...args);
}
/**
* Gets underlying AsyncLocalStorage and symbol to allow lookup of scope.
*/
getAsyncLocalStorageLookup() {
return {
// @ts-expect-error This is on the base class, but not part of the interface
asyncLocalStorage: this._asyncLocalStorage,
contextSymbol: SENTRY_SCOPES_CONTEXT_KEY
};
}
}
return SentryContextManager;
}
function groupSpansWithParents(spans) {
const nodeMap = /* @__PURE__ */ new Map();
for (const span of spans) {
createOrUpdateSpanNodeAndRefs(nodeMap, span);
}
return Array.from(nodeMap, function([_id, spanNode]) {
return spanNode;
});
}
function getLocalParentId(span) {
const parentIsRemote = span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE] === true;
return !parentIsRemote ? getParentSpanId(span) : void 0;
}
function createOrUpdateSpanNodeAndRefs(nodeMap, span) {
const id = span.spanContext().spanId;
const parentId = getLocalParentId(span);
if (!parentId) {
createOrUpdateNode(nodeMap, { id, span, children: [] });
return;
}
const parentNode = createOrGetParentNode(nodeMap, parentId);
const node = createOrUpdateNode(nodeMap, { id, span, parentNode, children: [] });
parentNode.children.push(node);
}
function createOrGetParentNode(nodeMap, id) {
const existing = nodeMap.get(id);
if (existing) {
return existing;
}
return createOrUpdateNode(nodeMap, { id, children: [] });
}
function createOrUpdateNode(nodeMap, spanNode) {
const existing = nodeMap.get(spanNode.id);
if (existing?.span) {
return existing;
}
if (existing && !existing.span) {
existing.span = spanNode.span;
existing.parentNode = spanNode.parentNode;
return existing;
}
nodeMap.set(spanNode.id, spanNode);
return spanNode;
}
const canonicalGrpcErrorCodesMap = {
"1": "cancelled",
"2": "unknown_error",
"3": "invalid_argument",
"4": "deadline_exceeded",
"5": "not_found",
"6": "already_exists",
"7": "permission_denied",
"8": "resource_exhausted",
"9": "failed_precondition",
"10": "aborted",
"11": "out_of_range",
"12": "unimplemented",
"13": "internal_error",
"14": "unavailable",
"15": "data_loss",
"16": "unauthenticated"
};
const isStatusErrorMessageValid = (message) => {
return Object.values(canonicalGrpcErrorCodesMap).includes(message);
};
function mapStatus(span) {
const attributes = spanHasAttributes(span) ? span.attributes : {};
const status = spanHasStatus(span) ? span.status : void 0;
if (status) {
if (status.code === SpanStatusCode.OK) {
return { code: SPAN_STATUS_OK };
} else if (status.code === SpanStatusCode.ERROR) {
if (typeof status.message === "undefined") {
const inferredStatus2 = inferStatusFromAttributes(attributes);
if (inferredStatus2) {
return inferredStatus2;
}
}
if (status.message && isStatusErrorMessageValid(status.message)) {
return { code: SPAN_STATUS_ERROR, message: status.message };
} else {
return { code: SPAN_STATUS_ERROR, message: "internal_error" };
}
}
}
const inferredStatus = inferStatusFromAttributes(attributes);
if (inferredStatus) {
return inferredStatus;
}
if (status?.code === SpanStatusCode.UNSET) {
return { code: SPAN_STATUS_OK };
} else {
return { code: SPAN_STATUS_ERROR, message: "unknown_error" };
}
}
function inferStatusFromAttributes(attributes) {
const httpCodeAttribute = attributes[HTTP_RESPONSE_STATUS_CODE] || attributes[HTTP_STATUS_CODE];
const grpcCodeAttribute = attributes[RPC_GRPC_STATUS_CODE];
const numberHttpCode = typeof httpCodeAttribute === "number" ? httpCodeAttribute : typeof httpCodeAttribute === "string" ? parseInt(httpCodeAttribute) : void 0;
if (typeof numberHttpCode === "number") {
return getSpanStatusFromHttpCode(numberHttpCode);
}
if (typeof grpcCodeAttribute === "string") {
return { code: SPAN_STATUS_ERROR, message: canonicalGrpcErrorCodesMap[grpcCodeAttribute] || "unknown_error" };
}
return void 0;
}
const MAX_SPAN_COUNT = 1e3;
const DEFAULT_TIMEOUT = 300;
const SENT_SPANS_MAX_SIZE = 1e4;
class SentrySpanExporter {
constructor(options) {
this._finishedSpanBucketSize = options?.timeout || DEFAULT_TIMEOUT;
this._finishedSpanBuckets = new Array(this._finishedSpanBucketSize).fill(void 0);
this._lastCleanupTimestampInS = Math.floor(_INTERNAL_safeDateNow() / 1e3);
this._spansToBucketEntry = /* @__PURE__ */ new WeakMap();
this._sentSpans = new LRUMap(SENT_SPANS_MAX_SIZE);
this._debouncedFlush = debounce(this.flush.bind(this), 1, { maxWait: 100 });
}
/**
* Export a single span.
* This is called by the span processor whenever a span is ended.
*/
export(span) {
const currentTimestampInS = Math.floor(_INTERNAL_safeDateNow() / 1e3);
if (this._lastCleanupTimestampInS !== currentTimestampInS) {
let droppedSpanCount = 0;
this._finishedSpanBuckets.forEach((bucket, i) => {
if (bucket && bucket.timestampInS <= currentTimestampInS - this._finishedSpanBucketSize) {
droppedSpanCount += bucket.spans.size;
this._finishedSpanBuckets[i] = void 0;
}
});
if (droppedSpanCount > 0) {
DEBUG_BUILD && debug.log(
`SpanExporter dropped ${droppedSpanCount} spans because they were pending for more than ${this._finishedSpanBucketSize} seconds.`
);
}
this._lastCleanupTimestampInS = currentTimestampInS;
}
const currentBucketIndex = currentTimestampInS % this._finishedSpanBucketSize;
const currentBucket = this._finishedSpanBuckets[currentBucketIndex] || {
timestampInS: currentTimestampInS,
spans: /* @__PURE__ */ new Set()
};
this._finishedSpanBuckets[currentBucketIndex] = currentBucket;
currentBucket.spans.add(span);
this._spansToBucketEntry.set(span, currentBucket);
const localParentId = getLocalParentId(span);
if (!localParentId || this._sentSpans.get(localParentId)) {
this._debouncedFlush();
}
}
/**
* Try to flush any pending spans immediately.
* This is called internally by the exporter (via _debouncedFlush),
* but can also be triggered externally if we force-flush.
*/
flush() {
const finishedSpans = this._finishedSpanBuckets.flatMap((bucket) => bucket ? Array.from(bucket.spans) : []);
const sentSpans = this._maybeSend(finishedSpans);
const sentSpanCount = sentSpans.size;
const remainingOpenSpanCount = finishedSpans.length - sentSpanCount;
DEBUG_BUILD && debug.log(
`SpanExporter exported ${sentSpanCount} spans, ${remainingOpenSpanCount} spans are waiting for their parent spans to finish`
);
for (const span of sentSpans) {
this._sentSpans.set(span.spanContext().spanId, 1);
const bucketEntry = this._spansToBucketEntry.get(span);
if (bucketEntry) {
bucketEntry.spans.delete(span);
}
}
this._debouncedFlush.cancel();
}
/**
* Clear the exporter.
* This is called when the span processor is shut down.
*/
clear() {
this._finishedSpanBuckets = this._finishedSpanBuckets.fill(void 0);
this._sentSpans.clear();
this._debouncedFlush.cancel();
}
/**
* Send the given spans, but only if they are part of a finished transaction.
*
* Returns the sent spans.
* Spans remain unsent when their parent span is not yet finished.
* This will happen regularly, as child spans are generally finished before their parents.
* But it _could_ also happen because, for whatever reason, a parent span was lost.
* In this case, we'll eventually need to clean this up.
*/
_maybeSend(spans) {
const grouped = groupSpansWithParents(spans);
const sentSpans = /* @__PURE__ */ new Set();
const rootNodes = this._getCompletedRootNodes(grouped);
for (const root of rootNodes) {
const span = root.span;
sentSpans.add(span);
const transactionEvent = createTransactionForOtelSpan(span);
if (root.parentNode && this._sentSpans.get(root.parentNode.id)) {
const traceData = transactionEvent.contexts?.trace?.data;
if (traceData) {
traceData["sentry.parent_span_already_sent"] = true;
}
}
const spans2 = transactionEvent.spans || [];
let hasGenAiSpans = false;
for (const child of root.children) {
if (createAndFinishSpanForOtelSpan(child, spans2, sentSpans)) {
hasGenAiSpans = true;
}
}
transactionEvent.spans = spans2.length > MAX_SPAN_COUNT ? spans2.sort((a, b) => a.start_timestamp - b.start_timestamp).slice(0, MAX_SPAN_COUNT) : spans2;
if (hasGenAiSpans) {
transactionEvent.sdkProcessingMetadata = {
...transactionEvent.sdkProcessingMetadata,
hasGenAiSpans: true
};
}
const measurements = timedEventsToMeasurements(span.events);
if (measurements) {
transactionEvent.measurements = measurements;
}
captureEvent(transactionEvent);
}
return sentSpans;
}
/** Check if a node is a completed root node or a node whose parent has already been sent */
_nodeIsCompletedRootNodeOrHasSentParent(node) {
return !!node.span && (!node.parentNode || !!this._sentSpans.get(node.parentNode.id));
}
/** Get all completed root nodes from a list of nodes */
_getCompletedRootNodes(nodes) {
return nodes.filter((node) => this._nodeIsCompletedRootNodeOrHasSentParent(node));
}
}
function parseSpan(span) {
const attributes = span.attributes;
const origin = attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN];
const op = attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP];
const source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
return { origin, op, source };
}
function createTransactionForOtelSpan(span) {
const { op, description, data, origin = "manual", source } = getSpanData(span);
const capturedSpanScopes = getCapturedScopesOnSpan(span);
const sampleRate = span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
const attributes = {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate,
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
...data,
...removeSentryAttributes(span.attributes)
};
const { links } = span;
const { traceId: trace_id, spanId: span_id } = span.spanContext();
const parent_span_id = getParentSpanId(span);
const status = mapStatus(span);
const traceContext = {
parent_span_id,
span_id,
trace_id,
data: attributes,
origin,
op,
status: getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
links: convertSpanLinksForEnvelope(links)
};
const statusCode = attributes[HTTP_RESPONSE_STATUS_CODE];
const responseContext = typeof statusCode === "number" ? { response: { status_code: statusCode } } : void 0;
const transactionEvent = {
contexts: {
trace: traceContext,
otel: {
resource: span.resource.attributes
},
...responseContext
},
spans: [],
start_timestamp: spanTimeInputToSeconds(span.startTime),
timestamp: spanTimeInputToSeconds(span.endTime),
transaction: description,
type: "transaction",
sdkProcessingMetadata: {
capturedSpanScope: capturedSpanScopes.scope,
capturedSpanIsolationScope: capturedSpanScopes.isolationScope,
sampleRate,
dynamicSamplingContext: getDynamicSamplingContextFromSpan(span)
},
...source && {
transaction_info: {
source
}
}
};
return transactionEvent;
}
function createAndFinishSpanForOtelSpan(node, spans, sentSpans) {
const span = node.span;
if (span) {
sentSpans.add(span);
}
const shouldDrop = !span;
if (shouldDrop) {
let hasGenAiSpans2 = false;
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans2 = true;
}
});
return hasGenAiSpans2;
}
const span_id = span.spanContext().spanId;
const trace_id = span.spanContext().traceId;
const parentSpanId = getParentSpanId(span);
const { attributes, startTime, endTime, links } = span;
const { op, description, data, origin = "manual" } = getSpanData(span);
const allData = {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
...removeSentryAttributes(attributes),
...data
};
const status = mapStatus(span);
const spanJSON = {
span_id,
trace_id,
data: allData,
description,
parent_span_id: parentSpanId,
start_timestamp: spanTimeInputToSeconds(startTime),
// This is [0,0] by default in OTEL, in which case we want to interpret this as no end time
timestamp: spanTimeInputToSeconds(endTime) || void 0,
status: getStatusMessage(status),
// As per protocol, span status is allowed to be undefined
op,
origin,
measurements: timedEventsToMeasurements(span.events),
links: convertSpanLinksForEnvelope(links)
};
spans.push(spanJSON);
let hasGenAiSpans = !!op?.startsWith("gen_ai.");
node.children.forEach((child) => {
if (createAndFinishSpanForOtelSpan(child, spans, sentSpans)) {
hasGenAiSpans = true;
}
});
return hasGenAiSpans;
}
function getSpanData(span) {
const { op: definedOp, source: definedSource, origin } = parseSpan(span);
const { op: inferredOp, description, source: inferredSource, data: inferredData } = parseSpanDescription(span);
const op = definedOp || inferredOp;
const source = definedSource || inferredSource;
const data = { ...inferredData, ...getData(span) };
return {
op,
description,
source,
origin,
data
};
}
function removeSentryAttributes(data) {
const cleanedData = { ...data };
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE];
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE];
delete cleanedData[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME];
return cleanedData;
}
function getData(span) {
const attributes = span.attributes;
const data = {};
if (span.kind !== SpanKind.INTERNAL) {
data["otel.kind"] = SpanKind[span.kind];
}
const maybeHttpStatusCodeAttribute = attributes[HTTP_STATUS_CODE];
if (maybeHttpStatusCodeAttribute) {
data[HTTP_RESPONSE_STATUS_CODE] = maybeHttpStatusCodeAttribute;
}
const requestData = getRequestSpanData(span);
if (requestData.url) {
data.url = requestData.url;
}
if (requestData["http.query"]) {
data["http.query"] = requestData["http.query"].slice(1);
}
if (requestData["http.fragment"]) {
data["http.fragment"] = requestData["http.fragment"].slice(1);
}
return data;
}
class SentrySpanProcessor {
constructor(options) {
this._unsubscribePreprocessSpan = void 0;
setIsSetup("SentrySpanProcessor");
this._exporter = new SentrySpanExporter(options);
this._client = options?.client ?? getClient();
if (this._client && hasSpanStreamingEnabled(this._client)) {
this._unsubscribePreprocessSpan = this._client.on("preprocessSpan", backfillStreamedSpanDataFromOtel);
}
}
/**
* @inheritDoc
*/
async forceFlush() {
this._exporter.flush();
}
/**
* @inheritDoc
*/
async shutdown() {
this._unsubscribePreprocessSpan?.();
this._exporter.clear();
}
/**
* @inheritDoc
*/
onStart(span, parentContext) {
const parentSpan = trace.getSpan(parentContext);
let scopes = getScopesFromContext(parentContext);
if (parentSpan && !parentSpan.spanContext().isRemote) {
addChildSpanToSpan(parentSpan, span);
}
if (parentSpan?.spanContext().isRemote) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE, true);
}
if (parentContext === ROOT_CONTEXT) {
scopes = {
scope: getDefaultCurrentScope(),
isolationScope: getDefaultIsolationScope()
};
}
if (scopes) {
setCapturedScopesOnSpan(span, scopes.scope, scopes.isolationScope);
}
logSpanStart(span);
this._client?.emit("spanStart", span);
}
/** @inheritDoc */
onEnd(span) {
logSpanEnd(span);
this._client?.emit("spanEnd", span);
if (this._client && hasSpanStreamingEnabled(this._client)) {
this._client.emit("afterSpanEnd", span);
} else {
this._exporter.export(span);
}
}
}
function backfillStreamedSpanDataFromOtel(spanJSON, hint) {
const attributes = spanJSON.attributes;
if (!attributes) {
return;
}
const kind = hint?.spanKind ?? SpanKind.INTERNAL;
const { op, description, source, data } = inferSpanData(spanJSON.name, attributes, kind);
spanJSON.name = description;
safeSetSpanJSONAttributes(spanJSON, {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
...data
});
if (kind !== SpanKind.INTERNAL) {
safeSetSpanJSONAttributes(spanJSON, {
"otel.kind": SpanKind[kind]
});
}
}
class SentrySampler {
constructor(client) {
this._client = client;
this._isSpanStreaming = hasSpanStreamingEnabled(client);
setIsSetup("SentrySampler");
}
/** @inheritDoc */
shouldSample(context, traceId, spanName, spanKind, spanAttributes, _links) {
const options = this._client.getOptions();
const { ignoreSpans } = options;
const parentSpan = getValidSpan(context);
const parentContext = parentSpan?.spanContext();
if (!hasSpansEnabled(options)) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const maybeSpanHttpMethod = spanAttributes[HTTP_METHOD] || spanAttributes[HTTP_REQUEST_METHOD];
if (spanKind === SpanKind.CLIENT && maybeSpanHttpMethod && (!parentSpan || parentContext?.isRemote)) {
if (!this._isSpanStreaming) {
this._client.recordDroppedEvent("no_parent_span", "span");
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
}
const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : void 0;
const isRootSpan = !parentSpan || parentContext?.isRemote;
if (!isRootSpan) {
if (this._isSpanStreaming) {
if (parentSampled) {
if (ignoreSpans?.length) {
const { description: inferredChildName, op: childOp } = inferSpanData(spanName, spanAttributes, spanKind);
if (shouldIgnoreSpan(
{
description: inferredChildName,
op: spanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? childOp,
attributes: spanAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredChildSpan: true
});
}
}
}
if (!parentSampled) {
const parentSegmentIgnored = parentContext?.traceState?.get(SENTRY_TRACE_STATE_SEGMENT_IGNORED) === "1";
this._client.recordDroppedEvent(parentSegmentIgnored ? "ignored" : "sample_rate", "span");
}
}
return wrapSamplingDecision({
decision: parentSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
context,
spanAttributes
});
}
const {
description: inferredSpanName,
data: inferredAttributes,
op
} = inferSpanData(spanName, spanAttributes, spanKind);
const mergedAttributes = {
...inferredAttributes,
...spanAttributes
};
if (op) {
mergedAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] = op;
}
if (this._isSpanStreaming && ignoreSpans?.length && shouldIgnoreSpan(
{
description: inferredSpanName,
op: mergedAttributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] ?? op,
attributes: mergedAttributes
},
ignoreSpans
)) {
this._client.recordDroppedEvent("ignored", "span");
return wrapSamplingDecision({
decision: SamplingDecision.NOT_RECORD,
context,
spanAttributes,
ignoredSegmentSpan: true
});
}
const mutableSamplingDecision = { decision: true };
this._client.emit(
"beforeSampling",
{
spanAttributes: mergedAttributes,
spanName: inferredSpanName,
parentSampled,
parentContext
},
mutableSamplingDecision
);
if (!mutableSamplingDecision.decision) {
return wrapSamplingDecision({ decision: void 0, context, spanAttributes });
}
const { isolationScope } = getScopesFromContext(context) ?? {};
const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : void 0;
const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : void 0;
const sampleRand = parseSampleRate(dsc?.sample_rand) ?? _INTERNAL_safeMathRandom();
const [sampled, sampleRate, localSampleRateWasApplied] = sampleSpan(
options,
{
name: inferredSpanName,
attributes: mergedAttributes,
normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest,
parentSampled,
parentSampleRate: parseSampleRate(dsc?.sample_rate)
},
sampleRand
);
const method = `${maybeSpanHttpMethod}`.toUpperCase();
if (method === "OPTIONS" || method === "HEAD") {
DEBUG_BUILD && debug.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
return wrapSamplingDecision({
decision: SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: 0
// we don't want to sample anything in the downstream trace either
});
}
if (!sampled && // We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace)
parentSampled === void 0) {
DEBUG_BUILD && debug.log("[Tracing] Discarding root span because its trace was not chosen to be sampled.");
this._client.recordDroppedEvent("sample_rate", this._isSpanStreaming ? "span" : "transaction");
}
return {
...wrapSamplingDecision({
decision: sampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : void 0
}),
attributes: {
// We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : void 0
}
};
}
/** Returns the sampler name or short description with the configuration. */
toString() {
return "SentrySampler";
}
}
function getParentSampled(parentSpan, traceId, spanName) {
const parentContext = parentSpan.spanContext();
if (isSpanContextValid(parentContext) && parentContext.traceId === traceId) {
if (parentContext.isRemote) {
const parentSampled2 = getSamplingDecision(parentSpan.spanContext());
DEBUG_BUILD && debug.log(`[Tracing] Inheriting remote parent's sampled decision for ${spanName}: ${parentSampled2}`);
return parentSampled2;
}
const parentSampled = getSamplingDecision(parentContext);
DEBUG_BUILD && debug.log(`[Tracing] Inheriting parent's sampled decision for ${spanName}: ${parentSampled}`);
return parentSampled;
}
return void 0;
}
function wrapSamplingDecision({
decision,
context,
spanAttributes,
sampleRand,
downstreamTraceSampleRate,
ignoredChildSpan,
ignoredSegmentSpan
}) {
let traceState = getBaseTraceState(context, spanAttributes);
if (downstreamTraceSampleRate !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RATE, `${downstreamTraceSampleRate}`);
}
if (sampleRand !== void 0) {
traceState = traceState.set(SENTRY_TRACE_STATE_SAMPLE_RAND, `${sampleRand}`);
}
if (ignoredChildSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_CHILD_IGNORED, "1");
}
if (ignoredSegmentSpan) {
traceState = traceState.set(SENTRY_TRACE_STATE_SEGMENT_IGNORED, "1");
}
if (decision == void 0) {
return { decision: SamplingDecision.NOT_RECORD, traceState };
}
if (decision === SamplingDecision.NOT_RECORD) {
return { decision, traceState: traceState.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, "1") };
}
return { decision, traceState };
}
function getBaseTraceState(context, spanAttributes) {
const parentSpan = trace.getSpan(context);
const parentContext = parentSpan?.spanContext();
let traceState = parentContext?.traceState || new TraceState();
const url = spanAttributes[HTTP_URL] || spanAttributes[URL_FULL];
if (url && typeof url === "string") {
traceState = traceState.set(SENTRY_TRACE_STATE_URL, url);
}
return traceState;
}
function getValidSpan(context) {
const span = trace.getSpan(context);
return span && isSpanContextValid(span.spanContext()) ? span : void 0;
}
const ATTR_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language";
const ATTR_TELEMETRY_SDK_NAME = "telemetry.sdk.name";
const ATTR_TELEMETRY_SDK_VERSION = "telemetry.sdk.version";
const SEMRESATTRS_SERVICE_NAMESPACE = "service.namespace";
class SentryResource {
constructor(attributes) {
this._attributes = attributes;
}
get attributes() {
return this._attributes;
}
merge(other) {
if (!other) {
return this;
}
return new SentryResource({ ...this._attributes, ...other.attributes });
}
getRawAttributes() {
return Object.entries(this._attributes);
}
}
function parseOtelResourceAttributes(raw) {
if (!raw) {
return {};
}
const result = {};
for (const pair of raw.split(",")) {
const eq = pair.indexOf("=");
if (eq === -1) {
continue;
}
const key = pair.substring(0, eq).trim();
const value = pair.substring(eq + 1).trim();
if (key) {
try {
result[key] = decodeURIComponent(value);
} catch {
result[key] = value;
}
}
}
return result;
}
function getSentryResource(serviceNameFallback) {
const env = typeof process !== "undefined" ? process.env : {};
const otelServiceName = env.OTEL_SERVICE_NAME;
const otelResourceAttrs = parseOtelResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
return new SentryResource({
// Lowest priority: Sentry defaults
// eslint-disable-next-line typescript/no-deprecated
[SEMRESATTRS_SERVICE_NAMESPACE]: "sentry",
[SERVICE_NAME]: serviceNameFallback,
// OTEL_RESOURCE_ATTRIBUTES overrides defaults (including service.name and service.namespace)
...otelResourceAttrs,
// OTEL_SERVICE_NAME explicitly overrides service.name
...otelServiceName ? { [SERVICE_NAME]: otelServiceName } : {},
// Highest priority: Sentry SDK telemetry attrs (cannot be overridden by env vars)
[SERVICE_VERSION]: SDK_VERSION,
[ATTR_TELEMETRY_SDK_LANGUAGE]: SDK_INFO[ATTR_TELEMETRY_SDK_LANGUAGE],
[ATTR_TELEMETRY_SDK_NAME]: SDK_INFO[ATTR_TELEMETRY_SDK_NAME],
[ATTR_TELEMETRY_SDK_VERSION]: SDK_INFO[ATTR_TELEMETRY_SDK_VERSION]
});
}
export { startSpanManual as A, suppressTracing as B, withActiveSpan as C, wrapClientClass as D, wrapContextManagerClass as E, wrapSamplingDecision as F, SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION as S, SENTRY_SCOPES_CONTEXT_KEY as a, SentryPropagator as b, SentrySampler as c, SentrySpanProcessor as d, buildContextWithSentryScopes as e, continueTrace as f, enhanceDscWithOpenTelemetryRootSpanName as g, getActiveSpan as h, getRequestSpanData as i, getScopesFromContext as j, getSentryResource as k, getSpanKind as l, getTraceContextForScope as m, isSentryRequestSpan as n, openTelemetrySetupCheck as o, setOpenTelemetryContextAsyncContextStrategy as p, setupEventContextTrace as q, spanHasAttributes as r, setIsSetup as s, spanHasEvents as t, spanHasKind as u, spanHasName as v, spanHasParentId as w, spanHasStatus as x, startInactiveSpan as y, startSpan as z };
//# sourceMappingURL=resource-CB9ITx8O.js.map

Sorry, the diff of this file is too big to display