JAVA主流日志框架梳理学习及使用

前言目前市面上有挺多JAVA的日志框架,比如JUL(JDK自带的日志框架),Log4j,Logback,Log4j2等,有人可能有疑问说还有slf4j,不过slf4j不是一种日志框架的具体实现,而是一种日志门面(日志门面可以理解为是一种统一的接口,只是为了规范日志,没有具体的实现;可以类比USB接口,对于USB接口有好多个厂商,这多个厂商生产出来的USB头都是一样的,使得我们用起来就很方便,slf4j其实也是一样)。虽然市面上有很多日志框架,但是笔者之前只是能够简单的使用日志框架,而且使用时总感觉似懂非懂,对目前市面上的日志框架并没有一个整体且系统的了解。因此,笔者利用周末这两天系统的学习了一下目前市面上的框架,也就有了这篇文章,希望这篇文章能够帮助大家对JAVA日志框架有个清晰的认识。

笔者这篇文件是对该视频内容的提炼,需要的可以自行观看。如果想快速了解日志的重点内容,那么可以继续往下看。
视频链接

一、市面上主要日志简介

目前市面上主要的日志框架有:JUL(JDK自带的日志框架),Log4j,Logback,Log4j2
日志门面:slf4jlog4j2也是一种日志门面,不过因为其实现了丰富的日志功能,所以不把其当做一种日志门面了
日志框架(门面)出现的先后顺序:JUL,Log4j在slf4j门面技术出现之前,logback,Log4j2在slf4j门面技术出现之后。因为JUL,Log4j在slf4j出现之前就有了,所以这两个日志的实现并没有遵循slf4j的规范(不过可以通过导入适配器的依赖来使用)。因为Log4j2是对Log4j的升级,JUL功能比较弱(不过因为是JDK自带的日志实现,不需要导入依赖,使用起来比较方便),所以本文只介绍Logback和Log4j2。
主流日志使用方式:slf4j+Logback、slf4j+Log4j2

使用方式:配置文件。

在使用各种库时,第一步都是导入对应的依赖,日志框架也是一样。此外,对于记录的日志会有不同的日志级别,日志显示(或写入文件)的条件就是当前日志级别大于所设置的日志级别时才会打印,如果不设置日志级别,系统会有默认的日志级别。

日志级别由高到低: error > warn>info>debug>trace(这个日志级别是针对slf4j门面的日志级别,各种日志框架的日志级别都大同小异)

二、slf4j+Logback使用

2.1 导入依赖

在导入依赖时我们需要导入slf4j的依赖(slf4j-api)logback的依赖,因为依赖的传递性,logback-classic的依赖中包含了slf4j-api的依赖,所以我们在实际的使用中只需要导入logback-classic的依赖即可:
依赖情况
依赖

 <dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-classic</artifactId>
     <version>1.2.3</version>
 </dependency>

2.2 使用方式

对于日志,我们分别测试下面这几种情况:

2.2.1不使用配置文件
2.2.2使用配置文件

2.2.2.1输出到控制台
2.2.2.2输出到文件
2.2.2.3输出到文件(对文件拆分)

2.2.1 不使用配置文件

项目结构
在这里插入图片描述
执行右面的test01输出如下:
在这里插入图片描述
如上图所示,当不添加配置文件时,默认的输出格式如上所示。只有trace日志级别的日志没有输出,所以logback系统默认的日志级别是debug级别。

日志级别的值

/**
 * The <code>OFF</code> is used to turn off logging.
 */
public static final Level OFF = new Level(OFF_INT, "OFF");

/**
 * The <code>ERROR</code> level designates error events which may or not
 * be fatal to the application.
 */
public static final Level ERROR = new Level(ERROR_INT, "ERROR");

/**
 * The <code>WARN</code> level designates potentially harmful situations.
 */
public static final Level WARN = new Level(WARN_INT, "WARN");

/**
 * The <code>INFO</code> level designates informational messages
 * highlighting overall progress of the application.
 */
public static final Level INFO = new Level(INFO_INT, "INFO");

/**
 * The <code>DEBUG</code> level designates informational events of lower
 * importance.
 */
public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG");

