自行实现的jar包中,日志库的适配实现
日常情况下,我们自己都会自行实现一些基础的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的日志实现,如果小伙伴们有其他需要可自行实现其他的日志库
相应的代码实现,可以点击以下链接进行下载。
[日志库适配简单实现方式](
最新文章
- thinkphp + 美图秀秀api 实现图片裁切上传,带数据库
- 二叉树遍历(Binary Tree Traversal)
- java io流 创建文件、写入数据、设置输出位置
- hibernate中session的获取使用以及其他注意事项
- Java中的数组排序
- java随机生成字符串并排序
- 如何给Qlabel添加clicked属性(覆盖mousePressEvent,处理QMouseEvent消息,反正是软绘制,想怎么样就怎么样)
- 【模拟】Codeforces 705B Spider Man
- iOS开发之网络篇-各种网络状态码
- js 效果样式大全
- 手机浏览器wap网页点击链接触发颜色区块的问题解决办法
- windows.onload和 document.ready区别
- media query 单位
- Server 2008 R2远程桌面授权,解决120天过期问题
- ESP8266最小系统
- 【漫画解读】HDFS存储原理
- crc循环冗余检验
- [HTML] HTML Lists
- PAT 1081 Rational Sum
- s11 day Linux 和nginx 部署