【问题标题】:SLF4J - Logging to different files based on a tagSLF4J - 基于标签记录到不同的文件
【发布时间】:2020-06-24 20:03:56
【问题描述】:

我有一个要求,我需要创建日志文件,以便所有基于标签(最好是文本)的日志都需要记录到相应的日志文件中。

例如,

我有关于苹果、橙子和芒果的日志。

logger.info("Apples: They are red in color");
logger.info("Oranges: They are orange in color");
logger.info("Mangoes: They are yellowish in color");

根据我的要求,第一个日志应该记录到 Apples.log,第二个到 Oranges.log,第三个到 Mangoes.log

日志文件应该是动态创建的。

下面显示的是我的 logback.xml 文件

    <statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>
                %d %-5p - %marker%m%n
            </Pattern>
        </encoder>
    </appender>
  <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    <discriminator>
      <key>fruitName</key>
      <defaultValue>Common_logs</defaultValue>
    </discriminator>
    <sift>
      <appender name="FILE-${instanceName}" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <fileNamePattern>./Logs/${fruitName}/${instanceName}.log</fileNamePattern>
            <maxHistory>50</maxHistory>
            <cleanHistoryOnStart>false</cleanHistoryOnStart>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%d %-5p - %m%n</pattern>
        </encoder>
        </appender>
    </sift>
  </appender>

  <logger name="AssetInstanceService" level="info" additivity="false">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="FILE"/>
    <appender-ref ref="SIFT" />
  </logger>

  <root level="info">
    <appender-ref ref="SIFT" />
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="FILE"/>
  </root>
</configuration>

下面是我的 LogManager.java 文件

package Data;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;

public class LogManager {

    static String configFile = "./logback.xml";

    private static Logger _logger = LoggerFactory.getLogger(LogManager.class);
    private static boolean _isInitialized = false;

    private LogManager() {

    }

    public static Logger Instance() {
        if (_logger == null) {
            _logger = LoggerFactory.getLogger(LogManager.class);
        }
        return _logger;
    }

    public static Logger Instance(String instanceName) {
        if (!_isInitialized) {
            if (!CommonMethods.CheckFileExist(configFile)) {
                configFile = "../logback.xml";
                if (!CommonMethods.CheckFileExist(configFile)) {
                    return null;
                }
            }
            LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
            JoranConfigurator configurator = new JoranConfigurator();
            lc.reset();
            configurator.setContext(lc);
            try {
                configurator.doConfigure(configFile);
                MDC.put("fruitName", instanceName);
            } catch (JoranException e) {
                System.out.println(e);
            }
            _isInitialized = true;
        }
        if (_logger == null) {
            _logger = LoggerFactory.getLogger(LogManager.class);
        }
        return _logger;
    }
}

这里我的日志是在我在 main 方法中初始化记录器时运行 jar 文件时创建的。

但我的需要是根据日志中的标签或ID创建不同名称的不同日志文件。