/**
 * The <code>TRACE</code> level designates informational events of very low
 * importance.
 */
public static final Level TRACE = new Level(TRACE_INT, "TRACE");

/**
 * The <code>ALL</code> is used to turn on all logging.
 */
public static final Level ALL = new Level(ALL_INT, "ALL");

//对应的具体值
public static final int OFF_INT = Integer.MAX_VALUE;
public static final int ERROR_INT = 40000;
public static final int WARN_INT = 30000;
public static final int INFO_INT = 20000;
public static final int DEBUG_INT = 10000;
public static final int TRACE_INT = 5000;
public static final int ALL_INT = Integer.MIN_VALUE;

2.2.2 使用配置文件

2.2.1 输出到控制台

配置文件名:logback.xml
内容

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <root level="ALL">
        <appender-ref ref="consoleAppender"/>
    </root>
</configuration>

看这个配置文件,我们可以看到所有的内容都包含在configuration标签中,configuration标签内部定义了property,appender,root标签,其中property标签是定义配置文件内的变量,方便我们使用(即在后面直接引用该变量就行)。其中appender标签和root标签是需要我们重点关注的内容。
我们知道日志一般可以输出到控制台或者文件中,对于具体输出到控制台还是输出到文件中就是通过appender来控制的。因此,我们可以通过appender标签来定义一个变量。其中,appender标签的name属性就是变量名,class属性就是变量的类型。appender标签内的target标签不太重要,设置的具体值等同于Java中的System.outSystem.errappender标签内的encoder标签其实是定义日志的输出格式,我们看到的日志一般都会包括日志级别,线程名称,时间,文件全类名,消息等信息,就是通过该标签控制。所以这些是appender标签的核心内容。

虽然appender标签定义了具体的appender变量,但决定是否使用该变量时通过下面的root标签来控制的,我们可以看到root标签有个level属性,我们将该属性值设置为ALL,则表示所有的日志级别都会显示。root标签下的appender-ref属性则是决定具体使用哪个appender变量。可以看到appender-ref标签的ref属性值为consoleAppender,说明使用了变量名为consoleAppenderappender输出器。
执行效果如下:
在这里插入图片描述
可以看到,所有的日志信息全都输出了(同样执行的test01()方法)。此外,日志显示为红色,这其实就是System.err设置的效果。而且我们可以看到输出的日志具有特定的格式,即[INFO ] 2023-12-18 08:26:32.869 com.xlb.LogBackDemo test01 main 14:info 信息含义为日志级别 时间 当前类全限定名 当前执行日志的方法 线程名 行号:消息体。这种输出格式即是我们通过encoder标签下的pattern标签定义的。格式化的含义具体如下:

%-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 信息
%n 换行
2.2.2.2 输出到文件

我们上面是将日志成功输出到了控制台,下面我们将日志输出到文件,大致过程和上面类似。
修改后的配置文件如下:

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <root level="ALL">
        <appender-ref ref="fileAppender"/>
    </root>
</configuration>

实际上我们就只修改了这三个地方
在这里插入图片描述

最上面指定了日志文件的输出路径的变量(指定位置为当前项目下的log文件夹),中间添加了一个输出到文件的appender,增加了file标签,在其内部定义了日志文件的输出路径,下面在root标签中指定了我们新添加的appender
输出结果如下:
在这里插入图片描述
在这里插入图片描述
可以看到,控制台没有日志输出,但是文件中有日志输出。

2.2.2.3 输出到控制台和文件

修改后的配置如下

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <root level="ALL">
        <appender-ref ref="fileAppender"/>
        <appender-ref ref="consoleAppender"/>
    </root>
</configuration>

在这里插入图片描述
可以看到,对于新的配置文件我们只是增加了上面一行,即在root标签中同时也使用了控制台的appender
输出结果如下
在这里插入图片描述
在这里插入图片描述
可以看到,日志记录输出到了控制台和文件中,而且文件中的日志是追加的方式记录的(可以查看日志的时间来看当前的日志记录)。
从这个案例我们可以知道,日志可以指定多个appender,指定多个appender只需要增加对应的appender引用即可。

2.2.2.3 输出到文件(对文件拆分)

