diff --git a/packages/frontend-2/lib/core/helpers/observability.ts b/packages/frontend-2/lib/core/helpers/observability.ts index 8258f95b1c..e6c9935052 100644 --- a/packages/frontend-2/lib/core/helpers/observability.ts +++ b/packages/frontend-2/lib/core/helpers/observability.ts @@ -157,7 +157,7 @@ export function enableCustomLoggerHandling(params: { const firstString = args.find(isString) const otherData: unknown[] = args.filter( - (o) => !(o instanceof Error) && o !== firstString + (o) => o !== firstString && o !== firstError ) const errorMessage = firstError?.message ?? firstString ?? `Unknown error` diff --git a/packages/frontend-2/plugins/001-logger.ts b/packages/frontend-2/plugins/001-logger.ts index 4e6bf27edd..270e873fe3 100644 --- a/packages/frontend-2/plugins/001-logger.ts +++ b/packages/frontend-2/plugins/001-logger.ts @@ -1,3 +1,4 @@ +import { collectLongTrace } from '@speckle/shared' import { omit } from 'lodash-es' import type { SetRequired } from 'type-fest' import { useReadUserId } from '~/lib/auth/composables/activeUser' @@ -170,6 +171,7 @@ export default defineNuxtPlugin(async (nuxtApp) => { }) => { if (!args.length) return + const stack = collectLongTrace() const isError = ['error', 'fatal'].includes(level) const isImportant = !!otherData?.important if (!isError && !isImportant) return @@ -189,6 +191,9 @@ export default defineNuxtPlugin(async (nuxtApp) => { properties: { mainSeqErrorMessage: errorMessage, // weird name to avoid collision with otherData extraData: nonObjectOtherData, + stack, + firstError, + firstString, ...otherData, ...collectCoreInfo() }, @@ -202,6 +207,7 @@ export default defineNuxtPlugin(async (nuxtApp) => { properties: { extraData: nonObjectOtherData, firstError, + stack, ...otherData, ...collectCoreInfo() } diff --git a/packages/shared/src/core/helpers/utility.ts b/packages/shared/src/core/helpers/utility.ts index 48d28223a6..898832b4d4 100644 --- a/packages/shared/src/core/helpers/utility.ts +++ b/packages/shared/src/core/helpers/utility.ts @@ -61,12 +61,16 @@ export const isNonNullable = (v: V): v is NonNullable => !!v /** * Make the promise throw after enough time has passed. Useful for implementing timeout functionality in various flows. */ -export const timeoutAt = (ms: number, optionalMessage?: string) => - new Promise((_resolve, reject) => +export const timeoutAt = (ms: number, optionalMessage?: string) => { + // create error beforehand, so we have a better stack trace + const err = new TimeoutError(optionalMessage || 'timeoutAt() timed out') + + return new Promise((_resolve, reject) => setTimeout(() => { - reject(new TimeoutError(optionalMessage || 'timeoutAt() timed out')) + reject(err) }, ms) ) +} /** * Invoke and return fn(), but retry it up to n times if it throws