Skip to content

[fastify 4] Request body is not captured with fastify integration #16090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 tasks done
tdournet opened this issue Apr 17, 2025 · 8 comments · Fixed by #16105
Closed
3 tasks done

[fastify 4] Request body is not captured with fastify integration #16090

tdournet opened this issue Apr 17, 2025 · 8 comments · Fixed by #16105

Comments

@tdournet
Copy link

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/node

SDK Version

9.11.0

Framework Version

fastify 4.28.0

Link to Sentry event

No response

Reproduction Example/SDK Setup

    Sentry.init({
      dsn: ...,
      tracesSampleRate: 0.1,
      release: process.env.RELEASE_NAME,
    });

    [...]

    this.fastifyServer = fastify();
    Sentry.setupFastifyErrorHandler(this.fastifyServer);

Steps to Reproduce

I installed sentry with fastify 4, following the documentation :

    Sentry.init({
      dsn: ...,
      tracesSampleRate: 0.1,
      release: process.env.RELEASE_NAME,
    });

    [...]

    this.fastifyServer = fastify();
    Sentry.setupFastifyErrorHandler(this.fastifyServer);

In every one of my sentry issues, I do not have a "body block" telling me the body payload of the incoming HTTP request. I have the request URL, the request headers, but no body payload.

Expected Result

Having the body payload in my issues in the incoming http requests informations, along with headers , and url (of couse, I tested for POST, PUT... in which I know I have a body with data in it)

Actual Result

Image

Copy link
Member

mydea commented Apr 17, 2025

Hey, are you running your app in esm or cjs mode? Did you make sure to follow the respective docs:

My best guess for this not working would be that instrumentation is not fully correct, then we may not be able to capture the request body. Could you enable debug: true in your init call and paste the logs you get, both on app startup as well as for an incoming request that should have body data?

@tdournet
Copy link
Author

tdournet commented Apr 18, 2025

Hello, thanks for your answer.
I am running my app in cjs mode. Here is my init function :

Sentry.init({
    dsn,
    tracesSampleRate: tracesSampleRate ?? 0.1,
    release: process.env.RELEASE_NAME,
  });

This function is called before fastify is imported

I tried, as you said, to enable debug: true and here are my logs :

First, the logs for sentry initialization :

