有时当我看到我的日志代码时,我想知道我是否做得对.可能没有明确的答案,但我有以下问题:
图书馆课程
我有几个库类可能会记录一些INFO
消息.致命错误报告为例外.目前我的类中有一个静态记录器实例,类名作为日志名称.(Log4j的:Logger.getLogger(MyClass.class)
)
这是正确的方法吗?也许这个库类的用户不希望我的实现发送任何消息,或者想要将它们重定向到特定于应用程序的日志.我应该允许用户从"外部世界"设置记录器吗?你如何处理这种情况?
一般日志
在某些应用程序中,我的类可能希望将日志消息写入未由类名称标识的特定日志.(即:) HTTP Request log
这样做的最佳方法是什么?想到一个查找服务......
你的约定很标准,非常好(imho).
要注意的一件事是来自过多的无限制调试调用的内存碎片,因此,使用Log4J(以及大多数其他Java日志框架),您最终得到如下内容:
if (log.isDebugEnabled()) { log.debug("..."); }
因为构建该日志消息(您可能没有使用)可能很昂贵,特别是如果完成数千或数百万次.
你的INFO级别日志记录不应该太"健谈"(从你说的,听起来不是这样).INFO消息通常应该是有意义且重要的,例如启动和停止应用程序.如果遇到问题,您可能想知道的事情.当您确实遇到正在尝试诊断的问题时,调试/精细级别日志记录会更常用.调试/精细记录通常仅在需要时打开.信息通常始终在线.
如果有人不想从您的类中获取特定的INFO消息,他们当然可以自由地更改您的log4j配置以获取它们.Log4j在这个部门非常简单(而不是Java 1.4日志记录).
至于你的HTTP事情,我一般都没有发现这是Java日志记录的问题,因为通常一个类负责你感兴趣的内容,所以你只需要把它放在一个地方.在(我的经验很少见)当你想要看似不相关的类的常见日志消息时,只需添加一些可以轻松获取的令牌.
以下是我在所有项目中遵循的指导方针,以确保良好的性能.我已经根据互联网上各种来源的输入来制定这套指导方针.
就像今天一样,我相信Log4j 2是迄今为止用于登录Java的最佳选择.
基准测试可在此处获得.为了获得最佳性能,我遵循的做法如下:
由于以下原因,我现在避免使用SLF4J:
它与标记有一些并发问题,我想用它来管理SQL语句的记录(标记不如slf4j强大 - 参考Ralph Goers的第一条评论)
它不支持Java 8 Lambda,我想再次使用它来获得更好的性能(在Logger中支持lambda表达式)
使用异步记录器执行所有常规日志记录以获得更好的性
使用同步记录器在单独的文件中记录错误消息,因为我们希望一旦发生错误消息就会看到错误消息
不要在常规日志记录中使用位置信息,例如文件名,类名,方法名,行号,因为为了获取这些信息,框架会获取堆栈的快照并遍历它.这会影响性能.因此,仅在错误日志中使用位置信息,而不是在常规日志中使用
对于跟踪由单独的线程来处理单个请求的目的,可以考虑使用线程上下文和随机UUID作为解释这里
由于我们在单独的文件中记录错误,因此我们在错误日志中记录上下文信息非常重要.例如,如果应用程序在处理文件时遇到错误,请在错误日志文件中打印文件名和正在处理的文件记录以及堆栈跟踪
日志文件应该易于理解且易于理解.例如,如果应用程序处理多个文件中的客户记录,则每条日志消息应如下所示:
12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts 12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756 12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends
使用SQL标记记录所有SQL语句,如下所示,并使用过滤器启用或禁用它:
private static final Marker sqlMarker = MarkerManager.getMarker("SQL"); private void method1() { logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE"); }
使用Java 8 Lambdas记录所有参数.这将在禁用给定日志级别时保存应用程序的格式化消息:
int i=5, j=10; logger.info("Sample output {}, {}", ()->i, ()->j);
不要使用字符串连接.使用参数化消息,如上所示
使用日志配置的动态重新加载,以便应用程序自动重新加载日志记录配置中的更改,而无需重新启动应用程序
不要使用printStackTrace()
或System.out.println()
应用程序应在退出前关闭记录器:
LogManager.shutdown();
最后,对于每个人的参考,我使用以下配置:
${env:LOG_ROOT}/SAMPLE ${env:LOG_ROOT}/SAMPLE/sample 10 MB %d{HH:mm:ss,SSS} %m%n %d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n
所需的Maven依赖项如下:
org.apache.logging.log4j log4j-api 2.8.1 org.apache.logging.log4j log4j-core 2.8.1 com.lmax disruptor 3.3.6 org.apache.logging.log4j log4j-1.2-api 2.8.1
在@cletus的回答中,他写到了问题
if (log.isDebugEnabled()) { log.debug("val is " + value); }
这可以通过使用SL4J来克服.它提供格式化帮助
log.debug("val is {}", value);
仅在级别为debug时才构造消息.
所以,现在,出于性能和稳定性的原因,建议使用SL4J及其伴随记录器Logback .
关于实例化记录器,我使用Eclipse Java模板设置记录器取得了一些成功:
private static Logger log = Logger.getLogger(${enclosing_type}.class);
这样可以避免JVM在堆栈跟踪中出现问题,并且可以减少(通常可能)创建堆栈跟踪的开销.
使用这样的模板的好处是,如果要为记录器设置一致的标准,可以与团队共享.
看起来IntelliJ支持表示封闭类型名称的模板变量的相同概念.我没有看到在NetBeans中轻松实现这一目标的方法.