Log4J:创建Logger实例的策略

时间:2009-11-20 12:08:27

标签: java logging log4j

我决定将Log4J日志框架用于新的Java项目。 我想知道我应该使用什么策略来创建/管理Logger实例以及为什么?

  • 每个类的一个Logger实例 e.g。

    class Foo {
        private static final Logger log = Logger.getLogger(Foo.class);
    }
    
  • 每个线程的一个Logger实例
  • 每个应用程序的一个Logger实例
  • 水平切片:应用程序每层中的一个Logger实例(例如视图层,控制器层和持久层)
  • 垂直切片:应用程序功能分区中的一个Logger实例

注意:这些问题在某些程度上已经考虑过了:

Whats the overhead of creating a Log4j Logger

10 个答案:

答案 0 :(得分:42)

通常,您需要为每个类设置记录器,因为这是一个很好的逻辑组件。线程已经是日志消息的一部分(如果您的过滤器显示它们),因此切片记录器可能是多余的。

关于应用程序或基于图层的记录器,问题是您必须找到一个地方来粘贴该Logger对象。不是什么大不了的事。更大的问题是某些类可能会在多个应用程序的多个级别使用...可能很难让您的记录器正确。或者至少是棘手的。

...你想要的最后一件事就是你的日志记录设置中的错误假设。

如果您关心应用程序和图层并且具有易于分离的点,那么NDC就是您的选择。代码有时可能有点过分,但我不知道有多少次我通过准确的上下文堆栈保存了多少次,表明我从层Y中的应用程序X调用了Foo.bar()。

答案 1 :(得分:31)

最常用的策略是为每个类创建一个记录器。如果你创建新的线程给它们一个有用的名字,那么它们的日志记录很容易区分。

每个类创建记录器的好处是能够在类的包结构中打开/关闭日志记录:

log4j.logger.org.apache = INFO
log4j.logger.com.example = DEBUG
log4j.logger.com.example.verbose = ERROR

以上将所有apache库代码设置为INFO级别,将日志记录从您自己的代码切换到DEBUG级别,但详细软件包除外。

答案 2 :(得分:13)

我确定这不是最佳做法,但我在应用程序之前已经解雇了一些启动时间以保存代码行。特别是,粘贴时:

Logger logger = Logger.getLogger(MyClass.class);

...开发人员经常忘记将“MyClass”更改为当前的类名,并且几个记录器总是指向错误的位置。这很糟糕。

我偶尔会写:

static Logger logger = LogUtil.getInstance(); 

class LogUtil {
   public Logger getInstance() {
      String callingClassName = 
         Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName();
      return Logger.getLogger(callingClassName);
   }
}

该代码中的“2”可能是错误的,但要点就在那里;将性能命中(在类加载时,作为静态变量)找到类名,这样开发人员就没有办法错误输入或引入任何错误。

我一般不会因为在运行时阻止开发人员错误而失去性能,但如果它以单例形式出现,那么一次?通常听起来对我来说是个好交易。

答案 3 :(得分:10)

正如其他人所说,我会为每个类创建一个Logger:

private final static Logger LOGGER = Logger.getLogger(Foo.class);

private final Logger logger = Logger.getLogger(this.getClass());

但是,我发现过去在记录器中包含其他信息很有用。例如,如果您有一个网站,则可以在每条日志消息中包含用户ID。这样,您可以跟踪用户正在做的所有事情(对调试问题非常有用等)。

最简单的方法是使用MDC,但您可以使用为每个类实例创建的Logger,其名称包括用户ID。

使用MDC的另一个好处是,如果您使用SL4J,则可以根据MDC中的值更改设置。因此,如果您希望在DEBUG级别记录特定用户的所有活动,并将所有其他用户留在ERROR,则可以。您还可以根据MDC将不同的输出重定向到不同的位置。

一些有用的链接:

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html

http://www.slf4j.org/api/index.html?org/slf4j/MDC.html

