Skip to content

Commit 6cbcecc

Browse files
committed
feat(remix): Update remix SDK to be OTEL-powered
1 parent 0f1ea2f commit 6cbcecc

File tree

22 files changed

+132
-89
lines changed

22 files changed

+132
-89
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ jobs:
871871
yarn test
872872
873873
job_remix_integration_tests:
874-
name: Remix v${{ matrix.remix }} (Node ${{ matrix.node }}) ${{ matrix.tracingIntegration && 'TracingIntegration'}} Tests
874+
name: Remix v${{ matrix.remix }} (Node ${{ matrix.node }}) Tests
875875
needs: [job_get_metadata, job_build]
876876
if: needs.job_get_metadata.outputs.changed_remix == 'true' || github.event_name != 'pull_request'
877877
runs-on: ubuntu-20.04

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/app/entry.server.tsx

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1+
import * as Sentry from '@sentry/remix';
2+
3+
Sentry.init({
4+
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
5+
environment: 'qa', // dynamic sampling bias to keep transactions
6+
dsn: process.env.E2E_TEST_DSN,
7+
tunnel: 'http://localhost:3031/', // proxy server
8+
});
9+
110
import { PassThrough } from 'node:stream';
211

312
import type { AppLoadContext, EntryContext } from '@remix-run/node';
413
import { createReadableStreamFromReadable } from '@remix-run/node';
514
import { installGlobals } from '@remix-run/node';
615
import { RemixServer } from '@remix-run/react';
7-
import * as Sentry from '@sentry/remix';
816
import * as isbotModule from 'isbot';
917
import { renderToPipeableStream } from 'react-dom/server';
1018

1119
installGlobals();
1220

1321
const ABORT_DELAY = 5_000;
1422

15-
Sentry.init({
16-
environment: 'qa', // dynamic sampling bias to keep transactions
17-
dsn: process.env.E2E_TEST_DSN,
18-
// Performance Monitoring
19-
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
20-
tunnel: 'http://localhost:3031/', // proxy server
21-
});
22-
2323
export const handleError = Sentry.wrapRemixHandleError;
2424

