dan*_*rod 42 java logging log4j logback slf4j
我已经阅读了很多帖子和文档(在这个网站和其他地方),指出SFL4J日志记录的推荐模式是:
public class MyClass {
final static Logger logger = LoggerFactory.getLogger(MyClass.class);
public void myMethod() {
//do some stuff
logger.debug("blah blah blah");
}
}
Run Code Online (Sandbox Code Playgroud)
我的老板更喜欢我们只使用一个包装器来拦截日志调用,并避免使用样板代码来声明每个类的记录器:
public class MyLoggerWrapper {
public static void debug(Class clazz, String msg){
LoggerFactory.getLogger(clazz).debug(msg));
}
}
Run Code Online (Sandbox Code Playgroud)
并简单地使用它:
public class MyClass {
public void myMethod() {
//do some stuff
MyLoggerWrapper.debug(this.getClass(), "blah blah blah");
}
}
Run Code Online (Sandbox Code Playgroud)
我假设每次我们记录时实例化一个记录器有点贵,但我一直无法找到支持该假设的任何文件.除此之外他肯定说框架(LogBack或Log4J我们仍在决定)将"缓存"记录器,而且无论如何服务器的运行速度都远低于它们的容量,所以这不是问题.
有任何帮助指出这种方法的潜在问题?
mat*_*t b 30
这个方法有一个明显的问题:String消息将在每次调用时构造debug(),没有明显的方法在你的包装器中使用一个guard子句.
log4j/commons-logging/slf4j的标准习惯用法是使用一个保护子句,例如:
if (log.isDebugEnabled()) log.debug("blah blah blah");
Run Code Online (Sandbox Code Playgroud)
目的是如果DEBUG没有为记录器启用级别,编译器可以避免将您可能发送的任何更长的字符串连接在一起:
if (log.isDebugEnabled()) log.debug("the result of method foo is " + bar
+ ", and the length is " + blah.length());
Run Code Online (Sandbox Code Playgroud)
请参阅"(不)记录的最快方式是什么?" 在SLF4J或log4j FAQ中.
我建议不要反对老板建议的"包装".像slf4j或commons-logging这样的库已经是围绕所使用的实际底层日志记录实现的一个外观.此外,记录器的每次调用都会变得更长 - 比较上面的内容
MyLoggerWrapper.debug(Foo.class, "some message");
Run Code Online (Sandbox Code Playgroud)
这是微不足道的,不重要"包装"和模糊处理,供应比增加间接的和丑陋的,fying你的代码层之外没有什么实际意义的类型.我认为你的老板可以找到更重要的问题来讨论.
Ste*_*n C 11
重复调用LoggerFactory.getLogger(clazz)不应每次都产生新的Logger对象.但这并不意味着电话是免费的.虽然实际行为取决于外观后面的日志记录系统,但每个getLogger很可能需要在并发或同步数据结构1中查找以查找预先存在的实例.
如果您的应用程序对您的MyLoggerWrapper.debug方法进行了大量调用,那么这些都可能会对性能产生重大影响.在多线程应用程序中,它可能是并发瓶颈.
其他答案提到的其他问题也很重要:
logger.isDebugEnabled()在禁用调试时,您的应用程序无法再用于最小化开销.MyLoggerWrapper班掩盖您的应用程序的调试调用的类名和行号.MyLoggerWrapper如果您进行多个记录器调用,则使用的代码可能会更加冗长.冗长将出现在最易影响可读性的领域; 即在做需要记录的事情的方法中.最后,这只是"不是它完成的方式".
1 - 显然它是Hashtable在Logback和Log4j中,这意味着并发瓶颈的可能性肯定存在.请注意,这不是对那些日志框架的批评.相反,该getLogger方法未被设计/优化以便以这种方式使用.
为了补充已经提到的原因,老板的建议很糟糕,因为:
this.getClass()this在静态上下文中没有)小智 7
使用类似的东西MyLoggerWrapper.debug(this.getClass(), "blah")
时:使用AOP框架或代码注入工具时,您将得到错误的类名.类名不是原点,而是生成的类名.使用包装器的另一个缺点:对于每个日志语句,您必须包含其他代码"MyClass.class".
记录器的"缓存"取决于使用的框架.但即使它确实如此,它仍然必须为您创建的每个日志语句查找所需的记录器.因此,在方法中有3个语句,它必须查找3次.使用它作为static变量,它只能查找一次!
之前说过:你失去了使用if( log.isXXXEnabled() ){}一组语句的能力.
你的老板对"社区默认接受和推荐的方式"有什么看法?介绍包装器并没有增加更多的效率.相反,您必须为每个日志语句使用classname.过了一段时间你想"改进"那个,所以你添加另一个变量,或者另一个包装器让你自己更难.
这里有一种方法可以让您轻松地在Java 8中进行日志记录 - 定义一个接口来为您完成.例如:
package logtesting;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public interface Loggable {
enum LogLevel {
TRACE, DEBUG, INFO, WARN, ERROR
}
LogLevel TRACE = LogLevel.TRACE;
LogLevel DEBUG = LogLevel.DEBUG;
LogLevel INFO = LogLevel.INFO;
LogLevel WARN = LogLevel.WARN;
LogLevel ERROR = LogLevel.ERROR;
default void log(Object...args){
log(DEBUG, args);
}
default void log(final LogLevel level, final Object...args){
Logger logger = LoggerFactory.getLogger(this.getClass());
switch(level){
case ERROR:
if (logger.isErrorEnabled()){
logger.error(concat(args));
}
break;
case WARN:
if (logger.isWarnEnabled()){
logger.warn(concat(args));
}
break;
case INFO:
if (logger.isInfoEnabled()){
logger.info(concat(args));
}
case TRACE:
if (logger.isTraceEnabled()){
logger.trace(concat(args));
}
break;
default:
if (logger.isDebugEnabled()){
logger.debug(concat(args));
}
break;
}
}
default String concat(final Object...args){
return Arrays.stream(args).map(o->o.toString()).collect(Collectors.joining());
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您所要做的就是确保您的类声明实现Logged,并且从其中任何一个,您可以执行以下操作:
log(INFO, "This is the first part ","of my string ","and this ","is the last");
Run Code Online (Sandbox Code Playgroud)
log()函数负责连接字符串,但只有在测试启用后才能使用.默认情况下,它会记录到调试,如果要记录到调试,则可以省略LogLevel参数.这是一个非常简单的例子.你可以做很多事情来改进,例如实现各个方法,即error(),trace(),warn()等.你也可以简单地将"logger"实现为一个返回记录器的函数:
public interface Loggable {
default Logger logger(){
return LoggerFactory.getLogger(this.getClass());
}
}
Run Code Online (Sandbox Code Playgroud)
然后使用您的记录器变得非常简单:
logger().debug("This is my message");
Run Code Online (Sandbox Code Playgroud)
您甚至可以通过为所有Logger方法生成委托方法使其完全正常运行,这样每个实现类都是Logger的一个实例.
package logtesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
public interface Loggable extends Logger {
default Logger logger(){
return LoggerFactory.getLogger(this.getClass());
}
default String getName() {
return logger().getName();
}
default boolean isTraceEnabled() {
return logger().isTraceEnabled();
}
default void trace(String msg) {
logger().trace(msg);
}
default void trace(String format, Object arg) {
logger().trace(format, arg);
}
default void trace(String format, Object arg1, Object arg2) {
logger().trace(format, arg1, arg2);
}
default void trace(String format, Object... arguments) {
logger().trace(format, arguments);
}
default void trace(String msg, Throwable t) {
logger().trace(msg, t);
}
default boolean isTraceEnabled(Marker marker) {
return logger().isTraceEnabled(marker);
}
default void trace(Marker marker, String msg) {
logger().trace(marker, msg);
}
default void trace(Marker marker, String format, Object arg) {
logger().trace(marker, format, arg);
}
default void trace(Marker marker, String format, Object arg1, Object arg2) {
logger().trace(marker, format, arg1, arg2);
}
default void trace(Marker marker, String format, Object... argArray) {
logger().trace(marker, format, argArray);
}
default void trace(Marker marker, String msg, Throwable t) {
logger().trace(marker, msg, t);
}
default boolean isDebugEnabled() {
return logger().isDebugEnabled();
}
default void debug(String msg) {
logger().debug(msg);
}
default void debug(String format, Object arg) {
logger().debug(format, arg);
}
default void debug(String format, Object arg1, Object arg2) {
logger().debug(format, arg1, arg2);
}
default void debug(String format, Object... arguments) {
logger().debug(format, arguments);
}
default void debug(String msg, Throwable t) {
logger().debug(msg, t);
}
default boolean isDebugEnabled(Marker marker) {
return logger().isDebugEnabled(marker);
}
default void debug(Marker marker, String msg) {
logger().debug(marker, msg);
}
default void debug(Marker marker, String format, Object arg) {
logger().debug(marker, format, arg);
}
default void debug(Marker marker, String format, Object arg1, Object arg2) {
logger().debug(marker, format, arg1, arg2);
}
default void debug(Marker marker, String format, Object... arguments) {
logger().debug(marker, format, arguments);
}
default void debug(Marker marker, String msg, Throwable t) {
logger().debug(marker, msg, t);
}
default boolean isInfoEnabled() {
return logger().isInfoEnabled();
}
default void info(String msg) {
logger().info(msg);
}
default void info(String format, Object arg) {
logger().info(format, arg);
}
default void info(String format, Object arg1, Object arg2) {
logger().info(format, arg1, arg2);
}
default void info(String format, Object... arguments) {
logger().info(format, arguments);
}
default void info(String msg, Throwable t) {
logger().info(msg, t);
}
default boolean isInfoEnabled(Marker marker) {
return logger().isInfoEnabled(marker);
}
default void info(Marker marker, String msg) {
logger().info(marker, msg);
}
default void info(Marker marker, String format, Object arg) {
logger().info(marker, format, arg);
}
default void info(Marker marker, String format, Object arg1, Object arg2) {
logger().info(marker, format, arg1, arg2);
}
default void info(Marker marker, String format, Object... arguments) {
logger().info(marker, format, arguments);
}
default void info(Marker marker, String msg, Throwable t) {
logger().info(marker, msg, t);
}
default boolean isWarnEnabled() {
return logger().isWarnEnabled();
}
default void warn(String msg) {
logger().warn(msg);
}
default void warn(String format, Object arg) {
logger().warn(format, arg);
}
default void warn(String format, Object... arguments) {
logger().warn(format, arguments);
}
default void warn(String format, Object arg1, Object arg2) {
logger().warn(format, arg1, arg2);
}
default void warn(String msg, Throwable t) {
logger().warn(msg, t);
}
default boolean isWarnEnabled(Marker marker) {
return logger().isWarnEnabled(marker);
}
default void warn(Marker marker, String msg) {
logger().warn(marker, msg);
}
default void warn(Marker marker, String format, Object arg) {
logger().warn(marker, format, arg);
}
default void warn(Marker marker, String format, Object arg1, Object arg2) {
logger().warn(marker, format, arg1, arg2);
}
default void warn(Marker marker, String format, Object... arguments) {
logger().warn(marker, format, arguments);
}
default void warn(Marker marker, String msg, Throwable t) {
logger().warn(marker, msg, t);
}
default boolean isErrorEnabled() {
return logger().isErrorEnabled();
}
default void error(String msg) {
logger().error(msg);
}
default void error(String format, Object arg) {
logger().error(format, arg);
}
default void error(String format, Object arg1, Object arg2) {
logger().error(format, arg1, arg2);
}
default void error(String format, Object... arguments) {
logger().error(format, arguments);
}
default void error(String msg, Throwable t) {
logger().error(msg, t);
}
default boolean isErrorEnabled(Marker marker) {
return logger().isErrorEnabled(marker);
}
default void error(Marker marker, String msg) {
logger().error(marker, msg);
}
default void error(Marker marker, String format, Object arg) {
logger().error(marker, format, arg);
}
default void error(Marker marker, String format, Object arg1, Object arg2) {
logger().error(marker, format, arg1, arg2);
}
default void error(Marker marker, String format, Object... arguments) {
logger().error(marker, format, arguments);
}
default void error(Marker marker, String msg, Throwable t) {
logger().error(marker, msg, t);
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如前所述,这意味着每次登录时,您都必须在LoggerFactory中完成Logger查找过程 - 除非您覆盖logger()方法......在这种情况下,您可以执行此操作"推荐"的方式.
正如SLF4J 团队在此处所述,您可以使用 JDK 1.7 中引入的 MethodLookup()。
final static Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Run Code Online (Sandbox Code Playgroud)
这样您就可以引用该类,而无需使用关键字“this”。
不,除了它会弄乱调用堆栈之外。这会破坏允许您查看执行日志的代码的方法名称和类的方法。
您可以考虑查看 Jetty Web 容器,它包含构建在 slf4j 之上的自己的抽象。很不错。
| 归档时间: |
|
| 查看次数: |
64966 次 |
| 最近记录: |