如何为log4j设置单独的日志记录流?

时间:2015-05-17 22:21:58

标签: java logging log4j

说我有这样的课程:

public class MyClass
{

    private Logger log = LoggerFactory.getLogger(MyClass.class); //org.slf4j.LoggerFactory

    public void foo(Params p)
    {
         log.info("Foo params: " + p);
         long t1 = System.currentTimeMillis();

         Result r = someMethod(p);

         long t2 = System.currentTimeMillis();
         log.info("Foo result: " + r)
         log.info("Foo time taken: + (t2-t1)/1000); 
    }

}

现在,在打印此信息时,我希望能够打开和关闭不同类型的信息(参数,结果,拍摄时间)。

使用日志记录级别进行区分的问题在于,更精细的粒度级别也包含更粗略的级别。

如何轻松设置?

3 个答案:

答案 0 :(得分:2)

通过创建自定义日志记录类来设置不同的日志记录行为。

这是我的解决方案:

public abstract class AbstractLogger {

    protected Logger log;
    protected Class callingClass;
    public AbstractLogger(Class c)
    {
        this.log = LoggerFactory.getLogger(this.getClass());
        this.callingClass = c;
    }

    public void log(String s)
    {
        log.debug(this.callingClass + " :" + s);
    }

}

public class LoggerOne extends AbstractLogger {    

    public LoggerOne(Class c) {
        super(c);           
    }

}

public class LoggerTwo extends AbstractLogger {   

    public LoggerTwo(Class c) {
        super(c);           
    }    

}

public class LoggerThree extends AbstractLogger {    

    public LoggerThree(Class c) {
        super(c);           
    }    
}

Log4j.properties中的设置

#Define custom levels by package
#set to ERROR to turn them off
log4j.logger.org.myproject.loggers.LoggerOne=ERROR
log4j.logger.org.myproject.loggers.LoggerTwo=DEBUG
log4j.logger.org.myproject.loggers.LoggerThree=DEBUG

使用这些记录器时:

使用这些记录器:

 public class MyMain {


    // private Logger log = LoggerFactory.getLogger(MyMain.class);

     private AbstractLogger l1= new LoggerOne(this.getClass());
     private AbstractLogger l2= new LoggerTwo(this.getClass());
     private AbstractLogger l3= new LoggerThree(this.getClass());


    public void run()
    {

            l1.log("log 1");

             long t1 = System.currentTimeMillis();

             try {
                    Thread.sleep(1000);                 //1000 milliseconds is one second.
                } catch(InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }

             long t2 = System.currentTimeMillis();
             l2.log("log 2");
             l3.log("Foo time taken:" + (t2-t1)/1000); 

    }

    public static void main(String[] args) {

        MyMain me = new MyMain();
        me.run();

    }

}

日志输出:

12:27:29 DEBUG LoggerTwo:18 - class maventestspace.MyMain :log 2
12:27:29 DEBUG LoggerThree:18 - class maventestspace.MyMain :Foo time taken:1

请注意,LoggerOne不打印,因为它在属性文件中设置为ERROR。

要重定向这些单独的日志文件,您需要设置新的appender和logger。

log4j.logger.org.myproject.loggers.LoggerOne=DEBUG, file1, stdout
log4j.logger.org.myproject.loggers.LoggerTwo=DEBUG, file2, stdout
log4j.logger.org.myproject.loggers.LoggerThree=DEBUG, file3, stdout


# Direct log messages to a log file
log4j.appender.file1=org.apache.log4j.RollingFileAppender
#log4j.appender.TextProcessor.Threshold=debug
log4j.appender.file1.File=E:\\logs\\log1.txt
log4j.appender.file1.MaxFileSize=10MB
log4j.appender.file1.MaxBackupIndex=1
log4j.appender.file1.layout=org.apache.log4j.PatternLayout
log4j.appender.file1.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n




# Direct log messages to a log file
log4j.appender.file2=org.apache.log4j.RollingFileAppender
#log4j.appender.TextProcessor.Threshold=debug
log4j.appender.file2.File=E:\\logs\\log2.txt
log4j.appender.file2.MaxFileSize=10MB
log4j.appender.file2.MaxBackupIndex=1
log4j.appender.file2.layout=org.apache.log4j.PatternLayout
log4j.appender.file2.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n



# Direct log messages to a log file
log4j.appender.file3=org.apache.log4j.RollingFileAppender
#log4j.appender.TextProcessor.Threshold=debug
log4j.appender.file3.File=E:\\logs\\log3.txt
log4j.appender.file3.MaxFileSize=10MB
log4j.appender.file3.MaxBackupIndex=1
log4j.appender.file3.layout=org.apache.log4j.PatternLayout
log4j.appender.file3.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n

答案 1 :(得分:2)

我认为John Ament的意思是,记录器名称(或类别,有时也称为)可以自由选择。电话

LoggerFactory.getLogger(MyClass.class)

主要是方便调用

LoggerFactory.getLogger(MyClass.class.getName())

日志记录框架没有要求您根据类的全名命名记录器。这只是上面第一个getLogger重载支持的约定。

因此,不要使用与示例中名称相同的三个不同的Logger实现:

