1
1
import type { TransactionContext } from '@sentry/types' ;
2
2
import { isThenable } from '@sentry/utils' ;
3
3
4
+ import type { Hub } from '../hub' ;
4
5
import { getCurrentHub } from '../hub' ;
5
6
import { hasTracingEnabled } from '../utils/hasTracingEnabled' ;
6
7
import type { Span } from './span' ;
@@ -23,15 +24,10 @@ export function trace<T>(
23
24
// eslint-disable-next-line @typescript-eslint/no-empty-function
24
25
onError : ( error : unknown ) => void = ( ) => { } ,
25
26
) : T {
26
- const ctx = { ...context } ;
27
- // If a name is set and a description is not, set the description to the name.
28
- if ( ctx . name !== undefined && ctx . description === undefined ) {
29
- ctx . description = ctx . name ;
30
- }
27
+ const ctx = normalizeContext ( context ) ;
31
28
32
29
const hub = getCurrentHub ( ) ;
33
30
const scope = hub . getScope ( ) ;
34
-
35
31
const parentSpan = scope . getSpan ( ) ;
36
32
37
33
function createChildSpanOrTransaction ( ) : Span | undefined {
@@ -42,6 +38,7 @@ export function trace<T>(
42
38
}
43
39
44
40
const activeSpan = createChildSpanOrTransaction ( ) ;
41
+
45
42
scope . setSpan ( activeSpan ) ;
46
43
47
44
function finishAndSetSpan ( ) : void {
@@ -89,25 +86,13 @@ export function trace<T>(
89
86
* and the `span` returned from the callback will be undefined.
90
87
*/
91
88
export function startSpan < T > ( context : TransactionContext , callback : ( span : Span | undefined ) => T ) : T {
92
- const ctx = { ...context } ;
93
- // If a name is set and a description is not, set the description to the name.
94
- if ( ctx . name !== undefined && ctx . description === undefined ) {
95
- ctx . description = ctx . name ;
96
- }
89
+ const ctx = normalizeContext ( context ) ;
97
90
98
91
const hub = getCurrentHub ( ) ;
99
92
const scope = hub . getScope ( ) ;
100
-
101
93
const parentSpan = scope . getSpan ( ) ;
102
94
103
- function createChildSpanOrTransaction ( ) : Span | undefined {
104
- if ( ! hasTracingEnabled ( ) ) {
105
- return undefined ;
106
- }
107
- return parentSpan ? parentSpan . startChild ( ctx ) : hub . startTransaction ( ctx ) ;
108
- }
109
-
110
- const activeSpan = createChildSpanOrTransaction ( ) ;
95
+ const activeSpan = createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
111
96
scope . setSpan ( activeSpan ) ;
112
97
113
98
function finishAndSetSpan ( ) : void {
@@ -146,6 +131,52 @@ export function startSpan<T>(context: TransactionContext, callback: (span: Span
146
131
*/
147
132
export const startActiveSpan = startSpan ;
148
133
134
+ /**
135
+ * Similar to `Sentry.startSpan`. Wraps a function with a transaction/span, but does not finish the span
136
+ * after the function is done automatically.
137
+ *
138
+ * The created span is the active span and will be used as parent by other spans created inside the function
139
+ * and can be accessed via `Sentry.getActiveSpan()`, as long as the function is executed while the scope is active.
140
+ *
141
+ * Note that if you have not enabled tracing extensions via `addTracingExtensions`
142
+ * or you didn't set `tracesSampleRate`, this function will not generate spans
143
+ * and the `span` returned from the callback will be undefined.
144
+ */
145
+ export function startSpanManual < T > (
146
+ context : TransactionContext ,
147
+ callback : ( span : Span | undefined , finish : ( ) => void ) => T ,
148
+ ) : T {
149
+ const ctx = normalizeContext ( context ) ;
150
+
151
+ const hub = getCurrentHub ( ) ;
152
+ const scope = hub . getScope ( ) ;
153
+ const parentSpan = scope . getSpan ( ) ;
154
+
155
+ const activeSpan = createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
156
+ scope . setSpan ( activeSpan ) ;
157
+
158
+ function finishAndSetSpan ( ) : void {
159
+ activeSpan && activeSpan . finish ( ) ;
160
+ hub . getScope ( ) . setSpan ( parentSpan ) ;
161
+ }
162
+
163
+ let maybePromiseResult : T ;
164
+ try {
165
+ maybePromiseResult = callback ( activeSpan , finishAndSetSpan ) ;
166
+ } catch ( e ) {
167
+ activeSpan && activeSpan . setStatus ( 'internal_error' ) ;
168
+ throw e ;
169
+ }
170
+
171
+ if ( isThenable ( maybePromiseResult ) ) {
172
+ Promise . resolve ( maybePromiseResult ) . then ( undefined , ( ) => {
173
+ activeSpan && activeSpan . setStatus ( 'internal_error' ) ;
174
+ } ) ;
175
+ }
176
+
177
+ return maybePromiseResult ;
178
+ }
179
+
149
180
/**
150
181
* Creates a span. This span is not set as active, so will not get automatic instrumentation spans
151
182
* as children or be able to be accessed via `Sentry.getSpan()`.
@@ -178,3 +209,24 @@ export function startInactiveSpan(context: TransactionContext): Span | undefined
178
209
export function getActiveSpan ( ) : Span | undefined {
179
210
return getCurrentHub ( ) . getScope ( ) . getSpan ( ) ;
180
211
}
212
+
213
+ function createChildSpanOrTransaction (
214
+ hub : Hub ,
215
+ parentSpan : Span | undefined ,
216
+ ctx : TransactionContext ,
217
+ ) : Span | undefined {
218
+ if ( ! hasTracingEnabled ( ) ) {
219
+ return undefined ;
220
+ }
221
+ return parentSpan ? parentSpan . startChild ( ctx ) : hub . startTransaction ( ctx ) ;
222
+ }
223
+
224
+ function normalizeContext ( context : TransactionContext ) : TransactionContext {
225
+ const ctx = { ...context } ;
226
+ // If a name is set and a description is not, set the description to the name.
227
+ if ( ctx . name !== undefined && ctx . description === undefined ) {
228
+ ctx . description = ctx . name ;
229
+ }
230
+
231
+ return ctx ;
232
+ }
0 commit comments