Skip to content

Commit a80d33a

Browse files
committed
dev
1 parent 32e6b88 commit a80d33a

File tree

3 files changed

+83
-68
lines changed

3 files changed

+83
-68
lines changed

src/addons/consoleCatcher.ts

Lines changed: 29 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,20 @@
22
* @file Module for intercepting console logs with stack trace capture
33
*/
44
import safeStringify from 'safe-stringify';
5-
import type { ConsoleLogEvent } from '@hawk.so/types';
5+
import type { ExtendedConsoleLogEvent as ConsoleLogEvent } from '../types/console';
66

77
/**
8-
* Creates a console interceptor that captures and formats console output
8+
* Console interceptor that captures and formats console output
99
*/
10-
function createConsoleCatcher(): {
11-
initConsoleCatcher: () => void;
12-
addErrorEvent: (event: ErrorEvent | PromiseRejectionEvent) => void;
13-
getConsoleLogStack: () => ConsoleLogEvent[];
14-
} {
15-
const MAX_LOGS = 20;
16-
const consoleOutput: ConsoleLogEvent[] = [];
17-
let isInitialized = false;
10+
export class ConsoleCatcher {
11+
private readonly MAX_LOGS = 20;
12+
private readonly consoleOutput: ConsoleLogEvent[] = [];
13+
private isInitialized = false;
1814

1915
/**
2016
* Converts any argument to its string representation
21-
*
22-
* @param arg - Value to convert to string
2317
*/
24-
function stringifyArg(arg: unknown): string {
18+
private stringifyArg(arg: unknown): string {
2519
if (typeof arg === 'string') {
2620
return arg;
2721
}
@@ -34,10 +28,8 @@ function createConsoleCatcher(): {
3428

3529
/**
3630
* Formats console arguments handling %c directives
37-
*
38-
* @param args - Console arguments that may include style directives
3931
*/
40-
function formatConsoleArgs(args: unknown[]): {
32+
private formatConsoleArgs(args: unknown[]): {
4133
message: string;
4234
styles: string[];
4335
} {
@@ -52,7 +44,7 @@ function createConsoleCatcher(): {
5244

5345
if (typeof firstArg !== 'string' || !firstArg.includes('%c')) {
5446
return {
55-
message: args.map(stringifyArg).join(' '),
47+
message: args.map((arg) => this.stringifyArg(arg)).join(' '),
5648
styles: [],
5749
};
5850
}
@@ -76,7 +68,7 @@ function createConsoleCatcher(): {
7668
// Add remaining arguments that aren't styles
7769
const remainingArgs = args
7870
.slice(styles.length + 1)
79-
.map(stringifyArg)
71+
.map((arg) => this.stringifyArg(arg))
8072
.join(' ');
8173

8274
return {
@@ -87,34 +79,26 @@ function createConsoleCatcher(): {
8779

8880
/**
8981
* Adds a console log event to the output buffer
90-
*
91-
* @param logEvent - The console log event to be added to the output buffer
9282
*/
93-
function addToConsoleOutput(logEvent: ConsoleLogEvent): void {
94-
if (consoleOutput.length >= MAX_LOGS) {
95-
consoleOutput.shift();
83+
private addToConsoleOutput(logEvent: ConsoleLogEvent): void {
84+
if (this.consoleOutput.length >= this.MAX_LOGS) {
85+
this.consoleOutput.shift();
9686
}
97-
consoleOutput.push(logEvent);
87+
this.consoleOutput.push(logEvent);
9888
}
9989

10090
/**
10191
* Creates a console log event from an error or promise rejection
102-
*
103-
* @param event - The error event or promise rejection event to convert
10492
*/
105-
function createConsoleEventFromError(
106-
event: ErrorEvent | PromiseRejectionEvent
107-
): ConsoleLogEvent {
93+
private createConsoleEventFromError(event: ErrorEvent | PromiseRejectionEvent): ConsoleLogEvent {
10894
if (event instanceof ErrorEvent) {
10995
return {
11096
method: 'error',
11197
timestamp: new Date(),
11298
type: event.error?.name || 'Error',
11399
message: event.error?.message || event.message,
114100
stack: event.error?.stack || '',
115-
fileLine: event.filename
116-
? `${event.filename}:${event.lineno}:${event.colno}`
117-
: '',
101+
fileLine: event.filename ? `${event.filename}:${event.lineno}:${event.colno}` : '',
118102
};
119103
}
120104

@@ -131,23 +115,22 @@ function createConsoleCatcher(): {
131115
/**
132116
* Initializes the console interceptor by overriding default console methods
133117
*/
134-
function initConsoleCatcher(): void {
135-
if (isInitialized) {
118+
public init(): void {
119+
if (this.isInitialized) {
136120
return;
137121
}
138122

139-
isInitialized = true;
123+
this.isInitialized = true;
140124
const consoleMethods: string[] = ['log', 'warn', 'error', 'info', 'debug'];
141125

142-
consoleMethods.forEach(function overrideConsoleMethod(method) {
126+
consoleMethods.forEach((method) => {
143127
if (typeof window.console[method] !== 'function') {
144128
return;
145129
}
146130

147131
const oldFunction = window.console[method].bind(window.console);
148132

149-
window.console[method] = function (...args: unknown[]): void {
150-
133+
window.console[method] = (...args: unknown[]): void => {
151134
/**
152135
* If the console call originates from Vue's internal runtime bundle, skip interception
153136
* to avoid capturing Vue-internal warnings and causing recursive loops.
@@ -157,9 +140,8 @@ function createConsoleCatcher(): {
157140
return oldFunction(...args);
158141
}
159142

160-
const stack = new Error().stack?.split('\n').slice(2)
161-
.join('\n') || '';
162-
const { message, styles } = formatConsoleArgs(args);
143+
const stack = new Error().stack?.split('\n').slice(2).join('\n') || '';
144+
const { message, styles } = this.formatConsoleArgs(args);
163145

164146
const logEvent: ConsoleLogEvent = {
165147
method,
@@ -171,38 +153,24 @@ function createConsoleCatcher(): {
171153
styles,
172154
};
173155

174-
addToConsoleOutput(logEvent);
156+
this.addToConsoleOutput(logEvent);
175157
oldFunction(...args);
176158
};
177159
});
178160
}
179161

180162
/**
181163
* Handles error events by converting them to console log events
182-
*
183-
* @param event - The error or promise rejection event to handle
184164
*/
185-
function addErrorEvent(event: ErrorEvent | PromiseRejectionEvent): void {
186-
const logEvent = createConsoleEventFromError(event);
187-
188-
addToConsoleOutput(logEvent);
165+
public addErrorEvent(event: ErrorEvent | PromiseRejectionEvent): void {
166+
const logEvent = this.createConsoleEventFromError(event);
167+
this.addToConsoleOutput(logEvent);
189168
}
190169

191170
/**
192171
* Returns the current console output buffer
193172
*/
194-
function getConsoleLogStack(): ConsoleLogEvent[] {
195-
return [ ...consoleOutput ];
173+
public getConsoleLogStack(): ConsoleLogEvent[] {
174+
return [...this.consoleOutput];
196175
}
197-
198-
return {
199-
initConsoleCatcher,
200-
addErrorEvent,
201-
getConsoleLogStack,
202-
};
203176
}
204-
205-
const consoleCatcher = createConsoleCatcher();
206-
207-
export const { initConsoleCatcher, getConsoleLogStack, addErrorEvent } =
208-
consoleCatcher;

src/catcher.ts

Lines changed: 13 additions & 7 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

2121
/**
2222
* Allow to use global VERSION, that will be overwritten by Webpack
@@ -97,6 +97,11 @@ export default class Catcher {
9797
*/
9898
private readonly consoleTracking: boolean;
9999

100+
/**
101+
* Console catcher instance
102+
*/
103+
private consoleCatcher: ConsoleCatcher | null = null;
104+
100105
/**
101106
* Catcher constructor
102107
*
@@ -143,7 +148,8 @@ export default class Catcher {
143148
});
144149

145150
if (this.consoleTracking) {
146-
initConsoleCatcher();
151+
this.consoleCatcher = new ConsoleCatcher();
152+
this.consoleCatcher.init();
147153
}
148154

149155
/**
@@ -244,9 +250,8 @@ export default class Catcher {
244250
/**
245251
* Add error to console logs
246252
*/
247-
248-
if (this.consoleTracking) {
249-
addErrorEvent(event);
253+
if (this.consoleTracking && this.consoleCatcher) {
254+
this.consoleCatcher.addErrorEvent(event);
250255
}
251256

252257
/**
@@ -513,7 +518,8 @@ export default class Catcher {
513518
const userAgent = window.navigator.userAgent;
514519
const location = window.location.href;
515520
const getParams = this.getGetParams();
516-
const consoleLogs = this.consoleTracking && getConsoleLogStack();
521+
const consoleLogs =
522+
this.consoleTracking && this.consoleCatcher ? this.consoleCatcher.getConsoleLogStack() : null;
517523

518524
const addons: JavaScriptAddons = {
519525
window: {
@@ -533,7 +539,7 @@ export default class Catcher {
533539
}
534540

535541
if (consoleLogs && consoleLogs.length > 0) {
536-
addons.consoleOutput = consoleLogs;
542+
(addons as any).consoleOutput = consoleLogs;
537543
}
538544

539545
return addons;

src/types/console.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { ConsoleLogEvent as BaseConsoleLogEvent } from '@hawk.so/types';
2+
3+
/**
4+
* Extended ConsoleLogEvent with styles support for local usage
5+
*/
6+
export interface ExtendedConsoleLogEvent extends Omit<BaseConsoleLogEvent, never> {
7+
/**
8+
* Log method used (e.g., "log", "warn", "error")
9+
*/
10+
method: string;
11+
12+
/**
13+
* Timestamp of the log event
14+
*/
15+
timestamp: Date;
16+
17+
/**
18+
* Type of the log message (e.g., "error", "warning", "info")
19+
*/
20+
type: string;
21+
22+
/**
23+
* The main log message
24+
*/
25+
message: string;
26+
27+
/**
28+
* Stack trace if available
29+
*/
30+
stack?: string;
31+
32+
/**
33+
* File and line number where the log occurred
34+
*/
35+
fileLine?: string;
36+
37+
/**
38+
* CSS styles for %c formatting
39+
*/
40+
styles?: string[];
41+
}

0 commit comments

Comments
 (0)