Skip to content

Commit 4d4ce33

Browse files
committed
Added browser plugins
1 parent b7c280c commit 4d4ce33

File tree

5 files changed

+89
-50
lines changed

5 files changed

+89
-50
lines changed

packages/browser/src/BrowserExceptionlessClient.ts

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import {
44
} from "@exceptionless/core";
55

66
import { BrowserConfiguration } from "./configuration/BrowserConfiguration.js";
7+
import { BrowserGlobalHandlerPlugin } from "./plugins/BrowserGlobalHandlerPlugin.js";
8+
import { BrowserLifeCyclePlugin } from "./plugins/BrowserLifeCyclePlugin.js";
9+
import { BrowserWrapFunctions } from "./plugins/BrowserWrapFunctions.js";
710
import { BrowserErrorParser } from "./services/BrowserErrorParser.js";
811
import { BrowserModuleCollector } from "./services/BrowserModuleCollector.js";
912
import { BrowserRequestInfoCollector } from "./services/BrowserRequestInfoCollector.js";
@@ -15,30 +18,33 @@ export class BrowserExceptionlessClient extends ExceptionlessClient {
1518
}
1619

1720
public async startup(configurationOrApiKey?: (config: BrowserConfiguration) => void | string): Promise<void> {
21+
const config = this.config;
1822
if (configurationOrApiKey) {
1923
const settings = this.getDefaultsSettingsFromScriptTag();
2024
if (settings?.apiKey) {
21-
this.config.apiKey = settings.apiKey;
25+
config.apiKey = settings.apiKey;
2226
}
2327

2428
if (settings?.serverUrl) {
25-
this.config.serverUrl = settings.serverUrl;
29+
config.serverUrl = settings.serverUrl;
2630
}
2731

2832
if (settings?.serverUrl) {
29-
this.config.serverUrl = settings.serverUrl;
33+
config.serverUrl = settings.serverUrl;
3034
}
3135

3236
if (settings?.includePrivateInformation) {
33-
this.config.includePrivateInformation = settings.includePrivateInformation === "true";
37+
config.includePrivateInformation = settings.includePrivateInformation === "true";
3438
}
3539

36-
this.config.services.errorParser = new BrowserErrorParser();
37-
this.config.services.moduleCollector = new BrowserModuleCollector();
38-
this.config.services.requestInfoCollector = new BrowserRequestInfoCollector();
39-
this.config.services.submissionClient = new BrowserFetchSubmissionClient(this.config);
40+
config.addPlugin(new BrowserGlobalHandlerPlugin());
41+
config.addPlugin(new BrowserLifeCyclePlugin());
42+
config.addPlugin(new BrowserWrapFunctions());
4043

41-
// TODO: Register platform specific plugins.
44+
config.services.errorParser = new BrowserErrorParser();
45+
config.services.moduleCollector = new BrowserModuleCollector();
46+
config.services.requestInfoCollector = new BrowserRequestInfoCollector();
47+
config.services.submissionClient = new BrowserFetchSubmissionClient(config);
4248
}
4349

4450
await super.startup(configurationOrApiKey);
@@ -58,31 +64,3 @@ export class BrowserExceptionlessClient extends ExceptionlessClient {
5864
return null;
5965
}
6066
}
61-
62-
/*
63-
TODO: We currently are unable to parse string exceptions.
64-
function processJQueryAjaxError(event, xhr, settings, error:string): void {
65-
let client = ExceptionlessClient.default;
66-
if (xhr.status === 404) {
67-
client.submitNotFound(settings.url);
68-
} else if (xhr.status !== 401) {
69-
client.createUnhandledException(error, "JQuery.ajaxError")
70-
.setSource(settings.url)
71-
.setProperty("status", xhr.status)
72-
.setProperty("request", settings.data)
73-
.setProperty("response", xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024))
74-
.submit();
75-
}
76-
}
77-
*/
78-
79-
//TraceKit.report.subscribe(processUnhandledException);
80-
//TraceKit.extendToAsynchronousCallbacks();
81-
82-
83-
// if (typeof $ !== "undefined" && $(document)) {
84-
// $(document).ajaxError(processJQueryAjaxError);
85-
// }
86-
//declare var $;
87-
88-
// browser plugin startup method wires up all handlers?

packages/browser/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
export { BrowserConfiguration } from "./configuration/BrowserConfiguration.js";
2+
export { BrowserGlobalHandlerPlugin } from "./plugins/BrowserGlobalHandlerPlugin.js";
3+
export { BrowserLifeCyclePlugin } from "./plugins/BrowserLifeCyclePlugin.js";
4+
export { BrowserWrapFunctions } from "./plugins/BrowserWrapFunctions.js";
25
export { BrowserErrorParser } from "./services/BrowserErrorParser.js";
36
export { BrowserModuleCollector } from "./services/BrowserModuleCollector.js";
47
export { BrowserRequestInfoCollector } from "./services/BrowserRequestInfoCollector.js";

packages/browser/src/plugins/GlobalHandlerPlugin.ts renamed to packages/browser/src/plugins/BrowserGlobalHandlerPlugin.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import {
22
ExceptionlessClient,
33
IEventPlugin,
4-
PluginContext,
5-
nameof
4+
PluginContext
65
} from "@exceptionless/core";
76

