Skip to content

AsyncHandler stops working if a parent failed to publish a LogRecord #4182

Description

@myazinn

If a parent handler in wvlet.log.AsyncHandler throws an exception when parent.publish(record) is called in a poller thread, that poller thread silently dies and stops producing any logs.
We've encountered this issue because we use custom LogFormatter which was throwing an NPE in some rare cases. Here's a test case that emulates this issue

object FlakyFormatter extends LogFormatter {
  override def formatLog(r: LogRecord): String = {
    if (r.getMessage == "boom") {
      throw new RuntimeException("boom!")
    } else {
      r.getMessage
    }
  }
}

test("keep writing logs if parent is flaky") {
    val buf = new BufferedLogHandler(FlakyFormatter)

    val l1 = "l1"
    val l2 = "l2"
    val l3 = "boom"
    val l4 = "l4"

    val logs =
      withResource(new AsyncHandler(buf)) { h =>
        val logger = Logger("internal.asyncerrortest")
        logger.resetHandler(h)
        logger.setLogLevel(LogLevel.ALL)

        logger.info(l1)
        logger.info(l2)
        logger.info(l3)
        logger.info(l4)

        Thread.sleep(1000) // wait for the background thread to process the logs
        buf.logs
      }

    logs.size shouldBe 3
    logs(0).contains(l1) shouldBe true
    logs(1).contains(l2) shouldBe true
    logs(2).contains(l4) shouldBe true
  }

It seems wvlet.airframe.log.AsyncHandler for Scala 3 is also affected.

I guess the fix is straightforward (wrap parent.publish(record) into Try(..) or try ... catch ...), but I'm not sure about what to do with a LogRecord that caused an error. So I decided to create an issue instead

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions