​ 日常情况下,我们自己都会自行实现一些基础的jar包,如dao包、service包或一些其他完成特定功能的jar包。如果没有一套调试日志信息,出现问题时想查找问题非常不方便。可能大多数小伙伴都会有自己的一套查找问题的方法。

​ 但在jar包中添加日志信息输出还是有必要的。

​ 我们自己实现的jar包中不能写死使用哪一种日志库输出,必竟现在有好几个比较流行的日志库。jdk中自带有一个,log4j ,logback,apache logging,slf4j等都是效率比较高的日志信息。有这么多可用的日志库,我们完全没有必要自行实现一个日志库。

​ 在自己实现的jar包中,可以实现一个适配,我们将jar提供给别人使用时,别人在使用的过程中如果使用的是slf4j或logback,我们的jar包可以做到自行适配相应的日志库,这种方案是比较完美可行的。

1.实现接口,日志操作接口

public interface Log {
boolean isDebugEnabled();
void debug(String msg);
void debug(String msg, Throwable e);
boolean isErrorEnabled();
void error(String msg, Throwable e);
void error(String msg);
boolean isInfoEnabled();
void info(String msg);
boolean isWarnEnabled();
void warn(String msg);
void warn(String msg, Throwable e);
}

​ 与大多数日志库的接口一样,日志输出分为debug,info,error,warn级别,同理也做拉相应级别的日志输出开关。

2.SLF4J日志库的适配实现

public class SLF4JImpl implements Log {

    private static final String callerFQCN = SLF4JImpl.class.getName();
private static final Logger testLogger = LoggerFactory.getLogger(SLF4JImpl.class);
static {
// if the logger is not a LocationAwareLogger instance, it can not get correct stack StackTraceElement
// so ignore this implementation.
if (!(testLogger instanceof LocationAwareLogger)) {
throw new UnsupportedOperationException(testLogger.getClass() + " is not a suitable logger");
}
}
private LocationAwareLogger log;
public SLF4JImpl(LocationAwareLogger log){
this.log = log;
} public SLF4JImpl(String loggerName){
this.log = (LocationAwareLogger) LoggerFactory.getLogger(loggerName);
}
public boolean isDebugEnabled() {
return log.isDebugEnabled();
}
public void debug(String msg) {
log.log(null, callerFQCN, LocationAwareLogger.DEBUG_INT, msg, null, null);
}
public void debug(String msg, Throwable e) {
log.log(null, callerFQCN, LocationAwareLogger.DEBUG_INT, msg, null, e);
}
public boolean isErrorEnabled() {
return log.isErrorEnabled();
}
public void error(String msg, Throwable e) {
log.log(null, callerFQCN, LocationAwareLogger.ERROR_INT, msg, null, e);
}
public void error(String msg) {
log.log(null, callerFQCN, LocationAwareLogger.ERROR_INT, msg, null, null);
}
public boolean isInfoEnabled() {
return log.isInfoEnabled();
}
public void info(String msg) {
log.log(null, callerFQCN, LocationAwareLogger.INFO_INT, msg, null, null);
}
public boolean isWarnEnabled() {
return log.isWarnEnabled();
}
public void warn(String msg) {
log.log(null, callerFQCN, LocationAwareLogger.WARN_INT, msg, null, null);
}
public void warn(String msg, Throwable e) {
log.log(null, callerFQCN, LocationAwareLogger.WARN_INT, msg, null, e);
}
}

​ 从上述代码可以看到,SLF4J的日志库适配实现,类的内部实现还是以slf4j的内部对象来完成相应的日志输出,与日志级别开关的控制。最重要的一句代码是在静态代码块中,对创建出来的testLogger对象进行对象的比对。

3.日志工厂实现,获取具体的日志操作实现类

