全网最全Log配置教程及框架性能比较,看这篇就够了!

【可能是全网最全的】JAVA日志框架适配/冲突解决方案 theme: smartblue 前言 你是否遇到过配置了日志,但打印不出来的情况? 你是否遇到过配置了logback,启动时却提示log4j错误的情况?像下面这样: log4j:WARN No appenders could be found for logger (org.example.App). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j. 阅读详情

不管是使用何种编程语言,何种框架,日志输出几乎无处不再,也是任何商业软件中必不可少的一部分。

总结起来,日志的用途大致可以归纳成以下三种:

  • 问题追踪:通过日志不仅仅包括我们程序的一些bug,也可以在安装配置时,通过日志可以发现问题。

  • 状态监控:通过实时分析日志,可以监控系统的运行状态,做到早发现问题、早处理问题。

  • 安全审计:审计主要体现在安全上,通过对日志进行分析,可以发现是否存在非授权的操作。

以 Java 编程语言为例,打印日志的方式有很多,例如通过System.out.print()方法将关键信息输出到控制台,也可以通过 JDK 自带的日志Logger类输出,虽然 JDK 从1.4开始支持日志输出,但是功能单一,无法更好的满足商业要求,于是诞生了很多第三方日志库,像我们所熟悉的主流框架log4j、log4j2、logback等,提供的 API 功能都远胜 JDK 提供的Logger。

二、Log4j

2.1、介绍

Log4j 是一种非常流行的日志框架,由Ceki Gülcü首创,之后将其开源贡献给 Apache 软件基金会。

Log4j 有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局)。这里可简单理解为日志类别、日志要输出的地方和日志以何种形式输出。

综合使用这三个组件可以轻松地记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置。

Log4j 的架构大致如下:

当我们使用 Log4j 输出一条日志时,Log4j 自动通过不同的Appender(输出源)把同一条日志输出到不同的目的地。例如:

  • console:输出到屏幕;

  • file:输出到文件;

  • socket:通过网络输出到远程计算机;

  • jdbc:输出到数据库

在输出日志的过程中,通过Filter来过滤哪些log需要被输出,哪些log不需要被输出。

在Loggers(记录器)组件中,级别分五种:DEBUG、INFO、WARN、ERROR和FATAL。

这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,分别用来指定这条日志信息的重要程度,明白这一点很重要,Log4j有一个规则:只输出级别不低于设定级别的日志信息。

假设Loggers级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。

最后,通过Layout来格式化日志信息,例如,自动添加日期、时间、方法名称等信息。

具体输出样式配置,可以参考如下内容Log4j2 - Layouts布局介绍

2.2、项目应用

以 Java 项目为例,在 Maven 的pom.xml中添加如下依赖!

2.2.1、添加 maven 依赖
<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.6.6</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.6</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>
2.2.2、创建log4j配置

在实际应用中,要使Log4j在系统中运行须事先设定配置文件。

配置文件实际上也就是对Logger、Appender及Layout进行相应设定。

Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties属性文件,二选一。

创建一个log4j.xml或者log4j.properties,将其放入项目根目录下。

1、XML格式
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- 控制台输出配置 -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <!-- 目标为控制台 -->
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <!-- 输出格式 -->
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %l %m%n" />
        </layout>
    </appender>
    <!-- 文件输出配置 -->
    <appender name="log_file" class="org.apache.log4j.DailyRollingFileAppender">
        <!-- 目标为文件 -->
        <param name="File" value="/logs/log/file.log" />
        <!-- 向文件追加输出 -->
        <param name="Append" value="true" />
        <!-- 每个小时生成一个log -->
        <param name="DatePattern" value="'.'yyyy-MM-dd-HH" />
        <layout class="org.apache.log4j.PatternLayout">
            <!-- 输出格式 -->
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %l %m%n" />
        </layout>
    </appender>
    <!-- Application Loggers -->
    <logger name="org.example">
        <level value="info" />
    </logger>
    <!-- 根目录 -->
    <!-- Root Logger -->
    <root>
        <priority value="info" />
        <appender-ref ref="console" />
        <appender-ref ref="log_file" />
    </root>
</log4j:configuration>
2、XML格式
log4j.rootLogger=INFO,M,C,E
log4j.additivity.monitorLogger=false

# INFO级别文件输出配置
log4j.appender.M=org.apache.log4j.DailyRollingFileAppender
log4j.appender.M.File=/logs/info.log
log4j.appender.M.ImmediateFlush=false
log4j.appender.M.BufferedIO=true
log4j.appender.M.BufferSize=16384
log4j.appender.M.Append=true
log4j.appender.M.Threshold=INFO
log4j.appender.M.DatePattern='.'yyyy-MM-dd
log4j.appender.M.layout=org.apache.log4j.PatternLayout
log4j.appender.M.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %p %l %m %n

