【问题标题】:make sure slf4j captures stack traces in the log files确保 slf4j 在日志文件中捕获堆栈跟踪
【发布时间】:2021-09-10 20:00:36
【问题描述】:

假设我有简单的代码:

public static void Main(){
throw new NullPointerException("this is an npe");
}

如何使用 slf4j 确保将异常记录到日志文件中。您可以在应用程序属性或 logback 文件中设置 spring 中的默认设置,以确保捕获它,即使它不在 try catch 中?

请注意,我不是在寻找网络控制器的解决方案,或者如何使用 log.error()。我知道该怎么做。我正在寻找一个总体设置,以便我们不会在整个应用程序中丢失异常。这可以应用于 OOM,或打印堆转储等。我将 spring 作为标签包含在内,以防万一解决方案在 applications.properties 中,因为这是一个 spring 项目。

【问题讨论】:

  • 那么您不是也在寻找全局异常处理程序吗?
  • 当然。可以捕获每个异常的东西。我尝试做类似 Thread.setUncaught... 但它只适用于当前线程
  • 我添加了一个答案,希望下面的帮助

标签: java spring-boot logging slf4j


【解决方案1】:

基于此:https://sysgears.com/articles/how-to-redirect-stdout-and-stderr-writing-to-a-log4j-appender/,我发现了一些我认为可行的方法。

在psvm的开头:

 public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
        System.setErr(new PrintStream(new LoggingOutputStream(), true));
}

这表示将错误打印到我的 LoggingOutputStream。 这个类的代码:


public class LoggingOutputStream extends OutputStream {

    /**
     * Default number of bytes in the buffer.
     */
    private static final int DEFAULT_BUFFER_LENGTH = 2048;

    /**
     * Indicates stream state.
     */
    private boolean hasBeenClosed = false;

    /**
     * Internal buffer where data is stored.
     */
    private byte[] buf;

    /**
     * The number of valid bytes in the buffer.
     */
    private int count;

    /**
     * Remembers the size of the buffer.
     */
    private int curBufLength;

    /**
     * The logger to write to.
     */
    private Logger log = LoggerFactory.getLogger(LoggingOutputStream.class);
    public LoggingOutputStream(){
        curBufLength = DEFAULT_BUFFER_LENGTH;
        buf = new byte[curBufLength];
        count = 0;
    }
    /**
     *
     * Writes the specified byte to this output stream.
     *
     * @param b the byte to write
     * @throws IOException if an I/O error occurs.
     */
    public void write(final int b) throws IOException {
        if (hasBeenClosed) {
            log.error("The stream has been closed.");
            throw new IOException("The stream has been closed.");
        }
        // don't log nulls
        if (b == 0) {
            return;
        }
        // would this be writing past the buffer?
        if (count == curBufLength) {
            // grow the buffer
            final int newBufLength = curBufLength +
                    DEFAULT_BUFFER_LENGTH;
            final byte[] newBuf = new byte[newBufLength];
            System.arraycopy(buf, 0, newBuf, 0, curBufLength);
            buf = newBuf;
            curBufLength = newBufLength;
        }

        buf[count] = (byte) b;
        count++;
    }

    /**
     * Flushes this output stream and forces any buffered output
     * bytes to be written out.
     */
    @Override
    public void flush() {
        if (count == 0) {
            return;
        }
        final byte[] bytes = new byte[count];
        System.arraycopy(buf, 0, bytes, 0, count);
        String str = new String(bytes);
        log.error(str);
        count = 0;
    }

    /**
     * Closes this output stream and releases any system resources
     * associated with this stream.
     */
    @Override
    public void close() {
        flush();
        hasBeenClosed = true;
    }
}

现在,所有错误都被“拦截”并发送到我的日志文件。

【讨论】:

    【解决方案2】:

    好的,正如您所说,您可以添加控制器建议:

    @ControllerAdvice
    public class YourCustomHandler extends ResponseEntityExceptionHandler {
    
    @Autowired
    MessageSource messageSource;
    
    @Override
    protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
       // you can do logs here and pass a custom object that will parse to json object
        return buildResponseEntity(apiError);
    }
    
    @ExceptionHandler(YourCustomException.class)
    protected ResponseEntity<Object> handleTthException(
            YourCustomException ex, Locale locale) {
        
        return buildResponseEntity(apiError);
    }
    
    private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
        return new ResponseEntity<>(apiError, apiError.getStatus());
    }
    

    }

    【讨论】:

    • 这是一个全局错误处理程序吗?它会捕获 OOM 错误吗?还是随机线程中的堆栈溢出?似乎主要与网络请求有关?
    • 我说我专门寻找比 crontroller 设备更广泛的东西?
    猜你喜欢
    • 2021-09-10
    • 1970-01-01
    • 2012-04-19
    • 2023-01-20
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多