如果日志文件一直追加,那么日志文件就可能变得非常大,不方便日志管理,或者有时候同类型的日志需要归档,所以我们需要对日志文件进行拆分管理,对文件拆分管理方式如下。
修改配置文件

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="rollAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <file>${logDir}\roll_logback.log</file>

        <!--        指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>

    </appender>

    <root level="ALL">
        <appender-ref ref="rollAppender"/>
    </root>
</configuration>

在这里插入图片描述
对于日志规则的拆分,如上所示,同样只需要修改这两处。

因为设置了日志文件的大小为1KB,所以我们使用for循环来追加操作,达到拆分文件的目的,演示如下:

代码:

@Test
    public void test02(){
        Logger logger = LoggerFactory.getLogger(LogBackDemo.class);

        for (int i = 0; i < 200; i++) {
            logger.trace("trace 信息");
            logger.debug("debug 信息");
            logger.info("info 信息");
            logger.warn("warn 信息");
            logger.error("error 信息");
        }
    }

输出:
在这里插入图片描述
在这里插入图片描述
可以看到日志文件按照我们设置的格式进行了拆分。

2.2.2.4 输出为html格式

除了上面几种格式,还可以将logback输出为html的格式,只需要下面这样设置即可:

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="rollAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <file>${logDir}\roll_logback.log</file>

        <!--        指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>

    </appender>

<!--    html格式-->
    <appender name="htmlAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\myHtmllog.html</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern}</pattern>
            </layout>
        </encoder>
    </appender>

    <root level="ALL">
        <appender-ref ref="htmlAppender"/>
    </root>
</configuration>

在这里插入图片描述
同样改动很小,输出如下(使用test01):
在这里插入图片描述
可以看到有个html文件。
浏览器打开该html日志文件展示如下:
在这里插入图片描述
可以看到自动为我们添加了特定的样式,使得日志以表格的形式展示。

2.2.2.5 过滤器

实际使用时,可能每个appender我们想设置的日志级别都不同,那么此时我们就需要一个过滤器,该过滤器也是通过设置日志级别来控制是否输出当前appender的日志。

配置如下

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--    过滤器-->
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <!--        配置过滤器(等于配置的级别才会输出)-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--            设置日志的输出级别-->
            <level>INFO</level>
            <!--            高于level中设置的级别,则打印日志-->
            <onMatch>ACCEPT</onMatch>
            <!--            低于level中设置的级别,则不打印-->
            <onMismatch>DENY</onMismatch>
        </filter>

<!--        &lt;!&ndash;        配置临界值过滤器(输出大于等于level的日志)&ndash;&gt;-->
<!--        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">-->
<!--            &lt;!&ndash;            设置日志的输出级别&ndash;&gt;-->
<!--            <level>INFO</level>-->
<!--        </filter>-->
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="rollAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <file>${logDir}\roll_logback.log</file>

        <!--        指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>

    </appender>

<!--    html格式-->
    <appender name="htmlAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\myHtmllog.html</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern}</pattern>
            </layout>
        </encoder>
    </appender>

    <root level="ALL">
        <appender-ref ref="consoleFilterAppender"/>
    </root>

</configuration>

在这里插入图片描述
在这里插入图片描述
在consoleAppender里设置了INFO级别的日志级别,不过root标签下的日志级别是ALL,我们看下输出:
在这里插入图片描述
可以看到只输出了INFO级别的日志,即我们在日志中配的INFO级别。下面我们看一下另一种过滤器:

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
   <!--            设置日志的输出级别-->
    <level>INFO</level>
</filter>

将上面的过滤器换成这个(ThresholdFilter),我们再看下输出:
在这里插入图片描述
可以看到大于等于INFO级别的日志都进行了输出。从上面的输出可以看出这两种过滤器的差别。

2.2.2.6 异步日志

