java-web系列(三)---(slf4j + logback)进行日志分层

前言

这个项目的github地址:extensible项目的github地址

extensible项目当前功能模块如下:

java-web系列(一)—搭建一个基于SSM框架的java-web项目
java-web系列(二)—以dockerfile的方式发布java-web项目
java-web系列(三)—(slf4j + logback)进行日志分层
java-web系列(四)—几种常见的加密算法
java-web系列(五)—SpringBoot整合Redis
java-web系列(六)—Mybatis动态多数据源配置
java-web系列(七)—SpringBoot整合Quartz实现多定时任务
java-web系列(八)—RabbitMQ在java-web中的简单应用
java-web系列(九)—SpringBoot整合ElasticSearch

如对该项目有疑问,可在我的博客/github下面留言,也可以以邮件的方式告知。
我的联系方式:dzy930724@163.com

slf4j和logback的概念

SLF4J,是Simple Logging Facade for Java的简称。简而言之,slf4j是一个日志门面,它只提供了通用的日志接口,而未提供日志功能的具体实现
log4j(log for java)以及logback框架都提供了日志的具体实现,而且SLF4J和log4j都是由一个开发人员Ceki完成,因此使用log4j进行日志实现的web项目,能够很好地切换为logback。
目前,slf4j + logback也是业内最流行的日志管理方式。

如何使用slf4j进行日志记录

详情参见com.netopstec.extensible.service.impl.StudentServiceImpl

/**
 * @author zhenye 2018/8/7
 */
@Service
@Slf4j
public class StudentServiceImpl implements StudentService{

    @Resource
    private StudentMapper studentMapper;
    @Resource
    private ClassroomMapper classroomMapper;

