简体   繁体   English

Log4J:创建Logger实例的策略

[英]Log4J: Strategies for creating Logger instances

I decided to use Log4J logging framework for a new Java project. 我决定将Log4J日志框架用于新的Java项目。 I am wondering what strategy should I use for creating/managing Logger instances and why? 我想知道我应该使用什么策略来创建/管理Logger实例以及为什么?

  • one instance of Logger per class eg 每个类的一个Logger实例,例如

     class Foo { private static final Logger log = Logger.getLogger(Foo.class); } 
  • one instance of Logger per thread 每个线程一个Logger实例
  • one instance of Logger per application 每个应用程序的一个Logger实例
  • horizontal slicing : one instance of Logger in each layer of an application (eg the view layer, the controller layer and the persistence layer) 水平切片:应用程序每层中的一个Logger实例(例如视图层,控制器层和持久层)
  • vertical slicing : one instance of Logger within functional partitions of the application 垂直切片:应用程序功能分区中的一个Logger实例

Note: This issue is already considered to some extent in these articles: 注意:在这些文章中已经在某种程度上考虑了这个问题:

Whats the overhead of creating a Log4j Logger 什么是创建Log4j记录器的开销

Typically, you'd have loggers setup per class because that's a nice logical component. 通常,您需要为每个类设置记录器,因为这是一个很好的逻辑组件。 Threads are already part of the log messages (if your filter displays them) so slicing loggers that way is probably redundant. 线程已经是日志消息的一部分(如果您的过滤器显示它们),因此切片记录器可能是多余的。

Regarding application or layer based loggers, the problem is that you have to find a place to stick that Logger object. 关于应用程序或基于图层的记录器,问题是您必须找到一个地方来粘贴该Logger对象。 Not a really big deal. 不是什么大不了的事。 The bigger issue is that some classes may be used at multiple levels of from multiple applications... it could be difficult to get your logger right. 更大的问题是某些类可能会在多个应用程序的多个级别使用...可能很难让您的记录器正确。 Or at least tricky. 或者至少是棘手的。

...and the last thing you want is bad assumptions in your logging setup. ...而你想要的最后一件事是你的日志记录设置中的错误假设。

If you care about applications and layers and have easy separation points, the NDC is the way to go. 如果您关心应用程序和图层并拥有简单的分离点,那么NDC就是您的选择。 The code can be a little excessive sometimes but I don't know how many times I've been saved by an accurate context stack showing me that Foo.bar() was called from application X in layer Y. 代码有时可能有点过分,但我不知道有多少次我被一个准确的上下文堆栈保存,显示我从层Y中的应用程序X调用了Foo.bar()。

The strategy that is most used is to create a logger per class. 最常用的策略是为每个类创建一个记录器。 If you create new threads give them a usefull name, so their logging is easily distinguishable. 如果您创建新线程,则为它们提供有用的名称,以便可以轻松区分它们的日志记录。

Creating loggers per class has the benefit of being able to switch on/off logging in the package structure of your classes: 每个类创建记录器的好处是能够在类的包结构中打开/关闭日志记录:

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

The above would set all apache library code to INFO level, switch logging from your own code to DEBUG level with the exception of the verbose package. 上面将所有apache库代码设置为INFO级别,将日志记录从您自己的代码切换到DEBUG级别,但详细软件包除外。

I'm certain this isn't a best practice, but I've sacked some startup time on applications before to save lines of code. 我确定这不是最佳实践,但我在应用程序之前已经解除了一些启动时间以保存代码行。 Specifically, when pasting in: 特别是,粘贴时:

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

...developers often forget to change "MyClass" to the current class name, and several loggers always wind up pointing at the wrong place. ...开发人员经常忘记将“MyClass”更改为当前的类名,并且几个记录器总是指向错误的位置。 This Is Bad. 这是不好的。

I've occasionally written: 我偶尔会写:

static Logger logger = LogUtil.getInstance(); 

And: 和:

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

The "2" in that code might be wrong, but the gist is there; 该代码中的“2”可能是错误的,但要点就在那里; take a performance hit to (on class load, as a static variable) find the class name, so that a developer doesn't really have a way to mistype this or introduce any error. 将性能命中(在类加载时,作为静态变量)找到类名,以便开发人员实际上没有办法输入错误或引入任何错误。

I'm generally not thrilled with losing performance to prevent developer error at runtime, but if it happens as a singleton, once? 我一般不会因为失去性能而在运行时防止开发人员错误而感到兴奋,但如果它以单例形式出现,那么一次? Often sounds like a good trade to me. 通常听起来对我来说是个好交易。

As has been said by others, I would create a Logger per class: 正如其他人所说,我会为每个类创建一个Logger:

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

or 要么

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

However, I have found it useful in the past to have other information in the logger. 但是,我发现过去在记录器中包含其他信息很有用。 For instance, if you have a web site, you could include the user ID in every log message. 例如,如果您有一个网站,则可以在每条日志消息中包含用户ID。 That way,, you can trace everything a user is doing (very useful for debugging problems etc). 这样,您可以跟踪用户正在做的所有事情(对调试问题非常有用等)。

The easiest way to do this is to use an MDC, but you can use a Logger created for each instance of the class with the name including the user ID. 最简单的方法是使用MDC,但您可以使用为类的每个实例创建的Logger,其名称包括用户ID。