因为日志和业务逻辑无关,日志只是便于我们处理问题。所以在项目中一般都会使用异步日志,来提升业务逻辑的处理速度。异步日志的使用如下:

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    
    <!--    过滤器-->
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <!--        配置过滤器(等于配置的级别才会输出)-->
<!--        <filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!--            &lt;!&ndash;            设置日志的输出级别&ndash;&gt;-->
<!--            <level>INFO</level>-->
<!--            &lt;!&ndash;            高于level中设置的级别,则打印日志&ndash;&gt;-->
<!--            <onMatch>ACCEPT</onMatch>-->
<!--            &lt;!&ndash;            低于level中设置的级别,则不打印&ndash;&gt;-->
<!--            <onMismatch>DENY</onMismatch>-->
<!--        </filter>-->

<!--        &lt;!&ndash;        配置临界值过滤器(输出大于等于level的日志)&ndash;&gt;-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!--            设置日志的输出级别-->
            <level>INFO</level>
        </filter>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="rollAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <file>${logDir}\roll_logback.log</file>

        <!--        指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>

    </appender>

<!--    html格式-->
    <appender name="htmlAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\myHtmllog.html</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern}</pattern>
            </layout>
        </encoder>
    </appender>

    <!--    引入异步日志-->
    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="consoleAppender"/>
    </appender>
    
    <root level="ALL">
        <appender-ref ref="asyncAppender"/>
    </root>

</configuration>

在这里插入图片描述
可以看到,使用异步日志很简单,我们只需要如上定义,最终日志输出来的就是异步日志,注意异步日志里面引用我们定义好的输出到控制台的consoleAppender
下面我们看下输出:

@Test
    public void test03(){
        Logger logger = LoggerFactory.getLogger(LogBackDemo.class);

        for (int i = 0; i < 200; i++) {
            logger.trace("trace 信息");
            logger.debug("debug 信息");
            logger.info("info 信息");
            logger.warn("warn 信息");
            logger.error("error 信息");
        }

        for (int i = 0; i < 200; i++) {
            System.out.println("======================");
        }
    }

在这里插入图片描述
我们可以看到输出的日志时穿插执行的,即可以证明日志时异步输出的。

2.2.2.7 自定义日志

我们在上面使用的日志其实都是rootLogger,我们应该记得在XML文件最后定义了root标签,这个定义的其实就是rootLogger,rootLogger是日志框架自带的,是所有日志的父级日志。什么是父级日志?对于市面上的所有日志框架,其实是有父子关系的,不过这种父子关系不是通过继承实现,而是通过包路径来体现的。如果包路径越具体,说明其越是儿子。例如:三个包com.aaa.bbb.ccc,和com.aaa.bbb和com.aaa中,com.aaa是com.aaa.bbb与com.aaa.bbb.ccc的父级日志,com.aaa.bbb是com.aaa.bbb.ccc的父级日志。对于父级日志的设置可以对子级日志生效;所以如果想一次生效多个日志,那么可以对父级进行设置。而rootLogger就是所有包的父级日志。那这个日志包路径什么时候指定的呢?我们可以看下面这一行代码:

Logger logger = LoggerFactory.getLogger(LogBackDemo.class);

可以看到,上面参数列表中我们传入了类,java内部通过反射机制可以获得该类的全路径;因为这个方法进行了重载,所以也可以直接指定全路径名。
想要引入自定义日志,需要下面这样配置
自定义日志引入

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

    <!--
            %-10level 日志级别(设置10个字符,字符不够空格补齐,左对齐)
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
            %c 当前类全限定名
            %M 当前执行日志的方法
            %L 行号
            %thread 线程名称
            %m或者%msg 信息
            %n 换行
            -->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n"/>
    <property name="logDir" value="F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOGBACK_StudyV2\\log"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--    过滤器-->
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        <target>
            System.err
        </target>

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <!--        配置过滤器(等于配置的级别才会输出)-->
<!--        <filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!--            &lt;!&ndash;            设置日志的输出级别&ndash;&gt;-->
<!--            <level>INFO</level>-->
<!--            &lt;!&ndash;            高于level中设置的级别,则打印日志&ndash;&gt;-->
<!--            <onMatch>ACCEPT</onMatch>-->
<!--            &lt;!&ndash;            低于level中设置的级别,则不打印&ndash;&gt;-->
<!--            <onMismatch>DENY</onMismatch>-->
<!--        </filter>-->

