-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathwrapServerAction.ts
More file actions
80 lines (76 loc) · 2.45 KB
/
wrapServerAction.ts
File metadata and controls
80 lines (76 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';
import type { SpanAttributes } from '@sentry/core';
import {
flushIfServerless,
getActiveSpan,
getRootSpan,
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
spanToJSON,
startSpan,
updateSpanName,
} from '@sentry/core';
import type { ActionFunctionArgs } from 'react-router';
type SpanOptions = {
name?: string;
attributes?: SpanAttributes;
};
/**
* Wraps a React Router server action function with Sentry performance monitoring.
* @param options - Optional span configuration options including name, operation, description and attributes
* @param actionFn - The server action function to wrap
*
* @example
* ```ts
* // Wrap an action function with custom span options
* export const action = wrapServerAction(
* {
* name: 'Submit Form Data',
* description: 'Processes form submission data',
* },
* async ({ request }) => {
* // ... your action logic
* }
* );
* ```
*/
export function wrapServerAction<T>(options: SpanOptions = {}, actionFn: (args: ActionFunctionArgs) => Promise<T>) {
return async function (args: ActionFunctionArgs) {
const name = options.name || 'Executing Server Action';
const active = getActiveSpan();
if (active) {
const root = getRootSpan(active);
const spanData = spanToJSON(root);
if (spanData.origin === 'auto.http.otel.http') {
// eslint-disable-next-line deprecation/deprecation
const target = spanData.data[SEMATTRS_HTTP_TARGET];
if (target) {
// We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route
// So we force this to be a more sensible name here
updateSpanName(root, `${args.request.method} ${target}`);
root.setAttributes({
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.action',
});
}
}
}
try {
return await startSpan(
{
name,
...options,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.action',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',
...options.attributes,
},
},
() => actionFn(args),
);
} finally {
await flushIfServerless();
}
};
}