你好,SLF4J

解决SlF4J配置冲突警告:【SLF4J: Class path contains multiple SLF4J providers】 SLF4J :Class path contains multiple SLF4J bindings.这样的异常情况还有很多,这是一大类问题,解决的思路都是一样的:分析pom依赖,找到依赖冲突的jar,在对应的pom中将期排除就ok。 阅读详情

作为 CV 工程师,咱们开发的应用并不总是按预期运行,为了方便排查出潜在的问题,一般会在代码中添加日志记录语句。但在 Java 刚刚问世时,日志记录方式好像除了System.out和System.err之外也没啥别的选择了,主要痛点有:1) 日志无法分级,有些日志纯属 DEBUG,在生产环境是不需要的;2) 日志内容不支持格式化,如 XML、HTML。后来,一位名叫Ceki Gülcü的大神无奈之下发布了大名鼎鼎的log4j。尽管现在 log4j 逐渐退出历史舞台,但在当时却备受 Java 开发人员的喜爱,甚至 JDK 1.4 也是借鉴了 log4j 之后,终于在官方类库中补齐了日志记录这一短板,它就是j.u.l包。

在《阿里 Java 开发手册》中有一条日志规约:【强制】应用中不可直接使用日志系统 (log4j、logback) 中的 API,而应依赖日志门面 (slf4j、jcl) 中的 API 。slf4j (Simple Logging Facade for Java) 同样由 Ceki Gülcü 完成开发,作为一款日志门面,其精心设计了org.slf4j.Logger接口,用于抽象日志记录行为,开发人员只需要与该接口交互即可,这样也就将 Java 应用与具体的日志系统 (jul、log4j、logback 和 log4j2) 解耦开来了。本文重点关注 slf4j 是如何实现 Java 应用与一众日志系统解耦的!

slf4j-1.7.36 于2022年2月份发布,至此,1.X 系列也走到了尽头,后续 slf4j 将基于 2.X 演进下去。目前大多数项目都还在使用 1.X 系列,因此,笔者这里基于 1.7.36 来分析。源码中有十几个模块,笔者以为可以将其核心模块归为三类,如下图所示。

1. binding module

如果当前应用的classpath下不仅有日志系统,而且还有与该日志系统匹配的绑定模块,那么面向 slf4j-api 的日志记录请求最终会被路由到该日志系统中去。

slf4j 共计提供了3个绑定模块,分别是 slf4j-jdk14、slf4j-log4j12 和 slf4j-jcl。难道没有为logback和log4j2提供绑定模块吗?这俩可是当前主流的日志系统啊!非也,logback 也是 Ceki Gülcü 大佬干出来的,绑定模块直接集成到其 logback-classic 模块了;而 log4j2 的绑定模块位于 log4j2 源码的 log4j-slf4j-impl 模块内。

我们一般通过org.slf4j.LoggerFactory.getLogger()这种方式来获取实现了org.slf4j.Logger接口的实例,LoggerFactory 主要内容如下:

package org.slf4j;

public final class LoggerFactory {

    static final int UNINITIALIZED = 0;
    static final int ONGOING_INITIALIZATION = 1;
    static final int FAILED_INITIALIZATION = 2;
    static final int SUCCESSFUL_INITIALIZATION = 3;
    static final int NOP_FALLBACK_INITIALIZATION = 4;

    static volatile int INITIALIZATION_STATE = UNINITIALIZED;
    static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

    private LoggerFactory() {}

    public static Logger getLogger(Class<?> clazz) {
        return getLogger(clazz.getName());
    }

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

