Hi,
Here is the description I made:
It turns out that the leak is caused by Log4j (1.x), that is used as a back-end to Akka ActorLogging – I tried both SprayActorLogging and Akka ActorLogging and both produced the leak. Log4j actually keeps forever an instance of Logger for each Actor that has been started. This, I believe, is due to the fact that ActorLogging uses the full path of the Actor as the name of the Logger. Log4j keeps a HashMap of instances of Logger in case they need to be reused, but doesn't seem to flush unused ones. As they are referenced in this map, they cannot be garbage collected.
Since then, I have been able to reproduce this bug with several Slf4j back-ends: Log4j 1.x, Log4j 2 and Logback, so it is not specific to any of them. Also, I have found out that only Spray internal logging is affected: neither Akka IO-level actors nor application-level actors are, and all of them use Akka ActorLogging.
Then, the
Slf4jLogger receives events and binds them to the appropriate Slf4j
Logger. To do so, it calls
SLFLoggerFactory.getLogger, using the logSource as the name of the Logger. I guess this method then instanciates a
Logger with the provided name, and stores it in a HashMap, which grows forever.
Would logSource and logClass be correctly set, Slf4jLogger would use a Logger based on logClass but would still log the appropriate logSource using MDC, just as Akka ActorLogging does.
I'd like to suggest a fix, but I wouldn't know how to handle the logActorPathsWithDots and logActorSystemName settings. It looks like you can't create a LoggingAdapter with an Actor if you want to set a different logSource than the actor's name.
Any thoughts?
Thanks,
--
Clément