<!--        &lt;!&ndash;        配置临界值过滤器(输出大于等于level的日志)&ndash;&gt;-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!--            设置日志的输出级别-->
            <level>INFO</level>
        </filter>
    </appender>

    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\mylog.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="rollAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>

        <file>${logDir}\roll_logback.log</file>

        <!--        指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>

    </appender>

<!--    html格式-->
    <appender name="htmlAppender" class="ch.qos.logback.core.FileAppender">
        <file>${logDir}\myHtmllog.html</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>${pattern}</pattern>
            </layout>
        </encoder>
    </appender>

    <!--    引入异步日志-->
    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="consoleAppender"/>
    </appender>

    <root level="ALL">
        <appender-ref ref="consoleAppender"/>
    </root>

    <!--    自定义日志-->
    <logger name="com.xlb" level="info" additivity="false">
        <appender-ref ref="consoleAppender"/>
    </logger>

</configuration>

在这里插入图片描述
输出如下(使用test01)
在这里插入图片描述
可以看到输出了INFO,WARN,ERROR日志级别的日志。如果我们既有父级目录,又有子级目录,以子级日志目录设置的日志级别为准,如下我将父级日志设置成INFO级别,将子级日志设置成ALL级别,并且去掉additivity=false属性,显示如下:
在这里插入图片描述
可以看到同样的日志输出了两份,下面加上additivity=false再看下输出:
在这里插入图片描述
可以看到日志输出只有一份,并且是以子级日志级别为准的。

三、slf4j+Log4j2使用

slf4j+Log4j2日志的使用和slf4j+logback的使用大同小异

3.1 导入依赖

<dependencies>
   <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.19.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.19.0</version>
    </dependency>
</dependencies>

测试类:

package com.xlb;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LOG4J2Demo {
    @Test
    public void test01(){
        Logger logger = LoggerFactory.getLogger(LOG4J2Demo.class);

        logger.trace("trace 信息");
        logger.debug("debug 信息");
        logger.info("info 信息");
        logger.warn("warn 信息");
        logger.error("error 信息");
    }
}

3.2.1 不使用配置文件

在这里插入图片描述
可以看到只输出了ERROR级别的日志,因此log4j2的日志的默认日志级别为error级别。

3.2.2 使用配置文件

3.2.2.1 输出到控制台
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
        </Console>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

默认输出
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

格式化输出
在这里插入图片描述

3.2.2.2 输出到文件

我们上面是将日志成功输出到了控制台,下面我们将日志输出到文件,大致过程和上面类似。
修改配置文件如下:

<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="fileAppender"/>
        </Root>
    </Loggers>
</Configuration>
3.2.2.3 输出到控制台和文件
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="consoleAppender"/>
            <AppenderRef ref="fileAppender"/>
        </Root>
    </Loggers>
</Configuration>
3.2.2.4 输出到文件(对文件拆分)
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>

        <!--
        fileName:日志文件名
        $${date:yyyy-MM-dd}:表示以日期为文件夹对日志文件进行归档
-->
        <RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
                     filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <Policies>
                <!--                在系统启动时,触发拆分规则,产生一个日志文件-->
                <OnStartupTriggeringPolicy/>
                <!--                按照文件的大小进行拆分-->
                <SizeBasedTriggeringPolicy size="10KB"/>
                <!--                按照时间节点进行拆分,拆分规则就是filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="rollingFile"/>
        </Root>
    </Loggers>
</Configuration>
3.2.2.5 自定义日志
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>

        <!--
        fileName:日志文件名
        $${date:yyyy-MM-dd}:表示以日期为文件夹对日志文件进行归档
-->
        <RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
                     filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <Policies>
                <!--                在系统启动时,触发拆分规则,产生一个日志文件-->
                <OnStartupTriggeringPolicy/>
                <!--                按照文件的大小进行拆分-->
                <SizeBasedTriggeringPolicy size="10KB"/>
                <!--                按照时间节点进行拆分,拆分规则就是filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>

    <Loggers>
        <!--        自定义Logger-->
        <Logger name="com.xlb" level="INFO"
                     includeLocation="false" additivity="false">
            <!--            将控制台输出的consoleAppender,设置为异步打印-->
            <AppenderRef ref="consoleAppender"/>
        </Logger>

        <Root level="trace">
            <AppenderRef ref="consoleAppender"/>
        </Root>

    </Loggers>
