Skip to content

Commit 643ff11

Browse files
committed
refactor: implement ConsoleCatcher class for improved console log handling in Catcher
1 parent bbe13cc commit 643ff11

2 files changed

Lines changed: 71 additions & 52 deletions

File tree

src/addons/consoleCatcher.ts

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,37 @@ const MAX_LOGS = 20;
1515
const CONSOLE_METHODS: string[] = ['log', 'warn', 'error', 'info', 'debug'];
1616

1717
/**
18-
* Creates a console interceptor that captures and formats console output
18+
* Console catcher class for intercepting and capturing console logs
1919
*/
20-
function createConsoleCatcher(): {
21-
initConsoleCatcher: () => void;
22-
addErrorEvent: (event: ErrorEvent | PromiseRejectionEvent) => void;
23-
getConsoleLogStack: () => ConsoleLogEvent[];
24-
} {
25-
const consoleOutput: ConsoleLogEvent[] = [];
26-
let isInitialized = false;
20+
export class ConsoleCatcher {
21+
/**
22+
* Singleton instance
23+
*/
24+
private static instance: ConsoleCatcher | null = null;
25+
26+
/**
27+
* Console output buffer
28+
*/
29+
private readonly consoleOutput: ConsoleLogEvent[] = [];
30+
31+
/**
32+
* Initialization flag
33+
*/
34+
private isInitialized = false;
35+
36+
/**
37+
* Private constructor to enforce singleton pattern
38+
*/
39+
private constructor() {}
40+
41+
/**
42+
* Get singleton instance
43+
*/
44+
public static getInstance(): ConsoleCatcher {
45+
ConsoleCatcher.instance ??= new ConsoleCatcher();
46+
47+
return ConsoleCatcher.instance;
48+
}
2749

2850
/**
2951
* Converts any argument to its string representation
@@ -32,7 +54,7 @@ function createConsoleCatcher(): {
3254
* @throws Error if the argument can not be stringified, for example by such reason:
3355
* SecurityError: Failed to read a named property 'toJSON' from 'Window': Blocked a frame with origin "https://codex.so" from accessing a cross-origin frame.
3456
*/
35-
function stringifyArg(arg: unknown): string {
57+
private stringifyArg(arg: unknown): string {
3658
if (typeof arg === 'string') {
3759
return arg;
3860
}
@@ -54,7 +76,7 @@ function createConsoleCatcher(): {
5476
*
5577
* @param args - Console arguments that may include style directives
5678
*/
57-
function formatConsoleArgs(args: unknown[]): {
79+
private formatConsoleArgs(args: unknown[]): {
5880
message: string;
5981
styles: string[];
6082
} {
@@ -71,7 +93,7 @@ function createConsoleCatcher(): {
7193
return {
7294
message: args.map(arg => {
7395
try {
74-
return stringifyArg(arg);
96+
return this.stringifyArg(arg);
7597
} catch (error) {
7698
return '[Error stringifying argument: ' + (error instanceof Error ? error.message : String(error)) + ']';
7799
}
@@ -101,7 +123,7 @@ function createConsoleCatcher(): {
101123
.slice(styles.length + 1)
102124
.map(arg => {
103125
try {
104-
return stringifyArg(arg);
126+
return this.stringifyArg(arg);
105127
} catch (error) {
106128
return '[Error stringifying argument: ' + (error instanceof Error ? error.message : String(error)) + ']';
107129
}
@@ -119,21 +141,19 @@ function createConsoleCatcher(): {
119141
*
120142
* @param logEvent - The console log event to be added to the output buffer
121143
*/
122-
function addToConsoleOutput(logEvent: ConsoleLogEvent): void {
123-
if (consoleOutput.length >= MAX_LOGS) {
124-
consoleOutput.shift();
144+
private addToConsoleOutput(logEvent: ConsoleLogEvent): void {
145+
if (this.consoleOutput.length >= MAX_LOGS) {
146+
this.consoleOutput.shift();
125147
}
126-
consoleOutput.push(logEvent);
148+
this.consoleOutput.push(logEvent);
127149
}
128150

129151
/**
130152
* Creates a console log event from an error or promise rejection
131153
*
132154
* @param event - The error event or promise rejection event to convert
133155
*/
134-
function createConsoleEventFromError(
135-
event: ErrorEvent | PromiseRejectionEvent
136-
): ConsoleLogEvent {
156+
private createConsoleEventFromError(event: ErrorEvent | PromiseRejectionEvent): ConsoleLogEvent {
137157
if (event instanceof ErrorEvent) {
138158
return {
139159
method: 'error',
@@ -160,24 +180,23 @@ function createConsoleCatcher(): {
160180
/**
161181
* Initializes the console interceptor by overriding default console methods
162182
*/
163-
function initConsoleCatcher(): void {
164-
if (isInitialized) {
183+
public init(): void {
184+
if (this.isInitialized) {
165185
return;
166186
}
167187

168-
isInitialized = true;
188+
this.isInitialized = true;
169189

170-
CONSOLE_METHODS.forEach(function overrideConsoleMethod(method) {
171-
if (typeof window.console[method] !== 'function') {
190+
CONSOLE_METHODS.forEach((method) => {
191+
if (typeof globalThis.console[method] !== 'function') {
172192
return;
173193
}
174194

175195
const oldFunction = window.console[method].bind(window.console);
176196

177-
window.console[method] = function (...args: unknown[]): void {
178-
const stack = new Error().stack?.split('\n').slice(2)
179-
.join('\n') || '';
180-
const { message, styles } = formatConsoleArgs(args);
197+
window.console[method] = (...args: unknown[]): void => {
198+
const stack = new Error().stack?.split('\n').slice(2).join('\n') || '';
199+
const { message, styles } = this.formatConsoleArgs(args);
181200

182201
const logEvent: ConsoleLogEvent = {
183202
method,
@@ -189,7 +208,7 @@ function createConsoleCatcher(): {
189208
styles,
190209
};
191210

192-
addToConsoleOutput(logEvent);
211+
this.addToConsoleOutput(logEvent);
193212
oldFunction(...args);
194213
};
195214
});
@@ -200,28 +219,16 @@ function createConsoleCatcher(): {
200219
*
201220
* @param event - The error or promise rejection event to handle
202221
*/
203-
function addErrorEvent(event: ErrorEvent | PromiseRejectionEvent): void {
204-
const logEvent = createConsoleEventFromError(event);
222+
public addErrorEvent(event: ErrorEvent | PromiseRejectionEvent): void {
223+
const logEvent = this.createConsoleEventFromError(event);
205224

206-
addToConsoleOutput(logEvent);
225+
this.addToConsoleOutput(logEvent);
207226
}
208227

209228
/**
210229
* Returns the current console output buffer
211230
*/
212-
function getConsoleLogStack(): ConsoleLogEvent[] {
213-
return [ ...consoleOutput ];
231+
public getConsoleLogStack(): ConsoleLogEvent[] {
232+
return [...this.consoleOutput];
214233
}
215-
216-
return {
217-
initConsoleCatcher,
218-
addErrorEvent,
219-
getConsoleLogStack,
220-
};
221234
}
222-
223-
const consoleCatcher = createConsoleCatcher();
224-
225-
export const { initConsoleCatcher, getConsoleLogStack, addErrorEvent } =
226-
consoleCatcher;
227-

src/catcher.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type { JavaScriptCatcherIntegrations } from './types/integrations';
1616
import { EventRejectedError } from './errors';
1717
import type { HawkJavaScriptEvent } from './types';
1818
import { isErrorProcessed, markErrorAsProcessed } from './utils/event';
19-
import { addErrorEvent, getConsoleLogStack, initConsoleCatcher } from './addons/consoleCatcher';
19+
import { ConsoleCatcher } from './addons/consoleCatcher';
2020
import { validateUser, validateContext } from './utils/validation';
2121

2222
/**
@@ -98,6 +98,11 @@ export default class Catcher {
9898
*/
9999
private readonly consoleTracking: boolean;
100100

101+
/**
102+
* Console catcher instance
103+
*/
104+
private readonly consoleCatcher: ConsoleCatcher;
105+
101106
/**
102107
* Catcher constructor
103108
*
@@ -116,8 +121,15 @@ export default class Catcher {
116121
this.setUser(settings.user || Catcher.getGeneratedUser());
117122
this.setContext(settings.context || undefined);
118123
this.beforeSend = settings.beforeSend;
119-
this.disableVueErrorHandler = settings.disableVueErrorHandler !== null && settings.disableVueErrorHandler !== undefined ? settings.disableVueErrorHandler : false;
120-
this.consoleTracking = settings.consoleTracking !== null && settings.consoleTracking !== undefined ? settings.consoleTracking : true;
124+
this.disableVueErrorHandler =
125+
settings.disableVueErrorHandler !== null && settings.disableVueErrorHandler !== undefined
126+
? settings.disableVueErrorHandler
127+
: false;
128+
this.consoleTracking =
129+
settings.consoleTracking !== null && settings.consoleTracking !== undefined
130+
? settings.consoleTracking
131+
: true;
132+
this.consoleCatcher = ConsoleCatcher.getInstance();
121133

122134
if (!this.token) {
123135
log(
@@ -144,7 +156,7 @@ export default class Catcher {
144156
});
145157

146158
if (this.consoleTracking) {
147-
initConsoleCatcher();
159+
this.consoleCatcher.init();
148160
}
149161

150162
/**
@@ -284,7 +296,7 @@ export default class Catcher {
284296
*/
285297

286298
if (this.consoleTracking) {
287-
addErrorEvent(event);
299+
this.consoleCatcher.addErrorEvent(event);
288300
}
289301

290302
/**
@@ -551,7 +563,7 @@ export default class Catcher {
551563
const userAgent = window.navigator.userAgent;
552564
const location = window.location.href;
553565
const getParams = this.getGetParams();
554-
const consoleLogs = this.consoleTracking && getConsoleLogStack();
566+
const consoleLogs = this.consoleTracking && this.consoleCatcher.getConsoleLogStack();
555567

556568
const addons: JavaScriptAddons = {
557569
window: {

0 commit comments

Comments
 (0)