    public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    performInitialization();
                }
            }
        }
        switch (INITIALIZATION_STATE) {
            case SUCCESSFUL_INITIALIZATION:
                return StaticLoggerBinder.getSingleton().getLoggerFactory();
            case NOP_FALLBACK_INITIALIZATION:
                return NOP_FALLBACK_FACTORY;
            case FAILED_INITIALIZATION:
                throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
        }
    }

    private final static void performInitialization() {
        bind();
    }

    private final static void bind() {
        try {
            Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
            if(staticLoggerBinderPathSet.size() > 1) {
                Util.report("Class path contains multiple SLF4J bindings.");
                for (URL path : staticLoggerBinderPathSet) {
                    Util.report("Found binding in [" + path + "]");
                }
            }
            // the next line does the binding
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            if (staticLoggerBinderPathSet != null && staticLoggerBinderPathSet.size() > 1) {
                Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
            }
        } catch (NoClassDefFoundError ncde) {
            String msg = ncde.getMessage();
            if (msg.contains("org/slf4j/impl/StaticLoggerBinder") || msg.contains("org.slf4j.impl.StaticLoggerBinder")) {
                INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
                Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
                Util.report("Defaulting to no-operation (NOP) logger implementation");
            } else {
                INITIALIZATION_STATE = FAILED_INITIALIZATION;
                Util.report("Failed to instantiate SLF4J LoggerFactory", ncde);
                throw ncde;
            }
        } catch (java.lang.NoSuchMethodError nsme) {
            String msg = nsme.getMessage();
            if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                INITIALIZATION_STATE = FAILED_INITIALIZATION;
                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
                Util.report("Your binding is version 1.5.5 or earlier.");
                Util.report("Upgrade your binding to version 1.6.x.");
            }
            throw nsme;
        } catch (Exception e) {
            INITIALIZATION_STATE = FAILED_INITIALIZATION;
            Util.report("Failed to instantiate SLF4J LoggerFactory", e);
            throw new IllegalStateException("Unexpected initialization failure", e);
        }
    }

    static Set<URL> findPossibleStaticLoggerBinderPathSet() {
        Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
        try {
            ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
            Enumeration<URL> paths;
            if (loggerFactoryClassLoader == null) {
                paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
            } else {
                paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
            }
            while (paths.hasMoreElements()) {
                URL path = paths.nextElement();
                staticLoggerBinderPathSet.add(path);
            }
        } catch (IOException ioe) {
            Util.report("Error getting resources from path", ioe);
        }
        return staticLoggerBinderPathSet;
    }
}

可见,LoggerFactory 中维护了一个名 INITIALIZATION_STATE 的类变量,用来标识其是否已初始化。初始化逻辑由performInitialization()方法来承载,具体是执行bind()方法内的绑定逻辑。在 bind() 方法内,首先会在当前应用的classpath下搜寻org/slf4j/impl/StaticLoggerBinder.class资源,如果存在多个绑定模块,staticLoggerBinderPathSet 这一 Set<URL> 类型的局部变量中肯定是会有多个元素的;然后进行真正的绑定操作,即StaticLoggerBinder.getSingleton()。细心点的同学应该能发现,在 slf4j-api 和一众绑定模块中,均含有org/slf4j/impl/StaticLoggerBinder.java,那么如何确保一定是绑定模块中的StaticLoggerBinder脱颖而出呢?slf4j-api 模块在打包的时候会排除掉该 Class 文件。另外,StaticLoggerBinder.getSingleton() 这一行代码究竟有何深意呢?毕竟 getSingleton() 方法返回的 StaticLoggerBinder 实例并没有在 bind() 方法内使用。这是为了唤醒 JVM 去主动加载 StaticLoggerBinder,JVM 并不会一次性将所有 Class 文件加载到内存中,而是基于一种延迟加载策略。当 StaticLoggerBinder 调用其 getSingleton() 这一静态方法时,JVM 便会执行 StaticLoggerBinder 中的<clinit>()方法,<clinit>() 方法并不是咱们在 Java 代码中直接编写的方法,而是由编译器自动收集类中静态变量(不包含由 final 关键字修饰的静态常量)的赋值语句和静态初始化代码块合并而产生的;而执行 <clinit>() 方法的过程恰恰对应着类的初始化,初始化是类加载过程的最后一个阶段,必须先执行完加载和连接这俩阶段才行,进而针对 StaticLoggerBinder 类加载的三个阶段也就完成了,使得绑定模块中的 StaticLoggerBinder 成为可以被 JVM 直接使用的 Java 类型,这正是类加载机制的一个典型应用场景,即通过类加载机制来满足灵活的可插拔需求,也许这才是这一行代码背后的深意吧。注意:如果当前应用的classpath下有多个绑定模块,那么 JVM 究竟加载哪一个绑定模块中的 StaticLoggerBinder 呢?这就取决于类加载的顺序了!

下面以 slf4j-jdk14 这一绑定模块为例,分析下该模块提供的 StaticLoggerBinder。如下所示:

package org.slf4j.impl;

import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;

public class StaticLoggerBinder implements LoggerFactoryBinder {

    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

    private static final String loggerFactoryClassStr = org.slf4j.impl.JDK14LoggerFactory.class.getName();

    private final ILoggerFactory loggerFactory;

