Java Logging:记录所有>调试但仅在发生错误时

Tom*_*asz 5 java logging

在请求处理期间,会记录大量调试日志。在某些时候,如果出现问题,可能会记录错误。想法:当在整个请求处理过程中没有发生错误时,我想忽略所有调试,但是如果发生错误,我想在日志中看到所有之前和之后的调试。

是否有任何开箱即用的解决方案(在 log4j、logback 或任何其他中),或者我必须为我的 Logger 实现某种包装器,它将“缓存”每个线程的调试直到需要它们?

tak*_*sot 5

它不是一个内置的解决方案,但这里是我的草案实现,它可以是非常小的并且易于集成。它使用 log4j2,如果您已经在使用 log4j2 或 slf4j,则无需更改客户端代码。

所以对应的测试用例

  @Test
  public void no_error_so_no_log() throws Exception {
    LogEventCollector.clean();
    try {
      log.debug("hello world");
      log.debug("some debug again");
    } finally {
      LogEventCollector.clean();
    }
  }

  @Test
  public void error_so_log_from_the_beginning() throws Exception {
    LogEventCollector.clean();
    try {
      log.debug("hello world");
      log.error("some error", new RuntimeException("whatever"));
      log.debug("some debug again");
    } finally {
      LogEventCollector.clean();
    }
  }
Run Code Online (Sandbox Code Playgroud)

LogEventCollector将负责 Web 应用程序请求线程的初始化和清理。可以将其放入 servlet 中Filter

我已经创建了我的自定义 Appender。Appender 的核心如下:

    @Override
    public void append(LogEvent event) {
      if (Level.ERROR.isLessSpecificThan(event.getLevel()))
        LogEventCollector.markError();
      if (!LogEventCollector.hadError()) {
        LogEventCollector.collect(event);
        return;
      }
      for (LogEvent collected : LogEventCollector.events())
        push_log_out(collected);
      //and the current one
      push_log_out(event);
    }
Run Code Online (Sandbox Code Playgroud)

方法push_log_event is the real logging. It could your custom implementation or a delegate to an other one (like AsyncAppender).

LogEventCollector本身:

  public class LogEventCollector {
    static ThreadLocal<Context> LOG_COLLECTOR = new ThreadLocal<Context>() {
      @Override
      protected Context initialValue() {
        return new Context();
      }
    };

    public static void clean() {
      LOG_COLLECTOR.get().clean();
    }

    static class Context {
      boolean had_error = false;
      List<LogEvent> events = new ArrayList<LogEvent>();

      public void clean() {
        had_error = false;
        events = new ArrayList<LogEvent>();
      }
    }

    public static void markError() {
      LOG_COLLECTOR.get().had_error = true;
    }

    public static boolean hadError() {
      return LOG_COLLECTOR.get().had_error;
    }

    public static void collect(LogEvent event) {
      LOG_COLLECTOR.get().events.add(event);
    }

    public static List<LogEvent> events() {
      List<LogEvent> ret = LOG_COLLECTOR.get().events;
      LOG_COLLECTOR.get().events = new ArrayList<LogEvent>();
      return ret;
    }

  }
Run Code Online (Sandbox Code Playgroud)

我想根据自己的需求定制应该就够了。


dsh*_*dsh -1

我不知道有哪个系统可以这样工作。它需要足够的内存来缓冲无限量的日志,以便稍后将其写出(这可能需要很长时间,甚至耗尽磁盘空间)。我遇到的每个日志系统都会在记录日志消息时决定是否写入日志消息,而不是稍后。

但是,您可以在调试消息中包含合适的(可能对请求而言是唯一的)标记,然后在事后搜索或过滤日志文件以查找与错误关联的调试消息。或者,您需要构建自己的数据结构来包含稍后可能会写入的数据,并在遇到错误时写入它,并在到达某个边界时丢弃它,作为应用程序编写者,您知道它会发生错误。永远不会被需要。