Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Sentry.init({
transport: loggingTransport,
integrations: [
Sentry.httpIntegration({
dropSpansForIncomingRequestStatusCodes: [499, [300, 399]],
ignoreStatusCodes: [499, [300, 399]],
}),
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Sentry.init({
transport: loggingTransport,
integrations: [
Sentry.httpIntegration({
ignoreIncomingRequestBody: url => {
ignoreRequestBody: url => {
if (url.includes('/test-post-ignore-body')) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ Sentry.init({
release: '1.0',
tracesSampleRate: 1.0,
transport: loggingTransport,
integrations: [Sentry.httpIntegration({ maxIncomingRequestBodySize: 'always' })],
integrations: [Sentry.httpIntegration({ maxRequestBodySize: 'always' })],
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ Sentry.init({
release: '1.0',
tracesSampleRate: 1.0,
transport: loggingTransport,
integrations: [Sentry.httpIntegration({ maxIncomingRequestBodySize: 'medium' })],
integrations: [Sentry.httpIntegration({ maxRequestBodySize: 'medium' })],
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Sentry.init({
transport: loggingTransport,
integrations: [
Sentry.httpIntegration({
maxIncomingRequestBodySize: 'none',
ignoreIncomingRequestBody: url => url.includes('/ignore-request-body'),
maxRequestBodySize: 'none',
ignoreRequestBody: url => url.includes('/ignore-request-body'),
}),
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ Sentry.init({
tracesSampleRate: 1.0,
transport: loggingTransport,
dataCollection: { httpBodies: [] },
integrations: [Sentry.httpIntegration({ maxIncomingRequestBodySize: 'small' })],
integrations: [Sentry.httpIntegration({ maxRequestBodySize: 'small' })],
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const MAX_GENERAL = 1024 * 1024; // 1MB
const MAX_MEDIUM = 10_000;
const MAX_SMALL = 1000;

describe('express with httpIntegration and not defined maxIncomingRequestBodySize', () => {
describe('express with httpIntegration and not defined maxRequestBodySize', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down Expand Up @@ -60,7 +60,7 @@ describe('express with httpIntegration and not defined maxIncomingRequestBodySiz
});
});

describe('express with httpIntegration, disabled httpBodies, and explicit maxIncomingRequestBodySize', () => {
describe('express with httpIntegration, disabled httpBodies, and explicit maxRequestBodySize', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down Expand Up @@ -88,7 +88,7 @@ describe('express with httpIntegration, disabled httpBodies, and explicit maxInc
});
});

describe('express with httpIntegration and maxIncomingRequestBodySize: "none"', () => {
describe('express with httpIntegration and maxRequestBodySize: "none"', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand All @@ -114,7 +114,7 @@ describe('express with httpIntegration and maxIncomingRequestBodySize: "none"',
await runner.completed();
});

test('does not capture any request bodies with "none" setting and "ignoreIncomingRequestBody"', async () => {
test('does not capture any request bodies with "none" setting and "ignoreRequestBody"', async () => {
const runner = createRunner()
.expect({
transaction: {
Expand Down Expand Up @@ -149,7 +149,7 @@ describe('express with httpIntegration and maxIncomingRequestBodySize: "none"',
});
});

describe('express with httpIntegration and maxIncomingRequestBodySize: "always"', () => {
describe('express with httpIntegration and maxRequestBodySize: "always"', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down Expand Up @@ -197,7 +197,7 @@ describe('express with httpIntegration and maxIncomingRequestBodySize: "always"'
});
});

describe('express with httpIntegration and maxIncomingRequestBodySize: "small"', () => {
describe('express with httpIntegration and maxRequestBodySize: "small"', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down Expand Up @@ -266,7 +266,7 @@ describe('express with httpIntegration and maxIncomingRequestBodySize: "small"',
});
});

describe('express with httpIntegration and maxIncomingRequestBodySize: "medium"', () => {
describe('express with httpIntegration and maxRequestBodySize: "medium"', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Sentry.init({

integrations: [
Sentry.httpIntegration({
incomingRequestSpanHook: (span, req, res) => {
span.setAttribute('incomingRequestSpanHook', 'yes');
Sentry.setExtra('incomingRequestSpanHookCalled', {
onSpanCreated: (span, req, res) => {
span.setAttribute('onSpanCreated', 'yes');
Sentry.setExtra('onSpanCreatedCalled', {
reqUrl: req.url,
reqMethod: req.method,
resUrl: res.req.url,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
const Sentry = require('@sentry/node');

const url = process.env.SERVER_URL;

Sentry.init({
traceLifecycle: 'static',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
tracesSampleRate: 1.0,
transport: loggingTransport,

integrations: [
Sentry.httpIntegration({
// Each hook derives its attribute from the objects it is handed, so a hook that fires with
// the wrong span, request or response fails the assertion rather than passing silently.
outgoingRequestHook: (span, request) => {
span.setAttribute('outgoingRequestHook', request.method);
},
outgoingResponseHook: (span, response) => {
span.setAttribute('outgoingResponseHook', response.statusCode);
},
outgoingRequestApplyCustomAttributes: (span, request, response) => {
span.setAttribute('outgoingRequestApplyCustomAttributes', `${request.method} ${response.statusCode}`);
},
}),
],
});

const http = require('http');

// express must be required after Sentry is initialized
const express = require('express');
const cors = require('cors');
const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');

const app = express();

app.use(cors());

app.get('/testOutgoing', (_req, response) => {
makeHttpRequest(`${url}/api/users/42`).then(() => {
response.send({ response: 'done' });
});
});

Sentry.setupExpressErrorHandler(app);

startExpressServerAndSendPortToRunner(app);

function makeHttpRequest(url) {
return new Promise((resolve, reject) => {
http
.get(url, res => {
res.on('data', () => {});
res.on('end', () => {
resolve();
});
})
.on('error', error => {
reject(error);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ describe('httpIntegration', () => {
cleanupChildProcesses();
});

describe('instrumentation options', () => {
describe('onSpanCreated option', () => {
createEsmAndCjsTests(__dirname, 'server.mjs', 'instrument-options.mjs', (createRunner, test) => {
test('allows to configure incomingRequestSpanHook', async () => {
test('allows to configure onSpanCreated', async () => {
const runner = createRunner()
.expect({
transaction: {
Expand All @@ -33,14 +33,14 @@ describe('httpIntegration', () => {
data: {
'url.full': expect.stringMatching(/\/test$/),
'http.response.status_code': 200,
incomingRequestSpanHook: 'yes',
onSpanCreated: 'yes',
},
op: 'http.server',
status: 'ok',
},
},
extra: expect.objectContaining({
incomingRequestSpanHookCalled: {
onSpanCreatedCalled: {
reqUrl: expect.stringMatching(/\/test$/),
reqMethod: 'GET',
resUrl: expect.stringMatching(/\/test$/),
Expand All @@ -56,6 +56,33 @@ describe('httpIntegration', () => {
});
});

describe('outgoing request span hooks', () => {
test('runs outgoingRequestHook, outgoingResponseHook and outgoingRequestApplyCustomAttributes', async () => {
const [SERVER_URL, closeTestServer] = await createTestServer()
.get('/api/users/42', () => {}, 200)
.start();

const runner = createRunner(__dirname, 'server-outgoingHooks.js')
.withEnv({ SERVER_URL })
.expect({
transaction: event => {
const clientSpans = event.spans?.filter(span => span.op === 'http.client');
expect(clientSpans).toHaveLength(1);

// All three hooks run before the span ends, so every attribute has to survive to the envelope.
const data = clientSpans![0]?.data;
expect(data?.['outgoingRequestHook']).toBe('GET');
expect(data?.['outgoingResponseHook']).toBe(200);
expect(data?.['outgoingRequestApplyCustomAttributes']).toBe('GET 200');
},
})
.start();
runner.makeRequest('get', '/testOutgoing');
await runner.completed();
closeTestServer();
});
});

describe('http.server spans', () => {
createEsmAndCjsTests(__dirname, 'server.mjs', 'instrument.mjs', (createRunner, test) => {
test('captures correct attributes for GET requests', async () => {
Expand Down
68 changes: 61 additions & 7 deletions docs/migration/v11-end-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,11 +516,13 @@ Two consequences to be aware of when upgrading:
- **Issue grouping:** Grouping in Sentry differs for events with and without stack traces, so you may see new issue groups after upgrading.
- **Release health:** Events with a stack trace are counted as errors, so a `captureMessage` call (including messages emitted by `captureConsoleIntegration`) now marks the current session as _errored_. This affects errored-session counts but does **not** mark sessions as crashed, so crash-free session rate is unaffected. If you use `captureMessage` for purely informational output, consider using Sentry Logs instead, which is better suited and does not affect release health.

### Incoming HTTP span hooks moved to `incomingRequestSpanHook`
### Incoming HTTP span hooks moved to `onSpanCreated`

Affected SDKs: `@sentry/node` and dependents.

The deprecated `httpIntegration` / `httpServerSpansIntegration` hooks `instrumentation.requestHook`, `instrumentation.responseHook`, and `instrumentation.applyCustomAttributesOnSpan` no longer run for incoming request spans. Use `incomingRequestSpanHook` (on `httpIntegration`) or `onSpanCreated` (on `httpServerSpansIntegration`) instead:
The deprecated `httpIntegration` / `httpServerSpansIntegration` hooks `instrumentation.requestHook`, `instrumentation.responseHook`, and `instrumentation.applyCustomAttributesOnSpan` no longer run for incoming request spans. Use `onSpanCreated` instead. For outgoing request spans, `httpIntegration` has `outgoingRequestHook`, `outgoingResponseHook`, and `outgoingRequestApplyCustomAttributes`.

In v10 these hooks ran for both directions, so which replacement you want depends on which spans your hook was mutating:

```js
// before
Expand All @@ -532,15 +534,20 @@ Sentry.httpIntegration({
},
});

// after
// after — incoming (server) spans
Sentry.httpIntegration({
incomingRequestSpanHook: (span, req, res) => {
onSpanCreated: (span, req, res) => {
span.setAttribute('custom', true);
},
});
```

`httpIntegration`'s `instrumentation` option is still honored for **outgoing** request spans.
// after — outgoing (client) spans
Sentry.httpIntegration({
outgoingRequestHook: (span, req) => {
span.setAttribute('custom', true);
},
});
```

### Node HTTP transport `keepAlive` defaults to `true`

Expand Down Expand Up @@ -843,7 +850,54 @@ Sentry.init({
- (Express) The deprecated `patchExpressModule(options)` signature was removed. Use `patchExpressModule(moduleExports, getOptions)` instead.
- The `@sentry/node-core/light/otlp` entry point was removed, along with its optional `@opentelemetry/exporter-trace-otlp-http` peer dependency. `otlpIntegration` is now exported directly from every server-side SDK, so `Sentry.otlpIntegration()` needs no extra import or install.
- The `otlpIntegration` options `setupOtlpTracesExporter` and `collectorUrl` were removed, and the integration no longer sets up a span exporter, span processor, or tracer provider. Configure your own exporter and point it at `Sentry.getOtlpTracesEndpoint(dsn)`, or at your collector's URL if you route through one. See [Connecting Sentry to your OpenTelemetry traces](#connecting-sentry-to-your-opentelemetry-traces).
- The deprecated `httpServerSpansIntegration` `instrumentation.{requestHook,responseHook,applyCustomAttributesOnSpan}` option was removed. Use `onSpanCreated`, or `httpIntegration({ incomingRequestSpanHook })`, to mutate incoming request spans.
- The deprecated `httpServerSpansIntegration` `instrumentation.{requestHook,responseHook,applyCustomAttributesOnSpan}` option was removed. Use `onSpanCreated` instead. `httpServerSpansIntegration` only covers incoming requests; the outgoing hooks (`outgoingRequestHook`, `outgoingResponseHook`, `outgoingRequestApplyCustomAttributes`) are on `httpIntegration`.

#### `httpIntegration` options were consolidated

`httpIntegration` option names now match `httpServerIntegration` / `httpServerSpansIntegration` and the other server SDKs. The deprecated `instrumentation` hooks were removed.

| Removed option | Replacement |
| --------------------------------------------- | ------------------------------------------------------------------------------- |
| `trackIncomingRequestsAsSessions` | `sessions` |
| `maxIncomingRequestBodySize` | `maxRequestBodySize` |
| `ignoreIncomingRequestBody` | `ignoreRequestBody` |
| `dropSpansForIncomingRequestStatusCodes` | `ignoreStatusCodes` |
| `incomingRequestSpanHook` | `onSpanCreated` |
| `instrumentation.requestHook` | `onSpanCreated` (incoming) or `outgoingRequestHook` (outgoing) |
| `instrumentation.responseHook` | `onSpanCreated` (incoming) or `outgoingResponseHook` (outgoing) |
| `instrumentation.applyCustomAttributesOnSpan` | `onSpanCreated` (incoming) or `outgoingRequestApplyCustomAttributes` (outgoing) |

```js
// before
Sentry.httpIntegration({
trackIncomingRequestsAsSessions: false,
maxIncomingRequestBodySize: 'small',
ignoreIncomingRequestBody: url => url.includes('/health'),
dropSpansForIncomingRequestStatusCodes: [404],
incomingRequestSpanHook: (span, req, res) => {
span.setAttribute('custom', true);
},
instrumentation: {
responseHook: () => {
void flushIfServerless();
},
},
});

// after
Sentry.httpIntegration({
sessions: false,
maxRequestBodySize: 'small',
ignoreRequestBody: url => url.includes('/health'),
ignoreStatusCodes: [404],
onSpanCreated: (span, req, res) => {
span.setAttribute('custom', true);
},
outgoingResponseHook: () => {
void flushIfServerless();
},
});
```

### `@sentry/cloudflare`

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/integrations/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export interface HttpInstrumentationOptions {
sessions?: boolean;

/**
* Number of milliseconds until sessions tracked with `trackIncomingRequestsAsSessions` will be flushed as a session aggregate.
* Number of milliseconds until sessions tracked with `sessions` will be flushed as a session aggregate.
*
* Defaults to `60000` (60s).
*/
Expand Down
Loading
Loading