    private StaticLoggerBinder() {
        loggerFactory = new org.slf4j.impl.JDK14LoggerFactory();
    }

    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }

    public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
    }
}

从上述内容来看,slf4j-jdk14 中 StaticLoggerBinder 实现了 slf4j-api 中LoggerFactoryBinder接口的两个方法,分别是:getLoggerFactory()和getLoggerFactoryClassStr()。记住,每一个绑定模块中的 StaticLoggerBinder 总是会实现 LoggerFactoryBinder 接口。getLoggerFactory() 方法返回的是JDK14LoggerFactory,JDK14LoggerFactory 是绑定模块 slf4j-jdk14 提供的,其实现了 slf4j-api 中的ILoggerFactory接口,具体如下。

public class JDK14LoggerFactory implements ILoggerFactory {

    ConcurrentMap<String, Logger> loggerMap;

    public JDK14LoggerFactory() {
        loggerMap = new ConcurrentHashMap<String, Logger>();
        // ensure jul initialization
        java.util.logging.Logger.getLogger("");
    }

    public Logger getLogger(String name) {
        // the root logger is called "" in JUL
        if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) {
            name = "";
        }

        Logger slf4jLogger = loggerMap.get(name);
        if (slf4jLogger != null)
            return slf4jLogger;
        else {
            java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(name);
            Logger newInstance = new JDK14LoggerAdapter(julLogger);
            Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
            return oldInstance == null ? newInstance : oldInstance;
        }
    }
}

原来,绑定模块 slf4j-jdk14 针对 JDK 日志系统的java.util.logging.Logger进行了适配,这个适配器就是JDK14LoggerAdapter,其核心内容如下。

public final class JDK14LoggerAdapter implements Logger {
    transient final java.util.logging.Logger logger;

    JDK14LoggerAdapter(java.util.logging.Logger logger) {
        this.logger = logger;
    }

    public void trace(String format, Object arg) {
        if (logger.isLoggable(Level.FINEST)) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
        }
    }

    public void debug(String format, Object arg) {
        if (logger.isLoggable(Level.FINE)) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
        }
    }

    public void info(String format, Object arg) {
        if (logger.isLoggable(Level.INFO)) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
        }
    }

    public void warn(String format, Object arg) {
        if (logger.isLoggable(Level.WARNING)) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
        }
    }

    public void error(String format, Object arg) {
        if (logger.isLoggable(Level.SEVERE)) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
        }
    }

    private void log(String callerFQCN, Level level, String msg, Throwable t) {
        LogRecord record = new LogRecord(level, msg);
        record.setLoggerName(getName());
        record.setThrown(t);
        fillCallerData(callerFQCN, record);
        logger.log(record);
    }
}

2. bridge module

如果你开发了一个组件,而且希望上层应用直接通过该组件的 GAV 信息来引入,那么在你这个组件中建议使用日志门面来记录日志,但往往事与愿违,很多开发者还是直接选择具体的日志系统来记录日志,这就有点不规范了。想象一下,如果上层应用使用的日志系统是 logback,而你的组件选用的日志系统却是 log4j,为了将整个应用的日志系统拉齐为 logback,此时你可以用 slf4j 提供的针对 log4j 的桥接模块去替换 log4j 依赖,这样你就不用修改任何代码。从这个例子中,大家可能猜到了桥接模块的作用,就是将面向特定日志系统的日志记录请求路由到 slf4j-api 中去。但有一点需要注意,针对同一款日志系统的绑定模块不能与桥接模块同时存在于当前应用的classpath中,否则会造成死循环!

slf4j 共计提供了3个桥接模块,分别是 jul-to-slf4j、log4j-over-slf4j 和 jcl-over-slf4j。难道也没有为 logback 和 log4j2 提供桥接模块吗?笔者还真的没找到针对 logback 的桥接模块,这也许是大神的自信吧;而 log4j2 的桥接模块位于 log4j2 源码的 log4j-to-slf4j 模块内。

针对 jul 日志系统的桥接模块比较简单,就一个SLF4JBridgeHandler类文件,因而这里选择 jul-to-slf4j 来简单分析下。主要内容有:

package org.slf4j.bridge;

public class SLF4JBridgeHandler extends Handler {
    private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue();
    private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue();
    private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
    private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();

    public static void install() {
        java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
        rootLogger.addHandler(new org.slf4j.bridge.SLF4JBridgeHandler());
    }

