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,25 +24,14 @@ 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
- function createChildSpanOrTransaction ( ) : Span | undefined {
38
- if ( ! hasTracingEnabled ( ) ) {
39
- return undefined ;
40
- }
41
- return parentSpan ? parentSpan . startChild ( ctx ) : hub . startTransaction ( ctx ) ;
42
- }
33
+ const activeSpan = createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
43
34
44
- const activeSpan = createChildSpanOrTransaction ( ) ;
45
35
scope . setSpan ( activeSpan ) ;
46
36
47
37
function finishAndSetSpan ( ) : void {
@@ -89,25 +79,13 @@ export function trace<T>(
89
79
* and the `span` returned from the callback will be undefined.
90
80
*/
91
81
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
- }
82
+ const ctx = normalizeContext ( context ) ;
97
83
98
84
const hub = getCurrentHub ( ) ;
99
85
const scope = hub . getScope ( ) ;
100
-
101
86
const parentSpan = scope . getSpan ( ) ;
102
87
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 ( ) ;
88
+ const activeSpan = createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
111
89
scope . setSpan ( activeSpan ) ;
112
90
113
91
function finishAndSetSpan ( ) : void {
@@ -146,6 +124,52 @@ export function startSpan<T>(context: TransactionContext, callback: (span: Span
146
124
*/
147
125
export const startActiveSpan = startSpan ;
148
126
127
+ /**
128
+ * Similar to `Sentry.startSpan`. Wraps a function with a transaction/span, but does not finish the span
129
+ * after the function is done automatically.
130
+ *
131
+ * The created span is the active span and will be used as parent by other spans created inside the function
132
+ * and can be accessed via `Sentry.getActiveSpan()`, as long as the function is executed while the scope is active.
133
+ *
134
+ * Note that if you have not enabled tracing extensions via `addTracingExtensions`
135
+ * or you didn't set `tracesSampleRate`, this function will not generate spans
136
+ * and the `span` returned from the callback will be undefined.
137
+ */
138
+ export function startSpanManual < T > (
139
+ context : TransactionContext ,
140
+ callback : ( span : Span | undefined , finish : ( ) => void ) => T ,
141
+ ) : T {
142
+ const ctx = normalizeContext ( context ) ;
143
+
144
+ const hub = getCurrentHub ( ) ;
145
+ const scope = hub . getScope ( ) ;
146
+ const parentSpan = scope . getSpan ( ) ;
147
+
148
+ const activeSpan = createChildSpanOrTransaction ( hub , parentSpan , ctx ) ;
149
+ scope . setSpan ( activeSpan ) ;
150
+
151
+ function finishAndSetSpan ( ) : void {
152
+ activeSpan && activeSpan . finish ( ) ;
153
+ hub . getScope ( ) . setSpan ( parentSpan ) ;
154
+ }
155
+
156
+ let maybePromiseResult : T ;
157
+ try {
158
+ maybePromiseResult = callback ( activeSpan , finishAndSetSpan ) ;
159
+ } catch ( e ) {
160
+ activeSpan && activeSpan . setStatus ( 'internal_error' ) ;
161
+ throw e ;
162
+ }
163
+
164
+ if ( isThenable ( maybePromiseResult ) ) {
165
+ Promise . resolve ( maybePromiseResult ) . then ( undefined , ( ) => {
166
+ activeSpan && activeSpan . setStatus ( 'internal_error' ) ;
167
+ } ) ;
168
+ }
169
+
170
+ return maybePromiseResult ;
171
+ }
172
+
149
173
/**
150
174
* Creates a span. This span is not set as active, so will not get automatic instrumentation spans
151
175
* as children or be able to be accessed via `Sentry.getSpan()`.
@@ -178,3 +202,24 @@ export function startInactiveSpan(context: TransactionContext): Span | undefined
178
202
export function getActiveSpan ( ) : Span | undefined {
179
203
return getCurrentHub ( ) . getScope ( ) . getSpan ( ) ;
180
204
}
205
+
206
+ function createChildSpanOrTransaction (
207
+ hub : Hub ,
208
+ parentSpan : Span | undefined ,
209
+ ctx : TransactionContext ,
210
+ ) : Span | undefined {
211
+ if ( ! hasTracingEnabled ( ) ) {
212
+ return undefined ;
213
+ }
214
+ return parentSpan ? parentSpan . startChild ( ctx ) : hub . startTransaction ( ctx ) ;
215
+ }
216
+
217
+ function normalizeContext ( context : TransactionContext ) : TransactionContext {
218
+ const ctx = { ...context } ;
219
+ // If a name is set and a description is not, set the description to the name.
220
+ if ( ctx . name !== undefined && ctx . description === undefined ) {
221
+ ctx . description = ctx . name ;
222
+ }
223
+
224
+ return ctx ;
225
+ }
0 commit comments