package.build.esm.utils.spanUtils.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Base implementation for all Sentry JavaScript SDKs
import { dropUndefinedKeys, generateSentryTraceHeader, timestampInSeconds, addNonEnumerableProperty } from '@sentry/utils';
import { getAsyncContextStrategy } from '../asyncContext/index.js';
import { getMainCarrier } from '../carrier.js';
import { getCurrentScope } from '../currentScopes.js';
import { getMetricSummaryJsonForSpan, updateMetricSummaryOnSpan } from '../metrics/metric-summary.js';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes.js';
import { SPAN_STATUS_UNSET, SPAN_STATUS_OK } from '../tracing/spanstatus.js';
import { _getSpanForScope } from './spanOnScope.js';
// These are aligned with OpenTelemetry trace flags
const TRACE_FLAG_NONE = 0x0;
const TRACE_FLAG_SAMPLED = 0x1;
/**
* Convert a span to a trace context, which can be sent as the `trace` context in an event.
* By default, this will only include trace_id, span_id & parent_span_id.
* If `includeAllData` is true, it will also include data, op, status & origin.
*/
function spanToTransactionTraceContext(span) {
const { spanId: span_id, traceId: trace_id } = span.spanContext();
const { data, op, parent_span_id, status, origin } = spanToJSON(span);
return dropUndefinedKeys({
parent_span_id,
span_id,
trace_id,
data,
op,
status,
origin,
});
}
/**
* Convert a span to a trace context, which can be sent as the `trace` context in a non-transaction event.
*/
function spanToTraceContext(span) {
const { spanId: span_id, traceId: trace_id } = span.spanContext();
const { parent_span_id } = spanToJSON(span);
return dropUndefinedKeys({ parent_span_id, span_id, trace_id });
}
/**
* Convert a Span to a Sentry trace header.
*/
function spanToTraceHeader(span) {
const { traceId, spanId } = span.spanContext();
const sampled = spanIsSampled(span);
return generateSentryTraceHeader(traceId, spanId, sampled);
}
/**
* Convert a span time input into a timestamp in seconds.
*/
function spanTimeInputToSeconds(input) {
if (typeof input === 'number') {
return ensureTimestampInSeconds(input);
}
if (Array.isArray(input)) {
// See {@link HrTime} for the array-based time format
return input[0] + input[1] / 1e9;
}
if (input instanceof Date) {
return ensureTimestampInSeconds(input.getTime());
}
return timestampInSeconds();
}
/**
* Converts a timestamp to second, if it was in milliseconds, or keeps it as second.
*/
function ensureTimestampInSeconds(timestamp) {
const isMs = timestamp > 9999999999;
return isMs ? timestamp / 1000 : timestamp;
}
/**
* Convert a span to a JSON representation.
*/
// Note: Because of this, we currently have a circular type dependency (which we opted out of in package.json).
// This is not avoidable as we need `spanToJSON` in `spanUtils.ts`, which in turn is needed by `span.ts` for backwards compatibility.
// And `spanToJSON` needs the Span class from `span.ts` to check here.
function spanToJSON(span) {
if (spanIsSentrySpan(span)) {
return span.getSpanJSON();
}
try {
const { spanId: span_id, traceId: trace_id } = span.spanContext();
// Handle a span from @opentelemetry/sdk-base-trace's `Span` class
if (spanIsOpenTelemetrySdkTraceBaseSpan(span)) {
const { attributes, startTime, name, endTime, parentSpanId, status } = span;
return dropUndefinedKeys({
span_id,
trace_id,
data: attributes,
description: name,
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) || undefined,
status: getStatusMessage(status),
op: attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP],
origin: attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] ,
_metrics_summary: getMetricSummaryJsonForSpan(span),
});
}
// Finally, at least we have `spanContext()`....
return {
span_id,
trace_id,
};
} catch (e) {
return {};
}
}
function spanIsOpenTelemetrySdkTraceBaseSpan(span) {
const castSpan = span ;
return !!castSpan.attributes && !!castSpan.startTime && !!castSpan.name && !!castSpan.endTime && !!castSpan.status;
}
/** Exported only for tests. */
/**
* Sadly, due to circular dependency checks we cannot actually import the Span class here and check for instanceof.
* :( So instead we approximate this by checking if it has the `getSpanJSON` method.
*/
function spanIsSentrySpan(span) {
return typeof (span ).getSpanJSON === 'function';
}
/**
* Returns true if a span is sampled.
* In most cases, you should just use `span.isRecording()` instead.
* However, this has a slightly different semantic, as it also returns false if the span is finished.
* So in the case where this distinction is important, use this method.
*/
function spanIsSampled(span) {
// We align our trace flags with the ones OpenTelemetry use
// So we also check for sampled the same way they do.
const { traceFlags } = span.spanContext();
return traceFlags === TRACE_FLAG_SAMPLED;
}
/** Get the status message to use for a JSON representation of a span. */
function getStatusMessage(status) {
if (!status || status.code === SPAN_STATUS_UNSET) {
return undefined;
}
if (status.code === SPAN_STATUS_OK) {
return 'ok';
}
return status.message || 'unknown_error';
}
const CHILD_SPANS_FIELD = '_sentryChildSpans';
const ROOT_SPAN_FIELD = '_sentryRootSpan';
/**
* Adds an opaque child span reference to a span.
*/
function addChildSpanToSpan(span, childSpan) {
// We store the root span reference on the child span
// We need this for `getRootSpan()` to work
const rootSpan = span[ROOT_SPAN_FIELD] || span;
addNonEnumerableProperty(childSpan , ROOT_SPAN_FIELD, rootSpan);
// We store a list of child spans on the parent span
// We need this for `getSpanDescendants()` to work
if (span[CHILD_SPANS_FIELD]) {
span[CHILD_SPANS_FIELD].add(childSpan);
} else {
addNonEnumerableProperty(span, CHILD_SPANS_FIELD, new Set([childSpan]));
}
}
/** This is only used internally by Idle Spans. */
function removeChildSpanFromSpan(span, childSpan) {
if (span[CHILD_SPANS_FIELD]) {
span[CHILD_SPANS_FIELD].delete(childSpan);
}
}
/**
* Returns an array of the given span and all of its descendants.
*/
function getSpanDescendants(span) {
const resultSet = new Set();
function addSpanChildren(span) {
// This exit condition is required to not infinitely loop in case of a circular dependency.
if (resultSet.has(span)) {
return;
// We want to ignore unsampled spans (e.g. non recording spans)
} else if (spanIsSampled(span)) {
resultSet.add(span);
const childSpans = span[CHILD_SPANS_FIELD] ? Array.from(span[CHILD_SPANS_FIELD]) : [];
for (const childSpan of childSpans) {
addSpanChildren(childSpan);
}
}
}
addSpanChildren(span);
return Array.from(resultSet);
}
/**
* Returns the root span of a given span.
*/
function getRootSpan(span) {
return span[ROOT_SPAN_FIELD] || span;
}
/**
* Returns the currently active span.
*/
function getActiveSpan() {
const carrier = getMainCarrier();
const acs = getAsyncContextStrategy(carrier);
if (acs.getActiveSpan) {
return acs.getActiveSpan();
}
return _getSpanForScope(getCurrentScope());
}
/**
* Updates the metric summary on the currently active span
*/
function updateMetricSummaryOnActiveSpan(
metricType,
sanitizedName,
value,
unit,
tags,
bucketKey,
) {
const span = getActiveSpan();
if (span) {
updateMetricSummaryOnSpan(span, metricType, sanitizedName, value, unit, tags, bucketKey);
}
}
export { TRACE_FLAG_NONE, TRACE_FLAG_SAMPLED, addChildSpanToSpan, getActiveSpan, getRootSpan, getSpanDescendants, getStatusMessage, removeChildSpanFromSpan, spanIsSampled, spanTimeInputToSeconds, spanToJSON, spanToTraceContext, spanToTraceHeader, spanToTransactionTraceContext, updateMetricSummaryOnActiveSpan };
//# sourceMappingURL=spanUtils.js.map