| 48 | * it may just be a non-recording span if the span is not sampled or if tracing is disabled. |
| 49 | */ |
| 50 | export function startSpan<T>(options: StartSpanOptions, callback: (span: Span) => T): T { |
| 51 | const acs = getAcs(); |
| 52 | if (acs.startSpan) { |
| 53 | return acs.startSpan(options, callback); |
| 54 | } |
| 55 | |
| 56 | const spanArguments = parseSentrySpanArguments(options); |
| 57 | const { forceTransaction, parentSpan: customParentSpan, scope: customScope } = options; |
| 58 | |
| 59 | // We still need to fork a potentially passed scope, as we set the active span on it |
| 60 | // and we need to ensure that it is cleaned up properly once the span ends. |
| 61 | const customForkedScope = customScope?.clone(); |
| 62 | |
| 63 | return withScope(customForkedScope, () => { |
| 64 | // If `options.parentSpan` is defined, we want to wrap the callback in `withActiveSpan` |
| 65 | const wrapper = getActiveSpanWrapper<T>(customParentSpan); |
| 66 | |
| 67 | return wrapper(() => { |
| 68 | const scope = getCurrentScope(); |
| 69 | const parentSpan = getParentSpan(scope, customParentSpan); |
| 70 | const client = getClient(); |
| 71 | |
| 72 | const missingRequiredParent = options.onlyIfParent && !parentSpan; |
| 73 | const activeSpan = missingRequiredParent |
| 74 | ? startMissingRequiredParentSpan(scope, client) |
| 75 | : createChildOrRootSpan({ |
| 76 | parentSpan, |
| 77 | spanArguments, |
| 78 | forceTransaction, |
| 79 | scope, |
| 80 | }); |
| 81 | |
| 82 | // Ignored root spans still need to be set on scope so that `getActiveSpan()` returns them |
| 83 | // and descendants are also non-recording. Ignored child spans don't need this because |
| 84 | // the parent span is already on scope. |
| 85 | if (!_isIgnoredSpan(activeSpan) || !parentSpan) { |
| 86 | _setSpanForScope(scope, activeSpan); |
| 87 | } |
| 88 | |
| 89 | return handleCallbackErrors( |
| 90 | () => callback(activeSpan), |
| 91 | () => { |
| 92 | // Only update the span status if it hasn't been changed yet, and the span is not yet finished |
| 93 | const { status } = spanToJSON(activeSpan); |
| 94 | if (activeSpan.isRecording() && (!status || status === 'ok')) { |
| 95 | activeSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); |
| 96 | } |
| 97 | }, |
| 98 | () => { |
| 99 | activeSpan.end(); |
| 100 | }, |
| 101 | ); |
| 102 | }); |
| 103 | }); |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Similar to `Sentry.startSpan`. Wraps a function with a transaction/span, but does not finish the span |