代码之家  ›  专栏  ›  技术社区  ›  Adrian

log4j:创建记录器实例的策略

  •  67
  • Adrian  · 技术社区  · 15 年前

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

    • 每个类一个记录器实例 例如

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

    注:本条款已在一定程度上考虑了该问题:

    Whats the overhead of creating a Log4j Logger

    10 回复  |  直到 8 年前
        1
  •  42
  •   PSpeed    15 年前

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

    关于应用程序或基于层的记录器,问题是您必须找到一个地方来放置该记录器对象。没什么大不了的。更大的问题是,一些类可以在多个应用程序的多个级别上使用…很难把你的记录器弄好。或者至少很棘手。

    …您最不希望看到的是日志设置中的错误假设。

    如果您关心应用程序和层,并且有简单的分离点,那么NDC就是解决问题的方法。有时代码可能有点过多,但我不知道准确的上下文堆栈保存了多少次,它向我展示了从Y层的应用程序X调用foo.bar()。

        2
  •  31
  •   rsp    15 年前

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

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

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

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

        3
  •  13
  •   ripper234 Jonathan    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”可能是错误的,但要点就在这里;进行性能测试(在类加载时,作为一个静态变量)以查找类名,这样开发人员就没有办法错误地输入或引入任何错误。

    我通常不会为在运行时丢失性能以防止开发人员出错而感到兴奋,但是如果它作为单例发生,会发生一次吗?对我来说经常是个不错的交易。

        4
  •  10
  •   Matthew Farwell    11 年前

    正如其他人所说,我将为每个类创建一个记录器:

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

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

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

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

    使用MDC的另一个优点是,如果使用SL4J,则可以根据MDC中的值更改设置。因此,如果您希望在调试级别记录某个特定用户的所有活动,并使所有其他用户都处于错误状态,则可以这样做。您还可以根据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

        5
  •  4
  •   yawn    15 年前
    • 每个类创建一个记录器。
    • 如果您有需要的依赖项 共用日志记录 (很可能)使用SLF4J bridge 用于公共记录。使用commons日志接口实例化日志记录器(每个类): private static final Log log = LogFactory.getLog(MyClass.class);
    • 使用快捷方式在您的IDE中显示此模式。我使用的想法 live templates 为此目的。
    • 使用 NDC (线程本地字符串堆栈)或 MDC (串的线局部图?).

    模板示例:

    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' ...
    
        6
  •  3
  •   bora.oren    11 年前

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

    附笔: 面向方面编程 会更好的,是的 DRY(Don't Repeat Yourself) 方式。

        7
  •  2
  •   Rodislav Moldovan    12 年前

    创建未链接到任何类名的自定义记录器的最佳和最简单方法是:

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

    现在看到上面的代码并创建新的文件附加器,这样您的输出将被发送到其他文件中。

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

    • 当需要将错误与信息和警告分开时

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

    还有问题吗?请随便问!:)

        8
  •  0
  •   Thorbjørn Ravn Andersen    15 年前

    常见的约定是“logger pr类,并使用类名作为其名称”。这是个好建议。

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

    此外,您还应该了解您选择的日志后端的所有可能性。你可能有一些你没有预料到的可能。

        9
  •  0
  •   Bas    15 年前

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

        10
  •  -1
  •   Qaiser Habib    10 年前

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

    1. 控制器
    2. 服务实现
    3. 遗嘱执行人
    4. 坚持不懈

    所以它使生活更容易有一个 控制日志 阿斯维克原木 原木 和persistance.log

    这是基于层的分离,因此所有远程处理/REST/SOAP类都将写入acontroller.log。

    您的所有调度机制、后端服务等都将写入aseservice.log。

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

    如果您有一个多线程的执行器,那么您可能需要使用日志累加器或其他技术来为多个线程正确地对齐日志消息。

    这样的话,你总会有4个日志文件,这些文件不会太多,也不会太少,我从经验中告诉你,这会让生活变得更轻松。