    @Override
    public List<Student> findAll() {
        log.info("获取所有的学生信息");
        return studentMapper.findAll();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String insertOne(Student student) {
        log.info("插入一条新的学生数据");
        Integer classroomId = student.getClassroomId();
        Classroom classroom = classroomMapper.findById(classroomId);
        if (classroom == null){
            log.error("找不到这个班级");
            throw new RuntimeException("学生信息不全,无法入库");
        }
        Integer ID = studentMapper.insertOne(student);

        return null;
    }
}

在application.properties里面,设置spring.profiles.active=dev,修改为开发环境后,再启动项目。在postman中键入如下参数:

"添加学生"方法的参数

在控制台会出现如下日志:

相关日志

这里我们是用注解@Slf4j的方式导入,然后使用了infoerror两种级别分别进行日志的记录。我们探索一下@Slf4j是如何帮助我们进行日志记录的。

在IDEA中通过快捷键(Ctrl + Alt + 鼠标左键)选中该注解,能够查看该注解的源码。

@Sl4j的源码如下:

package lombok.extern.slf4j;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface Slf4j {
    String topic() default "";
}

这是lombok框架提供的一个源码级(SOURCE)类(TYPE)注解。也就是说,在编译时该注解已经实现了自己的功能。该注解的topic字段是自定义日志记录器的名称,默认就是该类的全限定名,这个可以自己测试。现在我们对比StudentServiceImpl.classStudentServiceImpl.java,查看该注解到底帮我们做了些什么?

@Slf4j注解的功能

其实,就是多了一条语句:

public class StudentServiceImpl implements StudentService{
    private static final Logger log = LoggerFactory.getLogger(StudentServiceImpl.class);
}

也就是说,我们可以自己通过LoggerFactory获取到日志记录器。具体使用哪种方式,取决于自己的喜好。

走近logback

logback核心文件

从extensible项目的maven依赖层级图可以看出:logback-classic是依赖于slf4j-apilogback-core。spring-boot-starter默认集成了logback的核心文件logback-classic。因此一个springboot项目,默认就是用logback进行日志的管理的。

Logback的具体实现是建立于三个主要类之上:LoggerAppenderLayout。这三种组件协同工作,使开发者可以按照消息类型和级别来记录消息,还可以在程序运行期内控制消息的输出格式和输出目的地。

Logger

从注解@Slf4j的功能,我们可以看到:在serviceImpl类中关联一个Logger对象,我们才能使用该对象打印日志。然后Logger的部分源码如下:

package org.slf4j;

public interface Logger {
    String ROOT_LOGGER_NAME = "ROOT";
    boolean isTraceEnabled();
    void trace(String var1);
    boolean isDebugEnabled();
    void debug(String var1);
    boolean isInfoEnabled();
    void info(String var1);
    boolean isWarnEnabled();
    void warn(String var1);
    boolean isErrorEnabled();
    void error(String var1);
}

从源码我们可以看出:日志的有效级别有:trace/debug/info/warn/error(实际还有all和off级别,参见Level的源码)。然后Logger是分级别的,其根是ROOT。

logger L的有效级别等于其层次等级里的第一个非null级别,顺序是从L开始,向上直至根logger。为确保所有logger都能够最终继承一个级别,根logger总是有级别,默认情况下,这个级别是DEBUG。

测试的源码如下:

public class LogTest {
    @Test
    public void logTest(){
        Logger fooLogger = (Logger) LoggerFactory.getLogger("com.foo");
        fooLogger.setLevel(Level.INFO);
        fooLogger.debug("the level debug of foo is disable");
        fooLogger.info("the level info of foo is able and foo's level is {}", fooLogger.getLevel());
        fooLogger.warn("the level warn of foo is able");
        fooLogger.error("the level error of foo is able");
        System.out.println("-----------------------------------------------------------------");
        Logger barLogger = (Logger) LoggerFactory.getLogger("com.foo.bar");
        barLogger.debug("the level debug of bar is disable");
        barLogger.info("the level info of bar is able and bar's level is {}",barLogger.getLevel());
        barLogger.warn("the level warn of bar is able");
        barLogger.error("the level error of bar is able");
    }
}

测试结果如下:

logger测试

AppenderLayout

望文知意,Appender就是控制日志的输出位置:(控制台、文件、远程套接字服务器、MySQL、PostreSQL、Oracle和其他数据库、JMS和远程UNIX Syslog守护进程)。
一个Logger可以关联多个Appender,他们可以通过Logger.addAppender(Appender appender);来添加关联关系。
Layout就是控制日志的布局,我们可以通过如下方法进行布局自定义:

import ch.qos.logback.core.LayoutBase;
public class MySampleLayout extends LayoutBase<ILoggingEvent> {
    public String doLayout(ILoggingEvent event) {
        StringBuffer sbuf = new StringBuffer(128);
        sbuf.append(event.getTimeStamp()
        - event.getLoggerContextVO().getBirthTime());
        sbuf.append(" ");
        sbuf.append(event.getLevel());
        sbuf.append(" [");
        sbuf.append(event.getThreadName());
        sbuf.append("] ");
        sbuf.append(event.getLoggerName());
        sbuf.append(" - ");
        sbuf.append(event.getFormattedMessage());
        sbuf.append(CoreConstants.LINE_SEPARATOR);
        return sbuf.toString();
    }
}

用logback进行日志分层

(springboot项目)在resources目录下,新建logback-spring.xml,配置其内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="60 seconds" debug="false">
    <springProfile name="dev">
        <!--输出到控制台-->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
        <root level="info">
            <appender-ref ref="console" />
        </root>
    </springProfile>

    <springProfile name="prod,test">
        <!--ERROR级别的日志放在logErrorDir目录下,INFO级别的日志放在logInfoDir目录下-->
        <property name="logback.logErrorDir" value="/extensible/logs/error"/>
        <property name="logback.logInfoDir" value="/extensible/logs/info"/>
        <property name="logback.appName" value="extensible"/>
        <contextName>${logback.appName}</contextName>