Another advantage of using an MDC is if you use SL4J, you can change the settings depending upon the values in your MDC. 使用MDC的另一个好处是,如果使用SL4J,则可以根据MDC中的值更改设置。 So if you wish to log all activity for a particular user at DEBUG level, and leave all of the other users at ERROR, you can. 因此,如果您希望在DEBUG级别记录特定用户的所有活动,并将所有其他用户留在ERROR,则可以。 You can also redirect different output to different places depending upon your MDC. 您还可以根据MDC将不同的输出重定向到不同的位置。

Some useful links: 一些有用的链接:

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

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

  • Create one logger per class. 每个类创建一个记录器。
  • If you have dependencies that require Commons Logging (quite likely) use slf4j's bridge for Commons Logging. 如果您有依赖项需要Commons Logging (很可能)使用slf4j的桥接器进行Commons Logging。 Instantiate your loggers (per class) using the Commons Logging interface: private static final Log log = LogFactory.getLog(MyClass.class); 使用Commons Logging接口实例化您的记录器(每个类): private static final Log log = LogFactory.getLog(MyClass.class);
  • Manifest this pattern in your IDE using shortcuts. 使用快捷方式在IDE中显示此模式。 I use IDEA's live templates for this purpose. 我为此目的使用IDEA的实时模板
  • Provide contextual information to threads using an NDC (thread local stack of strings) or an MDC (thread local map of String → ?). 使用NDC (线程本地字符串堆栈)或MDC (String→?的线程本地映射)向线程提供上下文信息。

Examples for templates: 模板示例:

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' ...

Another Option : You can try AspectJ crosscutting on logging. 另一个选项:您可以在日志记录上尝试AspectJ横切。 Check here : Simplify Your Logging . 点击此处: 简化您的日志记录 (If you don't want to use AOP , you can look slf4j ) (如果你不想使用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 will be better, it is DRY(Don't Repeat Yourself) way. PS: AOP会更好,它是DRY(不要重复自己)的方式。

the best and easiest method to create custom loggers, notlinked to any classname is: 创建自定义记录器的最佳和最简单的方法,未链接到任何类名是:

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

now, if you need another logger in same class, no problem :) just create new one 现在,如果你需要同一个类中的另一个记录器,没问题:)只需创建一个新的

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

now see the code above and create new fileappender so your output will be sent in other file 现在看到上面的代码并创建新的fileappender,以便您的输出将在其他文件中发送

This is usefull for (at least) 2 situations 这对于(至少)2种情况是有用的

  • when you want separate error from info and warns 当你想从信息中发出单独的错误并发出警告时

  • when you manage multiple processes and you need output from each process 当您管理多个流程并且需要每个流程的输出时

ps. PS。 have questions ? 有问题吗? feel free to ask! 随便问! :) :)

Common convention is "a logger pr class and use the class name as its name". 常见的约定是“记录器pr类,并使用类名作为其名称”。 This is good advice. 这是个好建议。

My personal experience is that this logger variable should NOT be declared static but an instance variable which is retrieved for each new. 我个人的经验是,这个记录器变量不应该被声明为静态,而是一个为每个新变量检索的实例变量。 This allows the logging framework to treat two calls differently depending on where they come from. 这允许日志记录框架根据它们的来源不同地处理两个调用。 A static variable is the same for ALL instances of that class (in that class loader). 对于该类的所有实例(在该类加载器中),静态变量是相同的。

Also you should learn all the possibilities with your logging backend of choice. 此外,您应该了解所选日志后端的所有可能性。 You may have possibilities you did not expect possible. 您可能有可能没有想到的可能性。

When deploying multiple EARs / WARs, it may be better to package the log4j.jar higher up in the classloader hierarchy. 在部署多个EAR / WAR时,最好将log4j.jar打包到类加载器层次结构中更高的位置。
ie not in WAR or EAR, but in the System-classloader of your container, otherwise multiple Log4J instances will write to the same file concurrently leading to strange behaviour. 即不在WAR或EAR中,而是在容器的System-classloader中,否则多个Log4J实例将同时写入同一文件,从而导致奇怪的行为。

If your application is following SOA principles, for every service A you'll have the following components: 如果您的应用程序遵循SOA原则,那么对于每个服务A,您将拥有以下组件:

  1. A Controller 控制器
  2. A Service Implementation 服务实施
  3. A Executor 执行人
  4. A Persistance 坚持不懈

So it makes life easier to have a aController.log aService.log aExecutor.log and aPersistance.log 因此,使aController.log aService.log aExecutor.log和aPersistance.log变得更加容易。

This is a layer based separation so all your Remoting/REST/SOAP classes will write to the aController.log 这是一个基于层的分离,因此所有Remoting / REST / SOAP类都将写入aController.log

All your scheduling mechanism, backend service etc will write to aService.log 您的所有调度机制,后端服务等都将写入aService.log

And all task executions are written to aExecutor.log and so on. 并且所有任务执行都写入aExecutor.log等。

If you have a multi-threaded executor you might have to use a log accumulator or another technique to properly align log messages for multiple threads. 如果您有多线程执行程序,则可能必须使用日志累加器或其他技术来正确对齐多个线程的日志消息。

This way you'll always have 4 log files which is not alot and not too less, I'm telling you from experience this makes life really easier. 通过这种方式,你总会有4个日志文件,这些文件不是很多而且不会太少,我从经验告诉你,这让生活变得更加轻松。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM