Skip to content
Merged
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
2 changes: 2 additions & 0 deletions dev-packages/browser-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"@playwright/test": "~1.56.0",
"@sentry-internal/rrweb": "2.34.0",
"@sentry/browser": "10.50.0",
"@sentry-internal/replay": "10.50.0",
"@sentry/opentelemetry": "10.50.0",
"@supabase/supabase-js": "2.49.3",
"axios": "1.15.0",
"babel-loader": "^10.1.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as SentryOpenTelemetry from '@sentry/opentelemetry';
import * as Sentry from '@sentry/browser';

// Verify that generally all imports can be resolved
// oxlint-disable-next-line no-console
for (const key in SentryOpenTelemetry) {
console.log(key, SentryOpenTelemetry[key]);
}

// Verify that it console.errors if calling node-only thing
new SentryOpenTelemetry.SentryAsyncLocalStorageContextManager();

Sentry.captureException(new Error('test'));
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../utils/helpers';

sentryTest('Should allow importing from @sentry/opentelemetry package', async ({ getLocalTestUrl, page }) => {
const bundle = process.env.PW_BUNDLE;

if (bundle && bundle.includes('bundle')) {
sentryTest.skip();
return;
}

const consoleMessages: string[] = [];
page.on('console', msg => {
consoleMessages.push(msg.text());
});

const url = await getLocalTestUrl({ testDir: __dirname });
const req = await waitForErrorRequestOnUrl(page, url);
const eventData = envelopeRequestParser(req);

expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0].value).toBe('test');

