Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(vercel-edge): Replace WinterCGFetch with winterCGFetchIntegration #10436

Merged
merged 1 commit into from
Jan 31, 2024
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
1 change: 1 addition & 0 deletions packages/nextjs/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export declare function init(
// eslint-disable-next-line deprecation/deprecation
export declare const Integrations: typeof clientSdk.Integrations &
typeof serverSdk.Integrations &
// eslint-disable-next-line deprecation/deprecation
typeof edgeSdk.Integrations;

export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration;
Expand Down
6 changes: 3 additions & 3 deletions packages/vercel-edge/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ export {
import { Integrations as CoreIntegrations, RequestData } from '@sentry/core';

import { WinterCGFetch } from './integrations/wintercg-fetch';
export { winterCGFetchIntegration } from './integrations/wintercg-fetch';

const INTEGRATIONS = {
/** @deprecated Import the integration function directly, e.g. `inboundFiltersIntegration()` instead of `new Integrations.InboundFilter(). */
export const Integrations = {
// eslint-disable-next-line deprecation/deprecation
...CoreIntegrations,
WinterCGFetch,
RequestData,
};

export { INTEGRATIONS as Integrations };
154 changes: 91 additions & 63 deletions packages/vercel-edge/src/integrations/wintercg-fetch.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,52 @@
import { instrumentFetchRequest } from '@sentry-internal/tracing';
import { addBreadcrumb, getClient, isSentryRequestUrl } from '@sentry/core';
import type { FetchBreadcrumbData, FetchBreadcrumbHint, HandlerDataFetch, Integration, Span } from '@sentry/types';
import {
addBreadcrumb,
convertIntegrationFnToClass,
defineIntegration,
getClient,
isSentryRequestUrl,
} from '@sentry/core';
import type {
Client,
FetchBreadcrumbData,
FetchBreadcrumbHint,
HandlerDataFetch,
Integration,
IntegrationClass,
IntegrationFn,
Span,
} from '@sentry/types';
import { LRUMap, addFetchInstrumentationHandler, stringMatchesSomePattern } from '@sentry/utils';

const INTEGRATION_NAME = 'WinterCGFetch';

const HAS_CLIENT_MAP = new WeakMap<Client, boolean>();

export interface Options {
/**
* Whether breadcrumbs should be recorded for requests
* Defaults to true
*/
breadcrumbs: boolean;

/**
* Function determining whether or not to create spans to track outgoing requests to the given URL.
* By default, spans will be created for all outgoing requests.
*/
shouldCreateSpanForRequest?: (url: string) => boolean;
}

/**
* Creates spans and attaches tracing headers to fetch requests on WinterCG runtimes.
*/
export class WinterCGFetch implements Integration {
/**
* @inheritDoc
*/
public static id: string = 'WinterCGFetch';

/**
* @inheritDoc
*/
public name: string = WinterCGFetch.id;

private readonly _options: Options;
const _winterCGFetch = ((options: Partial<Options> = {}) => {
const breadcrumbs = options.breadcrumbs === undefined ? true : options.breadcrumbs;
const shouldCreateSpanForRequest = options.shouldCreateSpanForRequest;

private readonly _createSpanUrlMap: LRUMap<string, boolean> = new LRUMap(100);
private readonly _headersUrlMap: LRUMap<string, boolean> = new LRUMap(100);
const _createSpanUrlMap = new LRUMap<string, boolean>(100);
const _headersUrlMap = new LRUMap<string, boolean>(100);

public constructor(_options: Partial<Options> = {}) {
this._options = {
breadcrumbs: _options.breadcrumbs === undefined ? true : _options.breadcrumbs,
shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest,
};
}

/**
* @inheritDoc
*/
public setupOnce(): void {
const spans: Record<string, Span> = {};

addFetchInstrumentationHandler(handlerData => {
if (!getClient()?.getIntegrationByName?.('WinterCGFetch')) {
return;
}

if (isSentryRequestUrl(handlerData.fetchData.url, getClient())) {
return;
}

instrumentFetchRequest(
handlerData,
this._shouldCreateSpan.bind(this),
this._shouldAttachTraceData.bind(this),
spans,
'auto.http.wintercg_fetch',
);

if (this._options.breadcrumbs) {
createBreadcrumb(handlerData);
}
});
}
const spans: Record<string, Span> = {};

/** Decides whether to attach trace data to the outgoing fetch request */
private _shouldAttachTraceData(url: string): boolean {
function _shouldAttachTraceData(url: string): boolean {
const client = getClient();

if (!client) {
Expand All @@ -85,32 +59,86 @@ export class WinterCGFetch implements Integration {
return true;
}

const cachedDecision = this._headersUrlMap.get(url);
const cachedDecision = _headersUrlMap.get(url);
if (cachedDecision !== undefined) {
return cachedDecision;
}

const decision = stringMatchesSomePattern(url, clientOptions.tracePropagationTargets);
this._headersUrlMap.set(url, decision);
_headersUrlMap.set(url, decision);
return decision;
}

/** Helper that wraps shouldCreateSpanForRequest option */
private _shouldCreateSpan(url: string): boolean {
if (this._options.shouldCreateSpanForRequest === undefined) {
function _shouldCreateSpan(url: string): boolean {
if (shouldCreateSpanForRequest === undefined) {
return true;
}

const cachedDecision = this._createSpanUrlMap.get(url);
const cachedDecision = _createSpanUrlMap.get(url);
if (cachedDecision !== undefined) {
return cachedDecision;
}

const decision = this._options.shouldCreateSpanForRequest(url);
this._createSpanUrlMap.set(url, decision);
const decision = shouldCreateSpanForRequest(url);
_createSpanUrlMap.set(url, decision);
return decision;
}
}

return {
name: INTEGRATION_NAME,
// TODO v8: Remove this again
// eslint-disable-next-line @typescript-eslint/no-empty-function
setupOnce() {
addFetchInstrumentationHandler(handlerData => {
const client = getClient();
if (!client || !HAS_CLIENT_MAP.get(client)) {
return;
}

if (isSentryRequestUrl(handlerData.fetchData.url, client)) {
return;
}

instrumentFetchRequest(
handlerData,
_shouldCreateSpan,
_shouldAttachTraceData,
spans,
'auto.http.wintercg_fetch',
);

if (breadcrumbs) {
createBreadcrumb(handlerData);
}
});
},
setup(client) {
HAS_CLIENT_MAP.set(client, true);
},
};
}) satisfies IntegrationFn;

export const winterCGFetchIntegration = defineIntegration(_winterCGFetch);

/**
* Creates spans and attaches tracing headers to fetch requests on WinterCG runtimes.
*
* @deprecated Use `winterCGFetchIntegration()` instead.
*/
// eslint-disable-next-line deprecation/deprecation
export const WinterCGFetch = convertIntegrationFnToClass(
INTEGRATION_NAME,
winterCGFetchIntegration,
) as IntegrationClass<Integration & { setupOnce: () => void }> & {
new (options?: {
breadcrumbs: boolean;
shouldCreateSpanForRequest?: (url: string) => boolean;
}): Integration;
};

// eslint-disable-next-line deprecation/deprecation
export type WinterCGFetch = typeof WinterCGFetch;

function createBreadcrumb(handlerData: HandlerDataFetch): void {
const { startTimestamp, endTimestamp } = handlerData;
Expand Down
23 changes: 10 additions & 13 deletions packages/vercel-edge/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {
FunctionToString,
InboundFilters,
LinkedErrors,
RequestData,
functionToStringIntegration,
getIntegrationsToSetup,
inboundFiltersIntegration,
initAndBind,
linkedErrorsIntegration,
requestDataIntegration,
} from '@sentry/core';
import type { Integration, Options } from '@sentry/types';
import { GLOBAL_OBJ, createStackParser, nodeStackLineParser, stackParserFromStackParserOptions } from '@sentry/utils';

import { setAsyncLocalStorageAsyncContextStrategy } from './async';
import { VercelEdgeClient } from './client';
import { WinterCGFetch } from './integrations/wintercg-fetch';
import { winterCGFetchIntegration } from './integrations/wintercg-fetch';
import { makeEdgeTransport } from './transports';
import type { VercelEdgeClientOptions, VercelEdgeOptions } from './types';
import { getVercelEnv } from './utils/vercel';
Expand All @@ -24,21 +24,18 @@ const nodeStackParser = createStackParser(nodeStackLineParser());

/** @deprecated Use `getDefaultIntegrations(options)` instead. */
export const defaultIntegrations = [
/* eslint-disable deprecation/deprecation */
new InboundFilters(),
new FunctionToString(),
new LinkedErrors(),
/* eslint-enable deprecation/deprecation */
new WinterCGFetch(),
inboundFiltersIntegration(),
functionToStringIntegration(),
linkedErrorsIntegration(),
winterCGFetchIntegration(),
];

/** Get the default integrations for the browser SDK. */
export function getDefaultIntegrations(options: Options): Integration[] {
return [
// eslint-disable-next-line deprecation/deprecation
...defaultIntegrations,
// eslint-disable-next-line deprecation/deprecation
...(options.sendDefaultPii ? [new RequestData()] : []),
...(options.sendDefaultPii ? [requestDataIntegration()] : []),
];
}

Expand Down
Loading
Loading