Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
},
});
```

### Deno `node:http` server requests are tracked as sessions

Expand Down Expand Up @@ -858,7 +865,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