feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(solidstart): Add server action instrumentation helper - #13035

Merged
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function
Jul 30, 2024
Merged

feat(solidstart): Add server action instrumentation helper#13035
andreiborza merged 5 commits into
developfrom
ab/solidstart-wrap-server-function

Conversation

@andreiborza

@andreiborzaandreiborza commented Jul 24, 2024

Copy link
Copy Markdown
Member

Can be used like this:

constgetUserData=async()=>{'use server';returnawaitwithServerActionInstrumentation('getData',()=>{return{prefecture: 'Kanagawa'};});};

Shows up like this:
CleanShot 2024-07-29 at 10 55 53@2x

Can also be used for api routes like this:

exportasyncfunctionGET(){returnawaitwithServerActionInstrumentation('getUser',()=>{returnjson({prefecture: 'Akita'})})}

Shows up like this for pageloads on a route that makes a fetch call to an api route
CleanShot 2024-07-30 at 09 44 11@2x

Comment on lines +16 to +32
const sentryTrace = (headers && headers.get('sentry-trace')) || undefined;
const baggage = (headers && headers.get('baggage')) || null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L: is there a reason why one is set to null and one to undefined?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure tbh, the types were already like this. I think I could change it both to null but no strong feelings.

@Lms24Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We could think about extracting the getTracePropagationData and flushIfServerless out to utils because we have them in at least 3 SDKs each. But for now it's also fine to live with the duplication.

Comment on lines +47 to +49
const hasValidLocation = typeof error.headers.get('location') === 'string';
const hasValidStatus = error.status >= 300 && error.status <= 308;
return hasValidLocation && hasValidStatus;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity/no action required: the status code makes total sense to me. Why is thelocation header important?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose people might set some weird status codes on errors that aren't redirects and we wouldn't want to ignore those? Not sure tbh, no strong feelings on removing this check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, I read up on the location header. Makes sense to me!

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

Maybe we should also check if this works as expected on vercel/edge

@andreiborza
andreiborzaforce-pushed the ab/solidstart-wrap-server-function branch from 281463d to 426ce94CompareJuly 29, 2024 12:56
@andreiborza

Copy link
Copy Markdown
MemberAuthor

I rewrote this since the previous push to not use continueTrace and instead just start a span to get proper nesting under the http spans.

Additionally, I only rewrite the transaction name when the target isn't /_server which happens for example for pageloads to a route that uses a server action. That way we keep the more meaningful GET /some-route over GET someServerAction.

* function body with Sentry Error and Performance instrumentation.
*/
export async function withServerActionInstrumentation<A extends (...args: unknown[]) => unknown>(
serverActionName: string,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way to infer the name, possibly? That would be ideal... if not, this is fine for now!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. Accessing a parent's function name isn't available in strict mode, also it wouldn't work for minifed code either. Unfortunately I think we have to live with this :(

description: 'getPrefecture',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: I'm not sure if manual is the correct origin here b/c it's not users calling startSpan. However, the way I understand the usage, users have call the wrapper manually, right? So it's probably fine as long as we don't auto instrument server actions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing we emit from the SDK OOTB should have origin: manual - that's the base rule of thumb here :D We should always assign a proper origin that matches (see https://develop.sentry.dev/sdk/performance/trace-origin/) for any span that any SDK starts.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, user's have to manually wrap their server action function bodies with the wrapper.

How about auto.function.solidstart? The sveltekit sdk uses auto.function.sveltekit for wrapServerLoadWithSentry.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please have another look.

* Takes a request event and extracts traceparent and DSC data
* from the `sentry-trace` and `baggage` DSC headers.
*/
export function getTracePropagationData(event: RequestEvent | undefined): {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: do we still need this function?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! I had it removed and the re-added after trying some things out. Forgot to remove it again :)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

op: 'function.server_action',
name: serverActionName,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a route though? Users could specify anything here, right? I guess component makes more sense?
I tried looking up the transaction name source values but only found this orphaned page. To me the component definition makes sense but feel free to overrule me :D

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judging from that page, component also makes the most sense to me. I initially had it on url but changed it to route after @mydea looked over it.

wdyt about using component instead @mydea?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, component makes sense to me too! Sorry about the misdirection to use route 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@andreiborza
andreiborza requested review from Lms24 and mydeaJuly 29, 2024 15:52
@github-actions

github-actionsBot commented Jul 29, 2024

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize
@sentry/browser22.45 KB (0%)
@sentry/browser (incl. Tracing)34.22 KB (0%)
@sentry/browser (incl. Tracing, Replay)70.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags63.59 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas)74.66 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback)87.24 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics)89.08 KB (0%)
@sentry/browser (incl. metrics)26.75 KB (0%)
@sentry/browser (incl. Feedback)39.37 KB (0%)
@sentry/browser (incl. sendFeedback)27.06 KB (0%)
@sentry/browser (incl. FeedbackAsync)31.7 KB (0%)
@sentry/react25.22 KB (0%)
@sentry/react (incl. Tracing)37.22 KB (0%)
@sentry/vue26.6 KB (0%)
@sentry/vue (incl. Tracing)36.06 KB (0%)
@sentry/svelte22.58 KB (0%)
CDN Bundle23.64 KB (0%)
CDN Bundle (incl. Tracing)35.88 KB (0%)
CDN Bundle (incl. Tracing, Replay)70.26 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback)75.53 KB (0%)
CDN Bundle - uncompressed69.37 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed106.31 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed217.95 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed230.78 KB (0%)
@sentry/nextjs (client)37.08 KB (0%)
@sentry/sveltekit (client)34.81 KB (0%)
@sentry/node111.92 KB (0%)
@sentry/node - without tracing89.33 KB (+0.01% 🔺)
@sentry/aws-serverless98.5 KB (0%)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@andreiborza@mydea@Lms24@chargome@s1gr1d