有时当我看到我的日志代码时,我想知道我是否正确行事。可能没有明确的答案,但我有以下问题:
图书馆课程
我有几个库类可能会记录一些INFO
消息。致命错误报告为例外。目前我的类中有一个静态记录器实例,类名作为日志名称。 (Log4j:Logger.getLogger(MyClass.class)
)
这是正确的方法吗?也许这个库类的用户不希望来自我的实现的任何消息,或者想要将它们重定向到特定于应用程序的日志。我应该允许用户从“外部世界”设置记录器吗?你是如何处理这种情况的?
常规日志
在某些应用程序中,我的类可能希望将日志消息写入未由类名称标识的特定日志。 (即HTTP Request log
)这样做的最佳方法是什么?想到了查找服务......
答案 0 :(得分:37)
你的约定非常标准,非常好(imho)。
要注意的一件事是来自过多的无限制调试调用的内存碎片,因此,对于Log4J(以及大多数其他Java日志框架),您最终会得到类似这样的内容:
if (log.isDebugEnabled()) {
log.debug("...");
}
因为构建该日志消息(您可能没有使用)可能很昂贵,特别是如果完成数千或数百万次。
您的INFO级别日志记录不应该太“繁琐”(并且从您所说的,听起来不是这样)。 INFO消息通常应该是有意义且重要的,例如启动和停止应用程序。如果遇到问题,您可能想知道的事情。当您确实遇到正在尝试诊断的问题时,调试/精细级别日志记录会更多地用于。调试/精细记录通常仅在需要时打开。信息通常一直在上。
如果某人不希望您的课程中有特定的INFO消息,他们当然可以自由更改您的log4j配置以获取它们。 Log4j在这个部门非常简单(与Java 1.4日志记录相反)。
至于你的HTTP事情,我一般都没有发现这是Java日志记录的问题,因为通常一个类负责你感兴趣的内容,所以你只需要把它放在一个地方。在(在我的经验中很少见)当你想要看似不相关的类的常见日志消息时,只需输入一些可以轻易获取的令牌。
答案 1 :(得分:7)
在@cletus' answer中,他写了
的问题if (log.isDebugEnabled()) {
log.debug("val is " + value);
}
可以使用SL4J克服。它提供格式化帮助
log.debug("val is {}", value);
只有在级别为debug时才构造消息。
所以,现在,出于性能和稳定性原因,使用SL4J及其伴随记录器Logback为advised。
答案 2 :(得分:7)
以下是我在所有项目中遵循的准则,以确保良好的表现。我已经根据互联网上各种来源的输入来制定这套指导方针。
就像今天一样,我相信Log4j 2是迄今为止用于登录Java的最佳选择。
基准测试可用here。为了获得最佳性能,我遵循的做法如下:
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
private static final Marker sqlMarker = MarkerManager.getMarker("SQL"); private void method1() { logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE"); }
int i=5, j=10; logger.info("Sample output {}, {}", ()->i, ()->j);
不要使用字符串连接。使用参数化消息,如上所示
使用日志配置的动态重新加载,以便应用程序自动重新加载日志记录配置中的更改,而无需重新启动应用程序
请勿使用printStackTrace()
或System.out.println()
应用程序应在退出前关闭记录器:
LogManager.shutdown();
<?xml version="1.0" encoding="UTF-8"?> <Configuration monitorinterval="300" status="info" strict="true"> <Properties> <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property> <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample </Property> <property name="logSize">10 MB</property> </Properties> <Appenders> <RollingFile name="RollingFileRegular" fileName="${filename}.log" filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log"> <Filters> <MarkerFilter marker="SQL" onMatch="DENY" onMismatch="NEUTRAL" /> </Filters> <PatternLayout> <Pattern>%d{HH:mm:ss,SSS} %m%n </Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="${logSize}" /> </Policies> </RollingFile> <RollingFile name="RollingFileError" fileName="${filename}_error.log" filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log" immediateFlush="true"> <PatternLayout> <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n </Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="${logSize}" /> </Policies> </RollingFile> </Appenders> <Loggers> <AsyncLogger name="com" level="trace"> <AppenderRef ref="RollingFileRegular"/> </AsyncLogger> <Root includeLocation="true" level="trace"> <AppenderRef ref="RollingFileError" level="error" /> </Root> </Loggers> </Configuration>
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.6</version> </dependency> <!-- (Optional)To be used when working with the applications using Log4j 1.x --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-1.2-api</artifactId> <version>2.8.1</version> </dependency>
答案 3 :(得分:6)
关于实例化记录器,我使用Eclipse Java模板设置记录器取得了一些成功:
private static Logger log = Logger.getLogger(${enclosing_type}.class);
这样可以避免JVM在堆栈跟踪中出现问题,并且可以减少(通常可能)创建堆栈跟踪的开销。
使用这样的模板的好处是,如果您想为记录器设置一致的标准,您可以与团队共享。
看起来IntelliJ支持表示封闭类型名称的模板变量的相同概念。我没有看到在NetBeans中轻松实现这一目标的方法。
答案 4 :(得分:4)
您要描述的log4j配置类型的首选选项是使用 log4j配置文件。这允许您的实现的用户完全按照您的要求执行操作,因为他们可以稍后使用更适合自己实现的内容覆盖您的配置。有关非常详尽的入门知识,请参阅here。
答案 5 :(得分:4)
我可能从某个地方偷了这个,但这很好。
它可以降低复制和消息时混合记录器的风险,并且可以减少重写次数。
在您的代码中:
private final static Logger logger = LoggerFactory.make();
...并且在LoggerFactory中:
public static Logger make() {
Throwable t = new Throwable();
StackTraceElement directCaller = t.getStackTrace()[1];
return Logger.getLogger(directCaller.getClassName());
}
(请注意,stackdump是在初始化期间完成的。堆栈跟踪可能不会被JVM优化掉,但实际上没有保证)
答案 6 :(得分:4)
我正在审核某个应用程序的日志级别,而且我目前正在检测某个模式:
private static final Logger logger = Logger.getLogger(Things.class)
public void bla() {
logger.debug("Starting " + ...)
// Do stuff
...
logger.debug("Situational")
// Algorithms
for(Thing t : things) {
logger.trace(...)
}
// Breaking happy things
if(things.isEmpty){
logger.warn("Things shouldn't be empty!")
}
// Catching things
try {
...
} catch(Exception e) {
logger.error("Something bad happened")
}
logger.info("Completed "+...)
}
log4j2文件定义了一个socket-appender,带有一个故障转移文件追加器。还有一个控制台appender。有时我会在情况需要时使用log4j2标记。
认为额外的观点可能有所帮助。
答案 7 :(得分:2)
另外,我认为简单的Java日志记录(SLF4J)(http://www.slf4j.org/)很重要。由于在大项目的多样化部分中使用不同的日志框架的一些问题,SLF4J是解决成功管理这些部分的问题的事实上的标准,不是吗?
第二个概念:似乎有些老派任务可以用Aspect-Oriented-Programming代替,Spring frmwrk有自己的implementation,AOP方法用于记录在StackOverflow上考虑了here,在Spring博客上考虑了here。