Sentry Logger [log]: Initializing Sentry: process: 18, thread: main.
Sentry Logger [log]: Integration installed: InboundFilters
Sentry Logger [log]: Integration installed: FunctionToString
Sentry Logger [log]: Integration installed: LinkedErrors
Sentry Logger [log]: Integration installed: RequestData
Sentry Logger [log]: Integration installed: Console
Sentry Logger [log]: Integration installed: Http
Sentry Logger [log]: Integration installed: NodeFetch
Sentry Logger [log]: Integration installed: OnUncaughtException
Sentry Logger [log]: Integration installed: OnUnhandledRejection
Sentry Logger [log]: Integration installed: ContextLines
Sentry Logger [log]: Integration installed: LocalVariablesAsync
Sentry Logger [log]: Integration installed: Context
Sentry Logger [log]: Integration installed: ChildProcess
Sentry Logger [log]: Integration installed: ProcessSession
Sentry Logger [log]: Integration installed: Modules
Sentry Logger [log]: Integration installed: Express
Sentry Logger [log]: Integration installed: Fastify
Sentry Logger [log]: Integration installed: Graphql
Sentry Logger [log]: Integration installed: Mongo
Sentry Logger [log]: Integration installed: Mongoose
Sentry Logger [log]: Integration installed: Mysql
Sentry Logger [log]: Integration installed: Mysql2
Sentry Logger [log]: Integration installed: Redis
Sentry Logger [log]: Integration installed: Postgres
Sentry Logger [log]: Integration installed: Hapi
Sentry Logger [log]: Integration installed: Koa
Sentry Logger [log]: Integration installed: Connect
Sentry Logger [log]: Integration installed: Tedious
Sentry Logger [log]: Integration installed: GenericPool
Sentry Logger [log]: Integration installed: Kafka
Sentry Logger [log]: Integration installed: Amqplib
Sentry Logger [log]: Integration installed: LruMemoizer
Sentry Logger [log]: Integration installed: VercelAI
Sentry Logger [log]: Running in CommonJS mode.
Sentry Logger [debug]: @opentelemetry/api: Registered a global for diag v1.9.0.
Sentry Logger [debug]: @opentelemetry/api: Registered a global for trace v1.9.0.
Sentry Logger [debug]: @opentelemetry/api: Registered a global for propagation v1.9.0.
Sentry Logger [debug]: @opentelemetry/api: Registered a global for context v1.9.0.
Sentry Logger [debug]: @opentelemetry/instrumentation-kafkajs Applying instrumentation patch for module on require hook {
  module: 'kafkajs',
  version: '2.2.4',
  baseDir: '/app/node_modules/kafkajs'
}
Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http Applying instrumentation patch for nodejs core module on require hook { module: 'http' }
Sentry Logger [debug]: @sentry/instrumentation-http Applying instrumentation patch for nodejs core module on require hook { module: 'http' }
Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http Applying instrumentation patch for nodejs core module on require hook { module: 'https' }
Sentry Logger [debug]: @sentry/instrumentation-http Applying instrumentation patch for nodejs core module on require hook { module: 'https' }
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Applying instrumentation patch for module on require hook {
  module: 'fastify',
  version: '4.28.0',
  baseDir: '/app/node_modules/fastify'
}
Sentry Logger [debug]: @opentelemetry/instrumentation-pg Applying instrumentation patch for nodejs module file on require hook {
  module: 'pg',
  version: '8.12.0',
  fileName: 'pg/lib/client.js',
  baseDir: '/app/node_modules/pg'
}
Sentry Logger [debug]: @opentelemetry/instrumentation-pg Patching pg.Client.prototype.query
Sentry Logger [debug]: @opentelemetry/instrumentation-pg Applying instrumentation patch for module on require hook {
  module: 'pg-pool',
  version: '3.6.2',
  baseDir: '/app/node_modules/pg-pool'
}
Sentry Logger [debug]: @opentelemetry/instrumentation-pg Applying instrumentation patch for module on require hook { module: 'pg', version: '8.12.0', baseDir: '/app/node_modules/pg' }
Sentry Logger [debug]: @opentelemetry/instrumentation-pg Patching pg.Client.prototype.query
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify preHandler function
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify server.addHook function
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify route.handler function
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify route.handler function
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify route.handler function
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify route.handler function
Sentry Logger [debug]: @opentelemetry/instrumentation-fastify Patching fastify route.handler function
Sentry Logger [debug]: @sentry/instrumentation-http http instrumentation for outgoing requests
Sentry Logger [debug]: Recording is off, propagating context in a non-recording span
Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http http instrumentation outgoingRequest
Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http http.ClientRequest return request
Sentry Logger [debug]: @sentry/instrumentation-http http instrumentation for outgoing requests
Sentry Logger [debug]: Recording is off, propagating context in a non-recording span

and then the logs for an endpoint on which I don't see the body of my request.

2025-04-18T13:49:23.821+02:00Sentry Logger [debug]: Recording is off, propagating context in a non-recording span
2025-04-18T13:49:23.821+02:00Sentry Logger [log]: [Tracing] Inheriting parent's sampled decision for pg.query:SELECT crm: false
2025-04-18T13:49:23.821+02:00Sentry Logger [debug]: Recording is off, propagating context in a non-recording span
2025-04-18T13:49:23.822+02:00Sentry Logger [log]: [Tracing] Inheriting parent's sampled decision for pg.query:SELECT crm: false
2025-04-18T13:49:23.822+02:00Sentry Logger [debug]: Recording is off, propagating context in a non-recording span
2025-04-18T13:49:25.461+02:00Sentry Logger [debug]: @sentry/instrumentation-http http instrumentation for outgoing requests
2025-04-18T13:49:25.461+02:00Sentry Logger [debug]: Recording is off, propagating context in a non-recording span
2025-04-18T13:49:25.461+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http https instrumentation outgoingRequest
2025-04-18T13:49:25.461+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http http.ClientRequest return request
2025-04-18T13:49:25.477+02:00Sentry Logger [debug]: @sentry/instrumentation-http http instrumentation for outgoing requests
2025-04-18T13:49:25.478+02:00Sentry Logger [debug]: Recording is off, propagating context in a non-recording span
2025-04-18T13:49:25.480+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http https instrumentation outgoingRequest
2025-04-18T13:49:25.480+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http http.ClientRequest return request
2025-04-18T13:49:25.481+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on response()
2025-04-18T13:49:25.481+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on end()
2025-04-18T13:49:25.481+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on request close()
2025-04-18T13:49:25.506+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on response()
2025-04-18T13:49:25.507+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on end()
2025-04-18T13:49:25.507+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on request close()
2025-04-18T13:49:25.716+02:00Sentry Logger [log]: Flushing client reports based on interval.
2025-04-18T13:49:25.716+02:00Sentry Logger [log]: Flushing outcomes...
2025-04-18T13:49:25.716+02:00Sentry Logger [log]: Sending outcomes: [ { reason: 'sample_rate', category: 'transaction', quantity: 42 } ]
2025-04-18T13:49:25.716+02:00Sentry Logger [debug]: @sentry/instrumentation-http http instrumentation for outgoing requests
2025-04-18T13:49:25.717+02:00Sentry Logger [debug]: Instrumentation suppressed, returning Noop Span
2025-04-18T13:49:25.717+02:00Sentry Logger [log]: [Tracing] Not injecting trace data for url because tracing is suppressed.
2025-04-18T13:49:25.720+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http https instrumentation outgoingRequest
2025-04-18T13:49:25.720+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http http.ClientRequest return request
2025-04-18T13:49:25.753+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on response()
2025-04-18T13:49:25.753+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on end()
2025-04-18T13:49:25.753+02:00Sentry Logger [debug]: @opentelemetry_sentry-patched/instrumentation-http outgoingRequest on request close()

