所有分类
  • 所有分类
  • 未分类

Java日志–slf4j–使用/教程/实例

简介

说明

本文用示例介绍Java的slf4j这个日志框架的用法。同时也会介绍相关的知识。

官网

官网:SLF4J
官网文档:SLF4J Documentation

slf4j简介

概述

说明

说明
名词解释slf4j : simple log facade for java :简单日志门面。
作用类似适配器,直接使用slf4j的方法打印即可,最终会调用到具体的日志,比如java.util.logging、logback和log4j。
优点系统换了一个日志源后,不需要更改代码;省内存(可以使用占位符{})。
配置方法日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现。
支持的日志系统NOP, Simple,log4j, log4j2, java.util.logging, JCL and logback。
绑定日志实现库的时机编译时静态绑定真正的Log库
切换日志系统方法切换jar包即可。例如:从java.util.logging切换到 log4j,仅把 slf4j-jdk14-xxx.jar替换成 slf4j-log4j12-xxx.jar即可。

slf4j与commons-logging区别

slf4jcommons-logging
绑定日志实现库的时机编译时静态绑定真正的Log库。 可在OSGI中使用动态查找的机制,在程序运行时自动找出真正使用的日志库。 它使用ClassLoader寻找和载入日志库, 导致了像OSGI这样的框架无法正常工作,因为OSGI的不同的插件使用自己的ClassLoader(OSGI的这种机制保证了插件互相独立)。
占位符可以使用占位符“{}”不可使用占位符

slf4j的占位符

一般log用法(以log4j为例)

java文件

import org.apache.log4j.Logger;

public class TestLog4j {
    private static final Logger LOGGER = Logger.getLogger(TestLog4j.class);

    public static void main(String[] args) {
        String message = "Hello World.";
        LOGGER.info("This is a test message: " + message);
    }
}

此法缺点

1.字符串相加是一个比较消耗性能的操作
2.若log4j配置的输出级别是ERROR,则根本不会输出,占用内存。

第2个缺点的解决方法:加判断,但会使代码繁琐。例如:

	if (LOGGER.isInfoEnabled()) {
		LOGGER.info("This is a test message: " + message);
	}

slf4j的{}占位符方法

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestLog4j {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestLog4j.class);

    public static void main(String[] args) {
        String message = "Hello World.";
        LOGGER.info("This is a test message: {}", message);
    }
}

使用了{}占位符,不会有字符串拼接操作。

在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。

相关源码:slf4j-log4j12-1.6.1.jar=> Log4jLoggerAdapter

public void debug(String format, Object arg1, Object arg2) {
    if (logger.isDebugEnabled()) {
        FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
        logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
    }
}

slf4j的包

下表中,只需指定加粗的依赖即可,它会自动拉取其他依赖。也可以强制其他依赖一个确定的版本,只需将其他依赖放到加粗依赖的后边即可。

含义所需依赖
slf4j-api-xxx.jarslf4j核心接口包
slf4j-simple-xxx.jarslf4jSimpleLogger日志实现包slf4j-api-xxx.jar=>  slf4j-simple-xxx.jar
slf4j-log4j12-xxx.jarslf4j调用log4j的1.2版本的实现包slf4j-api-xxx.jar=>  slf4j-log4j12-xxx.jar=> log4j-xxx.jar(1.2版本)
slf4j-log4j13-xxx.jarslf4j调用log4j的1.3版本的实现包
slf4j-jdk14-xxx.jarslf4j调用jdk的java.util.logging的实现包(jdk1.4及之后才有)slf4j-api-xxx.jar=>  slf4j-jdk14-xxx.jar =>  java.util.logging
slf4j-nop-xxx.jarslf4j调用nop(没有任何打印)slf4j-api-xxx.jar=>  slf4j-nop-xxx.jar =>  /dev/null
slf4j-migrator-xxx.jar帮助迁移你的代码来使用SLF4J。
slf4j-ext-xxx.jar
jul-to-slf4j-xxx.jar原来jul的打印代码委托给slf4jslf4j-api-xxx.jar=>  jul-to-slf4j-xxx.jar=> slf4j-yyy(例:slf4j-simple-xxx.jar)
log4j-over-slf4j-xxx.jar原来log4j的代码保持不变,使用slf4j接口输出slf4j-api-xxx.jar=>  log4j-over-slf4j-xxx.jar=> slf4j-yyy(例:slf4j-simple-xxx.jar)
jcl-over-slf4j-xxx.jar原来jcl的代码保持不变,使用slf4j接口输出slf4j-api-xxx.jar=>  log4j-over-slf4j-xxx.jar=> slf4j-yyy(例:slf4j-simple-xxx.jar)
slf4j-jcl-xxx.jar原来slf4j的代码保持不变,使用jcl输出
logback-classic-xxx.jarlog4j的升级。不是由slf4j提供。slf4j-api-xxx.jar=>  logback-classic-xxx.jar, logback-core-xxx.jar logback天生支持slf4j,所以不需要连接层

调用结构

