Skip to content

Commit

Permalink
ref(replay): Extend ReplayLogger from core Logger
Browse files Browse the repository at this point in the history
- Moved replay-specific `_serializeFormData` to `browser-utils` and added test.

Signed-off-by: Kaung Zin Hein <[email protected]>
  • Loading branch information
Zen-cronic committed Jan 30, 2025
1 parent 200d810 commit 514ef47
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 56 deletions.
2 changes: 1 addition & 1 deletion packages/browser-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ export { fetch, setTimeout, clearCachedImplementation, getNativeImplementation }

export { addXhrInstrumentationHandler, SENTRY_XHR_DATA_KEY } from './instrument/xhr';

export { getBodyString, getFetchRequestArgBody } from './networkUtils';
export { getBodyString, getFetchRequestArgBody, serializeFormData } from './networkUtils';

export type { FetchHint, NetworkMetaWarning, XhrHint } from './types';
56 changes: 14 additions & 42 deletions packages/browser-utils/src/networkUtils.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,22 @@
import type { ConsoleLevel, Logger } from '@sentry/core';
import { logger } from '@sentry/core';
import type { Logger } from '@sentry/core';
import { DEBUG_BUILD } from './debug-build';
import type { NetworkMetaWarning } from './types';

type ReplayConsoleLevels = Extract<ConsoleLevel, 'info' | 'warn' | 'error' | 'log'>;
type LoggerMethod = (...args: unknown[]) => void;
type LoggerConsoleMethods = Record<ReplayConsoleLevels, LoggerMethod>;

interface LoggerConfig {
captureExceptions: boolean;
traceInternals: boolean;
}

// Duplicate from replay-internal
interface ReplayLogger extends LoggerConsoleMethods {
/**
* Calls `logger.info` but saves breadcrumb in the next tick due to race
* conditions before replay is initialized.
*/
infoTick: LoggerMethod;
/**
* Captures exceptions (`Error`) if "capture internal exceptions" is enabled
*/
exception: LoggerMethod;
/**
* Configures the logger with additional debugging behavior
*/
setConfig(config: Partial<LoggerConfig>): void;
}

function _serializeFormData(formData: FormData): string {
// This is a bit simplified, but gives us a decent estimate
// This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'
/**
* Serializes FormData.
*
* This is a bit simplified, but gives us a decent estimate.
* This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'.
*
*/
export function serializeFormData(formData: FormData): string {
// @ts-expect-error passing FormData to URLSearchParams actually works
return new URLSearchParams(formData).toString();
}

/** Get the string representation of a body. */
export function getBodyString(
body: unknown,
logger?: Logger | ReplayLogger,
): [string | undefined, NetworkMetaWarning?] {
export function getBodyString(body: unknown, _logger: Logger = logger): [string | undefined, NetworkMetaWarning?] {
try {
if (typeof body === 'string') {
return [body];
Expand All @@ -50,23 +27,18 @@ export function getBodyString(
}

if (body instanceof FormData) {
return [_serializeFormData(body)];
return [serializeFormData(body)];
}

if (!body) {
return [undefined];
}
} catch (error) {
// RelayLogger
if (DEBUG_BUILD && logger && 'exception' in logger) {
logger.exception(error, 'Failed to serialize body', body);
} else if (DEBUG_BUILD && logger) {
logger.error(error, 'Failed to serialize body', body);
}
DEBUG_BUILD && logger.error(error, 'Failed to serialize body', body);
return [undefined, 'BODY_PARSE_ERROR'];
}

DEBUG_BUILD && logger?.info('Skipping network body because of body type', body);
DEBUG_BUILD && logger.info('Skipping network body because of body type', body);

return [undefined, 'UNPARSEABLE_BODY_TYPE'];
}
Expand Down
13 changes: 12 additions & 1 deletion packages/browser-utils/test/networkUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import { describe, expect, it } from 'vitest';
import { getBodyString, getFetchRequestArgBody } from '../src/networkUtils';
import { getBodyString, getFetchRequestArgBody, serializeFormData } from '../src/networkUtils';

describe('getBodyString', () => {
it('should work with a string', () => {
Expand Down Expand Up @@ -94,3 +94,14 @@ describe('getFetchRequestArgBody', () => {
});
});
});

describe('serializeFormData', () => {
it('works with FormData', () => {
const formData = new FormData();
formData.append('name', 'Anne Smith');
formData.append('age', '13');

const actual = serializeFormData(formData);
expect(actual).toBe('name=Anne+Smith&age=13');
});
});
12 changes: 3 additions & 9 deletions packages/replay-internal/src/coreHandlers/util/networkUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { serializeFormData } from '@sentry-internal/browser-utils';
import type { NetworkMetaWarning } from '@sentry-internal/browser-utils';
import { dropUndefinedKeys, stringMatchesSomePattern } from '@sentry/core';

import type { NetworkMetaWarning } from '@sentry-internal/browser-utils';
import { NETWORK_BODY_MAX_SIZE, WINDOW } from '../../constants';
import type {
NetworkBody,
Expand Down Expand Up @@ -28,7 +29,7 @@ export function getBodySize(body: RequestInit['body']): number | undefined {
}

if (body instanceof FormData) {
const formDataStr = _serializeFormData(body);
const formDataStr = serializeFormData(body);
return textEncoder.encode(formDataStr).length;
}

Expand Down Expand Up @@ -170,13 +171,6 @@ export function getAllowedHeaders(headers: Record<string, string>, allowedHeader
}, {});
}

function _serializeFormData(formData: FormData): string {
// This is a bit simplified, but gives us a decent estimate
// This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'
// @ts-expect-error passing FormData to URLSearchParams actually works
return new URLSearchParams(formData).toString();
}

function normalizeNetworkBody(body: string | undefined): {
body: NetworkBody | undefined;
warnings?: NetworkMetaWarning[];
Expand Down
5 changes: 2 additions & 3 deletions packages/replay-internal/src/util/logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ConsoleLevel, SeverityLevel } from '@sentry/core';
import type { ConsoleLevel, Logger, SeverityLevel } from '@sentry/core';
import { addBreadcrumb, captureException, logger as coreLogger, severityLevelFromString } from '@sentry/core';
import { DEBUG_BUILD } from '../debug-build';

Expand All @@ -7,14 +7,13 @@ const CONSOLE_LEVELS: readonly ReplayConsoleLevels[] = ['info', 'warn', 'error',
const PREFIX = '[Replay] ';

type LoggerMethod = (...args: unknown[]) => void;
type LoggerConsoleMethods = Record<ReplayConsoleLevels, LoggerMethod>;

interface LoggerConfig {
captureExceptions: boolean;
traceInternals: boolean;
}

interface ReplayLogger extends LoggerConsoleMethods {
interface ReplayLogger extends Logger {
/**
* Calls `logger.info` but saves breadcrumb in the next tick due to race
* conditions before replay is initialized.
Expand Down

0 comments on commit 514ef47

Please sign in to comment.