如何在不使用Logback / slf4j重复的情况下同时登录到不同的日志文件

时间:2014-10-07 09:49:46

标签: logging concurrency slf4j logback

背景

我使用slf4j和Logback作为底层记录器。

客户端可以同时使用系统(即,一堆互操作类)。每次调用都应该有自己的日志文件。日志文件由客户端提供。

我想

  • 每次调用系统时都有一个单独的日志文件

  • 每次调用都应该记录到自己的日志文件中,而不是其他人

  • 系统本身有一些并发执行

  • 客户端可能会多次同时调用系统

我可以通过编程方式设置日志文件,类似于Logback - set log file name programmatically

问题

如果系统被调用两次,那么第一个日志文件仍然绑定到文件appender,这导致第二次调用写入两个日志文件。我可以在记录器上调用detachAndStopAllAppenders,但这会导致潜在的并发调用丢失其文件追加器。

添加新的File Appender而不分离以前连接的appender:

后续日志语句将写入新文件以及以前附加的文件。

分离File Appender然后附加新的File Appender:

现在正在运行的系统现在记录到新的日志文件而不是实际的日志文件。

问题根源

如果我没有弄错,问题的根源是静态Loggerfactory,即因为系统通过调用LoggerFactory.getLogger(getClass)来获取记录器,所以不同的调用返回相同的记录器。是否有LoggerFactory的非静态等价物?或者我怎么解决这个问题呢?

丑陋的解决方案

在编写MWE时,我想出了一个似乎有效的丑陋解决方案。但是,此解决方案需要同步每个日志记录调用,分离旧的appender,附加新的appender以及执行实际的日志记录。这是Scala中的代码:

package samples

import java.nio.file.{Files, Path}

import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.rolling.{SizeBasedTriggeringPolicy, FixedWindowRollingPolicy, RollingFileAppender}
import org.slf4j.LoggerFactory
import org.slf4j.helpers.SubstituteLogger

object SampleLogging {

  def main(args: Array[String]) {
    val sys1 = new Thread(new TheSystem(Files.createTempFile("sys1_",".log")))
    val sys2 = new Thread(new TheSystem(Files.createTempFile("sys2_",".log")))
    sys1.start()
    sys2.start()
    sys1.join()
    sys2.join()
    println("finished")
  }

  class TheSystem(logFile: Path) extends Runnable {
    def run() {
      val logger = new Logger(logFile)
      for (i <- 1 to 1000){
        logger.info(getClass)("Logging to file " + logFile)
      }
    }
  }

  class Logger(logFile: Path) {

    def info(context: Class[_])(msg: String){
      var slf4j = LoggerFactory.getLogger(context)
      while(slf4j.isInstanceOf[SubstituteLogger]){
        slf4j = LoggerFactory.getLogger(context)
      }
      slf4j match {
        case logback: ch.qos.logback.classic.Logger =>
          val appenderConfig = new RollingFileAppenderConfiguration(LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext])
          logback.synchronized {
            logback.detachAndStopAllAppenders()
            logback.addAppender(appenderConfig.appender)
            logback.info(msg)
          }
      }
    }

    private class RollingFileAppenderConfiguration(context: LoggerContext) {

      val appender: RollingFileAppender[ILoggingEvent] = {
        val rollingFileAppender = new RollingFileAppender[ILoggingEvent]
        rollingFileAppender.setFile(logFile.toAbsolutePath.toString)
        rollingFileAppender.setRollingPolicy(rollingPolicy(rollingFileAppender))
        rollingFileAppender.setTriggeringPolicy(triggeringPolicy)
        rollingFileAppender.setEncoder(encoder)
        rollingFileAppender.setContext(context)
        rollingFileAppender.start()
        rollingFileAppender
      }

      private def rollingPolicy(appender: => RollingFileAppender[ILoggingEvent]) = {
        val fixedWindowRollingPolicy = new FixedWindowRollingPolicy
        fixedWindowRollingPolicy.setFileNamePattern(s"log/${logFile.getFileName}.%i.log.zip")
        fixedWindowRollingPolicy.setMinIndex(1)
        fixedWindowRollingPolicy.setMaxIndex(3)
        fixedWindowRollingPolicy.setParent(appender)
        fixedWindowRollingPolicy.setContext(context)
        fixedWindowRollingPolicy
      }

      private def triggeringPolicy = {
        val sizeBasedTriggeringPolicy = new SizeBasedTriggeringPolicy[ILoggingEvent]
        sizeBasedTriggeringPolicy.setMaxFileSize("5MB")
        sizeBasedTriggeringPolicy.setContext(context)
        sizeBasedTriggeringPolicy
      }

      private def encoder = {
        val patternLayoutEncoder = new PatternLayoutEncoder
        patternLayoutEncoder.setPattern("%date{YYYY-MM-dd HH:mm:ss} %level [%thread] %logger{10} [%file:%line] %msg%n")
        patternLayoutEncoder.setContext(context)
        patternLayoutEncoder.start()
        patternLayoutEncoder
      }
    }
  }
}

我觉得这种过度锁定对性能非常不利。我错了吗?没有过多的锁定我可以实现相同的目标吗

0 个答案:

没有答案