# ERROR级别文件输出配置
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=/logs/error.log
log4j.appender.E.ImmediateFlush=true
log4j.appender.E.Append=true
log4j.appender.E.Threshold=ERROR
log4j.appender.E.DatePattern='.'yyyy-MM-dd
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %p %l %m %n

# 控制台输出配置
log4j.appender.C=org.apache.log4j.ConsoleAppender
log4j.appender.C.Threshold=INFO
log4j.appender.C.layout=org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %l %m %n
2.2.3、log4j使用

在需要打印日志的类中,引入Logger类,在需要的地方打印即可!

package org.example.log4j.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogPrintUtil {
    /**log静态常量*/
    private static final Logger logger = LoggerFactory.getLogger(LogPrintUtil.class);
    public static void main(String[] args){
            logger.info("info信息");
            logger.warn("warn信息");
            logger.error("error信息");
    }
}

当然你还可以这样写

if(logger.isInfoEnabled()) {
    logger.info("info信息");
}
if(logger.isWarnEnabled()) {
    logger.warn("warn信息");
}
2.2.4、isInfoEnabled()有何作用呢?

简单来说,在某些场景下,用isInfoEnabled()方法判断下是能提升性能的!

例如我们打印这段内容logger.info("User:" + userId + appId),程序在打印这行代码时,先对内容("User:" + userId + appId)进行字符串拼接,然后再输出。

如果当前配置文件中日志输出级别是info,是直接输出的,当日志输出级别是error时,logger.info()的内容时不输出的,但是我们却进行了字符串拼接,如果加上if(logger.isInfoEnabled())进行一次判定,logger.info()就不会执行,从而更好的提升性能,这个尤其是在高并发和复杂log打印情况下提升非常显著。

另外,ERROR及其以上级别的log信息是一定会被输出的,所以只有logger.isDebugEnabled、logger.isInfoEnabled和logger.isWarnEnabled()方法,而没有logger.isErrorEnabled方法。

三、Log4j2

3.1、介绍

log4j2 是 log4j 1.x 的升级版,参考了 logback 的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要特点有:

  • 异常处理:在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。

  • 性能提升, log4j2相较于log4j 1和logback都具有很明显的性能提升,后面会有官方测试的数据。

  • 自动重载配置:参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用——那对监控来说,是非常敏感的。

  • 无垃圾机制:log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvm gc。

3.2、项目应用
3.2.1、添加 maven 依赖
<dependencies>
    <!-- slf4j核心包 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.13</version>
    </dependency>
    <!--用于与common-log保持桥接 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.13</version>
        <scope>runtime</scope>
    </dependency>
    <!--核心log4j2jar包 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.4.1</version>
    </dependency>
    <!--用于与slf4j保持桥接 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.4.1</version>
    </dependency>
    <!--需要使用log4j2的AsyncLogger需要包含disruptor -->
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.2.0</version>
    </dependency>
</dependencies>
3.2.2、创建log4j2配置

在项目的根目录下创建一个log4j2.xml的文件,与log4j相比,log4j2的异步输出日志性能非常强劲,配置如下:

1、同步输出日志
<?xml version="1.0" encoding="UTF-8"?>
<!-- status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时。
 注:本配置文件的目标是将不同级别的日志输出到不同文件,最大1MB一个文件, 
    文件数据达到最大值时,旧数据会被压缩并放进指定文件夹 ,最多存放20个文件-->