    public static void uninstall() throws SecurityException {
        java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
        Handler[] handlers = rootLogger.getHandlers();
        for (int i = 0; i < handlers.length; i++) {
            if (handlers[i] instanceof org.slf4j.bridge.SLF4JBridgeHandler) {
                rootLogger.removeHandler(handlers[i]);
            }
        }
    }

    public void publish(LogRecord record) {
        Logger slf4jLogger = getSLF4JLogger(record);
        callPlainSLF4JLogger(slf4jLogger, record);
    }

    public void close() {}

    public void flush() {}

    protected Logger getSLF4JLogger(LogRecord record) {
        return LoggerFactory.getLogger(record.getLoggerName());
    }

    protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
        String i18nMessage = getMessageI18N(record);
        int julLevelValue = record.getLevel().intValue();
        if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
            slf4jLogger.trace(i18nMessage, record.getThrown());
        } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
            slf4jLogger.debug(i18nMessage, record.getThrown());
        } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
            slf4jLogger.info(i18nMessage, record.getThrown());
        } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
            slf4jLogger.warn(i18nMessage, record.getThrown());
        } else {
            slf4jLogger.error(i18nMessage, record.getThrown());
        }
    }
}

要想读懂上述源码内容,需要简单铺垫一下 jul 的相关知识。在 jul 中,有几个概念比较重要,LogManager用于读取日志配置文件、创建和维护 Logger 实例;Handler类似于 log4j 中的Appender,它决定了日志记录的输出方向,比如:Console、File 和 Memory 等,一个 Logger 实例可以持有多个 Handler;Formatter用于格式化日志记录,它决定了日志记录的样式。Logger通常是有名称的,一般是包的名称;每一个 Logger 实例会根据命名空间跟踪其父级 Logger,子 Logger 可以继承父 Logger 中的LogLevel和 Handler 等资源;根 (Root) Logger 的名称为""。

SLF4JBridgeHandler 继承了 jul 的 Handler 接口,重写了其publish()方法,主要逻辑就是委派 slf4j 来记录请求。现在自定义的 Handler 有了,还必须将其添加到 jul 的根 (Root) Logger 中去,这也恰是上述install()方法中的内容。

3. 总结

其实之所以写这篇文章,是看了美团一篇技术文章《日志级别动态调整——小工具解决大问题》,原文把绑定模块说成了桥接模块,这应该是一处错误的表述。另外,作者分享了一个真实案例,很值得我们警惕:发配送服务化项目由于间接依赖,引入了 logback 日志系统。在项目启动加载时,slf4j 动态绑定到 logback 上,但是由于发配送项目使用的 log4j,并未配置 logback.xml文件,导致在打印日志时,logback 无法匹配到具体的日志配置,从而为项目自动创建了一个日志级别为 DEBUG 的 Root Logger,所有的 Logger 以该级别打印输出,导致发配送服务化项目在中午左右高峰期,短时间内打印过多的系统日志,引起系统负载飙高,造成服务化宕机,损失非常严重。

slf4j + log4j2 实现日志系统:报错 近日使用slf4j + log4j2 实现日志系统 报出以下错误 SLF4J: No SLF4J providers were found. SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details. SLF4J: Class path contains SLF4J bindings targe 阅读详情

相关推荐

多种方法解决Failed to load class “org.slf4j.impl.StaticLoggerBinder“.的错误

多种方法解决Failed to load class “org.slf4j.impl.StaticLoggerBinder“.的错误

念兮为美 2万+

eclipse maven clean | install 报错

SLF4J: No SLF4J providers were found. SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See SLF4J Error Codes for further details. SLF4J: Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier. SLF4J: Ignoring b

xiaoqforever的专栏 1255

基于CLICKHOUSE的数据仓库分层规范

接入层。

zjyy1987的博客 771

SLF4J的错误汇总

SLF4J警告或错误消息及其含义 原文:https://www.slf4j.org/codes.html#StaticLoggerBinder No SLF4J providers were found. 如果在类路径中找不到SLF4J提供程序,则会报告此警告,即不是错误消息。在类路径中放置slf4j-nop.jar slf4j-simple.jar,slf4j-log4...

匿迹 1万+

ThinkBook 16 2024 Ubuntu驱动全攻略:从无线网卡到触控板

本文详细介绍了ThinkBook 16 2024在Ubuntu系统下的驱动问题解决方案,涵盖无线网卡、蓝牙、显卡和触控板等关键硬件的驱动修复步骤。通过具体命令和配置指南,帮助用户快速解决兼容性问题,提升Ubuntu在ThinkBook上的使用体验。

