自行实现的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的日志实现,如果小伙伴们有其他需要可自行实现其他的日志库
相应的代码实现,可以点击以下链接进行下载。
[日志库适配简单实现方式](
自行实现的jar包中,日志库的适配实现的更多相关文章
- 使用 jpype 库实现 Python 调用 java 的 jar 包中的功能
一.what's the JPype JPype 是一个能够让 python 代码方便地调用 Java 代码的工具.在某些时候 java 的能力更强,我们可以用 java 写一个模块的功能然后用 Py ...
- java 执行 jar 包中的 main 方法
java 执行 jar 包中的 main 方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar执行后总是运行指定的主方法,如果 jar 中有多 ...
- html或者jsp页面引用jar包中的js文件
一,页面上引用jar包中的js文件的方法 使用java web框架AppFuse的时候发现,jquery.bootstrap等js框架都封装到jar包里面了.这些js文件通过一个wro4j的工具对其进 ...
- 判断一个类到底是从哪个jar包中调用的工具类
项目中使用的jar包较多时,会出现jar冲突的情况,有时候很难判断当前使用的这个类是从哪个jar包中调用的.因为一般我们只能看到jar包的名称,不清楚其中的类的目录结构. 这个类的作用就是说明当前调用 ...
- 如何在大量jar包中搜索特定字符
欢迎关注我的社交账号: 博客园地址: http://www.cnblogs.com/jiangxinnju/p/4781259.html GitHub地址: https://github.com/ji ...
- 读取Jar包中的资源问题探究
最近在写一个可执行jar的程序,程序中包含了2个资源包,一个是images,一个是files.问题来了,在Eclipse里开发的时候,当用File类来获取files下面的文件时,没有任何问题.但是当程 ...
- 【解惑】深入jar包:从jar包中读取资源文件
[解惑]深入jar包:从jar包中读取资源文件 http://hxraid.iteye.com/blog/483115 TransferData组件的spring配置文件路径:/D:/develop/ ...
- java 从jar包中读取资源文件
在代码中读取一些资源文件(比如图片,音乐,文本等等),在集成环境(Eclipse)中运行的时候没有问题.但当打包成一个可执行的jar包(将资源文件一并打包)以后,这些资源文件找不到,如下代码: Jav ...
- 27 Java动态加载第三方jar包中的类
我加载的方法是://参数fileName是jar包的路径,processorName 是业务类的包名+类名public static A load(String fileName, String pr ...
随机推荐
- centos8安装zookeeper(单机方式)
一,下载zookeeper: 1,官网地址 http://zookeeper.apache.org/ 找到这个地址: https://mirrors.tuna.tsinghua.edu.cn/apac ...
- php中,posix_getpid() 和 getmypid() 有什么不同
getmypid:windows 和 linux都可以用posix_getpid:仅linux可以用
- 单元测试之道Java版——读书笔记
单元测试知道Java版读书笔记 首先我们必须要知道我们所写的代码,它的功能是什么,如果我们不了解代码的行为,那么也就无从测试. 我们测试的目的,是为了我们整个程序架构的稳定,代码其实就是欧文要实现功能 ...
- Deployer 的使用
假设我们的项目在本地 /www/demo-project 下,那么我们切换到该目录: $ cd /www/demo-project 然后执行 Deployer 的初始化命令: $ dep init 它 ...
- Go之NSQ简介,原理和使用
NSQ简介 NSQ是Go语言编写的一个开源的实时分布式内存消息队列,其性能十分优异. NSQ 是实时的分布式消息处理平台,其设计的目的是用来大规模地处理每天数以十亿计级别的消息.它具有分布式和去中心化 ...
- Spring Boot学习笔记(一)——Eclipse安装STS插件
一.简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式进行配置,从而使开发人员不再需要定义样板化的配 ...
- 《JavaScript高级程序设计》——第二章在HTML使用JavaScript
这章讲的是JavaScript在HTML中的使用,也就是<script>元素的属性.书中详细讲了async.defer.src和type四个<script>的属性. 下面是对第 ...
- vue中iframe加载慢,给它加loading效果
js框架:vue ui框架:element 因为iframe加载慢,所以在它加载完成前添加loading效果,loading用的是element家的加载效果 <template> < ...
- Python爬虫之多线程
详情点我跳转 关注公众号"轻松学编程"了解更多. 多线程 在介绍Python中的线程之前,先明确一个问题,Python中的多线程是假的多线程! 为什么这么说,我们先明确一个概念,全 ...
- C++代码雨
闲逛的时候发现了一个很好玩的程序 摘自:https://blog.csdn.net/u012837895/article/details/20849967#comments 效果如下 #include ...