<Configuration status="error">
    <!-- 配置日志文件输出目录,此配置将日志输出到根目录下的指定文件夹 -->
    <Properties>
        <Property name="fileDir">/logs/log4j2</Property>
        <Property name="fileHistory">/logs/log4j2/history</Property>
    </Properties>
    <Appenders>
        <!-- 优先级从高到低分别是 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL -->
        <!-- 单词解释:Match:匹配 DENY:拒绝 Mismatch:不匹配 ACCEPT:接受 -->
        <!-- DENY,日志将立即被抛弃不再经过其他过滤器;NEUTRAL,有序列表里的下个过滤器过接着处理日志;ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
        <!--输出日志的格式
        %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间
        %t 输出当前线程名称
        %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
        %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
        %msg 日志文本
        %n 换行
        其他常用的占位符有:
        %F 输出所在的类文件名,如Client.java
        %L 输出行号
        %M 输出所在方法名
        %l  输出语句所在的行数, 包括类名、方法名、文件名、行数
         -->
        <!--这个输出控制台的配置,这里输出all信息到System.out -->
        <console name="Console" target="SYSTEM_OUT">
            <!-- 输出日志的格式 -->
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
        </console>
        <!--这个输出文件的配置,这里输出info信息到junbao_info.log -->
        <RollingFile name="RollingFileInfo" fileName="${fileDir}/info.log" filePattern="${fileHistory}/info/%d{yyyy-MM-dd}-%i.log">
            <!-- 此Filter意思是,只输出info级别的数据 DENY,日志将立即被抛弃不再经过其他过滤器;NEUTRAL,有序列表里的下个过滤器过接着处理日志; 
                    ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
            <Policies>
            <!-- 如果启用此配置,则日志会按文件名生成新文件, 即如果filePattern配置的日期格式为 %d{yyyy-MM-dd HH} 
                    ,则每小时生成一个压缩文件, 如果filePattern配置的日期格式为 %d{yyyy-MM-dd} ,则天生成一个压缩文件,默认为1 -->
                <TimeBasedTriggeringPolicy />
                <!-- 每个日志文件最大1MB,超过1MB生产新的文件 ; -->
                <SizeBasedTriggeringPolicy size="100MB" />
            </Policies>
             <!--文件夹下最多的文件个数-->  
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${fileDir}/warn.log" filePattern="${fileHistory}/warn/%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${fileDir}/error.log" filePattern="${fileHistory}/error/%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
    </Appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <Loggers>
        <!--全异步输出info级以上的日志信息--> 
        <!-- <asyncRoot level="info" includeLocation="true">
            <appender-ref ref="Console" />
            <appender-ref ref="RollingFileInfo" />
        </asyncRoot> -->
        <!--同步输出info级以上的日志信息--> 
        <root level="info" includeLocation="true">
            <appender-ref ref="Console" />
        </root>
    </Loggers>
</Configuration>
2、异步输出日志
<?xml version="1.0" encoding="UTF-8"?>
<!-- status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时。
 注:本配置文件的目标是将不同级别的日志输出到不同文件,最大1MB一个文件, 
    文件数据达到最大值时,旧数据会被压缩并放进指定文件夹 ,最多存放20个文件-->
<Configuration status="error">
    <!-- 配置日志文件输出目录,此配置将日志输出到根目录下的指定文件夹 -->
    <Properties>
        <Property name="fileDir">/logs/log4j2</Property>
        <Property name="fileHistory">/logs/log4j2/history</Property>
    </Properties>
    <Appenders>
        <!-- 优先级从高到低分别是 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL -->
        <!-- 单词解释:Match:匹配 DENY:拒绝 Mismatch:不匹配 ACCEPT:接受 -->
        <!-- DENY,日志将立即被抛弃不再经过其他过滤器;NEUTRAL,有序列表里的下个过滤器过接着处理日志;ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
        <!--输出日志的格式
        %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间
        %t 输出当前线程名称
        %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
        %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
        %msg 日志文本
        %n 换行
        其他常用的占位符有:
        %F 输出所在的类文件名,如Client.java
        %L 输出行号
        %M 输出所在方法名
        %l  输出语句所在的行数, 包括类名、方法名、文件名、行数
         -->
        <!--这个输出控制台的配置,这里输出all信息到System.out -->
        <console name="Console" target="SYSTEM_OUT">
            <!-- 输出日志的格式 -->
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
        </console>
        <!--这个输出文件的配置,这里输出info信息到junbao_info.log -->
        <RollingFile name="RollingFileInfo" fileName="${fileDir}/info.log" filePattern="${fileHistory}/info/%d{yyyy-MM-dd}-%i.log">
            <!-- 此Filter意思是,只输出info级别的数据 DENY,日志将立即被抛弃不再经过其他过滤器;NEUTRAL,有序列表里的下个过滤器过接着处理日志; 
                    ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
            <Policies>
            <!-- 如果启用此配置,则日志会按文件名生成新文件, 即如果filePattern配置的日期格式为 %d{yyyy-MM-dd HH} 
                    ,则每小时生成一个压缩文件, 如果filePattern配置的日期格式为 %d{yyyy-MM-dd} ,则天生成一个压缩文件,默认为1 -->
                <TimeBasedTriggeringPolicy />
                <!-- 每个日志文件最大1MB,超过1MB生产新的文件 ; -->
                <SizeBasedTriggeringPolicy size="100MB" />
            </Policies>
             <!--文件夹下最多的文件个数-->  
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${fileDir}/warn.log" filePattern="${fileHistory}/warn/%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${fileDir}/error.log" filePattern="${fileHistory}/error/%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
    </Appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <Loggers>
        <!--全异步输出info级以上的日志信息--> 
        <asyncRoot level="info" includeLocation="true">
            <appender-ref ref="Console" />
            <appender-ref ref="RollingFileInfo" />
        </asyncRoot>
        <!--同步输出info级以上的日志信息--> 
        <!-- <root level="info" includeLocation="true">
            <appender-ref ref="Console" />
        </root> -->
    </Loggers>
