feat(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea
, '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(nestjs): Automatic instrumentation of nestjs middleware - #13065

Merged
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation
Jul 30, 2024
Merged

feat(nestjs): Automatic instrumentation of nestjs middleware#13065
nicohrubec merged 28 commits into
developfrom
nh/nestjs-middleware-instrumentation

Conversation

@nicohrubec

@nicohrubecnicohrubec commented Jul 26, 2024

Copy link
Copy Markdown
Member

Adds middleware instrumentation to the @sentry/nestjs. The implementation lives in @sentry/node so that both users using @sentry/nestjs directly as well as users still on @sentry/node benefit. The instrumentation is automatic without requiring any additional setup. The idea is to hook into the Injectable decorator (every class middleware is annotated with @Injectable and patch the use method if it is implemented.

Caveat: This implementation only works for class middleware, which implements the use method, which seems to be the standard for implementing middleware in nest. However, nest also provides functional middleware, for which this implementation does not work.

Trace from my sample app:
Screenshot 2024-07-29 at 15 49 17

Resolves#12769

@nicohrubecnicohrubec self-assigned this Jul 26, 2024
@github-actions

github-actionsBot commented Jul 26, 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/node114.55 KB (+2.36% 🔺)
@sentry/node - without tracing89.33 KB (0%)
@sentry/aws-serverless98.5 KB (0%)

@lforst
lforstforce-pushed the nh/nestjs-middleware-instrumentation branch from 4f394c4 to 6e35eb1CompareJuly 26, 2024 12:09
@nicohrubec
nicohrubec marked this pull request as ready for review July 29, 2024 15:01
Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated

@lforstlforst left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great job! I left a few comments that we need to address.

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
if (typeof target.prototype.use === 'function') {
const originalUse = target.prototype.use;

target.prototype.use = function (req: unknown, res: unknown, next: (error?: unknown) => void): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: We should make very sure that the this context stays intact. Also we should forward all arguments!

Maybe let's add a unit test?

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.

replaced this also with a proxy and pass all arguments along now.

*/
private _getInjectableFileInstrumentation(versions: string[]): InstrumentationNodeModuleFile {
return new InstrumentationNodeModuleFile(
'@nestjs/common/decorators/core/injectable.decorator.js',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: Since Injectable is exported from the top-level of @nestjs/common, I wonder if we can just do '@nestjs/common' here 🤔

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.

No doesn't work (changed it locally and it breaks the e2e tests)

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.

FWIW they also wrap specific files like this in the nestjs-core instrumentation (https://www.npmjs.com/package/@opentelemetry/instrumentation-nestjs-core)

@lforstlforstJul 30, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kinda sucks because if they change the file structure this instantly won't work anymore :/ Stuff like this makes us need canary test otherwise we'll never catch breakages like that.

@nicohrubec can you investigate whether OTEL has some other functionality that we can use where we can point at '@nestjs/common'.

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.

Have played around with it but could not make anything else work so far. FWIW I think it's fine here. They have not moved this file in 6 years: https://github.com/nestjs/nest/commits/master/packages/common/decorators/core/injectable.decorator.ts

},
(span: Span) => {
// patch next to end span before next middleware is being called
const wrappedNext = (error?: Error | unknown): void => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

h: We should forward all arguments not just the first one. Ideally we even use a proxy like for example here:

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.

using a proxy now

Comment threadpackages/node/src/integrations/tracing/nest.ts Outdated
return new SentryNestInstrumentation();
});

export const instrumentNest = Object.assign(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

m: The object assign here seems unnecessary. This could just be a simple function.

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.

we need the id for filtering out stuff in preload!

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 have updated it to use no Object.assign explicitly but still set an id, which should work too. No strong feelings either way

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.

Going with the original version with Object.assign to not introduce side-effects

@nicohrubec

Copy link
Copy Markdown
MemberAuthor

What should we put for instrumentationVersion?

@lforst

Copy link
Copy Markdown
Contributor

What should we put for instrumentationVersion?

We could do SDK_VERSION as exported by @sentry/utils.

@nicohrubec
nicohrubec merged commit e3af1ce into developJul 30, 2024
@nicohrubec
nicohrubec deleted the nh/nestjs-middleware-instrumentation branch July 30, 2024 14:14
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.

Instrumentation of NestJS middleware

3 participants

@nicohrubec@lforst@mydea