Каковы наилучшие методы для «обрезки» исключения stacktrace перед регистрацией с помощью SLF4J?

Примечание. Я кодирую в Kotlin, но я думаю, что ответ, вероятно, будет таким же для Kotlin или Java

Я создаю веб-фреймворк, в котором я хочу сообщать об ошибках пользователю вместе с stacktrace, относящимся к тому, где в коде пользователя ошибка (это немного сложно, потому что ошибки на самом деле являются ошибками JavaScript, сгенерированными веб-браузером, который «дистанционное управление» веб-каркасом).

Я могу создать Throwable (), но у stacktrace будет много лишних StackTraceElements из моего кода рамки, как до, так и после StackTraceElements, относящихся к пользователю кода, например:

[nioEventLoopGroup-3-2] ERROR com.github.sanity.kweb.KWeb - JavaScript error: 'Invalid left-hand side expression in postfix operation' Caused by executing: '1++2': at com.github.sanity.kweb.KWeb.execute(KWeb.kt:136) at com.github.sanity.kweb.RootReceiver.execute(RootReceiver.kt:30) at com.github.sanity.kweb.dom.element.Element.execute(Element.kt:24) at com.github.sanity.kweb.demos.todo.TodoKt$main$1.invoke(todo.kt:15) at com.github.sanity.kweb.KWeb$2.invoke(KWeb.kt:63) at com.github.sanity.kweb.KWeb$2.invoke(KWeb.kt:28) at org.wasabifx.wasabi.protocol.http.HttpRequestHandler.runHandlers(HttpRequestHandler.kt:123) at org.wasabifx.wasabi.protocol.http.HttpRequestHandler.channelRead0(HttpRequestHandler.kt:82) at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) at org.wasabifx.wasabi.core.HttpPipelineInitializer.applyHttp1Pipeline(HttpPipelineInitializer.kt:72) at org.wasabifx.wasabi.core.HttpPipelineInitializer.initHttpPipeline(HttpPipelineInitializer.kt:51) at org.wasabifx.wasabi.core.HttpPipelineInitializer.channelRead0(HttpPipelineInitializer.kt:32) at org.wasabifx.wasabi.core.HttpPipelineInitializer.channelRead0(HttpPipelineInitializer.kt:17) at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:610) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:551) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:465) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:437) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873) at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144) at java.lang.Thread.run(Thread.java:745) 

Поэтому я фильтрую StackTraceElements, чтобы удалить те, которые не имеют отношения к пользователю (в основном, путем фильтрации по имени класса и пакета), а затем добавление stacktrace к StringBuilder в стандартном формате stacktrace, прежде чем записывать его в SLF4J в виде строки (с линией разделители).

 val disregardClassPrefixes = listOf(KWeb::class.jvmName, RootReceiver::class.jvmName, Element::class.jvmName, "org.wasabifx", "io.netty", "java.lang") debugInfo.throwable.stackTrace .filter { ste -> ste.lineNumber >= 0 && !disregardClassPrefixes.any { ste.className.startsWith(it) } } .forEach { stackTraceElement -> logStatementBuilder.appendln(" at ${stackTraceElement.className}.${stackTraceElement.methodName}(${stackTraceElement.fileName}:${stackTraceElement.lineNumber})") } 

Это дает нам гораздо приятнее:

 [nioEventLoopGroup-3-2] ERROR com.github.sanity.kweb.KWeb - JavaScript error: 'Invalid left-hand side expression in postfix operation' Caused by executing: '1++2': at com.github.sanity.kweb.demos.todo.TodoKt$main$1.invoke(todo.kt:15) 

Однако этот подход немного пахнет, и мне интересно, есть ли лучший способ сделать это.

    Единственный действительный ответ – DO NOT.

    Вы не можете заранее знать, какие части трассировки стека являются релевантными, и если вы удаляете или обрезаете все, вы можете удалить один фрагмент данных, который позволяет устранить неполадки.

    Повторить: НЕ ДЕЛАЙТЕ ЭТО. Трассировка стека является уродливой, но необходимой. Также сопротивляйтесь желанию удалить разделы «Causeed By». Они существуют по какой-то причине и часто содержат «реальное» исключение.

    Я думаю, что лучший подход – уловить ошибку JavaScript и выбросить настраиваемое исключение, содержащее сообщение (или детали для создания сообщения), которое вы хотите получить пользователю.