slf4j-api(接口层
         | 
各日志实现包的连接层( slf4j-jdk14, slf4j-log4j等) 
         | 
各日志实现包(实现层

兼容问题

1. 连接层绑定一个具体的实现层
        比如: slf4j-jdk14.jar或 slf4j-log4j12.jar

2. 接口层与连接层版本要相同
        不同版本的slf4j-api(接口层)和连接层绑定混合在一起可能会引起问题。比如:slf4j-api-1.7.19.jar 对应 slf4j-simple-1.7.19.jar,使用 slf4j-simple-1.5.5.jar将不会正常工作。

3. 客户端与任意接口层兼容
        从客户端的观点来看,所有版本的 slf4j-api都是兼容的。可以使用任何版本的 slf4j-api.jar,只要满足第二条就不会有问题。

桥接

其他网址

官网:Bridging legacy APIs

简介 

假如你正在开发应用程序所调用的组件当中已经使用了 JCL 的,还有一些组建可能直接调用了 java.util.logging,这时你需要一个桥接器(名字为 xxx-over/to-slf4j.jar)把他们的日志输出重定向到 SLF4J。

桥接器就是一个假的日志实现工具。比如:把 jcl-over-slf4j.jar 放到 CLASS_PATH 时,即使某个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over-slf4j “骗到”SLF4J 里,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具。下边有相关例程:下边的示例2,示例3。

CLASS_PATH 里同时放置 log4j-over-slf4j.jar 和 slf4j-log4j12-version.jar 会发生的情况:日志会被踢来踢去,最终进入死循环,会造成内存溢出,其他包相互转发也是这种情况。(log4j-over-slf4j 与 slf4j-log4j12例外,log4j-over-slf4内部做了一个判断,可以防止造成内存溢出:判断slf4j-log4j12 jar包中的org.slf4j.impl.Log4jLoggerFactory是否存在,如果存在则表示冲突了,抛出异常提示用户要去掉对应的jar包,代码在slf4j-log4j12-xxx.jar=> org.apache.log4j.Log4jLoggerFactory=> static域)。

slf4j官方图

示例

@Slf4j

每次写新的类,就需要写:private static final Logger logger = = LoggerFactory.getLogger(Demo.class);简洁方法:使用lombok的@Slf4j。

步骤

1.引入lombok依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

2.类上写上@Slf4j

3.直接使用log变量

log.debug(“debug”)

slf4j-simple

java文件

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo {
    private static final Logger logger = = LoggerFactory.getLogger(Demo.class);
    public static void main(String[] args) {
        logger.info("Hello World");
    }
}

pom.xml

pom.xml(slf4j-simple)

       <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>

测试

编译并运行HelloWorld程序,现在在控制台会有下列输出:

[main] INFO org.example.a.Demo - Hello World

注意:若没配置slf4j-xxx(比如没有加入依赖),则会有如下报错。

SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

失败原因:class path下没有找到绑定的slf4j

slf4j-log4j12

要提供log4j.properties配置文件。

依赖

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.30</version>
    <scope>test</scope>
</dependency>

 用法跟slf4j-simple相同

jul转为slf4j-simple

转换之前:jdk自带的java.util.logging(jul)

java文件

使用jdk自带的java.util.logging(jul)。

package com.example;
 
import java.util.logging.Logger;
 
public class HelloWorld {
    private static final Logger logger = Logger.getLogger(HelloWorld.class.getName());
 
    public static void main(String[] args) {
        logger.severe("jul severe 测试");
        logger.warning("jul warning 测试");
        logger.info("jul info 测试");
    }
}

测试

三月 06, 2020 9:10:07 上午 com.example.HelloWorld main
严重: jul severe 测试
三月 06, 2020 9:10:07 上午 com.example.HelloWorld main
警告: jul warning 测试
三月 06, 2020 9:10:07 上午 com.example.HelloWorld main
信息: jul info 测试

转换之后:jul转到slf4j-simple

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>mytest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
       <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
</project>

java文件

main函数里面添加下列语句:

SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

添加之后的java文件

package com.example;

import org.slf4j.bridge.SLF4JBridgeHandler;
import java.util.logging.Logger;

public class Demo {
    private static final Logger logger = Logger.getLogger(Demo.class.getName());

    public static void main(String[] args) {
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();

        logger.severe("jul severe 测试");
        logger.warning("jul warning 测试");
        logger.info("jul info 测试");
    }
}

测试

[main] ERROR com.example.Demo - jul severe 测试
[main] WARN com.example.Demo - jul warning 测试
[main] INFO com.example.Demo - jul info 测试

jcl转为slf4j

转换之前:jcl

java程序

package com.example;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
 
public class Demo {
	private final static Log logger = LogFactory.getLog(Demo .class);
 
	public static void main(String[] args) {
		logger.debug("DEBUG ...");
		logger.info("INFO ...");
		logger.error("ERROR ...");
	}
}

maven依赖

<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>

配置文件

commons-logging.properties

org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog

测试

[INFO] Demo - INFO ...
[ERROR] Demo - ERROR ...

转换之后:jcl转为slf4j

只修改maven依赖

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录