</Configuration>

详细 API 可以参考官方网站!

3.2.3、log4j2使用

与 log4j 类似,直接在需要位置打印日志即可

package org.example.log4j.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogPrintUtil {
    /**log静态常量*/
    private static final Logger logger = LoggerFactory.getLogger(LogPrintUtil.class);
    public static void main(String[] args){
            logger.info("info信息");
            logger.warn("warn信息");
            logger.error("error信息");
    }
}

四、Logback

4.1、介绍

Logback 也是用 java 编写一款非常热门的日志开源框架,由 log4j 创始人写的,性能比 log4j 要好!

logback 主要分为3个模块:

  • logback-core:核心代码模块

  • logback-classic:log4j的一个改良版本,同时实现了slf4j的接口,这样你如果之后要切换其他日志组件也是一件很容易的事

  • logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能

4.2、项目应用
4.2.1、添加 maven 依赖
<!--这个依赖直接包含了 logback-core 以及 slf4j-api的依赖-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<!-- 支持在xml中写判断标签 -->
<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
    <version>2.7.8</version>
</dependency>
4.2.2、创建logback配置文件
1、配置说明

logback在启动的时候,会按照下面的顺序加载配置文件:

  • 如果java程序启动时指定了logback.configurationFile属性,就用该属性指定的配置文件。如java -Dlogback.configurationFile=/path/to/mylogback.xml Test,这样执行Test类的时候就会加载/path/to/mylogback.xml配置

  • 在classpath中查找logback.groovy文件

  • 在classpath中查找logback-test.xml文件

  • 在classpath中查找logback.xml文件

  • 如果是jdk6+,那么会调用ServiceLoader查找 com.qos.logback.classic.spi.Configurator接口的第一个实现类

  • 自动使用ch.qos.logback.classic.BasicConfigurator,在控制台输出日志

上面的顺序表示优先级,使用java -D配置的优先级最高,只要获取到配置后就不会再执行下面的流程。相关代码可以看ContextInitializer#autoConfig()方法。