【问题讨论】:

    标签: java logging log4j logback slf4j


    【解决方案1】:

    动态创建 RollingFileAppender 并向每个 appender 添加过滤器。

    package zinit;
    
    import org.slf4j.LoggerFactory;
    import org.slf4j.Marker;
    import org.slf4j.MarkerFactory;
    
    import ch.qos.logback.classic.Level;
    import ch.qos.logback.classic.Logger;
    import ch.qos.logback.classic.LoggerContext;
    import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import ch.qos.logback.classic.spi.LoggingEvent;
    import ch.qos.logback.core.filter.Filter;
    import ch.qos.logback.core.rolling.RollingFileAppender;
    import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
    import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
    import ch.qos.logback.core.spi.FilterReply;
    
    public class main {
    
        private static Logger logger = (Logger) LoggerFactory.getLogger(main.class);
    
        public static void main(String[] args) {
    
            LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
            String[] fruitList = { "Apple", "Orange", "Mango" };
            Marker apple = null;
            Marker orange = null;
            Marker mango = null;
    
            for (String instanceName : fruitList) {
    
                String fileName = "./Logs/" + instanceName + "/" + instanceName + "_%d{yyyy-MM-dd}-%i.log";
                apple = MarkerFactory.getMarker("Apple");
                orange = MarkerFactory.getMarker("Orange");
                mango = MarkerFactory.getMarker("Mango");
                logger = RollingFile(lc, fileName, instanceName);
            }
    
            for (int i = 0; i < 10000; i++) {
    
                logger.info(apple, "Apple is red");
                logger.info(apple, "An apple a day keeps the doctor away");
                logger.info(orange, "Oranges are orange");
                logger.info(orange, "Orange is a fruit and a color");
                logger.info(mango, "Mango is yellowish");
                logger.info(mango, "Color of mango is very much variying");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
            }
        }
    
        @SuppressWarnings({ "rawtypes", "unchecked" })
        private static Logger RollingFile(LoggerContext loggerContext, String filePath, String instanceName) {
    
            RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<>();
            rollingFileAppender.setName("ROLLINGFILE-" + instanceName);
            rollingFileAppender.setContext(loggerContext);
    
            TimeBasedRollingPolicy<ILoggingEvent> timeBasedRollingPolicy = new TimeBasedRollingPolicy<>();
            timeBasedRollingPolicy.setContext(loggerContext);
            timeBasedRollingPolicy.setMaxHistory(3);
            timeBasedRollingPolicy.setCleanHistoryOnStart(false);
            timeBasedRollingPolicy.setFileNamePattern(filePath);
            timeBasedRollingPolicy.setParent(rollingFileAppender);
            timeBasedRollingPolicy.start();
    
            SizeAndTimeBasedFNATP<ILoggingEvent> sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP<>();
            sizeAndTimeBasedFNATP.setContext(loggerContext);
            sizeAndTimeBasedFNATP.setMaxFileSize("10KB");
            sizeAndTimeBasedFNATP.setTimeBasedRollingPolicy(timeBasedRollingPolicy);
            sizeAndTimeBasedFNATP.start();
    
            timeBasedRollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
            timeBasedRollingPolicy.start();
    
            rollingFileAppender.setRollingPolicy(timeBasedRollingPolicy);
    
            PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder();
            patternLayoutEncoder.setContext(loggerContext);
            patternLayoutEncoder.setPattern("%d %-5p - %m%n");
            patternLayoutEncoder.start();
            Filter filter = new Filter() {
    
                @Override
                public FilterReply decide(Object obj) {
                    if (!isStarted()) {
                        return FilterReply.NEUTRAL;
                    }
                    LoggingEvent event = (LoggingEvent) obj;
                    if (event.getMarker().getName().equals(instanceName)) {
                        return FilterReply.NEUTRAL;
                    } else {
                        return FilterReply.DENY;
                    }
                }
    
            };
            filter.start();
            rollingFileAppender.addFilter(filter);
            rollingFileAppender.setEncoder(patternLayoutEncoder);
    
            rollingFileAppender.start();
    
            logger.addAppender(rollingFileAppender);
            logger.setLevel(Level.DEBUG);
            logger.setAdditive(false);
            return logger;
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      SLF4J 和 Log4j-API 都提供标记来做你想做的事。在 SLF4J 中,您可以使用以下命令创建标记:

      Marker apples = MarkerFactory.getMarker("Apples");
      Marker oranges = MarkerFactory.getMarker("Oranges");
      Marker mangos = MarkerFactory.getMarker("Mangos");
      

      此外,标记可以有父级,因此您可以这样做:

      Marker fruit = MarkerFactory.getMarker("Fruit");
      Marker apples = MarkerFactory.getMarker("Apples");
      apples.add(fruit);
      Marker oranges = MarkerFactory.getMarker("Oranges");
      apples.add(fruit);
      Marker mangos = MarkerFactory.getMarker("Mangos");
      apples.add(fruit);
      

      在配置中,您可以检查特定标记,或者如果您想检查所有属于水果的标记,您可以检查该标记。

      然后您在应用程序中使用标记:

      logger.info(apples, "They are red in color");
      logger.info(oranges, "They are orange in color");
      logger.info(mangoes, "They are yellowish in color");
      

      最后,在您的配置中,您可以使用涡轮过滤器来做:

        <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
          <Marker>Apples</Marker>
          <OnMatch>NEUTRAL</OnMatch>
          <OnMismatch>DENY</OnMismatch>
        </turboFilter>
      

      在全局级别进行过滤,或者您可以使用评估器过滤器之一在 Appender 上进行过滤。如果您编写自定义鉴别器,您还可以使用SiftingAppender 动态创建 Appender。

      使用 Log4j API 让事情变得简单一些。创建没有父标记的标记:

      Marker apples = MarkerManager.getMarker("Apples");
      Marker oranges = MarkerManager.getMarker("Oranges");
      Marker mangos = MarkerManager.getMarker("Mangos");
      

      或与父母一起:

      Marker fruit = MarkerManager.getMarker("Fruit");
      Marker apples = MarkerManager.getMarker("Apples").setParents(fruit);
      Marker oranges = MarkerManager.getMarker("Oranges").setParents(fruit);
      Marker mangos = MarkerManager.getMarker("Mangos").setParents(fruit);
      

      在 Log4j 中使用它们的代码完全相同:

      logger.info(apples, "They are red in color");
      logger.info(oranges, "They are orange in color");
      logger.info(mangoes, "They are yellowish in color");
      

      最大的区别在于配置。 Log4j 只有一种过滤器,它可以在全局级别(如涡轮过滤器)或 Logger、Appender 引用或 Appender 上使用。在你的情况下,你会想使用MarkerFilter

      <MarkerFilter marker="Apples" onMatch="NEUTRAL" onMismatch="DENY"/>
      

      并将其添加到每个 Appender 引用或每个 Appender。

      与 SiftingAppender 类似,Log4j 也提供了RoutingAppender。它使用查找来确定如何执行路由。开箱即用的 Log4j 不提供从日志事件中检索数据的功能,但编写一个或 add one to Log4j 会很简单,或者您可以使用脚本从事件中检索标记。

      您应该知道使用 MarkerFilters 会有一些开销,尽管并不多。这是在我的 MacBook Pro 上运行 4 个线程时来自 log4j-perf 模块的基准测试。即使在最坏的情况下,Logback 会针对包含子标记的事件检查父标记,但每次比较仍然平均只需要 17 纳秒。

      Benchmark                                  Mode  Cnt   Score   Error  Units
      MarkerFilterBenchmark.baseline             avgt   10   2.412 ± 0.088  ns/op
      MarkerFilterBenchmark.log4jParentMarker    avgt   10   8.337 ± 0.186  ns/op
      MarkerFilterBenchmark.log4jSimpleMarker    avgt   10   8.043 ± 0.145  ns/op
      MarkerFilterBenchmark.log4jTooFine         avgt   10   2.825 ± 0.281  ns/op
      MarkerFilterBenchmark.logbackParentMarker  avgt   10  17.865 ± 0.533  ns/op
      MarkerFilterBenchmark.logbackSimpleMarker  avgt   10  10.471 ± 0.089  ns/op
      MarkerFilterBenchmark.logbackTooFine       avgt   10   4.255 ± 0.014  ns/op
      

      最后的想法。 Ceki Gulcu 在创建 SLF4J 时发明了标记的概念,他值得称赞,因为这是一个绝妙的想法。

      【讨论】:

      • 感谢您的回答。它几乎涵盖了与我的问题相关的所有方面。
      • 此外,我尝试消除 logback.xml 文件并动态创建 FileAppenders。此外,每个 FileAppender 都应包含过滤器。我尝试创建 RollingFileAppender,它在大小和时间限制后创建新文件。另外,我添加了标记。由于这里没有多少空间可以发布代码 sn-p,所以我将其发布在下面作为答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-08
      • 1970-01-01
      • 2018-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多