public final class LogFactory {
private static Constructor logConstructor;
static {
String logType= System.getProperty("logType");
if(logType != null){
if(logType.equalsIgnoreCase("slf4j")){
tryImplementation("org.slf4j.Logger", "logging.slf4j.SLF4JImpl");
}
}
// 优先选择log4j,而非Apache Common Logging. 因为后者无法设置真实Log调用者的信息
tryImplementation("org.slf4j.Logger", "logging.slf4j.SLF4JImpl");
if (logConstructor == null) {
try {
logConstructor = NoLoggingImpl.class.getConstructor(String.class);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
private static void tryImplementation(String testClassName, String implClassName) {
if (logConstructor != null) {
return;
} try {
Resources.classForName(testClassName);
Class implClass = Resources.classForName(implClassName);
logConstructor = implClass.getConstructor(new Class[] { String.class }); Class<?> declareClass = logConstructor.getDeclaringClass();
if (!Log.class.isAssignableFrom(declareClass)) {
logConstructor = null;
} try {
if (null != logConstructor) {
logConstructor.newInstance(LogFactory.class.getName());
}
} catch (Throwable t) {
logConstructor = null;
//t.printStackTrace();
} } catch (Throwable t) {
//t.printStackTrace();
}
} public static Log getLog(Class clazz) {
return getLog(clazz.getName());
} public static Log getLog(String loggerName) {
try {
System.out.println(logConstructor);
return (Log) logConstructor.newInstance(loggerName);
} catch (Throwable t) {
throw new RuntimeException("Error creating logger for logger '" + loggerName + "'. Cause: " + t, t);
}
}
}
  • 使用时,通过LogFactory.getLog方法来获取log对象,进行日志输出。
  • 在静态代码块中,对系统中使用的日志库进行探测,优先选择slf4j日志库。
  • 对于系统中没有匹配到相应的日志,则会适配一个NoLoggingImpl空的日志输出控制对象

4.maven依赖

​ 代码实现需要使用slf4j中的api,所以在pom.xml中需要添加上slf4j包的依赖,但添加上此依赖后,说明我们的jar包就不在是一个比较干净的包实现,因此需要对jar包进行一定范围内的控制。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
<optional>true</optional>
</dependency>

​ optional选项,代表的是如果jar依赖者如果显示的调用则会添加上相关的jar依赖,否则不会进行jar的依赖

5.无日志库依赖时测试

​ 外部使用者没有添加任何的日志输出,则不会有相应的jar包来实现

​ 输出结果:

public logging.nologgin.NoLoggingImpl(java.lang.String)

​ 结果中可以查看到,日志库使用的是NoLoggingImpl一个内部自行实现的空的日志输出

6.有日志库依赖时

​ 这里的测试使用的是maven项目,相应的pom文件中,加入logback依赖包。

​ 并且在resource目录下,有相应的logback.xml日志配置文件。

​ 输出结果:

public logging.slf4j.SLF4JImpl(java.lang.String)

​ 结果中可以看到,我们的jar包中,已经自行实现SLF4J日志库的适配

至此一个日志库的自适应适配完成实现 ,代码中只是给出拉SLF4J的日志实现,如果小伙伴们有其他需要可自行实现其他的日志库

相应的代码实现,可以点击以下链接进行下载。

[日志库适配简单实现方式](

最新文章

  1. thinkphp + 美图秀秀api 实现图片裁切上传,带数据库
  2. 二叉树遍历(Binary Tree Traversal)
  3. java io流 创建文件、写入数据、设置输出位置
  4. hibernate中session的获取使用以及其他注意事项
  5. Java中的数组排序
  6. java随机生成字符串并排序
  7. 如何给Qlabel添加clicked属性(覆盖mousePressEvent,处理QMouseEvent消息,反正是软绘制,想怎么样就怎么样)
  8. 【模拟】Codeforces 705B Spider Man
  9. iOS开发之网络篇-各种网络状态码
  10. js 效果样式大全
  11. 手机浏览器wap网页点击链接触发颜色区块的问题解决办法
  12. windows.onload和 document.ready区别
  13. media query 单位
  14. Server 2008 R2远程桌面授权,解决120天过期问题
  15. ESP8266最小系统
  16. 【漫画解读】HDFS存储原理
  17. crc循环冗余检验
  18. [HTML] HTML Lists
  19. PAT 1081 Rational Sum
  20. s11 day Linux 和nginx 部署

热门文章

  1. js获取页面高度
  2. Python语言应用解析,如何入门学Python?
  3. python提取视频第一帧图片
  4. win10系统出现“VMware Workstation与Device/Credential Guard不兼容”的解决办法
  5. Mac 搭建 Redis 集群
  6. 一些常见JAVA问题
  7. 线上问题排查,一不小心踩到阿里的 arthas坑了
  8. 牛客1029A 袜子分配
  9. Flask简介与启动服务器
  10. 【Luogu】P6232 [eJOI2019]挂架 题解