expect(consoleMessages).toContainEqual('SentryAsyncLocalStorageContextManager is not supported in the browser');
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ export async function generatePage(
compiler.run(err => {
if (err) {
reject(err);
return;
}

compiler.close(err => {
if (err) {
reject(err);
return;
}

resolve();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ export const LOADER_CONFIGS: Record<string, { options: Record<string, unknown>;
* so that the compiled versions aren't included
*/
function generateSentryAlias(): Record<string, string> {
if (!useBundleOrLoader) {
return {};
}

const rootPackageJson = JSON.parse(fs.readFileSync(ROOT_PACKAGE_JSON_PATH, 'utf8')) as { workspaces: string[] };
const packageNames = rootPackageJson.workspaces
.filter(workspace => !workspace.startsWith('dev-packages/'))
Expand Down Expand Up @@ -189,7 +193,10 @@ class SentryScenarioGenerationPlugin {
}

public apply(compiler: Compiler): void {
compiler.options.resolve.alias = generateSentryAlias();
const sentryAlias = generateSentryAlias();
if (Object.keys(sentryAlias).length > 0) {
compiler.options.resolve.alias = sentryAlias;
}
compiler.options.externals = useBundleOrLoader
? {
// To help Webpack resolve Sentry modules in `import` statements in cases where they're provided in bundles rather than in `node_modules`
Expand Down
4 changes: 4 additions & 0 deletions dev-packages/browser-integration-tests/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import type { Configuration } from 'webpack';
const config = function (userConfig: Record<string, unknown>): Configuration {
return {
...userConfig,
target: 'web',
mode: 'none',
resolve: {
conditionNames: ['webpack', 'import', 'require', 'browser', 'default'],
},
module: {
rules: [
{
Expand Down
16 changes: 16 additions & 0 deletions packages/opentelemetry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,26 @@
"./package.json": "./package.json",
".": {
"import": {
"node": {
"types": "./build/types/index.d.ts",
"default": "./build/esm/index.js"
},
"browser": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're specifying browser here, it might be safer to put it above node, just to ensure that anything that claims to support this (even if it does so with weird shims etc) can pick up the browser one first.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't 100% sure about this (and still am not 🤔 ) my thinking for this was that node is def. the main target here, and I want to avoid somebody who has a node target not getting this. IMHO that's more important than somebody who has node & browser targets not getting the node stuff - I'd say it's reasonable that if your targets include node, you get the variant with the node import 😅 but we can always revisit the order if this turns out to be problematic!

"types": "./build/types/index.d.ts",
"default": "./build/esm/index.browser.js"
},
"types": "./build/types/index.d.ts",
"default": "./build/esm/index.js"
},
"require": {
"node": {
"types": "./build/types/index.d.ts",
"default": "./build/cjs/index.js"
},
"browser": {
"types": "./build/types/index.d.ts",
"default": "./build/cjs/index.browser.js"
},
"types": "./build/types/index.d.ts",
"default": "./build/cjs/index.js"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/opentelemetry/rollup.npm.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default makeNPMConfigVariants(
makeBaseNPMConfig({
// `tracingChannel` is a Node.js-only subpath so `node:diagnostics_channel`
// isn't pulled into the main bundle (breaks edge/browser builds).
entrypoints: ['src/index.ts', 'src/tracingChannel.ts'],
entrypoints: ['src/index.ts', 'src/tracingChannel.ts', 'src/index.browser.ts'],
packageSpecificConfig: {
output: {
// set exports to 'named' or 'auto' so that rollup doesn't warn
Expand Down
56 changes: 56 additions & 0 deletions packages/opentelemetry/src/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export { SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION } from './semanticAttributes';

export { getRequestSpanData } from './utils/getRequestSpanData';

export type { OpenTelemetryClient } from './types';
export { wrapClientClass } from './custom/client';

export { getSpanKind } from './utils/getSpanKind';

export { getScopesFromContext } from './utils/contextData';

export {
spanHasAttributes,
spanHasEvents,
spanHasKind,
spanHasName,
spanHasParentId,
spanHasStatus,
} from './utils/spanTypes';

// Re-export this for backwards compatibility (this used to be a different implementation)
export { getDynamicSamplingContextFromSpan } from '@sentry/core';

export { isSentryRequestSpan } from './utils/isSentryRequest';

export { enhanceDscWithOpenTelemetryRootSpanName } from './utils/enhanceDscWithOpenTelemetryRootSpanName';

export { getActiveSpan } from './utils/getActiveSpan';
export {
startSpan,
startSpanManual,
startInactiveSpan,
withActiveSpan,
continueTrace,
getTraceContextForScope,
} from './trace';

export { suppressTracing } from './utils/suppressTracing';

export { setupEventContextTrace } from './setupEventContextTrace';

export { setOpenTelemetryContextAsyncContextStrategy } from './asyncContextStrategy';
export { wrapContextManagerClass } from './contextManager';

export { SentryPropagator, shouldPropagateTraceForUrl } from './propagator';
export { SentrySpanProcessor } from './spanProcessor';
export { SentrySampler, wrapSamplingDecision } from './sampler';

export { openTelemetrySetupCheck } from './utils/setupCheck';

export { getSentryResource } from './resource';

export { withStreamedSpan } from '@sentry/core';

// Legacy
export { getClient } from '@sentry/core';
18 changes: 18 additions & 0 deletions packages/opentelemetry/src/index.browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { consoleSandbox } from '@sentry/core';

export * from './exports';

// Stubs for node-specific exports
export class SentryAsyncLocalStorageContextManager {
public constructor() {
consoleSandbox(() => {
// oxlint-disable-next-line no-console
console.error('SentryAsyncLocalStorageContextManager is not supported in the browser');
});
}
}

export type AsyncLocalStorageLookup = {
asyncLocalStorage: unknown;
contextSymbol: symbol;
};
56 changes: 2 additions & 54 deletions packages/opentelemetry/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,5 @@
export { SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION } from './semanticAttributes';
export * from './exports';

export { getRequestSpanData } from './utils/getRequestSpanData';

export type { OpenTelemetryClient } from './types';
export { wrapClientClass } from './custom/client';

export { getSpanKind } from './utils/getSpanKind';

export { getScopesFromContext } from './utils/contextData';

export {
spanHasAttributes,
spanHasEvents,
spanHasKind,
spanHasName,
spanHasParentId,
spanHasStatus,
} from './utils/spanTypes';

// Re-export this for backwards compatibility (this used to be a different implementation)
export { getDynamicSamplingContextFromSpan } from '@sentry/core';

export { isSentryRequestSpan } from './utils/isSentryRequest';

export { enhanceDscWithOpenTelemetryRootSpanName } from './utils/enhanceDscWithOpenTelemetryRootSpanName';

export { getActiveSpan } from './utils/getActiveSpan';
export {
startSpan,
startSpanManual,
startInactiveSpan,
withActiveSpan,
continueTrace,
getTraceContextForScope,
} from './trace';

export { suppressTracing } from './utils/suppressTracing';

export { setupEventContextTrace } from './setupEventContextTrace';

export { setOpenTelemetryContextAsyncContextStrategy } from './asyncContextStrategy';
export { wrapContextManagerClass } from './contextManager';
// Node-specific exports
export { SentryAsyncLocalStorageContextManager } from './asyncLocalStorageContextManager';
export type { AsyncLocalStorageLookup } from './contextManager';
export { SentryPropagator, shouldPropagateTraceForUrl } from './propagator';
export { SentrySpanProcessor } from './spanProcessor';
export { SentrySampler, wrapSamplingDecision } from './sampler';

export { openTelemetrySetupCheck } from './utils/setupCheck';

export { getSentryResource } from './resource';

export { withStreamedSpan } from '@sentry/core';

// Legacy
export { getClient } from '@sentry/core';
Loading