Skip to content

Commit 3ee7e00

Browse files
author
Luca Forstner
authored
Merge pull request #9103 from getsentry/prepare-release/7.71.0
2 parents f13e579 + 90b0c1f commit 3ee7e00

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+947
-466
lines changed

.craft.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,7 @@ targets:
176176
onlyIfPresent: /^sentry-sveltekit-\d.*\.tgz$/
177177
'npm:@sentry/opentelemetry-node':
178178
onlyIfPresent: /^sentry-opentelemetry-node-\d.*\.tgz$/
179+
'npm:@sentry/bun':
180+
onlyIfPresent: /^sentry-bun-\d.*\.tgz$/
181+
'npm:@sentry/vercel-edge':
182+
onlyIfPresent: /^sentry-vercel-edge-\d.*\.tgz$/

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
## 7.71.0
8+
9+
- feat(bun): Instrument Bun.serve (#9080)
10+
- fix(core): Ensure global event processors are always applied to event (#9064)
11+
- fix(core): Run client eventProcessors before global ones (#9032)
12+
- fix(nextjs): Use webpack module paths to attempt to resolve internal request async storage module (#9100)
13+
- fix(react): Add actual error name to boundary error name (#9065)
14+
- fix(react): Compare location against `basename`-prefixed route. (#9076)
15+
- ref(browser): Refactor browser integrations to use `processEvent` (#9022)
16+
17+
Work in this release contributed by @jorrit. Thank you for your contribution!
18+
719
## 7.70.0
820

921
### Important Changes

packages/browser/src/integrations/dedupe.ts

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Event, EventProcessor, Exception, Hub, Integration, StackFrame } from '@sentry/types';
1+
import type { Event, Exception, Integration, StackFrame } from '@sentry/types';
22
import { logger } from '@sentry/utils';
33

44
/** Deduplication filter */
@@ -22,36 +22,30 @@ export class Dedupe implements Integration {
2222
this.name = Dedupe.id;
2323
}
2424

25+
/** @inheritDoc */
26+
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
27+
// noop
28+
}
29+
2530
/**
2631
* @inheritDoc
2732
*/
28-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
29-
const eventProcessor: EventProcessor = currentEvent => {
30-
// We want to ignore any non-error type events, e.g. transactions or replays
31-
// These should never be deduped, and also not be compared against as _previousEvent.
32-
if (currentEvent.type) {
33-
return currentEvent;
34-
}
33+
public processEvent(currentEvent: Event): Event | null {
34+
// We want to ignore any non-error type events, e.g. transactions or replays
35+
// These should never be deduped, and also not be compared against as _previousEvent.
36+
if (currentEvent.type) {
37+
return currentEvent;
38+
}
3539

36-
const self = getCurrentHub().getIntegration(Dedupe);
37-
if (self) {
38-
// Juuust in case something goes wrong
39-
try {
40-
if (_shouldDropEvent(currentEvent, self._previousEvent)) {
41-
__DEBUG_BUILD__ && logger.warn('Event dropped due to being a duplicate of previously captured event.');
42-
return null;
43-
}
44-
} catch (_oO) {
45-
return (self._previousEvent = currentEvent);
46-
}
47-
48-
return (self._previousEvent = currentEvent);
40+
// Juuust in case something goes wrong
41+
try {
42+
if (_shouldDropEvent(currentEvent, this._previousEvent)) {
43+
__DEBUG_BUILD__ && logger.warn('Event dropped due to being a duplicate of previously captured event.');
44+
return null;
4945
}
50-
return currentEvent;
51-
};
46+
} catch (_oO) {} // eslint-disable-line no-empty
5247

53-
eventProcessor.id = this.name;
54-
addGlobalEventProcessor(eventProcessor);
48+
return (this._previousEvent = currentEvent);
5549
}
5650
}
5751

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core';
21
import type { Event, Integration } from '@sentry/types';
32

43
import { WINDOW } from '../helpers';
@@ -23,28 +22,28 @@ export class HttpContext implements Integration {
2322
* @inheritDoc
2423
*/
2524
public setupOnce(): void {
26-
addGlobalEventProcessor((event: Event) => {
27-
if (getCurrentHub().getIntegration(HttpContext)) {
28-
// if none of the information we want exists, don't bother
29-
if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {
30-
return event;
31-
}
32-
33-
// grab as much info as exists and add it to the event
34-
const url = (event.request && event.request.url) || (WINDOW.location && WINDOW.location.href);
35-
const { referrer } = WINDOW.document || {};
36-
const { userAgent } = WINDOW.navigator || {};
37-
38-
const headers = {
39-
...(event.request && event.request.headers),
40-
...(referrer && { Referer: referrer }),
41-
...(userAgent && { 'User-Agent': userAgent }),
42-
};
43-
const request = { ...event.request, ...(url && { url }), headers };
44-
45-
return { ...event, request };
46-
}
47-
return event;
48-
});
25+
// noop
26+
}
27+
28+
/** @inheritDoc */
29+
public preprocessEvent(event: Event): void {
30+
// if none of the information we want exists, don't bother
31+
if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {
32+
return;
33+
}
34+
35+
// grab as much info as exists and add it to the event
36+
const url = (event.request && event.request.url) || (WINDOW.location && WINDOW.location.href);
37+
const { referrer } = WINDOW.document || {};
38+
const { userAgent } = WINDOW.navigator || {};
39+
40+
const headers = {
41+
...(event.request && event.request.headers),
42+
...(referrer && { Referer: referrer }),
43+
...(userAgent && { 'User-Agent': userAgent }),
44+
};
45+
const request = { ...event.request, ...(url && { url }), headers };
46+
47+
event.request = request;
4948
}
5049
}

packages/browser/src/profiling/integration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class BrowserProfilingIntegration implements Integration {
3535
/**
3636
* @inheritDoc
3737
*/
38-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
38+
public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
3939
this.getCurrentHub = getCurrentHub;
4040
const client = this.getCurrentHub().getClient() as BrowserClient;
4141

packages/bun/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ To use this SDK, call `init(options)` as early as possible in the main entry mod
2323
hook into the environment. Note that you can turn off almost all side effects using the respective options.
2424

2525
```javascript
26-
// ES5 Syntax
26+
// CJS Syntax
2727
const Sentry = require('@sentry/bun');
28-
// ES6 Syntax
28+
// ESM Syntax
2929
import * as Sentry from '@sentry/bun';
3030

3131
Sentry.init({

packages/bun/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@ export { defaultIntegrations, init } from './sdk';
6969
import { Integrations as CoreIntegrations } from '@sentry/core';
7070
import { Integrations as NodeIntegrations } from '@sentry/node';
7171

72+
import * as BunIntegrations from './integrations';
73+
7274
const INTEGRATIONS = {
7375
...CoreIntegrations,
7476
...NodeIntegrations,
77+
...BunIntegrations,
7578
};
7679

7780
export { INTEGRATIONS as Integrations };
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { captureException, getCurrentHub, runWithAsyncContext, startSpan, Transaction } from '@sentry/core';
2+
import type { Integration } from '@sentry/types';
3+
import { addExceptionMechanism, getSanitizedUrlString, parseUrl, tracingContextFromHeaders } from '@sentry/utils';
4+
5+
function sendErrorToSentry(e: unknown): unknown {
6+
captureException(e, scope => {
7+
scope.addEventProcessor(event => {
8+
addExceptionMechanism(event, {
9+
type: 'bun',
10+
handled: false,
11+
data: {
12+
function: 'serve',
13+
},
14+
});
15+
return event;
16+
});
17+
18+
return scope;
19+
});
20+
21+
return e;
22+
}
23+
24+
/**
25+
* Instruments `Bun.serve` to automatically create transactions and capture errors.
26+
*/
27+
export class BunServer implements Integration {
28+
/**
29+
* @inheritDoc
30+
*/
31+
public static id: string = 'BunServer';
32+
33+
/**
34+
* @inheritDoc
35+
*/
36+
public name: string = BunServer.id;
37+
38+
/**
39+
* @inheritDoc
40+
*/
41+
public setupOnce(): void {
42+
instrumentBunServe();
43+
}
44+
}
45+
46+
/**
47+
* Instruments Bun.serve by patching it's options.
48+
*/
49+
export function instrumentBunServe(): void {
50+
Bun.serve = new Proxy(Bun.serve, {
51+
apply(serveTarget, serveThisArg, serveArgs: Parameters<typeof Bun.serve>) {
52+
instrumentBunServeOptions(serveArgs[0]);
53+
return serveTarget.apply(serveThisArg, serveArgs);
54+
},
55+
});
56+
}
57+
58+
/**
59+
* Instruments Bun.serve `fetch` option to automatically create spans and capture errors.
60+
*/
61+
function instrumentBunServeOptions(serveOptions: Parameters<typeof Bun.serve>[0]): void {
62+
serveOptions.fetch = new Proxy(serveOptions.fetch, {
63+
apply(fetchTarget, fetchThisArg, fetchArgs: Parameters<typeof serveOptions.fetch>) {
64+
return runWithAsyncContext(() => {
65+
const hub = getCurrentHub();
66+
67+
const request = fetchArgs[0];
68+
const upperCaseMethod = request.method.toUpperCase();
69+
if (upperCaseMethod === 'OPTIONS' || upperCaseMethod === 'HEAD') {
70+
return fetchTarget.apply(fetchThisArg, fetchArgs);
71+
}
72+
73+
const sentryTrace = request.headers.get('sentry-trace') || '';
74+
const baggage = request.headers.get('baggage');
75+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
76+
sentryTrace,
77+
baggage,
78+
);
79+
hub.getScope().setPropagationContext(propagationContext);
80+
81+
const parsedUrl = parseUrl(request.url);
82+
const data: Record<string, unknown> = {
83+
'http.request.method': request.method || 'GET',
84+
};
85+
if (parsedUrl.search) {
86+
data['http.query'] = parsedUrl.search;
87+
}
88+
89+
const url = getSanitizedUrlString(parsedUrl);
90+
return startSpan(
91+
{
92+
op: 'http.server',
93+
name: `${request.method} ${parsedUrl.path || '/'}`,
94+
origin: 'auto.http.bun.serve',
95+
...traceparentData,
96+
data,
97+
metadata: {
98+
source: 'url',
99+
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
100+
request: {
101+
url,
102+
method: request.method,
103+
headers: request.headers.toJSON(),
104+
},
105+
},
106+
},
107+
async span => {
108+
try {
109+
const response = await (fetchTarget.apply(fetchThisArg, fetchArgs) as ReturnType<
110+
typeof serveOptions.fetch
111+
>);
112+
if (response && response.status) {
113+
span?.setHttpStatus(response.status);
114+
span?.setData('http.response.status_code', response.status);
115+
if (span instanceof Transaction) {
116+
span.setContext('response', {
117+
headers: response.headers.toJSON(),
118+
status_code: response.status,
119+
});
120+
}
121+
}
122+
return response;
123+
} catch (e) {
124+
sendErrorToSentry(e);
125+
throw e;
126+
}
127+
},
128+
);
129+
});
130+
},
131+
});
132+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { BunServer } from './bunserver';

packages/bun/src/sdk.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Integrations as CoreIntegrations } from '@sentry/core';
33
import { init as initNode, Integrations as NodeIntegrations } from '@sentry/node';
44

55
import { BunClient } from './client';
6+
import { BunServer } from './integrations';
67
import { makeFetchTransport } from './transports';
78
import type { BunOptions } from './types';
89

@@ -25,6 +26,8 @@ export const defaultIntegrations = [
2526
new NodeIntegrations.RequestData(),
2627
// Misc
2728
new NodeIntegrations.LinkedErrors(),
29+
// Bun Specific
30+
new BunServer(),
2831
];
2932

3033
/**

packages/bun/test/helpers.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { createTransport } from '@sentry/core';
2+
import { resolvedSyncPromise } from '@sentry/utils';
3+
4+
import type { BunClientOptions } from '../src/types';
5+
6+
export function getDefaultBunClientOptions(options: Partial<BunClientOptions> = {}): BunClientOptions {
7+
return {
8+
integrations: [],
9+
transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})),
10+
stackParser: () => [],
11+
instrumenter: 'sentry',
12+
...options,
13+
};
14+
}

0 commit comments

Comments
 (0)