8-
export class GlobalHandlerPlugin implements IEventPlugin {
7+
declare var $: (document: Document) => { (): any; new(): any; ajaxError: { (document: (event: Event, xhr: { responseText: string; status: number; }, settings: { data: unknown; url: string; }, error: string) => void): void; new(): any; }; };
8+
9+
export class BrowserGlobalHandlerPlugin implements IEventPlugin {
910
public priority: number = 100;
10-
public name: string = "GlobalHandlerPlugin";
11+
public name: string = "BrowserGlobalHandlerPlugin";
1112

1213
private _client: ExceptionlessClient = null;
1314

@@ -17,13 +18,15 @@ export class GlobalHandlerPlugin implements IEventPlugin {
1718
}
1819

1920
this._client = context.client;
20-
Error.stackTraceLimit = Infinity;
21+
Error.stackTraceLimit = 50;
2122

2223
// TODO: Discus if we want to unwire this handler in suspend?
2324
const originalOnError: OnErrorEventHandlerNonNull = globalThis.onerror;
2425
globalThis.onerror = (event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error) => {
2526
// TODO: Handle async
26-
void this._client.submitUnhandledException(error || this.buildError(event, source, lineno, colno), nameof<Window>("onerror"));
27+
void this._client.createUnhandledException(error || this.buildError(event, source, lineno, colno), "onerror")
28+
.setSource(source)
29+
.submit();
2730

2831
// eslint-disable-next-line prefer-rest-params
2932
return originalOnError ? originalOnError.apply(this, ...arguments) : false;
@@ -41,18 +44,40 @@ export class GlobalHandlerPlugin implements IEventPlugin {
4144
} catch (ex) { }
4245

4346
// TODO: Handle async
44-
void this._client.submitUnhandledException(error, nameof<Window>("onunhandledrejection"));
47+
void this._client.submitUnhandledException(error, "onunhandledrejection");
4548

4649
// eslint-disable-next-line prefer-rest-params
4750
return originalOnunhandledrejection ? originalOnunhandledrejection.apply(this, ...arguments) : false;
4851
};
4952

53+
if (typeof $ !== "undefined" && $(document)) {
54+
$(document).ajaxError((event: Event, xhr: { responseText: string, status: number }, settings: { data: unknown, url: string }, error: string) => {
55+
if (xhr.status === 404) {
56+
// TODO: Handle async
57+
void this._client.submitNotFound(settings.url);
58+
} else if (xhr.status !== 401) {
59+
// TODO: Handle async
60+
void this._client.createUnhandledException(new Error(error), "JQuery.ajaxError")
61+
.setSource(settings.url)
62+
.setProperty("status", xhr.status)
63+
.setProperty("request", settings.data)
64+
.setProperty("response", xhr.responseText?.slice(0, 1024))
65+
.submit();
66+
}
67+
});
68+
}
69+
5070
return Promise.resolve();
5171
}
5272

5373
private buildError(event: Event | string, source?: string, lineno?: number, colno?: number): Error {
74+
if (Object.prototype.toString.call(event) === "[object ErrorEvent]") {
75+
// TODO: See if this is the error event.
76+
return (<ErrorEvent>event).error;
77+
}
78+
5479
let name: string = "Error";
55-
let message: string = Object.prototype.toString.call(event) === '[object ErrorEvent]' ? (<ErrorEvent>event).message : null;
80+
let message: string = Object.prototype.toString.call(event) === '[object ErrorEvent]' ? (<ErrorEvent>event).error : null;
5681
if (message) {
5782
const errorNameRegex: RegExp = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Aggregate|Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$)/i;
5883
const [_, errorName, errorMessage] = errorNameRegex.exec(message);
@@ -64,7 +89,7 @@ export class GlobalHandlerPlugin implements IEventPlugin {
6489
}
6590
}
6691

67-
const error = new Error(message || "Script error");
92+
const error = new Error(message || "Script error.");
6893
error.name = name;
6994
error.stack = `at ${source || ""}:${!isNaN(lineno) ? lineno : 0}${!isNaN(colno) ? ":" + colno : ""}`;
7095
return error;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
ExceptionlessClient,
3+
IEventPlugin,
4+
PluginContext
5+
} from "@exceptionless/core";
6+
7+
export class BrowserLifeCyclePlugin implements IEventPlugin {
8+
public priority: number = 105;
9+
public name: string = "BrowserLifeCyclePlugin";
10+
11+
private _client: ExceptionlessClient = null;
12+
13+
public startup(context: PluginContext): Promise<void> {
14+
if (this._client) {
15+
return;
16+
}
17+
18+
this._client = context.client;
19+
20+
globalThis.addEventListener("beforeunload", async () => await this._client.suspend());
21+
document.addEventListener("visibilitychange", async () => {
22+
if (document.visibilityState === 'visible') {
23+
await this._client.startup()
24+
} else {
25+
await this._client.suspend()
26+
}
27+
});
28+
29+
return Promise.resolve();
30+
}
31+
}

packages/browser/src/plugins/LifeCyclePlugin.ts renamed to packages/browser/src/plugins/BrowserWrapFunctions.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import {
44
PluginContext
55
} from "@exceptionless/core";
66

7-
export class LifeCyclePlugin implements IEventPlugin {
8-
public priority: number = 100;
9-
public name: string = "LifeCyclePlugin";
7+
export class BrowserWrapFunctions implements IEventPlugin {
8+
public priority: number = 110;
9+
public name: string = "BrowserWrapFunctions";
1010

1111
private _client: ExceptionlessClient = null;
1212

@@ -16,7 +16,9 @@ export class LifeCyclePlugin implements IEventPlugin {
1616
}
1717

1818
this._client = context.client;
19-
globalThis.addEventListener("beforeunload", async () => await this._client.processQueue());
19+
20+
// TODO: TraceKit.extendToAsynchronousCallbacks();
21+
2022
return Promise.resolve();
2123
}
2224
}

0 commit comments

Comments
 (0)