2、同步输出日志
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 
    debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 运行环境,dev:开发,test:测试,pre:预生产,pro:生产 -->
    <property name="system_host" value="dev" />
    <property file="system.properties" />
    <!-- 上下文变量设置,用来定义变量值,其中name的值是变量的名称,value的值时变量定义的值。 通过<property>定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
    <property name="CONTEXT_NAME" value="logback-test" />
    <!-- 日志文件存放路径设置,绝对路径 -->
    <property name="logs.dir" value="/opt/logs" />
    <!-- 日志文件存放路径设置,tomcat路径 -->
    <property name="logs.dir" value="${catalina.base}/logs" />
    <!-- 定义日志文件 相对输入位置 -->  
    <property name="log_dir" value="log" />
    <!-- 日志输出格式设置 -->
    <!-- 
    %d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n
      Logger: %logger
      Class: %class
      File: %file
      Caller: %caller
      Line: %line
      Message: %m
      Method: %M
      Relative: %relative
      Thread: %thread
      Exception: %ex
      xException: %xEx
      nopException: %nopex
      rException: %rEx
      Marker: %marker
      newline:%n
    -->
    <property name="CUSTOM_LOG_PATTERN"
        value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{90} - %msg%n" />
    <!-- 上下文名称:<contextName>, 每个logger都关联到logger上下文, 默认上下文名称为“default”。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。 
        一旦设置,不能修改。 -->
    <contextName>${CONTEXT_NAME}</contextName>
    <!-- <appender>是<configuration>的子节点,是负责写日志的组件。 有两个必要属性name和class。name指定appender名称, 
        class指定appender的实现类。 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 对日志进行格式化。 -->
        <encoder>
            <pattern>${CUSTOM_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="file"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 按天来回滚,如果需要按小时来回滚,则设置为{yyyy-MM-dd_HH} -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>log/testC.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 如果按天来回滚,则最大保存时间为30天,30天之前的都将被清理掉 -->
            <maxHistory>30</maxHistory>
            <!-- 按时间回滚的同时,按文件大小来回滚 -->
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 过滤器,只记录WARN级别的日志 -->
        <!-- 果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置过滤级别 -->
            <level>WARN</level>
            <!-- 用于配置符合过滤条件的操作 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 日志输出格式 -->
        <encoder>
            <pattern>${CUSTOM_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
 
    <appender name="log_file"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。 -->
        <file>${logs.dir}/logback-test.log</file>
        <!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <!-- 必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 
                或者 没有log%i.log.zip -->
            <FileNamePattern>${logs.dir}/logback-test.%i.log</FileNamePattern>
            <!-- 窗口索引最小值 -->
            <minIndex>1</minIndex>
            <!-- 窗口索引最大值 -->
            <maxIndex>3</maxIndex>
        </rollingPolicy>
        <!-- 日志级别过滤器 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 日志级别过滤器 -->
            <level>INFO</level>
            <!-- 符合要求的日志级别,过滤,ACCEPT:接受 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不符合要求的日志级别,过滤,DENY:拒绝 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 激活滚动的条件。 -->
        <triggeringPolicy
            class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!-- 活动文件的大小,默认值是10MB -->
            <maxFileSize>30MB</maxFileSize>
        </triggeringPolicy>
        <!-- 对记录事件进行格式化。 -->
        <encoder>
            <pattern>${CUSTOM_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
 
    <!-- 异步输出 -->
    <appender name="ASYNC_logback" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <!-- <discardingThreshold>0</discardingThreshold> -->
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <!-- <queueSize>256</queueSize> -->
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="log_file" />
    </appender>
    <!-- 指定包输出路径 -->
    <!-- 用来设置某一个 包 或者具体的某一个 类 的日志打印级别、以及指定<appender>, name:用来指定受此logger约束的某一个包或者具体的某一个类。 
        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前loger将会继承上级的级别。 
        additivity:是否向上级logger传递打印信息。默认是true。(这个logger的上级就是上面的root) <logger>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。 -->
    <logger name="org.logback.test" level="DEBUG" additivity="true">
        <appender-ref ref="stdout" />
    </logger>
    <!-- 特殊的<logger>元素,是根logger。只有一个level属性,应为已经被命名为"root". level:设置打印级别,大小写无关:TRACE, 
        DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。默认是DEBUG。 <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个loger。 -->
    <root>
        <level value="WARN" />
        <!-- if表达式,需要Janino jar -->
        <!-- Janino 2.6.0版本开始,除了janino.jar之外, commons-compiler.jar也需要在类路径中 -->
        <if condition='property("system_host").contains("dev")'>
            <then>
                <appender-ref ref="stdout" />
            </then>
        </if>
        <appender-ref ref="file" />
    </root>
</configuration>

注意:logback如果配置要输出行号,性能会明显降低,如果不是必须,建议不要配置!

4.2.3、logback使用
package org.example.logback.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogPrintUtil {
    /**log静态常量*/
    private static final Logger logger = LoggerFactory.getLogger(LogPrintUtil.class);
    public static void main(String[] args){
         logger.info("info信息");
            logger.warn("warn信息");
            logger.error("error信息");
    }
}

五、SLF4J桥接

细心的你,会发现上面代码使用时,都使用的是private static final Logger logger = LoggerFactory.getLogger(LogPrintUtil.class)这个,其中都来自org.slf4j包,SLF4J是啥?有什么作用呢?

SLF4J本身并不输出日志,最大的特色是**:它可以通过适配的方式挂接不同的日志系统,属于一个日志接口**。

如果项目适配到log4j就使用log4j日志库进行输出;如果适配到logback就使用logback日志库进行输出;如果适配到log4j2就使用log4j2日志库进行输出。

这样最大的好处,就是当你想将项目从log4j换成log4j2的时候,只需要在项目pom.xml中进行桥接适配即可,不用修改具体需要打印日志的代码!

六、三大主流日志框架性能比较

介绍了这么多,但是我们还不知道三个日志框架的日志输出性能如何,本文以10000条数据进行打印,比较log4j、log4j2、logback日志的输出时间。

本次测试采用的是本地电脑(win7),每个电脑的配置不一样,测试的结果也不一样,结果是真实的。

  • 同步输出

  • 异步输出

从测试结果上可以看出:

  • 不建议生产环境进行控制台输出;

  • 在纯文件输出的环境下,logback的输出优于log4j2,而log4j2要优于log4j,如果要进行生产环境的部署,建议采用logback,如果是使用log4j2,建议使用异步方式进行输出,输出结果基本是实时输出;

最后需要注意的地方是:log有风险,输出需谨慎!

由于输出log过程需要进行磁盘操作,且log4j为了保证log输出过程的线程安全性而使用同步锁,就使得输出log成为很耗时的操作,所以log信息一定要言简意赅,不要输出一些无用的log。

七、总结

本文主要围绕项目中使用到的日志框架进行应用介绍,限于笔者的才疏学浅,对本文内容可能还有理解不到位的地方,如有阐述不合理之处还望留言一起探讨。

八、参考

1、Log4j2官方地址

2、廖雪峰官方网站 - java教程

iOS WKWebView H5微信支付跳转 iOS WKWebView H5微信支付跳转 需求:iOS客户端实现嵌入H5进行微信支付跳转到微信客户端,支付完成后再跳转回我们的APP,解决WKWebView无法跳转回APP的BUG. 阅读前提: 了解WKWebView基本初始化及使用 了解如何利用URL Schemes进行应用间跳转 公司或个人已经在微信后台注册了一级域名 GitHub地址(附代码) :iOS WKWebView H5微信支付跳转 简书地址 :iOS WKWebView H5微信支付跳转 博客地址 :iOS WKWe... 阅读详情

相关推荐

泛微OA表单开发实战:如何用JavaScript动态控制明细表的必填与可编辑属性

本文详细介绍了如何在泛微OA系统中使用JavaScript动态控制明细表字段的必填与可编辑属性。通过主表字段变更事件监听和明细表字段属性批量修改,实现智能化的表单交互,提升用户体验和系统灵活性。文章深入解析了关键API的使用方法,并提供了实战代码示例和最佳实践。

weixin_30955617的博客 548

log日志配置简述

近期进行log升级,简单配置了下几种常用日志,分别是log4j、log4j2、logback。简单整理,不全不深,仅备后用。 需求:将应用日志以控制台或文件的形式输出。 一、log4j (1)引入jar包 下载并引入log4j的jar包,log4j-1.*.jar,笔者用的是 log4j-1.2.17.jar。 (2)log的主要节点 log配置文件主要包含三个节点:Logger、Appender、La...

lgxzzz的博客 2570

Matlab实战:如何用MMG方程预测船舶轨迹(附完整代码)

本文提供了一份基于MMG方程的Matlab实战指南,详细讲解了如何从零构建船舶轨迹预测模型。内容涵盖MMG模型的核心原理、Matlab代码框架设计、动力学函数实现、参数调优与验证方法,并附有完整代码示例,帮助工程师和研究者快速掌握这一船舶操纵性预测的关键技术。

v6b7n8m9q0的博客 518

Logback及Log4J性能测试

分别从两个方面测试: 1、日志输出的格式对性能的影响,即日志中包含动态方法名和行号对性能的影响 2、日志长度对性能的影响 本测试在Spring Boot框架基础上进行,日志配置文件logback-spring.xml内容如下: %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%-14.14thread] %-30.30logger{30} [

wudiyong22的专栏 1874

国外无人机蜂群作战典型战例及发展趋势

参与袭击的无人机为简易组装的固定翼飞机,采用小排量活塞发动机和手机GPS通信模块零件,机翼为轻木加泡沫板混合结构,尾梁为铝合金方管,主要依靠胶带粘接,以上全部配件均可在商用市场买到,成本较低,可在短时间内装配完成。③战场纵深短、瞬时性强。蜂群作战的参战数量显著增加,这一方面得益于无人机技术的飞速发展使得廉价无人机可以大量生产和仿制,另一方面凭借迅速发展的投放和回收技术以及集群控制技术,使得无人机蜂群可在极短时间内实现编队饱和攻击,在快速消耗敌方高价值防空武器系统的同时,保证己方物资系统的突防概率。

renhongxia1的博客 2479

FreeSWITCH安装指南:三种部署方式解析与实践对比

Freeswitch部署方案比较:Yum安装简单快捷但灵活性差;源码安装可定制性强,适合深度开发和性能优化;Docker部署环境一致性好但网络配置复杂。生产环境推荐源码安装以获得最佳性能和定制能力,而Docker适合快速部署和扩展。不同方案各有利弊,需根据具体需求选择。

c_zyer的博客 2140

logback性能优化详解

不正确的日志打印不但会降低程序运行性能,还会占用大量IO资源和硬盘存储空间。本文主要总结一些能提高日志打印性能的手段。

弹指天下 6018

【优化模型】求解混合整数线性规划 MILP 问题

混合整数线性规划(Mixed Integer Linear Programming,MILP)是一类优化问题,其中目标函数和约束条件均为线性,并且包含整数变量。MILP 在实际应用中具有广泛的应用,可以解决很多实际问题,如生产调度、网络设计、资源分配等。f — 系数向量。系数向量,指定为实数向量或实数数组。intcon — 整数约束组成的向量。整数约束组成的向量,指定为正整数向量。intcon 中的值指示决策变量 x 中应取整数值的分量。A — 线性不等式约束。线性不等式约束,指定为实矩阵。

Fanjufei的博客 2497

Logback日志这样配置,让你性能提升10倍

name属性指定appender命名 class属性指定输出策略,通常有两种,控制台输出和文件输出,文 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 件输出就是将日志进行一个持久化。ConsoleAppender将日志输出到控制台 标签,通过使用该标签指定过滤策略 标签指定过滤的类型 标签,使用该标签下的 标签指定日志输出格式。 标签指定收集策略,比如基于时间进行收集 标签指定生成日志保存地址 通过这样配置已经实..

java100001的博客 1002

手把手教你学PCIE(5.1)--pcie固件初始化的硬件初始化

PCIe 固件初始化的硬件初始化是一个复杂但至关重要的过程。通过正确配置 PCIe 控制器和相关硬件,可以确保系统的稳定性和性能。以上步骤和示例代码提供了一个基本的框架,实际开发中可能需要根据具体的硬件和需求进行调整和优化。

MHD0815的博客 1688

UI自动化控制PC端微信获取当前群聊的群友列表

在一个月前,某个群友在获取整个群的群友信息时遇到了一点问题: 对整个群进行群友数据获取经测试确实有点棘手,下面我将我的解决过程公布给大家。 基础教程详见: https://blog.youkuaiyun.com/as604049322/article/details/121391639 人工打开要抓取的群聊窗口后,首先获取微信窗口并点击聊天信息按钮: import uiautomation as auto auto.uiautomation.SetGlobalSearchTimeout(1) wechatWind

小小明-代码实体的专栏 3万+

全网最全Log配置教程及框架性能比较,看这篇就够了!!!

Log4j:Apache的开源项目,是一个功能强大的日志组件,提供方便的日志记录。在项目中使用Log4j可以控制日志信息输出到控制台、文件甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别可以更灵活的控制日志的输出过程,方便调试。

小斜同学的博客 1232

[STC32G144K246入门第一步]点亮LED

STC32G144K246是一款高性能32位8051单片机,集成了丰富外设资源,适用于工业控制和复杂算法处理。该芯片支持双模式内核,内置硬件加速器,提供246KB Flash和144KB SRAM存储空间。文章详细介绍了开发环境搭建步骤、寄存器配置方法,并给出了LED驱动示例代码,通过推挽输出控制P42口实现闪烁效果。虽然开发环境相比ARM存在不足,但通过合理配置仍可满足项目需求。该教程为开发者快速上手STC32G144K246提供了实用参考。

sinat_58149788的博客 795

Redis 配置详解 —— 全网最新最全

当中间件性能遇到所谓的 “瓶颈” 的时候,也许就是某个配置设置的不合理导致的问题。是项目开发中使用频率最高的中间件,那么试问有谁认真的全文解读过。今天借此机会花费一点时间来全文解读一次。全部版本的安装文件。

Rambo's Blog 6966

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

深入浅出MyBatis-Plus,全网最全笔记

MyBatis-Plus 作者: 是叶十三 特性 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 支持主键自动生成:支持多达 4 种主键策略(内含分

南迪叶先森 3107

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

全网最全、最细致的Java日志框架以及门面技术。

????‍????作者:Java学术趴 ????仓库:Github、Gitee ✏️博客:优快云、掘金、InfoQ、云+社区 ????公众号:Java学术趴 ????特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系小编授权。 ????版权声明:文章里的部分文字或者图片来自于互联网以及百度百科,如有侵权请尽快联系小编。微信搜索公众号Java学术趴联系小编。 ☠️每日毒鸡汤:生活是世上最罕见的事情,大多数人只是存在,仅此而已。 ????大家好!我是你们的老朋友Java学术趴,今天继续给大家分享

智码未来:AI与Java全栈的跨界对话 744

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

全网最细最全clickhouse教程(clickhouse视频教程)

表引擎(即表的类型)决定了: 数据的存储方式和位置,写到哪里以及从哪里读取数据 支持哪些查询以及如何支持。 并发数据访问。 索引的使用(如果存在)。 是否可以执行多线程请求。 数据复制参数。 在读取时,引擎只需要输出所请求的列,但在某些情况下,引擎可以在响应请求时部分处理数据。对于大多数正式的任务,应该使用MergeTree族中的引擎。 1 日志系列 具有最小功能的轻量级引擎。当您需要快速写入许多小表(最多约100万行)并在以后整体读取它们时,该类型的引擎是最有效的。 1.1S...

HANG.NIAN 1万+

全网最全 Java 日志框架适配方案!还有谁不会?

你是否遇到过配置了日志,但打印不出来的情况?你是否遇到过配置了logback,启动时却提示log4j错误的情况?你是否遇到过SLF4J的这种报错?你是否遇到过DUBBO日志打印不正常的情况?你是否遇到过Mybatis SQL日志打印不出来的情况?你是否遇到过JPA/Hibernate SQL日志无法打印的情况?你是否遇到过复杂项目中,很多框架内部日志无法打印的情况?你是否遇到过Tomcat工程,日志文件打印了多份,catalina.out和其他文件?

m0_71777195的博客 239

可能是全网最全的 Java 日志框架适配、冲突解决方案

你是否遇到过配置了日志,但打印不出来的情况?你是否遇到过配置了logback,启动时却提示log4j错误的情况?像下面这样:log4j:WARN No appenders could be found for logger (org.example.App). log4j:WARN Please initialize the log4j system properly. log4j:WARN Se...

十指波课堂的博客 223

图解Kafka适用场景,全网最全!

点击下方“JavaEdge”,选择“设为星标”第一时间关注技术干货!免责声明~任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」;不要急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人。怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」消...

JavaEdge全是干货的技术号 557

Docker搭建MySQL主从同步(全网最全保姆教程)

slave会从master读取binlog来进行数据同步IO 线程SQL线程。

日常技术分享 3787

干货 | Java开发十二大框架简介(附教程)

1.Spring 框架 Spring框架是现在Java后端框架家族里面最强大的一个,拥有IOC和AOP两大利器,简化了开发的复杂性。而且Spring现在能与所有主流开发框架集成,是一个万能框架,Spring让Java开发变得简单。 黑马程序员Java进阶教程spring框架,深入浅出讲解细致:https://www.bilibili.com/video/BV1yt411e7E8 2.Spring MVC 框架 这是一个MVC的开源框架用来代替Struts,是Spring项目里面的一个重要部分,能和Sprin

weixin_51689029的博客 570

【全网最全】Android 性能优化大杀器:Perfetto 全深度解析与实战指南

Android性能优化利器:Perfetto Trace Viewer深度解析 本文深入介绍了Android官方推荐的性能分析工具Perfetto Trace Viewer。相比Android Studio Profiler,Perfetto具有三大优势:提供全系统视角、擅长多进程交互分析、具备浏览器级流畅体验。文章详细讲解了环境配置要点,强调使用profileable标记而非debuggable。在实战部分,介绍了命令行脚本和Web端两种采集方式,并重点解析了Perfetto UI的关键功能

xhxh510573522125的博客 1041

【MyBatis-Plus保姆级教程】第七章:插件机制与性能优化(SQL分析/防误删/自定义插件)

嘿,各位追求卓越的工程师们,我是默语 👨‍💻!欢迎来到《MyBatis-Plus保姆级教程》的第七章,也是我们深入架构与底层原理的进阶篇!前面的章节我们学会了如何“用”,而这一章我们将探讨如何“用得好、用得安全”。🚀 你是否担心过在生产环境误执行了没有`WHERE`条件的`UPDATE`或`DELETE`?你是否想知道哪条SQL执行得最慢,拖累了系统性能?你是否有一些特殊的、全局性的需求(如数据加解密)不知如何优雅地实现?本章将为你揭晓答案!我们将深入MP的插件机制,学习如何配置**SQL分析打印**

沉淀所学,分享所思,让热爱与成长同行。商务记录AI实战经验,助力开发者快速成长。 热爱AI,希望做出有影响力的技术成果。 技术点亮生活,分享连接价值与机会。 专注AI落地实战,陪你走好技术每一步。 2737

MyBatis-Plus:全网最全攻略,从入门到精通

MyBatis-Plus是MyBatis的增强工具,通过"零配置、无侵入"的设计理念简化开发流程。它提供通用CRUD操作、条件构造器、分页插件等功能,无需编写XML映射文件即可完成数据库操作。文章详细介绍了环境配置、核心功能、高级特性(如代码生成器、逻辑删除)以及实战案例,并探讨了SQL优化、缓存优化等性能提升方法。同时针对常见问题如SQL注入、分页失效等提供了解决方案。作为MyBatis的扩展,MyBatis-Plus显著提升了开发效率,未来可能进一步增强NoSQL支持、分布式事务等功

小伙伴们全都Lucky! 949
上一篇: ​LeetCode刷题实战96:不同的二叉搜索树
下一篇: 后端Coder如何做好代码设计?
编程IT圈
博客等级 码龄6年 1130粉丝 544原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值