diff --git a/front/package.json b/front/package.json index 1ec1c5819..93bfbce6d 100644 --- a/front/package.json +++ b/front/package.json @@ -38,6 +38,7 @@ "lru-cache": "7.14.1", "moment": "2.29.4", "showdown": "2.1.0", + "stacktrace-js": "2.0.2", "standardized-audio-context": "25.3.55", "text-clipper": "2.2.0", "transliteration": "2.3.5", diff --git a/front/src/composables/useLogger.ts b/front/src/composables/useLogger.ts index d1c86c13a..719ab82f9 100644 --- a/front/src/composables/useLogger.ts +++ b/front/src/composables/useLogger.ts @@ -1,3 +1,5 @@ +import Stacktrace from 'stacktrace-js' + type LogLevel = 'info' | 'warn' | 'error' | 'debug' | 'time' const LOG_LEVEL_LABELS: Record = { @@ -42,31 +44,33 @@ const FILETYPE_COLOR: Record = { default: '#000' } -const getFile = () => { - const { stack } = new Error() - const line = stack?.split('\n')[2] ?? '' - const [, method, url, lineNo] = line.match(/^(\w+)?(?:\/<)*@(.+?)(?:\?.*)?:(\d+):\d+$/) ?? [] - const file = url.startsWith(location.origin) ? url.slice(location.origin.length) : url - return { method, file, lineNo } -} - // NOTE: We're pushing all logs to the end of the event loop const createLoggerFn = (level: LogLevel) => { // NOTE: We don't want to handle logs ourselves in tests // eslint-disable-next-line no-console if (import.meta.env.VITEST) return console[level] - return (...args: any[]) => { + // NOTE: Don't log time and debug in production + if (level === 'time' || level === 'debug') { + if (import.meta.env.PROD) return () => { } + } + + return async (...args: any[]) => { const timestamp = new Date().toUTCString() - const { method, file, lineNo } = getFile() + const stacktrace = await Stacktrace.get() + + // NOTE: First call is a call to logger.log, second one is a call to the function that called logger.log + const { functionName, fileName, lineNumber } = stacktrace[1] + + let file = fileName + + try { + const url = new URL(fileName ?? '') + file = url.pathname + } catch (error) { } const ext = file?.split('.').pop() ?? 'default' - // NOTE: Don't log time and debug in production - if (level === 'time' || level === 'debug') { - if (import.meta.env.PROD) return - } - // eslint-disable-next-line no-console console[level === 'time' ? 'debug' : level]( '%c %c [%s] %c %s %c%s', @@ -76,9 +80,9 @@ const createLoggerFn = (level: LogLevel) => { `background: ${LOG_LEVEL_BACKGROUND[level]}; color: ${LOG_LEVEL_COLOR[level]}; border-radius: 1em 0 0 1em`, LOG_LEVEL_LABELS[level], `background: ${FILETYPE_BACKGROUND[ext]}; color: ${FILETYPE_COLOR[ext]}; border-radius: 0 1em 1em 0`, - method !== undefined - ? ` ${file}:${lineNo} ${method}() ` - : ` ${file}:${lineNo} `, + functionName !== undefined + ? ` ${file}:${lineNumber} ${functionName}() ` + : ` ${file}:${lineNumber} `, ...args ) } diff --git a/front/yarn.lock b/front/yarn.lock index 40f5758b5..ec13ca97a 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -3555,6 +3555,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.22.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" @@ -5672,6 +5679,11 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== + source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -5689,6 +5701,35 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +stack-generator@^2.0.5: + version "2.0.10" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" + integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== + dependencies: + stackframe "^1.3.4" + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +stacktrace-gps@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" + integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== + dependencies: + source-map "0.5.6" + stackframe "^1.3.4" + +stacktrace-js@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + standardized-audio-context-mock@9.6.18: version "9.6.18" resolved "https://registry.yarnpkg.com/standardized-audio-context-mock/-/standardized-audio-context-mock-9.6.18.tgz#0b2dc9039a0f923a64012cf53407072a7e05c62e"