答案 4 :(得分:4)

  • 每个类创建一个记录器。
  • 如果你有依赖需要 Commons Logging (很有可能)使用slf4j的bridge进行Commons Logging。使用Commons Logging界面实例化您的记录器(每个类):private static final Log log = LogFactory.getLog(MyClass.class);
  • 使用快捷方式在IDE中显示此模式。我为此目的使用IDEA的live templates
  • 使用NDC(线程本地字符串堆栈)或MDC(String→?的线程本地映射)向线程提供上下文信息。

模板示例:

private static final Log log = LogFactory.getLog($class$.class); // live template 'log'

if (log.isDebugEnabled())
    log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...

答案 5 :(得分:3)

另一个选项:您可以在日志记录中尝试 AspectJ 横切。检查 here : Simplify Your Logging 。 (如果您不想使用 AOP ,可以查看 slf4j

//Without AOP

    Class A{
       methodx(){
        logger.info("INFO");
       }
    }

    Class B{
       methody(){
        logger.info("INFO");
       }
    }

//With AOP

    Class A{
       methodx(){
         ......
       }
    }

    Class B{
       methody(){
         ......
       }
    }

    Class LoggingInterceptor{

       //Catched defined method before process
       public void before(...xyz){
         logger.info("INFO" + ...xyz);
       }

       //Catched defined method after processed          
       public void after(...xyz){
         logger.info("INFO" + ...xyz);
       }
       .....

    }

P.S: AOP 会更好, DRY(Don't Repeat Yourself) 方式。

答案 6 :(得分:2)

创建自定义记录器的最佳和最简单的方法是:

// create logger
Logger customLogger = Logger.getLogger("myCustomLogName");

// create log file, where messages will be sent, 
// you can also use console appender
FileAppender fileAppender = new FileAppender(new PatternLayout(), 
                                             "/home/user/some.log");

// sometimes you can call this if you reuse this logger 
// to avoid useless traces
customLogger.removeAllAppenders();

// tell to logger where to write
customLogger.addAppender(fileAppender);

 // send message (of type :: info, you can use also error, warn, etc)
customLogger.info("Hello! message from custom logger");

现在,如果你需要同一个类中的另一个记录器,没问题:)只需创建一个新的

// create logger
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");

现在看到上面的代码并创建新的fileappender,这样你的输出就会在其他文件中发送

这对(至少)2种情况很有用

  • 如果您想要从信息中单独出错并发出警告

  • 当您管理多个流程并且需要从每个流程输出时

PS。有问题吗?随便问! :)

答案 7 :(得分:0)

在部署多个EAR / WAR时,最好将log4j.jar打包到类加载器层次结构中更高的位置。
即不在WAR或EAR中,而是在容器的System-classloader中,否则多个Log4J实例将同时写入同一文件,导致奇怪的行为。

答案 8 :(得分:0)

通用约定是“记录器pr类,并使用类名作为其名称”。这是一个很好的建议。

我个人的经验是,不应将此记录器变量声明为静态,而是为每个新变量检索一个实例变量。这允许日志记录框架根据它们的来源不同地处理两个调用。对于该类的所有实例(在该类加载器中),静态变量是相同的。

此外,您应该了解所选日志后端的所有可能性。你可能有不可能的可能性。

答案 9 :(得分:-1)

如果您的应用程序遵循SOA原则,那么对于每个服务A,您将拥有以下组件:

  1. 控制器
  2. 服务实施
  3. 执行官
  4. A Persistance
  5. 因此,生活更轻松     aController.log     aService.log     aExecutor.log     和aPersistance.log

    这是一个基于层的分离,因此所有Remoting / REST / SOAP类都将写入aController.log

    您的所有调度机制,后端服务等都将写入aService.log

    所有任务执行都写入aExecutor.log等等。

    如果您有多线程执行程序,则可能必须使用日志累加器或其他技术来正确对齐多个线程的日志消息。

    这样你就会有4个日志文件,这些文件不是很多而且也不会太少,我从经验中告诉你,这会让生活变得更加轻松。