Skip to content
Open
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ Breaking changes coming with Faro version 2
- Chore (`@grafana/faro-*`): Remove Node.js 23 from build and test matrix as it's EoL (#1343)
- Chore (`@grafana/faro-*`): upgrade to yarn 4

- chore(`@grafana/faro web-sdk`): Move the stackFrames from the errors instrumentation package to the utils package (#1316)
- improvement (`@grafana/faro-web-sdk`): Allow users to configure the stackTraceParser in the BrowserConfig (#1316)
- improvement (`@grafana/faro-web-sdk`): Add `createStacktraceParser` function to create parsers with custom options (#1316)
- chore(`@grafana/faro-web-sdk`): remove the setStackTraceParser function (#1316)
- **BREAKING**: Remove `newStackTraceParser` function and `StacktraceParser` interface. Use `parseStacktrace` function directly or `createStacktraceParser` for custom options. Also simplified ExceptionsAPI by replacing `getStacktraceParser()` with direct `parseStacktrace` property (#1316)

## 1.18.2

- Improvement (`@grafana/faro-web-sdk`): don't attach user action context to http request when in halt mode (#1249)
Expand Down
1 change: 0 additions & 1 deletion docs/sources/developer/architecture/components/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ like `web-sdk` or by the user.

Methods:

- `changeStacktraceParser` - replaces the existing stacktrace parser with a new one
- `getStacktraceParser` - returns the current stacktrace parser
- `pushError` - sends an exception

Expand Down
1 change: 0 additions & 1 deletion packages/core/src/api/exceptions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export type {
ExtendedError,
PushErrorOptions,
Stacktrace,
StacktraceParser,
ErrorWithIndexProperties,
ExceptionEventExtended,
} from './types';
22 changes: 4 additions & 18 deletions packages/core/src/api/exceptions/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import type { UserActionsAPI } from '../userActions';
import { shouldIgnoreEvent } from '../utils';

import { defaultExceptionType } from './const';
import type { ErrorWithIndexProperties, ExceptionEvent, ExceptionsAPI, StacktraceParser } from './types';

let stacktraceParser: StacktraceParser | undefined;
import type { ErrorWithIndexProperties, ExceptionEvent, ExceptionsAPI } from './types';

export function initializeExceptionsAPI({
internalLogger,
Expand All @@ -45,15 +43,7 @@ export function initializeExceptionsAPI({

let lastPayload: Pick<ExceptionEvent, 'type' | 'value' | 'stacktrace' | 'context'> | null = null;

stacktraceParser = config.parseStacktrace ?? stacktraceParser;

const changeStacktraceParser: ExceptionsAPI['changeStacktraceParser'] = (newStacktraceParser) => {
internalLogger.debug('Changing stacktrace parser');

stacktraceParser = newStacktraceParser ?? stacktraceParser;
};

const getStacktraceParser: ExceptionsAPI['getStacktraceParser'] = () => stacktraceParser;
const parseStacktrace = config.parseStacktrace;

const { ignoreErrors = [], preserveOriginalError } = config;

Expand Down Expand Up @@ -87,8 +77,7 @@ export function initializeExceptionsAPI({
},
type: TransportItemType.EXCEPTION,
};

stackFrames = stackFrames ?? (error.stack ? stacktraceParser?.(error).frames : undefined);
stackFrames = stackFrames ?? (error.stack ? parseStacktrace?.(error).frames : undefined);

if (stackFrames?.length) {
item.payload.stacktrace = {
Expand Down Expand Up @@ -124,11 +113,8 @@ export function initializeExceptionsAPI({
}
};

changeStacktraceParser(config.parseStacktrace);

return {
changeStacktraceParser,
getStacktraceParser,
parseStacktrace,
pushError,
};
}
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/api/exceptions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import type { SpanContext } from '@opentelemetry/api';
import type { TraceContext } from '../traces';
import type { UserAction } from '../types';

export type StacktraceParser = (err: ExtendedError) => Stacktrace;

export interface ExceptionStackFrame {
filename: string;
function: string;
Expand Down Expand Up @@ -72,7 +70,6 @@ export type ErrorWithIndexProperties = Error & {
};

export interface ExceptionsAPI {
changeStacktraceParser: (stacktraceParser: StacktraceParser) => void;
getStacktraceParser: () => StacktraceParser | undefined;
parseStacktrace: ((err: ExtendedError) => Stacktrace) | undefined;
pushError: (value: ErrorWithIndexProperties, options?: PushErrorOptions) => void;
}
1 change: 0 additions & 1 deletion packages/core/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export type {
ExtendedError,
PushErrorOptions,
Stacktrace,
StacktraceParser,
ExceptionEventExtended,
} from './exceptions';

Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/api/initialize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ describe('initialize', () => {
expect(api).toHaveProperty('pushMeasurement');
expect(api).toHaveProperty('pushTraces');
expect(api).toHaveProperty('pushEvent');
expect(api).toHaveProperty('changeStacktraceParser');
expect(api).toHaveProperty('getOTEL');
expect(api).toHaveProperty('getPage');
expect(api).toHaveProperty('getSession');
expect(api).toHaveProperty('getStacktraceParser');
expect(api).toHaveProperty('parseStacktrace');
expect(api).toHaveProperty('getTraceContext');
expect(api).toHaveProperty('getView');
expect(api).toHaveProperty('initOTEL');
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { APIEvent, LogArgsSerializer, StacktraceParser } from '../api';
import type { APIEvent, ExtendedError, LogArgsSerializer, Stacktrace } from '../api';
import type { Instrumentation } from '../instrumentations';
import type { InternalLoggerLevel } from '../internalLogger';
import type { Meta, MetaApp, MetaItem, MetaPage, MetaSession, MetaUser, MetaView } from '../metas';
Expand Down Expand Up @@ -57,9 +57,9 @@ export interface Config<P = APIEvent> {
metas: MetaItem[];

/**
* Custom function used to parse stack traces
* The stacktrace parser to use for parsing stack traces.
*/
parseStacktrace: StacktraceParser;
parseStacktrace?: (err: ExtendedError) => Stacktrace;

/**
* Pause sending data (default: false)
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export type {
PushLogOptions,
PushMeasurementOptions,
Stacktrace,
StacktraceParser,
TraceContext,
TraceEvent,
TracesAPI,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/testUtils/mockStacktraceParser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ExceptionStackFrame, StacktraceParser } from '../api';
import type { ExceptionStackFrame, ExtendedError, Stacktrace } from '../api';

export const mockStacktraceParser: StacktraceParser = (err) => {
export const mockStacktraceParser = (err: ExtendedError): Stacktrace => {
const frames: ExceptionStackFrame[] = [];
const stack = err.stack ?? err.stacktrace;

Expand Down
1 change: 0 additions & 1 deletion packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ export type {
PushLogOptions,
PushMeasurementOptions,
Stacktrace,
StacktraceParser,
TraceContext,
TraceEvent,
TracesAPI,
Expand Down
5 changes: 3 additions & 2 deletions packages/web-sdk/src/config/makeCoreConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import {
import type { Config, Instrumentation, MetaItem, MetaSession, Transport } from '@grafana/faro-core';

import { defaultEventDomain } from '../consts';
import { parseStacktrace } from '../instrumentations';
import { defaultSessionTrackingConfig } from '../instrumentations/session';
import { userActionDataAttribute } from '../instrumentations/userActions/const';
import { browserMeta } from '../metas';
import { k6Meta } from '../metas/k6';
import { createPageMeta } from '../metas/page';
import { FetchTransport } from '../transports';
import { parseStacktrace } from '../utils';

import { getWebInstrumentations } from './getWebInstrumentations';
import type { BrowserConfig } from './types';
Expand Down Expand Up @@ -79,7 +79,8 @@ export function makeCoreConfig(browserConfig: BrowserConfig): Config {
isolate,
logArgsSerializer,
metas,
parseStacktrace,
// Use browser-provided parser or use parseStacktrace as default
parseStacktrace: browserConfig.parseStacktrace ?? parseStacktrace,
paused,
preventGlobalExposure,
transports,
Expand Down
2 changes: 1 addition & 1 deletion packages/web-sdk/src/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Config } from '@grafana/faro-core';

export interface BrowserConfig extends Partial<Omit<Config, 'app' | 'parseStacktrace'>>, Pick<Config, 'app'> {
export interface BrowserConfig extends Partial<Omit<Config, 'app'>>, Pick<Config, 'app'> {
url?: string;
apiKey?: string;
}
Expand Down
11 changes: 7 additions & 4 deletions packages/web-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ export { defaultEventDomain } from './consts';
export { initializeFaro } from './initialize';

export {
buildStackFrame,
ConsoleInstrumentation,
ErrorsInstrumentation,
getDataFromSafariExtensions,
getStackFramesFromError,
parseStacktrace,
createStacktraceParser,
buildStackFrame,
} from './utils';

export {
ConsoleInstrumentation,
ErrorsInstrumentation,
ViewInstrumentation,
WebVitalsInstrumentation,
SessionInstrumentation,
Expand Down Expand Up @@ -151,7 +155,6 @@ export type {
PushLogOptions,
PushMeasurementOptions,
Stacktrace,
StacktraceParser,
TraceContext,
TraceEvent,
TracesAPI,
Expand Down
24 changes: 16 additions & 8 deletions packages/web-sdk/src/instrumentations/errors/getErrorDetails.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { isDomError, isDomException, isError, isErrorEvent, isEvent, isObject, isString } from '@grafana/faro-core';
import type { ExceptionStackFrame, LogArgsSerializer } from '@grafana/faro-core';
import type { ExceptionStackFrame, ExtendedError, LogArgsSerializer, Stacktrace } from '@grafana/faro-core';

import { buildStackFrame, parseStacktrace } from '../../utils/stackFrames';

import { domErrorType, domExceptionType, objectEventValue, unknownSymbolString } from './const';
import { getValueAndTypeFromMessage } from './getValueAndTypeFromMessage';
import { buildStackFrame, getStackFramesFromError } from './stackFrames';
import type { ErrorEvent } from './types';

export function getErrorDetails(evt: ErrorEvent): [string | undefined, string | undefined, ExceptionStackFrame[]] {
export function getErrorDetails(
evt: ErrorEvent,
stacktraceParser: (error: ExtendedError) => Stacktrace = parseStacktrace
): [string | undefined, string | undefined, ExceptionStackFrame[]] {
let value: string | undefined;
let type: string | undefined;
let stackFrames: ExceptionStackFrame[] = [];
Expand All @@ -16,15 +20,16 @@ export function getErrorDetails(evt: ErrorEvent): [string | undefined, string |
if (isErrorEvent(evt) && evt.error) {
value = evt.error.message;
type = evt.error.name;
stackFrames = getStackFramesFromError(evt.error);
const { frames } = stacktraceParser(evt.error);
stackFrames = frames;
} else if ((isDomErrorRes = isDomError(evt)) || isDomException(evt)) {
const { name, message } = evt;

type = name ?? (isDomErrorRes ? domErrorType : domExceptionType);
value = message ? `${type}: ${message}` : type;
} else if (isError(evt)) {
value = evt.message;
stackFrames = getStackFramesFromError(evt);
const { frames } = stacktraceParser(evt);
stackFrames = frames;
} else if (isObject(evt) || (isEventRes = isEvent(evt))) {
type = isEventRes ? evt.constructor.name : undefined;
value = `${objectEventValue} ${Object.keys(evt)}`;
Expand All @@ -39,7 +44,10 @@ export interface ErrorDetails {
stackFrames?: ExceptionStackFrame[];
}

export function getDetailsFromErrorArgs(args: [any?, ...any[]]): ErrorDetails {
export function getDetailsFromErrorArgs(
args: [any?, ...any[]],
stacktraceParser: (error: ExtendedError) => Stacktrace = parseStacktrace
): ErrorDetails {
const [evt, source, lineno, colno, error] = args;

let value: string | undefined;
Expand All @@ -49,7 +57,7 @@ export function getDetailsFromErrorArgs(args: [any?, ...any[]]): ErrorDetails {
const initialStackFrame = buildStackFrame(source, unknownSymbolString, lineno, colno);

if (error || !eventIsString) {
[value, type, stackFrames] = getErrorDetails((error ?? evt) as Error | Event);
[value, type, stackFrames] = getErrorDetails((error ?? evt) as Error | Event, stacktraceParser);

if (stackFrames.length === 0) {
stackFrames = [initialStackFrame];
Expand Down
2 changes: 0 additions & 2 deletions packages/web-sdk/src/instrumentations/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export { ErrorsInstrumentation } from './instrumentation';

export { buildStackFrame, getDataFromSafariExtensions, getStackFramesFromError, parseStacktrace } from './stackFrames';

export type { ErrorEvent, ExtendedPromiseRejectionEvent } from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function registerOnerror(api: API): void {

window.onerror = (...args) => {
try {
const { value, type, stackFrames } = getDetailsFromErrorArgs(args);
const { value, type, stackFrames } = getDetailsFromErrorArgs(args, api.parseStacktrace);
const originalError = args[4];

if (value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function registerOnunhandledrejection(api: API): void {
value = `${primitiveUnhandledValue} ${String(error)}`;
type = primitiveUnhandledType;
} else {
[value, type, stackFrames] = getErrorDetails(error);
[value, type, stackFrames] = getErrorDetails(error, api.parseStacktrace);
}

if (value) {
Expand Down

This file was deleted.

9 changes: 2 additions & 7 deletions packages/web-sdk/src/instrumentations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@ export { SessionInstrumentation } from './session';

export { ConsoleInstrumentation } from './console';

export {
buildStackFrame,
ErrorsInstrumentation,
getDataFromSafariExtensions,
getStackFramesFromError,
parseStacktrace,
} from './errors';
export { ErrorsInstrumentation } from './errors';

export type { ErrorEvent, ExtendedPromiseRejectionEvent } from './errors';

export { ViewInstrumentation } from './view';
Expand Down
8 changes: 8 additions & 0 deletions packages/web-sdk/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ export {
export { throttle } from './throttle';

export { getIgnoreUrls, getUrlFromResource } from './url';

export {
buildStackFrame,
getDataFromSafariExtensions,
getStackFramesFromError,
parseStacktrace,
createStacktraceParser,
} from './stackFrames';
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ describe('getStackFramesFromError', () => {
buildStackFrame('http://path/to/file.js', 'bar', 108, 168),
]);
});

it('should correctly skip lines if configured to', () => {
const result = getStackFramesFromError(CapturedExceptions.OPERA_25, { maximumLineLength: 36 });
expect(result).toEqual([buildStackFrame('http://path/to/file.js', undefined, 47, 22)]);
});
});

/* Taken from: https://github.com/stacktracejs/error-stack-parser/blob/master/spec/fixtures/captured-errors.js */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ import {
webkitLineRegex,
} from './const';
import { getDataFromSafariExtensions } from './getDataFromSafariExtensions';
import type { StackframeParserOptions } from './types';

export function getStackFramesFromError(error: ExtendedError): ExceptionStackFrame[] {
export function getStackFramesFromError(
error: ExtendedError,
options: StackframeParserOptions = {}
): ExceptionStackFrame[] {
let lines: string[] = [];

if (error.stacktrace) {
Expand All @@ -33,6 +37,13 @@ export function getStackFramesFromError(error: ExtendedError): ExceptionStackFra
let lineno: string | undefined;
let colno: string | undefined;

// skip attempting to parse stack frames over the limit, if it is set and above 0
if (options.maximumLineLength !== undefined && options.maximumLineLength > 0) {
if (line.length > options.maximumLineLength) {
return acc;
}
}

if ((parts = webkitLineRegex.exec(line))) {
func = parts[1];
filename = parts[2];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export { getDataFromSafariExtensions } from './getDataFromSafariExtensions';

export { getStackFramesFromError } from './getStackFramesFromError';

export { parseStacktrace } from './parseStacktrace';
export { parseStacktrace, createStacktraceParser } from './parseStacktrace';
Loading