自行实现的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包中,日志库的适配实现​ 日常情况下,我们自己都会自行实现一些基础的jar包,如dao包、service包或一些其他完成特定功能的jar包。如果没有一套调试日志信息,出现问题时想查找问题非常不方便。可能大多数小伙伴都会有自己的一套查找问题的方法。​ 但在jar包中添加日志信息输出还是有必要的。​ 我们自己实现的jar包中不能写死使用哪一种日志库输出,必竟现在有好几个比较流行的日志库。jdk中自带有一个,log4j ,logback,apache logging,slf4j等都是效 日志换成了 slf4j+logback,没看到本地有日志文件生成,调试时发现有错误提示(是在加载druid时出现的): SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logge...
Druid开启日志 最近总是会有线上机器的com.alibaba.druid.pool.DruidDataSource.CreateConnectionThread会无故的退出,导致池连接处小于minIdle甚至变成0, 所以就想打印这个线程或者DruidDataSource的日志来帮忙定位一下问题。 那么就让我们来看一下druid的日志体系吧。 Logger name如何生效的 首先先引入Loggername置的是如何生效的吧。 logger的name具有父子关系,比如org就是org.sprin.
问题描述: 九月 28, 2020 6:35:08 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl error 严重: create connection SQLException, url: jdbc:mysql://localhost:3306/node, errorCode 0, state 01S00 java.sql.SQ
com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl.error create connection SQLException, url: jdbc:mysql://localhost:3306/7shop?useSSL=false&useUnicode=true&characterEncoding=utf-8, errorCode 1045, state 28000 这个报错是因为jdbc.properties文件,用了u
Spring Boot 在读取 Jar 时,如果 Jar 有其他 Jar ,会自动扫描并加载这些 Jar ,让其成为 Spring Boot 应用程序的一部分。 Spring Boot 通过 Maven 或 Gradle 进行构建时,将 Jar 及其依赖项打到一个可执行的 Jar 。当我们运行这个可执行的 Jar 时,Spring Boot 自动解压 Jar Jar ,并将它们添加到应用程序的类路径。这使得应用程序可以使用这些 Jar 的类,而无需单独安装它们。 除此之外,如果在打时使用了 Spring Boot Maven 插件或 Gradle 插件,则还可以使用嵌套 Jar 。这意味着在可执行的 Jar ,每个依赖 Jar 都被打为一个 JAR 文件,而不是将它们全部解压缩到同一个目录。这使得可执行的 Jar 更容易管理和部署。 总的来说,Spring Boot 可以方便地处理 Jar 内嵌和依赖项,不需要手工处理。这使得将应用程序打成独立的可执行 Jar 变得更加容易,使应用程序的部署和维护更加简单。