</Configuration>
3.2.2.5 异步日志
3.2.2.6.1 xml中配置异步日志
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>

        <!--
        fileName:日志文件名
        $${date:yyyy-MM-dd}:表示以日期为文件夹对日志文件进行归档
-->
        <RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
                     filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <Policies>
                <!--                在系统启动时,触发拆分规则,产生一个日志文件-->
                <OnStartupTriggeringPolicy/>
                <!--                按照文件的大小进行拆分-->
                <SizeBasedTriggeringPolicy size="10KB"/>
                <!--                按照时间节点进行拆分,拆分规则就是filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!--        异步日志-->
        <Async name="myAsync">
            <AppenderRef ref="consoleAppender"/>
        </Async>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="rollingFile"/>
        </Root>
    </Loggers>
</Configuration>
3.2.2.6.2 全局异步日志

全局日志需要在properties文件中指明,xml文件中不需要特殊声明异步日志(文件名为log4j2.component.properties)

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>

        <!--
        fileName:日志文件名
        $${date:yyyy-MM-dd}:表示以日期为文件夹对日志文件进行归档
-->
        <RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
                     filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <Policies>
                <!--                在系统启动时,触发拆分规则,产生一个日志文件-->
                <OnStartupTriggeringPolicy/>
                <!--                按照文件的大小进行拆分-->
                <SizeBasedTriggeringPolicy size="10KB"/>
                <!--                按照时间节点进行拆分,拆分规则就是filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!--        异步日志-->
<!--        <Async name="myAsync">-->
<!--            <AppenderRef ref="consoleAppender"/>-->
<!--        </Async>-->
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

按照上面这种方式声明的也是异步日志

3.2.2.6.3 混合异步日志

混合异步日志需要引入这个依赖,否则会报错

<!--        混合异步日志用-->
 <dependency>
     <groupId>com.lmax</groupId>
     <artifactId>disruptor</artifactId>
     <version>3.4.3</version>
 </dependency>
<?xml version="1.0" encoding="UTF-8" ?>

<Configuration>
    <properties>
        <property name="logDir">F:\\CODE\\JAVA\\LogStudyV2\\LogStudy\\LOG4J2_StudyV2\\log</property>
        <property name="pattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n</property>
    </properties>

    <Appenders>
<!--        输出到控制台-->
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <patternLayout pattern="${pattern}"/>
        </Console>

        <!--        输出到文件-->
        <File name="fileAppender" fileName='${logDir}\\log4j2.log'>
            <!--            配置日志文件格式-->
            <patternLayout pattern="${pattern}"/>
        </File>

        <!--
        fileName:日志文件名
        $${date:yyyy-MM-dd}:表示以日期为文件夹对日志文件进行归档
-->
        <RollingFile name="rollingFile" fileName="${logDir}/rollog.log"
                     filePattern="${logDir}/$${date:yyyy-MM-dd}/rollog-%d{yyyy-MM-dd-HH-mm}-%i.log">

            <Policies>
                <!--                在系统启动时,触发拆分规则,产生一个日志文件-->
                <OnStartupTriggeringPolicy/>
                <!--                按照文件的大小进行拆分-->
                <SizeBasedTriggeringPolicy size="10KB"/>
                <!--                按照时间节点进行拆分,拆分规则就是filePattern-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!--        异步日志-->
<!--        <Async name="myAsync">-->
<!--            <AppenderRef ref="consoleAppender"/>-->
<!--        </Async>-->
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="consoleAppender"/>
        </Root>
        <!--        自定义Logger,让自定义的logger为异步logger
            includeLocation=false表示取出日志中的行号信息(记录日志的行号会使得日志记录的非常慢!)
-->
        <AsyncLogger name="com.xlb" level="trace"
                     includeLocation="false" additivity="false">
            <!--            将控制台输出的consoleAppender,设置为异步打印-->
            <AppenderRef ref="consoleAppender"/>
        </AsyncLogger>
    </Loggers>
</Configuration>

