diff --git a/src/Exceptionless.Web/ClientApp/package-lock.json b/src/Exceptionless.Web/ClientApp/package-lock.json
index 86c1892bda..dadf341854 100644
--- a/src/Exceptionless.Web/ClientApp/package-lock.json
+++ b/src/Exceptionless.Web/ClientApp/package-lock.json
@@ -11,11 +11,9 @@
"@exceptionless/browser": "^3.0.4",
"@iconify-json/mdi": "^1.1.54",
"@tanstack/svelte-table": "^8.10.3",
- "@types/dompurify": "^3.0.3",
"@web3-storage/parse-link-header": "^3.1.0",
"class-validator": "^0.14.0",
"daisyui": "^3.8.3",
- "dompurify": "^3.0.6",
"oidc-client-ts": "^2.3.0",
"svelte-local-storage-store": "^0.6.3",
"svelte-time": "^0.8.0",
@@ -1022,14 +1020,6 @@
"integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==",
"dev": true
},
- "node_modules/@types/dompurify": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.3.tgz",
- "integrity": "sha512-odiGr/9/qMqjcBOe5UhcNLOFHSYmKFOyr+bJ/Xu3Qp4k1pNPAlNLUVNNLcLfjQI7+W7ObX58EdD3H+3p3voOvA==",
- "dependencies": {
- "@types/trusted-types": "*"
- }
- },
"node_modules/@types/estree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
@@ -1065,11 +1055,6 @@
"integrity": "sha512-7yQiX6MWSFSvc/1wW5smJMZTZ4fHOd+hqLr3qr/HONDxHEa2bnYAsOcGBOEqFIjd4yetwMOdEDdeW+udRAQnHA==",
"dev": true
},
- "node_modules/@types/trusted-types": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz",
- "integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ=="
- },
"node_modules/@types/validator": {
"version": "13.11.1",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.1.tgz",
@@ -2161,11 +2146,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/dompurify": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz",
- "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w=="
- },
"node_modules/electron-to-chromium": {
"version": "1.4.495",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.495.tgz",
diff --git a/src/Exceptionless.Web/ClientApp/package.json b/src/Exceptionless.Web/ClientApp/package.json
index 351a300028..699eae9ef6 100644
--- a/src/Exceptionless.Web/ClientApp/package.json
+++ b/src/Exceptionless.Web/ClientApp/package.json
@@ -49,11 +49,9 @@
"@exceptionless/browser": "^3.0.4",
"@iconify-json/mdi": "^1.1.54",
"@tanstack/svelte-table": "^8.10.3",
- "@types/dompurify": "^3.0.3",
"@web3-storage/parse-link-header": "^3.1.0",
"class-validator": "^0.14.0",
"daisyui": "^3.8.3",
- "dompurify": "^3.0.6",
"oidc-client-ts": "^2.3.0",
"svelte-local-storage-store": "^0.6.3",
"svelte-time": "^0.8.0",
diff --git a/src/Exceptionless.Web/ClientApp/src/lib/components/events/SimpleStackTrace.svelte b/src/Exceptionless.Web/ClientApp/src/lib/components/events/SimpleStackTrace.svelte
index 6f9d64712c..b272c37d6c 100644
--- a/src/Exceptionless.Web/ClientApp/src/lib/components/events/SimpleStackTrace.svelte
+++ b/src/Exceptionless.Web/ClientApp/src/lib/components/events/SimpleStackTrace.svelte
@@ -1,112 +1,30 @@
{@html stackTrace}
+ class="max-h-[500px] overflow-y-scroll overflow-x-scroll break-normal resize-y whitespace-pre tab-size-2">
+
+
+ {#each errors.reverse() as error, index}
+ {#if error.stack_trace}
+ {cleanStackTrace(
+ error.stack_trace
+ )}
+ {#if index < errors.length - 1}
+ --- End of inner error stack trace ---
+ {/if}
+ {/if}
+ {/each}
+
+
diff --git a/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTrace.svelte b/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTrace.svelte
index 99091f3a21..129c9a0563 100644
--- a/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTrace.svelte
+++ b/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTrace.svelte
@@ -1,198 +1,27 @@
{@html stackTrace}
+ >
+ {#each errors.reverse() as error, index}
+ {#if error.stack_trace}
+
+ {#each error.stack_trace as frame}
+ {getStackFrame(frame)}
+ {/each}
+ {#if index < errors.length - 1}
+
--- End of inner exception stack trace ---
+ {/if}
+
+ {/if}
+ {/each}
+
+
diff --git a/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTraceHeader.svelte b/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTraceHeader.svelte
new file mode 100644
index 0000000000..42e9ed71a9
--- /dev/null
+++ b/src/Exceptionless.Web/ClientApp/src/lib/components/events/StackTraceHeader.svelte
@@ -0,0 +1,19 @@
+
+
+{#each errors as error, index}
+
+ {#if index > 0}
+ --->
+ {/if}
+ {#if error.type}
+ {error.type}:
+ {/if}
+ {#if error.message}
+ {error.message}
+ {/if}
+
+{/each}
diff --git a/src/Exceptionless.Web/ClientApp/src/lib/components/events/views/Overview.svelte b/src/Exceptionless.Web/ClientApp/src/lib/components/events/views/Overview.svelte
index 5c5a027f02..63b35caf37 100644
--- a/src/Exceptionless.Web/ClientApp/src/lib/components/events/views/Overview.svelte
+++ b/src/Exceptionless.Web/ClientApp/src/lib/components/events/views/Overview.svelte
@@ -199,7 +199,10 @@
Stack Trace
-
-
+ {#if event.data?.['@error']}
+
+ {:else}
+
+ {/if}
{/if}
diff --git a/src/Exceptionless.Web/ClientApp/src/lib/helpers/persistent-event.ts b/src/Exceptionless.Web/ClientApp/src/lib/helpers/persistent-event.ts
index a7161ae431..00e8109820 100644
--- a/src/Exceptionless.Web/ClientApp/src/lib/helpers/persistent-event.ts
+++ b/src/Exceptionless.Web/ClientApp/src/lib/helpers/persistent-event.ts
@@ -1,5 +1,10 @@
import type { PersistentEvent } from '$lib/models/api';
-import type { ErrorInfo, SimpleErrorInfo } from '$lib/models/client-data';
+import type {
+ ErrorInfo,
+ ParameterInfo,
+ SimpleErrorInfo,
+ StackFrameInfo
+} from '$lib/models/client-data';
import { buildUrl } from './url';
export function getLocation(event: PersistentEvent) {
@@ -80,3 +85,151 @@ export function getTargetInfoMessage(error: ErrorInfo) {
const target = getTargetInfo(error);
return target?.Message;
}
+
+function getParameter(parameter: ParameterInfo) {
+ let result = '';
+
+ const parts = [];
+ if (parameter.type_namespace) {
+ parts.push(parameter.type_namespace);
+ }
+
+ if (parameter.type) {
+ parts.push(parameter.type);
+ }
+
+ result += parts.join('.').replace('+', '.');
+
+ if (parameter.generic_arguments && parameter.generic_arguments.length > 0) {
+ result += `[${parameter.generic_arguments.join(',')}]`;
+ }
+
+ if (parameter.name) {
+ result += ` ${parameter.name}`;
+ }
+
+ return result;
+}
+
+function getParameters(parameters?: ParameterInfo[]) {
+ let result = '(';
+
+ parameters?.forEach((parameter, index) => {
+ if (index > 0) {
+ result += ', ';
+ }
+
+ result += getParameter(parameter);
+ });
+
+ return result + ')';
+}
+
+export function getStackFrame(frame: StackFrameInfo) {
+ if (!frame) {
+ return '\r\n';
+ }
+
+ const typeNameParts = [];
+ if (frame.declaring_namespace) {
+ typeNameParts.push(frame.declaring_namespace);
+ }
+
+ if (frame.declaring_type) {
+ typeNameParts.push(frame.declaring_type);
+ }
+
+ typeNameParts.push(frame.name || '');
+
+ let result = `at ${typeNameParts.join('.').replace('+', '.')}`;
+
+ if (frame.generic_arguments && frame.generic_arguments.length > 0) {
+ result += `[${frame.generic_arguments.join(',')}]`;
+ }
+
+ result += getParameters(frame.parameters);
+ if (frame.data && (frame.data.ILOffset || frame.data.NativeOffset)) {
+ result += ` at offset ${frame.data.ILOffset || frame.data.NativeOffset}`;
+ }
+
+ if (frame.file_name) {
+ result += ` in ${frame.file_name}`;
+ if (frame.line_number) {
+ result += `:line ${frame.line_number}`;
+ }
+
+ if (frame.column) {
+ result += `:col ${frame.column}`;
+ }
+ }
+
+ return `${result}\r\n`;
+}
+
+function getStackTraceHeader(errors: T[]) {
+ let header = '';
+ errors.forEach((error, index) => {
+ if (index > 0) {
+ header += ' ---> ';
+ }
+
+ const hasType = !!error.type;
+ if (hasType) {
+ header += `${error.type}: `;
+ }
+
+ if (error.message) {
+ header += error.message;
+ }
+
+ if (hasType) {
+ header += '\r\n';
+ }
+ });
+
+ return header;
+}
+
+export function getErrorInfoStackTrace(error: ErrorInfo) {
+ function buildStackFrames(errors: ErrorInfo[]) {
+ let frames = '';
+ errors.forEach((error, index) => {
+ const stackTrace = error.stack_trace;
+ if (stackTrace) {
+ stackTrace.forEach((trace) => {
+ frames += getStackFrame(trace);
+ });
+
+ if (index < errors.length - 1) {
+ frames += '--- End of inner exception stack trace ---';
+ }
+ }
+ });
+
+ return frames;
+ }
+
+ const errors = getErrors(error);
+ return getStackTraceHeader(errors) + buildStackFrames(errors.reverse());
+}
+
+export function getSimpleErrorInfoStackTrace(error: SimpleErrorInfo) {
+ function buildStackFrames(errors: SimpleErrorInfo[]) {
+ let frames = '';
+ errors.forEach((error, index) => {
+ const stackTrace = error.stack_trace;
+ if (stackTrace) {
+ frames += stackTrace.replace(' ', '');
+
+ if (index < errors.length - 1) {
+ frames += '--- End of inner error stack trace ---';
+ }
+ }
+ });
+
+ return frames;
+ }
+
+ const errors = getErrors(error);
+ return getStackTraceHeader(errors) + buildStackFrames(errors.reverse());
+}