weixin_29200741的博客 830

STC8单片机高精度EV1527解码实战:定时器捕获+状态机抗干扰方案

EV1527是一种广泛应用于433MHz无线遥控的曼彻斯特编码协议,其核心在于对脉宽时序(如同步头、逻辑0/1)的微秒级精确识别。传统外部中断+延时判别方式受MCU中断延迟、编译优化及环境噪声影响,极易导致误码;而基于硬件定时器输入捕获的解码方案,将时间测量交由硬件完成,显著提升时序鲁棒性与温度/电压适应性。该技术适用于STC8等无专用无线解码外设的8位单片机,在智能开关、晾衣架、车库门控等IoT终端中实现低至0.003%的稳定误码率。本文聚焦STC8H系列利用T1定时器下降沿捕获与有限状态机协同解码EV1

weixin_29188997的博客 183

org.slf4j.impl.StaticLoggerBinder异常处理

org.slf4j.impl.StaticLoggerBinder

weixin_46990454的博客 5322

SimNow模拟环境实战:用vnpy_ctp连接期货市场的5个关键配置步骤

本文提供了一份详细的量化交易实战指南,重点讲解如何使用vnpy_ctp插件连接SimNow期货模拟环境的5个关键配置步骤。内容涵盖SimNow环境选择、vnpy_ctp核心参数解析、实战连接脚本编写、深度排错技巧以及向实盘迁移的准备,旨在帮助量化交易开发者快速搭建可靠的测试环境,避免常见陷阱。

weixin_29063035的博客 1280

解决SLF4J: Class path contains multiple SLF4J bindings.

JDK版本:jdk17IDEA版本:IntelliJ IDEA 2022.1.3SpringBoot 版本:v2.5.7maven版本:3.6.3。

siaok的博客 1万+

Part03 数据结构

本文介绍了CSP-S初赛中常见的高级数据结构及其实现,包括: 线性结构:双端栈、双端队列、单调队列、优先队列和ST表,重点分析了它们的特点、时间复杂度及典型应用场景。 集合与森林:并查集(带路径压缩和按秩合并)和树的孩子兄弟表示法,展示了多叉树转化为二叉树存储的方法。 特殊树结构:包括二叉堆、树状数组、线段树和字典树,详细说明了它们的构建、查询和更新操作,特别强调了线段树的递归实现和字典树的前缀匹配特性。 每种数据结构都配有核心C++代码实现,涉及数组模拟、动态规划和树形结构等编程技巧,适合算法竞赛选手快速

weixin_43863957的博客 1181

基于 PID 算法的 STM32 步进电机高精度位置控制系统设计与实现(工业自动化应用)

硬件层面:选用 STM32F103C8T6+A4988 + 欧姆龙编码器的组合,兼顾工业可靠性与成本,接线需注意强弱电分离和信号抗干扰;软件层面:核心为位置式 PID 闭环控制,Mermaid 流程图清晰展现 “采集 - 运算 - 驱动” 的闭环逻辑,代码模块化易移植;性能层面:无负载定位误差≤±1 脉冲,带载误差≤±3 脉冲,满足工业自动化高精度位置控制需求。

weixin_42300449的博客 194

slf4j

简单的Java日志记录(SLF4J) 官方文档:https://www.slf4j.org/docs.html 一.什么是SLF4J? SLF4J是用于日志记录系统的简单外观,允许最终用户在部署时插入所需的日志记录系统。Simple Logging Facade for Java(SLF4J)用作各种日志框架(例如java.util.logging,logback,log4j)的简单外观或抽...

三木的博客 1万+

国科大学习资料--VLSI测试与可测试设计--2020期末考试试题

国科大学习资料–VLSI测试与可测试设计–2020期末考试试题 国科大学习资料–VLSI测试与可测试设计–2020期末考试试题 国科大学习资料–VLSI测试与可测试设计–2020期末考试试题 一、概念题 1简述可测试性测度与两个主要的测度指标的含义; 2以March C-算法为例,说明March算法的构成,以及March C-算法的故障覆盖率; 3“效果-原因”诊断法和“原因效果诊断法”的不同; 4列举四条扫描设计时出现的问 题及解决办法; 5最少写出1149.1与1500的五点不同; = = = = =

无名何许人的博客 1150

