Log4J:创buildlogging器实例的策略

我决定使用Log4J日志框架来创build一个新的Java项目。 我想知道我应该使用什么策略来创build/pipe理logging器实例,为什么?

  • logging器每个类的一个例子

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

注意:在这些文章中已经在一定程度上考虑了这个问题:

创buildLog4jlogging器的开销是多less?

通常情况下,你会为​​每个类设置logging器,因为这是一个很好的逻辑组件。 线程已经是日志消息的一部分(如果你的filter显示它们),那么以这种方式分割logging器可能是多余的。

对于基于应用程序或图层的logging器,问题是您必须find一个地方来粘住Logger对象。 没有什么大不了的。 更大的问题是某些类可能会在多个应用程序的多个层次上使用……要让您的logging器正确,可能会很困难。 或者至less棘手。

…你想要的最后一件事是在你的日志设置中的错误假设。

如果您关心应用程序和图层,并且具有简单的分隔点,NDC就是您的select。 有时代码可能有点过分,但我不知道有多less次我被一个精确的上下文堆栈保存,显示出Foo.bar()是从Y层的应用程序X调用的。

最常用的策略是为每个类创build一个logging器。 如果你创build新的线程给他们一个有用的名字,所以他们的日志很容易区分。

为每个类创buildlogging器的好处是能够在类的包结构中打开/closureslogging:

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

上面的代码将所有的apache库代码设置为INFO级别,除了详细的包之外,从你自己的代码切换到DEBUG级别。

我敢肯定,这不是一个好的做法,但是我之前解雇了一些应用程序的启动时间来节省代码行。 具体来说,粘贴时:

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

…开发人员经常忘记将“MyClass”更改为当前类名,而且有几个logging器总是指向错误的地方。 这不好。

我偶尔写道:

 static Logger logger = LogUtil.getInstance(); 

和:

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

该代码中的“2”可能是错误的,但要点在那里; 把性能打到(在类加载,作为一个静态variables)find类名称,以便开发人员真的没有办法错误input或引入任何错误。

我一般不会为了防止开发人员在运行时发生错误而失去性能,但是如果它发生为单例,一次呢? 对我来说,这听起来像是一笔很好的交易。

正如其他人所说,我会创build一个logging器每class:

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

要么

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

但是,我发现过去在logging器中有其他信息是有用的。 例如,如果您有一个网站,您可以在每个日志消息中包含用户标识。 这样,你可以跟踪用户正在做的一切(非常有用的debugging问题等)。

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

使用MDC的另一个优点是,如果使用SL4J,则可以根据MDC中的值更改设置。 因此,如果您希望在DEBUG级别上logging特定用户的所有活动,并将所有其他用户保留为ERROR,则可以。 您也可以根据您的MDC将不同的输出redirect到不同的地方。

一些有用的链接:

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

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

  • 为每个class级创build一个logging器。
  • 如果你有需要Commons Logging的依赖(很可能)使用slf4j的Commons Logging的桥接 。 使用Commons Logging接口实例化您的logging器(每个类): private static final Log log = LogFactory.getLog(MyClass.class);
  • 在您的IDE中使用快捷键清单显示此模式。 为此,我使用IDEA的实时模板 。
  • 使用NDC (线程局部string堆栈)或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' ... 

另一个选项:你可以尝试AspectJ横切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); } ..... } 

PS: AOP会更好,这是DRY(不要重复自己)的方式。

创build未链接到任何类名的自定义logging器的最好和最简单的方法是:

 // 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"); 

现在,如果你需要在同一个class级的另一个logging器,没有问题:)只是创build一个新的

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

现在看到上面的代码,并创build新的文件扩展名,所以你的输出将被发送到其他文件

这对于(至less)2种情况是有用的

  • 当你想从信息单独的错误,并警告

  • 当你pipe理多个进程时,你需要从每个进程输出

PS。 有问题吗? 随意问! 🙂

通用惯例是“一个logging器pr类,并使用类名作为它的名字”。 这是个好build议。

我个人的经验是,这个logging器variables不应被声明为静态的,而是为每个新的实例variables。 这允许日志框架根据它们来自哪里来区别对待两个调用。 对于该类的所有实例(在该类加载器中),静态variables是相同的。

你也应该学习所有的可能性与你的日志后端的select。 你可能有没有想到可能的可能性。

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

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

  1. 控制器
  2. 服务实施
  3. 执行者
  4. 坚持

所以它使生活更容易有一个aController.log aService.log aExecutor.log和aPersistance.log

这是一个基于层的分离,所以你所有的Remoting / REST / SOAP类都会写入到aController.log

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

所有的任务执行都被写入到Executor.log等等。

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

这样你总是会有4个日志文件,这些日志文件不是很多,也不是太less,我从经验告诉你,这使得生活变得更加简单。