总结:上面介绍大致介绍了市面上这两款日志的使用流程,其实日志的使用基本上就是看如何对配置文件进行配置,配置文件配置好后就可以很方便的使用日志进行记录。其实市面上虽然有好几种日志框架,不过其实现的基本思想都差不多,即有下面几点共性:

1.都会分为不同的日志级别

1.1高于当前设置的日志级别就会打印
1.2低于当前设置的日志级别就不打印
2.都设置有输出处理器
2.1输出到控制台
2.2 输出到文件
2.3拆分文件

3格式化输出:类似于[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %thread %L:%m%n
4异步日志
5日志父子级关系
6自定义日志

希望这篇文章对日志的使用有所帮助,希望能够一键三连呀~

如果想对日志有更深的理解,可以看下这门b站上的课程,这个可能前面20集左右是一个大佬现场直播手撸一个日志框架(没有借助IDE,在vim中一点点敲出来的,很强!!!),不过课程是使用C++讲解的,有兴趣的可以听一下,保证听完对日志会有一个更加深刻的认识!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/255065.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基于SpringBoot的二次元商城网站系统

简介 二次元商城网站的使用是更为便捷的&#xff0c;互联网的普及在这个社会是非常成功的&#xff0c;小到个人的交际交流&#xff0c;大到公司企业员工的交流&#xff0c;都已经离不开科技&#xff0c;所以&#xff0c;在这么成熟的平台上&#xff0c;各种类型的网站也就应运…

【数据结构复习之路】图(严蔚敏版)两万余字超详细讲解

专栏&#xff1a;数据结构复习之路 复习完上面四章【线性表】【栈和队列】【串】【数组和广义表】【树和二叉树】&#xff0c;我们接着复习 图&#xff0c;这篇文章我写的非常详细且通俗易懂&#xff0c;看完保证会带给你不一样的收获。如果对你有帮助&#xff0c;看在我这么辛…

计网02-计算机网络参考模型

一、OSI七层参考模型 1、分层的思想 分层模型用于网络协议的设计方法&#xff0c;本质是将网络节点间复杂的通信问题分成若干简单的问题逐一解决&#xff0c;通过网络的层次去找问题&#xff0c;将复杂问题简单化。 2、OSI参考模型 由于早期计算机厂商使用的是私有的网络模…

51单片机(STC8) -- 开发环境搭建(Keil C51)

文章目录 STC8H3K系列芯片概述STC8H3K系列芯片选型Keil C51简介Keil C51安装添加C51芯片包工程创建与编译工程烧录 STC8H3K系列芯片概述 文章中所用的芯片选型为STC8H3K64S4&#xff0c;后续STC8案例均以该芯片展开 内核 • 超高速 8051 内核&#xff08;1T&#xff09;&…

SearchWP WordPress高级网站内容搜索插件(包含所有专业扩展)

点击阅读SearchWP WordPress高级网站内容搜索插件(包含所有专业扩展)原文 SearchWP WordPress高级网站内容搜索插件是一个非常强大的工具&#xff0c;可以显着增强您网站的搜索功能。通过向网站访问者提供高度相关和精确的搜索结果&#xff0c;它可以有效地简化他们的搜索过程…

IP地址与实时位置之间的关系

在互联网的普及和信息技术的快速发展中&#xff0c;IP地址作为一种标识符&#xff0c;已经深入到我们的日常生活和工作中。然而&#xff0c;对于IP地址与实时位置的关系&#xff0c;许多人存在误解。本文将对此进行澄清&#xff0c;阐述IP地址与实时位置之间的关系。 首先&…

ORA-00257: 归档程序错误在释放之前仅限于内部连接

Oracle在windows服务器下异常断电或者长时间运行情况下&#xff0c;容易发生ORA-00257: 归档程序错误 “ORA-00257: 归档程序错误。在释放之前仅限于内部连接”错误由于由于归档日志占满了空间&#xff0c;此空间大小限制由参数&#xff1a;db_recovery_file_dest_size来指定…

双指针算法(二)

三数之和 三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重…

【gojs】Invalid div id; div already has a Diagram associated with it

刷新gojs&#xff0c;控制台报错 <div id"myDiagramDiv"></div>import go from "gojs"; data() {return {myDiagram: null,} }, mounted() {this.drawTopo(); }, method() {drawTopo() {const $ go.GraphObject.make;this.myDiagram $(go.Di…

【C++】POCO学习总结(十九):哈希、URL、UUID、配置文件、日志配置、动态库加载

【C】郭老二博文之&#xff1a;C目录 1、哈希 1.1 说明 std::map和std::set 的性能是&#xff1a;O(log n) POCO哈希的性能比STL容器更好&#xff0c;大约快两&#xff1b; POCO中对应std::map的是&#xff1a;Poco::HashMap&#xff1b; POCO中对应std::set的是 Poco::Hash…

推荐几款值得收藏的3DMAX插件

推荐几款值得收藏的3DMAX插件 StairGenerator StairGenerator一键楼梯插件&#xff0c;不需要花费太多的时间&#xff0c;轻松从2D平面图生成3D楼梯模型&#xff0c;生成的楼梯模型细节丰富真实。 【主要功能】 1.简单&#xff1a;轻松实现2D到3D建模。 2.具有最详细三维结…

接口优化的常见方案实战经验

一、背景 针对老项目&#xff0c;去年做了许多降本增效的事情&#xff0c;其中发现最多的就是接口耗时过长的问题&#xff0c;就集中搞了一次接口性能优化。本文将给小伙伴们分享一下接口优化的通用方案。 二、接口优化方案总结 1.批处理 批量思想&#xff1a;批量操作数据库…

css3实现动态心电图折线

css3实现动态心电图折线 M&#xff08;moveto&#xff09;&#xff1a;需要两个参数&#xff08;x轴和y轴坐标&#xff0c;移动到的点的x轴和y轴的坐标L&#xff08;lineto&#xff09;&#xff1a;需要两个参数&#xff08;x轴和y轴坐标&#xff09;&#xff0c;它会在当前位置…

Windows使用VNC Viewer远程桌面Ubuntu【内网穿透】

文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…

LSTM ——作业

习题6-4 推导LSTM网络中参数的梯度&#xff0c; 并分析其避免梯度消失的效果 习题6-3P 编程实现下图LSTM运行过程 1. 使用Numpy实现LSTM算子 import numpy as np # 创建一个numpy数组x&#xff0c;它是一个4x4的矩阵&#xff0c;包含9个元素 x np.array([[1, 0, 0, 1],[3, …

python排序算法,冒泡排序和快排

对于排序算法中比较知名的两个算法&#xff0c;分别就是冒泡排序和快速排序&#xff0c;在日常学习和使用中都会听到这两种排序算法的名称&#xff0c;这里主要介绍如何使用python来实现这两种排序算法。 冒泡排序的实现&#xff1a;一是从集合第一个元素开始&#xff0c;每两…

14:00面试,14:05就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到12月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40…

LINUX常用命令——gcc编译篇

LINUX常用命令——gcc编译篇 本文设计了LINUX一些基本命令的讲解包括基本命令ls,man;编译命令gcc以及相关参数说明&#xff0c;【练习&#xff1a;输出斐波那契数列】 文章目录 LINUX常用命令——gcc编译篇一、常用基本命令的讲解1.1 ls 列出目录内容1.2 man 查看命令手册 二、…

Linux shell编程学习笔记36:read命令

目录 0 前言1 read命令的功能、格式、返回值和注意 1.1 命令功能1.2 命令格式1.3 返回值1.4 注意事项2 命令应用实例 2.1 一次读入多个变量值2.2 不指定变量名2.3 测试read命令的返回值2.3 指定输入时限并进行相应处理2.4 -t 指定结束符2.5 -n 指定输入字符个数2.6 -N 指定输入…

存储:windows 10 硬盘盒 新盘 SSD分区

1.准备好绿联2.5英寸 2.准备好 SSD 磁盘 3.接入硬盘和盒子 4.win10 电脑 win x 然后选择磁盘管理 &#xff08;磁盘管理 K&#xff09; 5.它会提示需要初始化的一个新的磁盘&#xff0c;确定初始化 6.添加卷 7.命名盘符 8.检测是否识别到盘符 9.end