2525
export default function handleRequest(

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/tests/behaviour-server.test.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page
77
// We use this to identify the transactions
88
const testTag = uuid4();
99

10-
// no server span here!
10+
const httpServerTransactionPromise = waitForTransaction('create-remix-app-express-vite-dev', transactionEvent => {
11+
return (
12+
transactionEvent.type === 'transaction' &&
13+
transactionEvent.contexts?.trace?.op === 'http.server' &&
14+
transactionEvent.tags?.['sentry_test'] === testTag
15+
);
16+
});
1117

1218
const pageLoadTransactionPromise = waitForTransaction('create-remix-app-express-vite-dev', transactionEvent => {
1319
return (
@@ -20,16 +26,25 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page
2026
page.goto(`/?tag=${testTag}`);
2127

2228
const pageloadTransaction = await pageLoadTransactionPromise;
29+
const httpServerTransaction = await httpServerTransactionPromise;
2330

2431
expect(pageloadTransaction).toBeDefined();
32+
expect(httpServerTransaction).toBeDefined();
33+
34+
const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id;
35+
const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id;
2536

2637
const pageLoadTraceId = pageloadTransaction.contexts?.trace?.trace_id;
2738
const pageLoadSpanId = pageloadTransaction.contexts?.trace?.span_id;
2839
const pageLoadParentSpanId = pageloadTransaction.contexts?.trace?.parent_span_id;
2940

41+
expect(httpServerTransaction.transaction).toBe('routes/_index');
3042
expect(pageloadTransaction.transaction).toBe('routes/_index');
3143

32-
expect(pageLoadTraceId).toBeDefined();
33-
expect(pageLoadParentSpanId).toBeUndefined();
34-
expect(pageLoadSpanId).toBeDefined();
44+
expect(httpServerTraceId).toBeDefined();
45+
expect(httpServerSpanId).toBeDefined();
46+
47+
expect(pageLoadTraceId).toEqual(httpServerTraceId);
48+
expect(pageLoadParentSpanId).toEqual(httpServerSpanId);
49+
expect(pageLoadSpanId).not.toEqual(httpServerSpanId);
3550
});

dev-packages/e2e-tests/test-applications/create-remix-app-v2/app/entry.server.tsx

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import * as Sentry from '@sentry/remix';
2+
3+
Sentry.init({
4+
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
5+
environment: 'qa', // dynamic sampling bias to keep transactions
6+
dsn: process.env.E2E_TEST_DSN,
7+
tunnel: 'http://localhost:3031/', // proxy server
8+
});
9+
110
/**
211
* By default, Remix will handle generating the HTTP Response for you.
312
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
@@ -10,22 +19,13 @@ import type { AppLoadContext, EntryContext } from '@remix-run/node';
1019
import { createReadableStreamFromReadable } from '@remix-run/node';
1120
import { installGlobals } from '@remix-run/node';
1221
import { RemixServer } from '@remix-run/react';
13-
import * as Sentry from '@sentry/remix';
1422
import isbot from 'isbot';
1523
import { renderToPipeableStream } from 'react-dom/server';
1624

1725
installGlobals();
1826

1927
const ABORT_DELAY = 5_000;
2028

21-
Sentry.init({
22-
environment: 'qa', // dynamic sampling bias to keep transactions
23-
dsn: process.env.E2E_TEST_DSN,
24-
// Performance Monitoring
25-
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
26-
tunnel: 'http://localhost:3031/', // proxy server
27-
});
28-
2929
export const handleError = Sentry.wrapRemixHandleError;
3030

3131
export default function handleRequest(

dev-packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import * as Sentry from '@sentry/remix';
2+
3+
Sentry.init({
4+
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
5+
environment: 'qa', // dynamic sampling bias to keep transactions
6+
dsn: process.env.E2E_TEST_DSN,
7+
tunnel: 'http://localhost:3031/', // proxy server
8+
});
9+
110
/**
211
* By default, Remix will handle generating the HTTP Response for you.
312
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
@@ -9,20 +18,11 @@ import { PassThrough } from 'node:stream';
918
import type { AppLoadContext, EntryContext } from '@remix-run/node';
1019
import { Response } from '@remix-run/node';
1120
import { RemixServer } from '@remix-run/react';
12-
import * as Sentry from '@sentry/remix';
1321
import isbot from 'isbot';
1422
import { renderToPipeableStream } from 'react-dom/server';
1523

1624
const ABORT_DELAY = 5_000;
1725

18-
Sentry.init({
19-
environment: 'qa', // dynamic sampling bias to keep transactions
20-
dsn: process.env.E2E_TEST_DSN,
21-
// Performance Monitoring
22-
tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
23-
tunnel: 'http://localhost:3031/', // proxy server
24-
});
25-
2626
export const handleError = Sentry.wrapRemixHandleError;
2727

2828
export default function handleRequest(

dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const DEPENDENTS: Dependent[] = [
7171
},
7272
{
7373
package: '@sentry/remix',
74-
compareWith: nodeExperimentalExports,
74+
compareWith: nodeExports,
7575
exports: Object.keys(SentryRemix),
7676
},
7777
{

packages/astro/src/index.types.ts

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export declare function flush(timeout?: number | undefined): PromiseLike<boolean
2424

2525
// eslint-disable-next-line deprecation/deprecation
2626
export declare const makeMain: typeof clientSdk.makeMain;
27-
export declare const getActiveSpan: typeof clientSdk.getActiveSpan;
2827
// eslint-disable-next-line deprecation/deprecation
2928
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;
3029
export declare const getClient: typeof clientSdk.getClient;

packages/opentelemetry/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export {
1818
spanHasStatus,
1919
} from './utils/spanTypes';
2020

21+
export { getDynamicSamplingContextFromSpan } from './utils/dynamicSamplingContext';
22+
2123
export { isSentryRequestSpan } from './utils/isSentryRequest';
2224

2325
export { getActiveSpan } from './utils/getActiveSpan';

packages/remix/jest.config.js

+2
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ const baseConfig = require('../../jest/jest.config.js');
33
module.exports = {
44
...baseConfig,
55
testPathIgnorePatterns: ['<rootDir>/build/', '<rootDir>/node_modules/', '<rootDir>/test/integration/'],
6+
// Some tests take longer to finish, as flushing spans with OpenTelemetry takes some more time
7+
testTimeout: 15000,
68
};

packages/remix/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"@remix-run/router": "1.x",
4949
"@sentry/cli": "^2.30.0",
5050
"@sentry/core": "8.0.0-alpha.2",
51-
"@sentry/node-experimental": "8.0.0-alpha.2",
51+
"@sentry/node": "8.0.0-alpha.2",
52+
"@sentry/opentelemetry": "8.0.0-alpha.2",
5253
"@sentry/react": "8.0.0-alpha.2",
5354
"@sentry/types": "8.0.0-alpha.2",
5455
"@sentry/utils": "8.0.0-alpha.2",
@@ -82,7 +83,7 @@
8283
"fix": "eslint . --format stylish --fix",
8384
"lint": "eslint . --format stylish",
8485
"test": "yarn test:unit",
85-
"test:integration": "run-s test:integration:v1 test:integration:v2 test:integration:tracingIntegration",
86+
"test:integration": "run-s test:integration:v1 test:integration:v2",
8687
"test:integration:v1": "run-s test:integration:clean test:integration:prepare test:integration:client test:integration:server",
8788
"test:integration:v2": "export REMIX_VERSION=2 && run-s test:integration:v1",
8889
"test:integration:ci": "run-s test:integration:clean test:integration:prepare test:integration:client:ci test:integration:server",

packages/remix/src/index.server.ts

+22-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { applySdkMetadata } from '@sentry/core';
2-
import type { NodeOptions } from '@sentry/node-experimental';
3-
import { getClient, init as nodeInit, setTag } from '@sentry/node-experimental';
1+
import { applySdkMetadata, isInitialized } from '@sentry/core';
2+
import type { NodeOptions } from '@sentry/node';
3+
import { init as nodeInit, setTag } from '@sentry/node';
44
import { logger } from '@sentry/utils';
55

66
import { DEBUG_BUILD } from './utils/debug-build';
@@ -10,8 +10,6 @@ import type { RemixOptions } from './utils/remixOptions';
1010
// We need to explicitly export @sentry/node as they end up under `default` in ESM builds
1111
// See: https://github.com/getsentry/sentry-javascript/issues/8474
1212
export {
13-
// eslint-disable-next-line deprecation/deprecation
14-
addGlobalEventProcessor,
1513
addEventProcessor,
1614
addBreadcrumb,
1715
addIntegration,
@@ -22,8 +20,6 @@ export {
2220
captureMessage,
2321
createTransport,
2422
// eslint-disable-next-line deprecation/deprecation
25-
getActiveTransaction,
26-
// eslint-disable-next-line deprecation/deprecation
2723
getCurrentHub,
2824
getClient,
2925
getCurrentScope,
@@ -46,7 +42,6 @@ export {
4642
setHttpStatus,
4743
withScope,
4844
withIsolationScope,
49-
autoDiscoverNodePerformanceMonitoringIntegrations,
5045
makeNodeTransport,
5146
getDefaultIntegrations,
5247
defaultStackParser,
@@ -56,7 +51,6 @@ export {
5651
addRequestDataToEvent,
5752
DEFAULT_USER_INCLUDES,
5853
extractRequestData,
59-
Integrations,
6054
consoleIntegration,
6155
onUncaughtExceptionIntegration,
6256
onUnhandledRejectionIntegration,
@@ -68,7 +62,6 @@ export {
6862
functionToStringIntegration,
6963
inboundFiltersIntegration,
7064
linkedErrorsIntegration,
71-
Handlers,
7265
setMeasurement,
7366
getActiveSpan,
7467
getRootSpan,
@@ -83,15 +76,30 @@ export {
8376
parameterize,
8477
metrics,
8578
createGetModuleFromFilename,
86-
hapiErrorPlugin,
8779
SEMANTIC_ATTRIBUTE_SENTRY_OP,
8880
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
8981
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
9082
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
91-
} from '@sentry/node-experimental';
83+
expressIntegration,
84+
expressErrorHandler,
85+
setupExpressErrorHandler,
86+
fastifyIntegration,
87+
graphqlIntegration,
88+
mongoIntegration,
89+
mongooseIntegration,
90+
mysqlIntegration,
91+
mysql2Integration,
92+
nestIntegration,
93+
postgresIntegration,
94+
prismaIntegration,
95+
hapiIntegration,
96+
setupHapiErrorHandler,
97+
spotlightIntegration,
98+
setupFastifyErrorHandler,
99+
} from '@sentry/node';
92100

93101
// Keeping the `*` exports for backwards compatibility and types
94-
export * from '@sentry/node-experimental';
102+
export * from '@sentry/node';
95103

96104
export { captureRemixServerException, wrapRemixHandleError } from './utils/instrumentServer';
97105
export { ErrorBoundary, withErrorBoundary } from '@sentry/react';
@@ -102,15 +110,11 @@ export { wrapExpressCreateRequestHandler } from './utils/serverAdapters/express'
102110

103111
export type { SentryMetaArgs } from './utils/types';
104112

105-
function sdkAlreadyInitialized(): boolean {
106-
return !!getClient();
107-
}
108-
109113
/** Initializes Sentry Remix SDK on Node. */
110114
export function init(options: RemixOptions): void {
111115
applySdkMetadata(options, 'remix', ['remix', 'node']);
112116

113-
if (sdkAlreadyInitialized()) {
117+
if (isInitialized()) {
114118
DEBUG_BUILD && logger.log('SDK already initialized');
115119

116120
return;

packages/remix/src/index.types.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export declare function init(options: RemixOptions): void;
1414

1515
// We export a merged Integrations object so that users can (at least typing-wise) use all integrations everywhere.
1616
// eslint-disable-next-line deprecation/deprecation
17-
export declare const Integrations: typeof clientSdk.Integrations & typeof serverSdk.Integrations;
17+
export declare const Integrations: typeof clientSdk.Integrations;
1818

1919
export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration;
2020
export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration;
@@ -27,6 +27,13 @@ export declare const defaultStackParser: StackParser;
2727
// methods from `@sentry/core`.
2828
declare const runtime: 'client' | 'server';
2929

30+
// eslint-disable-next-line deprecation/deprecation
31+
export declare const makeMain: typeof clientSdk.makeMain;
32+
// eslint-disable-next-line deprecation/deprecation
33+
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;
34+
export declare const getClient: typeof clientSdk.getClient;
35+
export declare const continueTrace: typeof clientSdk.continueTrace;
36+
3037
export const close = runtime === 'client' ? clientSdk.close : serverSdk.close;
3138
export const flush = runtime === 'client' ? clientSdk.flush : serverSdk.flush;
3239

packages/remix/src/utils/instrumentServer.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import {
33
SEMANTIC_ATTRIBUTE_SENTRY_OP,
44
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
55
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
6+
captureException,
67
continueTrace,
8+
getActiveSpan,
79
getClient,
8-
getDynamicSamplingContextFromSpan,
10+
getRootSpan,
911
handleCallbackErrors,
1012
hasTracingEnabled,
1113
setHttpStatus,
@@ -14,7 +16,7 @@ import {
1416
startSpan,
1517
withIsolationScope,
1618
} from '@sentry/core';
17-
import { captureException, getActiveSpan, getRootSpan } from '@sentry/node-experimental';
19+
import { getDynamicSamplingContextFromSpan } from '@sentry/opentelemetry';
1820
import type { Span, TransactionSource, WrappedFunction } from '@sentry/types';
1921
import {
2022
addExceptionMechanism,

packages/remix/src/utils/remixOptions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { NodeOptions } from '@sentry/node-experimental';
1+
import type { NodeOptions } from '@sentry/node';
22
import type { BrowserOptions } from '@sentry/react';
33
import type { Options } from '@sentry/types';
44

packages/remix/src/utils/serverAdapters/express.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getClient, getCurrentHub, hasTracingEnabled, setHttpStatus, withIsolationScope } from '@sentry/core';
2-
import { flush } from '@sentry/node-experimental';
2+
import { flush } from '@sentry/node';
33
import type { Hub, Span } from '@sentry/types';
44
import { extractRequestData, fill, isString, logger } from '@sentry/utils';
55

packages/remix/test/index.server.test.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import * as SentryNode from '@sentry/node-experimental';
1+
import * as SentryNode from '@sentry/node';
22

3-
import { Integrations, init } from '../src/index.server';
3+
import { init } from '../src/index.server';
44

55
const nodeInit = jest.spyOn(SentryNode, 'init');
66

@@ -55,9 +55,4 @@ describe('Server init()', () => {
5555

5656
expect(SentryNode.getIsolationScope().getScopeData().tags).toEqual({ runtime: 'node' });
5757
});
58-
59-
it('has both node and tracing integrations', () => {
60-
expect(Integrations.Apollo).not.toBeUndefined();
61-
expect(Integrations.Http).not.toBeUndefined();
62-
});
6358
});

0 commit comments

Comments
 (0)