Skip to content

Commit

Permalink
Merge pull request #12073 from getsentry/prepare-release/8.2.0
Browse files Browse the repository at this point in the history
meta(changelog): Update changelog for v8.2.0
  • Loading branch information
mydea committed May 16, 2024
2 parents e94e551 + 46511e5 commit 0ef0c7f
Show file tree
Hide file tree
Showing 39 changed files with 1,029 additions and 59 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ jobs:
'create-remix-app-express-vite-dev',
'debug-id-sourcemaps',
'node-express-esm-loader',
'node-express-esm-without-loader',
'nextjs-app-dir',
'nextjs-14',
'react-create-hash-router',
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@

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

## 8.2.0

- feat(redis-cache): Create cache-span with prefixed keys (get/set commands) (#12070)
- feat(core): Add `beforeSendSpan` hook (#11886)
- feat(browser): Improve idle span handling (#12065)
- fix(node): Set transactionName for unsampled spans in httpIntegration (#12071)
- fix(core): Export Scope interface as `Scope` (#12067)
- fix(core): Avoid looking up client for `hasTracingEnabled()` if possible (#12066)
- fix(browser): Use consistent timestamps (#12063)
- fix(node): Fix check for performance integrations (#12043)
- ref(sveltekit): Warn to delete source maps if Sentry plugin enabled source maps generation (#12072)

## 8.1.0

This release mainly fixes a couple of bugs from the initial [8.0.0 release](#800). In addition to the changes below, we
Expand Down
32 changes: 32 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,38 @@ The `BrowserTracing` integration, together with the custom routing instrumentati
Instead, you should use `Sentry.browserTracingIntegration()`. See examples
[below](./MIGRATION.md#deprecated-browsertracing-integration)

#### Removal of `interactionsSampleRate` in `browserTracingIntegration` options

The `interactionsSampleRate` option that could be passed to `browserTracingIntegration` or `new BrowserTracing()` was
removed in v8, due to the option being redundant and in favour of bundle size minimization.

It's important to note that this sample rate only ever was applied when collecting INP (Interaction To Next Paint)
values. You most likely don't need to replace this option. Furthermore, INP values are already sampled by the
[`tracesSampleRate` SDK option](https://docs.sentry.io/platforms/javascript/configuration/options/#traces-sampler), like
any regular span. At the time of writing, INP value collection does not deplete your span or transaction quota.

If you used `interactionsSampleRate` before, and still want to reduce INP value collection, we recommend using the
`tracesSampler` SDK option instead:

```javascript
// v7
Sentry.init({
integrations: [new BrowserTracing({ interactionsSampleRate: 0.1 })],
});
```

```javascript
// v8 - please read the text above, you most likely don't need this :)
Sentry.init({
tracesSampler: (ctx) => {
if (ctx.attributes?['sentry.op']?.startsWith('ui.interaction')) {
return 0.1;
}
return 0.5;
}
})
```
#### Removal of the `Offline` integration
The `Offline` integration has been removed in favor of the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@sentry:registry=http://127.0.0.1:4873
@sentry-internal:registry=http://127.0.0.1:4873
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "node-express-esm-without-loader",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node src/app.mjs",
"clean": "npx rimraf node_modules pnpm-lock.yaml",
"test:build": "pnpm install",
"test:assert": "playwright test"
},
"dependencies": {
"@sentry/node": "latest || *",
"@sentry/opentelemetry": "latest || *",
"express": "4.19.2"
},
"devDependencies": {
"@sentry-internal/event-proxy-server": "link:../../../event-proxy-server",
"@playwright/test": "^1.27.1"
},
"volta": {
"extends": "../../package.json",
"node": "18.19.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';

// Fix urls not resolving to localhost on Node v17+
// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575
import { setDefaultResultOrder } from 'dns';
setDefaultResultOrder('ipv4first');

const eventProxyPort = 3031;
const expressPort = 3030;

/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: './tests',
/* Maximum time one test can run for. */
timeout: 150_000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000,
},
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: 0,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'list',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,

/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: `http://localhost:${expressPort}`,
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
],

/* Run your local dev server before starting the tests */
webServer: [
{
command: 'node start-event-proxy.mjs',
port: eventProxyPort,
stdout: 'pipe',
stderr: 'pipe',
},
{
command: 'pnpm start',
port: expressPort,
stdout: 'pipe',
stderr: 'pipe',
},
],
};

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import './instrument.mjs';

// Below other imports
import * as Sentry from '@sentry/node';
import express from 'express';

const app = express();
const port = 3030;

app.get('/test-success', function (req, res) {
setTimeout(() => {
res.status(200).end();
}, 100);
});

app.get('/test-params/:param', function (req, res) {
const { param } = req.params;
Sentry.setTag(`param-${param}`, 'yes');
Sentry.captureException(new Error(`Error for param ${param}`));

setTimeout(() => {
res.status(200).end();
}, 100);
});

app.get('/test-error', function (req, res) {
Sentry.captureException(new Error('This is an error'));
setTimeout(() => {
Sentry.flush(2000).then(() => {
res.status(200).end();
});
}, 100);
});

Sentry.setupExpressErrorHandler(app);

app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
// and optionally displayed to the user for support.
res.statusCode = 500;
res.end(res.sentry + '\n');
});

app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as Sentry from '@sentry/node';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { startEventProxyServer } from '@sentry-internal/event-proxy-server';

startEventProxyServer({
port: 3031,
proxyServerName: 'node-express-esm-without-loader',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect, test } from '@playwright/test';
import { waitForError } from '@sentry-internal/event-proxy-server';

test('Should record exceptions captured inside handlers', async ({ request }) => {
const errorEventPromise = waitForError('node-express-esm-without-loader', errorEvent => {
return !!errorEvent?.exception?.values?.[0]?.value?.includes('This is an error');
});

await request.get('/test-error');

await expect(errorEventPromise).resolves.toBeDefined();
});

test('Isolates requests', async ({ request }) => {
const errorEventPromise = waitForError('node-express-esm-without-loader', errorEvent => {
return !!errorEvent?.exception?.values?.[0]?.value?.includes('Error for param 1');
});

const errorEventPromise2 = waitForError('node-express-esm-without-loader', errorEvent => {
return !!errorEvent?.exception?.values?.[0]?.value?.includes('Error for param 2');
});

await request.get('/test-params/1');
await request.get('/test-params/2');

const errorEvent1 = await errorEventPromise;
const errorEvent2 = await errorEventPromise2;

expect(errorEvent1.tags).toEqual({ 'param-1': 'yes' });
expect(errorEvent2.tags).toEqual({ 'param-2': 'yes' });

expect(errorEvent1.transaction).toBe('GET /test-params/1');
expect(errorEvent2.transaction).toBe('GET /test-params/2');
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ app.get('/test/isolationScope/:id', (req, res) => {
const id = req.params.id;
Sentry.setTag('isolation-scope', 'tag');
Sentry.setTag(`isolation-scope-${id}`, id);
Sentry.setTag('isolation-scope-transactionName', `${Sentry.getIsolationScope().getScopeData().transactionName}`);

Sentry.captureException(new Error('This is an exception'));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ test('correctly applies isolation scope even without tracing', done => {
.ignore('session', 'sessions')
.expect({
event: {
transaction: 'GET /test/isolationScope/1',
tags: {
global: 'tag',
'isolation-scope': 'tag',
'isolation-scope-1': '1',
// We can't properly test non-existance of fields here, so we cast this to a string to test it here
'isolation-scope-transactionName': 'undefined',
},
// Request is correctly set
request: {
Expand All @@ -27,12 +26,11 @@ test('correctly applies isolation scope even without tracing', done => {
})
.expect({
event: {
transaction: 'GET /test/isolationScope/2',
tags: {
global: 'tag',
'isolation-scope': 'tag',
'isolation-scope-2': '2',
// We can't properly test non-existance of fields here, so we cast this to a string to test it here
'isolation-scope-transactionName': 'undefined',
},
// Request is correctly set
request: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: '3.9'

services:
db:
image: redis:latest
restart: always
container_name: integration-tests-redis
ports:
- '6379:6379'
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
const Sentry = require('@sentry/node');

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
tracesSampleRate: 1.0,
transport: loggingTransport,
integrations: [Sentry.redisIntegration({ cachePrefixes: ['ioredis-cache:'] })],
});

// Stop the process from exiting before the transaction is sent
setInterval(() => {}, 1000);

const Redis = require('ioredis');

const redis = new Redis({ port: 6379 });

async function run() {
await Sentry.startSpan(
{
name: 'Test Span',
op: 'test-span',
},
async () => {
try {
await redis.set('test-key', 'test-value');
await redis.set('ioredis-cache:test-key', 'test-value');

await redis.get('test-key');
await redis.get('ioredis-cache:test-key');
await redis.get('ioredis-cache:unavailable-data');
} finally {
await redis.disconnect();
}
},
);
}

// eslint-disable-next-line @typescript-eslint/no-floating-promises
run();

0 comments on commit 0ef0c7f

Please sign in to comment.