        <!--ERROR级别的日志配置如下-->
        <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
                如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
                的日志改名为今天的日期。即,<File> 的日志都是当天的。
            -->
            <File>${logback.logErrorDir}/error.${logback.appName}.log</File>
            <!-- 日志level过滤器,保证error.***.log中只记录ERROR级别的日志-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
                <FileNamePattern>${logback.logErrorDir}/error.${logback.appName}.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--只保留最近14天的日志-->
                <maxHistory>14</maxHistory>
                <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
                <!--<totalSizeCap>1GB</totalSizeCap>-->
            </rollingPolicy>
            <!--日志输出编码格式化-->
            <encoder>
                <charset>UTF-8</charset>
                <pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
            </encoder>
        </appender>

        <!--INFO级别的日志配置如下-->
        <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
                如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
                的日志改名为今天的日期。即,<File> 的日志都是当天的。
            -->
            <File>${logback.logInfoDir}/info.${logback.appName}.log</File>
            <!--自定义过滤器,保证info.***.log中只打印INFO级别的日志-->
            <filter class="com.netopstec.extensible.filter.InfoLevelFilter"/>
            <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
                <FileNamePattern>${logback.logInfoDir}/info.${logback.appName}.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--只保留最近14天的日志-->
                <maxHistory>14</maxHistory>
                <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
                <!--<totalSizeCap>1GB</totalSizeCap>-->
            </rollingPolicy>
            <!--日志输出编码格式化-->
            <encoder>
                <charset>UTF-8</charset>
                <pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
            </encoder>
        </appender>
        <root level="info">
            <appender-ref ref="fileErrorLog" />
            <appender-ref ref="fileInfoLog"/>
        </root>
    </springProfile>
</configuration>

然后在(com.netopstec.extensible.filter.InfoLevelFilter)添加自定义过滤器

/**
 * 自定义过滤器,info.***.log中只打印INFO级别的日志
 * (如果不在配置文件中指定该过滤器,info.***.log或打印INFO以上所有级别的日志---root指定是INFO)
 * @author zhenye 2018/8/16
 */
public class InfoLevelFilter extends Filter<ILoggingEvent> {

    @Override
    public FilterReply decide(ILoggingEvent iLoggingEvent) {
        if (iLoggingEvent.getLevel().toInt() == Level.INFO.toInt()){
            return FilterReply.ACCEPT;
        }
        return FilterReply.DENY;
    }
}

在dockerfile文件中,添加两个建目录语句(RUN mkdir /extensible/logs/error;RUN mkdir /extensible/logs/info),这两个目录用来存对应级别的日志。

发布成功后,进行多次测试,发现服务器日志如下图则说明日志分层成功。

日志分层

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SLF4J 是一个简单的日志门面,它提供了一组日志 API,可以与许多流行的日志框架(例如 logback、log4j、java.util.logging)集成。而 logback 则是一个功能强大的日志框架,它是 log4j 框架的继承者,提供了更好的性能和更丰富的特性。 要使用 SLF4J + logback 实现日志输出和记录,需要按照以下步骤进行: 1. 引入依赖:在项目的 pom.xml 文件中加入以下依赖: ``` <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> ``` 2. 配置 logback.xml 文件:在项目的 src/main/resources 目录下创建 logback.xml 文件,并配置日志输出的格式、级别、输出目标等信息。以下是一个简单的 logback.xml 配置示例: ``` <configuration> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="console" /> </root> </configuration> ``` 该配置将日志输出到控制台中,并显示日志的时间、线程、级别、类名和消息内容。 3. 在代码中使用 SLF4J API:在需要记录日志的代码中,使用 SLF4J API 进行日志记录。以下是一个示例: ``` import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MyClass { private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class); public void doSomething() { LOGGER.info("This is a log message."); } } ``` 在这个示例中,我们使用 LoggerFactory.getLogger() 方法获取了一个 Logger 对象,并使用该对象进行日志记录。在记录日志时,我们使用了 LOGGER.info() 方法,指定日志的级别为 INFO。 以上就是使用 SLF4J + logback 实现日志输出和记录的基本步骤。通过 SLF4J,我们可以方便地切换不同的日志框架,而 logback 则提供了强大的日志功能,例如异步日志记录、定时滚动日志文件等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值