【问题标题】:logback: Two appenders, multiple loggers, different levelslogback:两个appender,多个logger,不同级别
【发布时间】:2012-05-30 19:58:55
【问题描述】:

我想在我的应用程序(Spring 集成)中有两个日志文件,debug.log 和 main.log。我想在 INFO 级别运行 main.log,在 DEBUG 级别运行 debug.log。这可以通过 appenders 上的过滤器来实现。我想根据源将不同级别记录到附加程序。换句话说

<logger name="org.springframework" level="ERROR">
    <appender-ref ref="main" />
</logger>
<logger name="org.springframework" level="DEBUG">
    <appender-ref ref="debug" />
</logger>
<logger name="com.myapp" level="INFO">
    <appender-ref ref="main" />
</logger>
<logger name="com.myapp" level="DEBUG">
    <appender-ref ref="debug" />
</logger>

总结一下:

  1. 弹簧记录器
    • 主要 -> 错误
    • 调试->调试
  2. com.myapp 记录器
    • 主要 -> 信息
    • 调试->调试

因此,我必须让记录器在 DEBUG 下运行,并且 appender 上的阈值过滤器不够精细。

更新使问题更加清晰

【问题讨论】:

    标签: java logging logback


    【解决方案1】:

    创建一个 ThresholdLoggerFilter 类,它可以放在 appender 上,例如:

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <filter class="com.myapp.ThresholdLoggerFilter">
            <logger>org.springframework</logger>
            <level>ERROR</level>
        </filter>
        </appender>
    

    以下代码有效

    package com.myapp;
    
    import ch.qos.logback.classic.Level;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import ch.qos.logback.core.filter.Filter;
    import ch.qos.logback.core.spi.FilterReply;
    
    public class ThresholdLoggerFilter extends Filter<ILoggingEvent> {
        private Level level;
        private String logger;
    
        @Override
        public FilterReply decide(ILoggingEvent event) {
            if (!isStarted()) {
                return FilterReply.NEUTRAL;
            }
    
            if (!event.getLoggerName().startsWith(logger))
                return FilterReply.NEUTRAL;
    
            if (event.getLevel().isGreaterOrEqual(level)) {
                return FilterReply.NEUTRAL;
            } else {
                return FilterReply.DENY;
            }
        }
    
        public void setLevel(Level level) {
            this.level = level;
        }
    
        public void setLogger(String logger) {
            this.logger = logger;
        }
    
        public void start() {
            if (this.level != null && this.logger != null) {
                super.start();
            }
        }
    }
    

    【讨论】:

    • 不用自己实现,使用ch.qos.logback.classic.filter.ThresholdFilter
    • ThresholdFilter 只过滤阈值。我想同时过滤记录器和阈值
    • @JohnOxley 这是一个应该包含在 logback 中的功能!你考虑过contributing吗?
    • 我相信它的目的是有积极的过滤器,所以应该是 if (!event.getLoggerName().startsWith(logger)) return FilterReply.DENY;
    • “startsWith(logger)”对于大多数用例来说已经足够了,但并不完全正确。例如,如果事件的记录器名称为“com.examples”,则配置的记录器“com.example”将错误匹配。
    【解决方案2】:

    如果您愿意从根记录器继承,您也可以更简单地执行此操作,例如在这里,我们为错误添加了一个额外的记录器,该记录器记录到 stderr。它仅对特定记录器启用。

    <configuration>
        <appender name="CONSOLE-stdout" class="ch.qos.logback.core.ConsoleAppender">
            <target>System.out</target> <!-- the default -->
            <encoder>
                <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
            </encoder>
        </appender>
        <appender name="CONSOLE-stderr" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>ERROR</level>
            </filter>
    
            <target>System.err</target>
            <encoder>
                <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
            </encoder>
        </appender>
        <root level="DEBUG">
            <appender-ref ref="CONSOLE-stdout" />
        </root>
    
            <!-- We want error logging from this logger to go to an extra appender 
                 It still inherits CONSOLE-stdout from the root logger -->
        <logger name="org.springframework" level="INFO">
            <appender-ref ref="CONSOLE-stderr" />
        </logger>
    </configuration>
    

    【讨论】:

    • 感谢@artbristol,但这并没有提供我想要的控制粒度。我已更新问题,请说明原因。
    【解决方案3】:

    添加比现有解决方案更简单的附加解决方案

    这些解决方案都不适合我,因为我没有使用 Spark 或 Spring 这样的框架。所以我做了一些更简单的事情,似乎运行良好。虽然此解决方案可能不适用于 OP,但也许它对想要不那么笨重的东西的人有用。

    <property name="pattern" value="%d{yyyy.MMM.dd HH:mm:ss.SSS} [ProgramName] %level - %msg%n" />
    
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>/path/to/your/program.log</file>
        <append>true</append>
        <encoder>
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.out</target>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    
    <root level="debug">
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
    </root>
    

    使用此配置,我可以在将 DEBUG 语句输出到日志文件时保持控制台相当干净。

    【讨论】:

      【解决方案4】:

      刚刚找到了一个使用 logback 元素的实用解决方案,效果很好,基本上你需要有两个 appender,一个带有默认配置,另一个带有过滤器(在我的示例中,我使用的是控制台):

          <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
              <encoder>
                  <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
              </encoder>
          </appender>
      
          <appender name="WARN_FILTER_STDOUT" class="ch.qos.logback.core.ConsoleAppender">
              <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                  <level>WARN</level>
              </filter>
              <encoder>
                  <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
              </encoder>
          </appender>
      
          <logger name="org.apache.spark" level="INFO" additivity="false">
              <appender-ref ref="SPARK" /><!-- this line is not necessary, just here to ilustrate the need for the filter -->
              <appender-ref ref="WARN_FILTER_STDOUT" />
          </logger>
      
          <root level="info">
              <appender-ref ref="STDOUT" />
          </root>
      

      【讨论】:

      • 这对我很有用。我需要一个日志中的所有 INFO 和 up 以及另一个日志中的所有 WARN 和 up。为每个日志创建 1 个附加程序,然后像您一样将两个附加程序放在同一个记录器下是 100% 的解决方案,并且需要零个附加组件。
      【解决方案5】:

      对不同的消息使用多个记录器将如下所示:

      import ch.qos.logback.classic.Level;
      import ch.qos.logback.classic.Logger;
      
      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.core.FileAppender;
      
      public class ConfigureLogBack 
      {
          public static void programmaticConfiguration()
          {
              Logger camel = getLogger("MyRoute", C:\\Users\\amrut.malaji\\Desktop\\Oracle\\logback\\camel-Log.txt");
              Logger services = getLogger("webservices", "C:\\Users\\amrut.malaji\\Desktop\\Oracle\\logback\\services-log.txt");
          }
      
          private static Logger getLogger(String string, String file) {
              LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
          PatternLayoutEncoder ple = new PatternLayoutEncoder();
      
          ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
          ple.setContext(lc);
          ple.start();
          FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
          fileAppender.setFile(file);
          fileAppender.setEncoder(ple);
          fileAppender.setContext(lc);
          fileAppender.start();
      
          Logger logger = (Logger) LoggerFactory.getLogger(string);
          logger.addAppender(fileAppender);
          logger.setLevel(Level.INFO);
          logger.setAdditive(false); /* set to true if root should log too */
      
          return logger;
      }
      

      【讨论】:

        【解决方案6】:

        appender 上的阈值过滤器不够精细

        您可以使用 EvaluatorFilter。 JaninoEventEvaluator 需要对 janino (.jar) 的引用,而 logback.xml 示例将是:

        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>INFO</level>
            </filter>
            <filter class="ch.qos.logback.core.filter.EvaluatorFilter">      
                <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
                    <expression>
                        level &lt;= ERROR &amp;&amp; logger.equals(&quot;com.myapp.ThresholdLoggerFilter&quot;)
                    </expression>
                </evaluator>
                <OnMismatch>DENY</OnMismatch>
                <OnMatch>NEUTRAL</OnMatch>
            </filter>
         </appender>
        

        此方法在表达式标记中使用 java 表达式(必须是 xml 转义)来评估日志记录事件,并且不需要编写自定义 java 类。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-06-09
          • 2019-05-28
          • 1970-01-01
          • 2014-07-31
          • 1970-01-01
          • 2015-07-09
          • 1970-01-01
          • 2015-06-14
          相关资源
          最近更新 更多