【解决】SLF4J: Class path contains multiple SLF4J bindings.

【解决】SLF4J: Class path contains multiple SLF4J bindings. 在启动springboot项目时,遇到了这个问题,上网查询多个方案,最终这个方法得到解决 问题描述: SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/D:/Program%20Files/JavaEclipse/repo/ch/qos/logback/logback-cla

HKD的博客 8万+

软件设计师考试下午题代码部分详解:23种设计模式全面解析

软件设计师考试的下午题通常会考察设计模式的应用,这部分内容既是重点也是难点。本文将系统地介绍考试中常见的23种设计模式,通过详细代码示例和通俗易懂的讲解,帮助考生掌握这些核心知识点,提高应试能力。概念:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。适用场景:系统要独立于产品的创建、组合和表示时;系统要由多个产品系列中的一个来配置时。@Override@Override@Override@Override// 产品族A@Override。

4万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

SLF4J的使用

当我们的系统变的更加复杂的时候,我们的日志就容易发生混乱。随着系统开发的进行,可能会更新不同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。就算我们强制要求所有的模块使用相同的日志框架,系统中也难以避免使用其他类似spring,mybatis等其他的第三方框架,它们依赖于我们规定不同的日志框架,而且他们自身的日志系统就有着不一致性,依然会出来日志体系的混乱。...

凉茶铺的博客 6232

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

SLF4J深入剖析(涵盖SLF4J 1.8)

1.SLF4J SLF4J全称 Simple Logging Facade for Java,它为Java下的日志系统提供了一套统一的门面(接口)。通过引入SLF4J,我们可以使项目的logging与logging具体的实现分离,在提供了一致的接口的同时,提供了灵活选择logging实现的能力。 在SLF4J之前,Apache Common Logging(即Jakarta Commons L...

近路 2380

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

SLF4J 的几种实际应用模式--:SLF4J+Log4J 与 SLF4J+LogBack

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人。

2401_85730279的博客 1230

SLF4J 报错解决:No SLF4J providers were found

SLF4J 报错

tumu6889的博客 7881

关于SLF4j

SLF4J(Simple Logging Facade for Java)是一个Java平台上的日志框架,它提供了一种简单的、统一的接口,用于在应用程序中进行日志记录。SLF4J的目标是在代码中屏蔽底层的日志框架实现细节,使开发人员能够更轻松地切换不同的日志框架。简单易用:SLF4J的接口设计简单明了,易于理解和使用。统一接口:SLF4J提供了一套统一的接口,可以与不同的日志框架进行集成,包括Logback、Log4j、java.util.logging等。

Flying_Fish_roe的博客 1063

linux slf4j找不到,SLF4J错误消息

在本章中,我们将讨论在使用SLF4J时获得的各种错误消息或警告以及这些消息的原因/含义。无法加载类“org.slf4j.impl.StaticLoggerBinder”。这是在类路径中没有提供SLF4J绑定时引起的警告。以下是完整的警告 -SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: Defaultin...

weixin_30647853的博客 3781

slf4j中jcl-over-slf4j、jul-to-slf4j、log4j-over-slf4j、slf4j-api、slf4j-log4j12、log4j-slf4j-impl的区别是什么

SLF4J日志框架解析:slf4j-api是日志门面接口,不包含具体实现;*-over-slf4j用于桥接其他日志框架到SLF4J,其中to/over命名区别在于是否覆盖原实现(JUL用to转接,JCL/Log4j用over覆盖)。注意避免错误组合如同时使用slf4j-log4j12和log4j-slf4j-impl,推荐使用slf4j-api+log4j-slf4j-impl+log4j-core组合,配合log4j-over-slf4j接管旧日志。

zlpzlpzyd的专栏 417

Slf4j原理解析

slf4j原理解析

王文萱的博客 1万+

Springboot 中使用 SLF4J

SLF4j使用 如何在系统中使用SLF4J 以后开发时候,日志记录方法的调用,不应该直接调用日志的实现类,而是调用抽象层里面的方法 应该给系统里面导入 slf4j 的jar包和 logback 的 实现 jar 包 import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloWorld { publi...

qq_40990836的博客 1万+

SLF4J: Class path contains multiple SLF4J bindings.

SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/C:/Users/yys1405/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/S...

yzh_1346983557的博客 5309
上一篇: 如何在线上为所欲为
下一篇: okhttp 应用之 Interceptors 拦截器,「实践 + 原理」一样都不少
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值