Still can't see any body :

Image

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 18, 2025
@mydea
Copy link
Member

mydea commented Apr 22, 2025

Are you consuming the body yourself in your application? We capture request bodiues by patching request.on('data'), but we only do that if this is also listened to by the application. Can you share the code of the endpoint (simplified, if necessary) that should have the body captured?

mydea added a commit that referenced this issue Apr 22, 2025
We used to swallow most things when we try to capture request bodies of
incoming requests in node. This can make it hard to debug this.

This PR adds some log messages here:

1. Logs when we successfully patched `request.on`
2. Logs the errors that happen during patching

ref #16090
@tdournet
Copy link
Author

Hello,

Unfortunately, I won't be able to give you full visibility into the code.

I do not consume the body directly, but I have multiple hooks, middlewares, etc. It may come from this. Here is a description of my fastify instantiation.

    this.fastifyServer.setValidatorCompiler(validatorCompiler);
    this.fastifyServer.setSerializerCompiler(serializerCompiler);

    if (this.options?.docs?.generateDocs) {
      await this.fastifyServer.register(fastifySwagger, {
        ...this.options.docs.baseConfig,
        transform: jsonSchemaTransform,
      });
    }

    this.fastifyServer.withTypeProvider<ZodTypeProvider>();

    await this.fastifyServer.register(cors, {});
    if (this.options?.contentTypeParser?.urlEncoded) {
      await this.fastifyServer.register(formBodyParser);
    }

    this.fastifyServer.decorateRequest<
      | {
          id: number;
          email: string;
          roles: string[];
        }
      | undefined
    >('userAuthInfo', undefined);
    this.fastifyServer.decorateRequest<
      | {
          body?: any;
          query?: any;
          params?: any;
        }
      | undefined
    >('parsedData', undefined);
   
   [...]

    this.fastifyServer.addHook('onRequest', async (request, reply) => {
      [...]
      // Logger middleware
      // Authmiddleware
    });

    await this.fastifyServer.register(async (multipartServer) => {
      await multipartServer.register(fastifyMultipart, {
        limits: {
          fileSize: this.options?.multipartServer?.limits?.fileSize,
          files: this.options?.multipartServer?.limits?.files,
        },
      });
      this.fastifyMultipartSubServer = multipartServer;
    });

Sorry for not being able to include more details. Every POST endpoint ends up not showing any "body" section on Sentry dashboard (even the simplest endpoint with nothing in it)

Copy link
Member

mydea commented Apr 22, 2025

I totally understand, I do not expect full visibility into the code, just maybe a related snippet 🙂

We have merged a PR that will go out with the next release that adds further debug logs around this to make this easier to debug. In the meanwhile, I think we have identified the underlying problem - fastify does not use req.on('end') but req.on('close') under the hood, which we did not handle properly. We'll try to get this fixed!

@tdournet
Copy link
Author

tdournet commented Apr 23, 2025

Hello,

Thanks a lot. Let me know when the fix is released, I'll give it a try !

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 23, 2025
Copy link
Contributor

A PR closing this issue has just been released 🚀

This issue was referenced by PR #16105, which was included in the 9.14.0 release.

@tdournet
Copy link
Author

It worked ! :) (I updated @sentry/node to 9.14.0)

Thank you @mydea

onurtemizkan pushed a commit that referenced this issue Apr 24, 2025
I have a hunch that not all frameworks call `req.on('end')` but may only
do `req.on('close')` or whatever else and this is why we are not
reliably capturing bodies.

It should be safe to attach a `req.on('end')` handler, as that doesn't
change any semantics AFAIK.

Fixes #16090

---------

Co-authored-by: Francesco Gringl-Novy <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

2 participants