 private AbstractLogger l1= new LoggerOne(this.getClass());
 private AbstractLogger l2= new LoggerTwo(this.getClass());
 private AbstractLogger l3= new LoggerThree(this.getClass());

您可以简单地使用具有3个不同名称的标准Logger实现:

public class MyClass
{
    private static final String loggerBaseName = MyClass.class.getName();

    private final Logger paramsLogger = LoggerFactory.getLogger(loggerBaseName + ".params");
    private final Logger resultsLogger = LoggerFactory.getLogger(loggerBaseName + ".results");
    private final Logger durationLogger = LoggerFactory.getLogger(loggerBaseName + ".duration");

    public void foo(Params p)
    {
        paramsLogger.info("Foo params: {}", p);
        long t1 = System.currentTimeMillis();

        Result r = someMethod(p);

        long t2 = System.currentTimeMillis();
        resultsLogger.info("Foo result: {}", r)
        durationLogger.info("Foo time taken: {}", (t2-t1)/1000); 
    }
}

由于log4j记录器是分层的,您可以根据需要一起控制它们或单独控制它们。所以如果你想要启用所有这些:

log4j.logger.org.myproject.MyClass=DEBUG, stdout

如果您以后需要关闭结果:

log4j.logger.org.myproject.MyClass=DEBUG, stdout
log4j.logger.org.myproject.MyClass.results=OFF

同样,如果需要,您可以将输出发送到不同的目的地。

使用标记

以上所有内容均仅使用任何SLF4J实现中提供的基本功能编写。如果您正在使用Log4j 2或者愿意切换到logback,那么您可以使用markers来实现相同的目标,但在全局范围内。因此,您可以使用多个标记,而不是在类中使用多个记录器,如下所示:

public class GlobalMarkers
{
    public static final Marker PARAMS = MarkerFactory.getMarker("PARAMS");
    public static final Marker RESULTS = MarkerFactory.getMarker("RESULTS");
    public static final Marker DURATION = MarkerFactory.getMarker("DURATION");
}

public class MyClass
{
    private Logger logger = LoggerFactory.getLogger(MyClass.class);

    public void foo(Params p)
    {
        logger.info(GlobalMarkers.PARAMS, "Foo params: {}", p);
        long t1 = System.currentTimeMillis();

        Result r = someMethod(p);

        long t2 = System.currentTimeMillis();
        logger.info(GlobalMarkers.RESULTS, "Foo result: {}", r)
        logger.info(GlobalMarkers.DURATION, "Foo time taken: {}", (t2-t1)/1000); 
    }
}

这样,您就可以使用Log4j 2.0 MarkerFilterlogback MarkerFilter全局切换参数,结果和持续时间的记录。

Log4j 2.0中的配置

Log4j 2.0为您提供了如何使用MarkerFilter的灵活性:

  1. 您可以将其应用为上下文范围的过滤器,从而关闭所有记录的持续时间,例如。
  2. 您可以将其应用于org.myproject.MyClass记录器,以关闭该特定类的结果记录(例如)。
  3. 您可以将其应用于特定的appender,从而将参数记录到结果记录或类似的单独文件中。
  4. 在logback中配置

    在回归中,故事更复杂,取决于您希望实现的目标。要全局关闭给定标记的所有日志记录,只需使用MarkerFilter即可。这是一个TurboFilter,因此它适用于整个日志记录上下文。如果要将不同的标记记录到不同的源,可以使用SiftingAppender并通过扩展AbstractDiscriminator来编写基于标记的鉴别器。由于logback不直接在记录器上支持过滤器,如果你需要为每个标记配置每个类的输出,比如关闭MyClass的结果记录但是为其他类保留它,你应该使用特定于类的标记而不是全局标记

    以下是与SiftingAppender一起使用的基于标记的鉴别器的示例实现:

    public class MarkerBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> {
        private static final String KEY = "markerName";
        private String defaultValue;
    
        public String getDefaultValue() {
            return defaultValue;
        }
    
        public void setDefaultValue(String defaultValue) {
            this.defaultValue = defaultValue;
        }
    
        public String getKey() {
            return KEY;
        }
    
        public void setKey() {
            throw new UnsupportedOperationException("Key not settable. Using " + KEY);
        }
    
        public String getDiscriminatingValue(ILoggingEvent e) {
            Marker eventMarker = e.getMarker();
    
            if (eventMarker == null)
                return defaultValue;
    
            return eventMarker.getName();
        }
    }
    

    此实施受标准ContextBasedDiscriminator的启发。您可以像这样使用MarkerBasedDiscriminator:

    <configuration>
      <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
        <discriminator class="org.myproject.MarkerBasedDiscriminator">
          <defaultValue>general</defaultValue>
        </discriminator>
        <sift>
          <appender name="FILE-${markerName}" class="ch.qos.logback.core.FileAppender">
            <file>${markerName}.log</file>
            <append>false</append>
            <encoder>
              <pattern>%d [%thread] %level %logger{35} - %msg%n</pattern>
            </encoder>
          </appender>
        </sift>
      </appender>
    
      <root level="DEBUG">
        <appender-ref ref="SIFT" />
      </root>
    </configuration>
    

答案 2 :(得分:1)

听起来最直接的解决方案是利用不同的类别来记录您的日志。以此为例

String baseCategory = MyClass.class.getName();
String params = baseCategory+".params";
String duration = baseCategory+".duration";

您只需将这些名称作为记录器工厂类别传递。