• log4j、log4j2和slf4j的基本使用


    一、什么是log4j、log4j2和slf4j

      Log4j是Apache的一个开源项目,通过配置来控制日志的输出。主要是控制日志的输出级别、输出位置和输出内容格式。

      Log4j2是在log4j框架的基础上进行了优化和改造具有更好的性能。

      slf4j是一个日志输出接口,其本身是没有具体实现的,必须的借助上面的日志框架才能更好的实现日志输出。使用slf4j能够更好的让用户进行日志框架的切换而无需修改代码。

    二、log4j的主要组成

      Log4j由三个重要的组成构成:

      1.日志记录器(Loggers):控制要启用或禁用哪些日志记录语句,并对日志信息进行级别限制

        2.输出端(Appenders) : 指定了日志将打印到控制台还是文件中

        3.日志格式化器(Layout): 控制日志信息的显示格式

      

    三、log4j配置内容

      

      

    #######日志级别#############
    #Debug     调试信息   级别最低
    #Info      一般信息
    #Warn      警告信息
    #Error     错误信息
    #Fatal     严重错误信息  级别最高,错误最严重的(在slf4j中没有这个级别)
    ##########################
    
    #######日志的输出位置#########
    #org.apache.log4j.ConsoleAppender:将日志信息输出到控制台。
    #org.apache.log4j.FileAppender:将日志信息输出到一个文件。
    #org.apache.log4j.DailyRollingFileAppender:将日志信息输出到一个日志文件,并且每天输出到一个新的日志文件。
    #org.apache.log4j.RollingFileAppender:将日志信息输出到一个日志文件,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件。
    #org.apache.log4j.WriteAppender:将日志信息以流格式发送到任意指定地方。
    #org.apache.log4j.jdbc.JDBCAppender:通过JDBC把日志信息输出到数据库中。
    #org.apache.log4j.net.SMTPAppender:将日志输出到邮件
    #########################
    
    #######日志格式化器layout####
    #org.apache.log4j.HTMLLayout(以HTML表格形式布局),
    #org.apache.log4j.PatternLayout(灵活地指定布局模式),
    #org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
    #org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
    ##########################
    
    ##########日志信息格式中几个符号所代表的含义##############
    # -X号: X信息输出时左对齐;
    # %p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
    # %d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
    # %r: 输出自应用启动到输出该log信息耗费的毫秒数
    # %c: 输出日志信息所属的类目,通常就是所在类的全名
    # %t: 输出产生该日志事件的线程名
    # %l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)
    # %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
    # %%: 输出一个"%"字符
    # %F: 输出日志消息产生时所在的文件名称
    # %L: 输出代码中的行号
    # %m: 输出代码中指定的消息,产生的日志具体信息
    # %n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行
    # 可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
    # 1)%20:最小的宽度是20,如果小于20的话,默认的情况下右对齐。
    # 2)%-20:最小的宽度是20,如果小于20的话,"-"号指定左对齐。
    # 3)%.30:最大的宽度是30,如果大于30的话,就会将左边多出的字符截掉,但小于30的话也不会
    ##################################################

    四、log4j的使用

      1.引入log4j的jar包

         <!-- 日志相关jar包 -->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>

      2.编写配置文件

    #######日志#########
    
    log4j.rootLogger=DEBUG,Console,DayFile,FixedFile,logMail,logDB
    
    ###输出到控制台配置#########
    log4j.appender.Console=org.apache.log4j.ConsoleAppender
    log4j.appender.Console.layout=org.apache.log4j.PatternLayout
    log4j.appender.Console.layout.ConversionPattern=[%-5p][%-19d{yyyy/MM/dd HH:mm:ss}][%l]  %m%n
    log4j.appender.Console.Threshold=DEBUG
    log4j.appender.Console.Target=System.out
    log4j.appender.Console.ImmediateFlush=true
    
    
    
    ###输出到文件配置(每天一个日志文件)##########
    log4j.appender.DayFile=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.DayFile.layout=org.apache.log4j.PatternLayout
    log4j.appender.DayFile.layout.ConversionPattern=[%-5p][%-19d{yyyy/MM/dd HH:mm:ss}][%l]  %m%n
    log4j.appender.DayFile.Threshold=INFO
    log4j.appender.DayFile.ImmediateFlush=true
    log4j.appender.DayFile.Append=true
    log4j.appender.DayFile.Encoding=UTF-8
    log4j.appender.DayFile.File=/daylogs/log
    log4j.appender.DayFile.BufferedIO=true
    #缓存单位为byte
    log4j.appender.DayFile.BufferSize=8192
    log4j.appender.DayFile.DatePattern='.'yyyy-MM-dd
    # -DatePattern='.'yyyy-ww:每周滚动一次文件,即每周产生一个新的文件。还可以按用以下参数: 
    #              '.'yyyy-MM:每月 
    #              '.'yyyy-ww:每周 
    #              '.'yyyy-MM-dd:每天 
    #              '.'yyyy-MM-dd-a:每天两次 
    #              '.'yyyy-MM-dd-HH:每小时 
    #              '.'yyyy-MM-dd-HH-mm:每分钟
    
    
    
    
    ###输出到文件配置(文件大小达到某个值之后就产生一个新的日志文件)##########
    log4j.appender.FixedFile=org.apache.log4j.RollingFileAppender
    log4j.appender.FixedFile.layout=org.apache.log4j.PatternLayout
    log4j.appender.FixedFile.layout.ConversionPattern=[%-5p][%-19d{yyyy/MM/dd HH:mm:ss}][%l]  %m%n
    log4j.appender.FixedFile.Threshold=INFO
    log4j.appender.FixedFile.Append=true
    log4j.appender.FixedFile.ImmediateFlush=true
    log4j.appender.FixedFile.MaxFileSize=10MB
    log4j.appender.FixedFile.MaxBackupIndex=50
    log4j.appender.FixedFile.Encoding=UTF-8
    log4j.appender.FixedFile.File=/fixedlogs/log
    #开启缓存
    log4j.appender.FixedFile.BufferedIO=true
    #缓存单位为byte,默认8k
    log4j.appender.FixedFile.BufferSize=8192
    
    
    
    #用Email发送日志 
    log4j.appender.logMail=org.apache.log4j.net.SMTPAppender
    log4j.appender.logMail.layout=org.apache.log4j.PatternLayout
    log4j.appender.logMail.layout.ConversionPattern=[%-5p][%-19d{yyyy/MM/dd HH:mm:ss}][%l]  %m%n
    log4j.appender.logMail.layout.LocationInfo=TRUE
    log4j.appender.logMail.layout.Title=TestLog Mail LogFile
    log4j.appender.logMail.Threshold=ERROR
    log4j.appender.logMail.SMTPDebug=FALSE
    log4j.appender.logMail.SMTPHost=SMTP.163.com
    log4j.appender.logMail.From=abcd@163.com
    log4j.appender.logMail.To=123456@qq.com
    #抄送
    #log4j.appender.logMail.Cc=12345@gmail.com
    #密送
    #log4j.appender.logMail.Bcc=123456@gmail.com
    log4j.appender.logMail.SMTPUsername=abcd
    log4j.appender.logMail.SMTPPassword=123456
    log4j.appender.logMail.Subject=Log4j Log Messages
    #单位是KB
    log4j.appender.logMail.BufferSize=10
    log4j.appender.logMail.SMTPAuth=TRUE
    
    
    #将日志登录到MySQL数据库 
    log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
    log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
    log4j.appender.logDB.URL=jdbc:mysql://127.0.0.1:3306/zmyproject
    log4j.appender.logDB.User=root
    log4j.appender.logDB.Password=root
    log4j.appender.logDB.Threshold=ERROR
    ###这里是指缓存的sql条数,默认是1
    log4j.appender.logDB.BufferSize=10
    log4j.appender.logDB.Sql=INSERT INTO testlog_log4j(project_name,create_date,create_time,log_level,category,file_name,thread_name,line,all_category,message) values('testlog','%d{yyyy-MM-dd}','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
    
    
    #配置mybatis的日志(下面的的key对应上dao的路径即可)
    #mybatis的日志必须是DEBUG及以下的级别才会打印
    og4j.logger.com.kyle.testlog.dao=DEBUG

      3.编写代码

      

    @RestController
    @RequestMapping("user")
    public class UserController {
        
        Logger logger = Logger.getLogger(UserController.class);
        
        @Autowired
        private UserService userService;
        
        @RequestMapping("getUserInfoById")
        public String getUserInfoById(String id){
            logger.debug("DEBUG____________________入参:id:"+id);
            logger.info("INFO____________________入参:id:"+id);
            logger.warn("WARN____________________入参:id:"+id);
            logger.error("ERROR____________________入参:id:"+id);
            User user = userService.getUserInfoById(id);
            return user.toString();
        }
    }

      4.mybatis日志

        mybatis的日志默认不打印sql,需要进行相关的配置

        ①在mybatis的主配置文件mybatis-config.xml中加入相关配置

        

    <?xml version="1.0" encoding="UTF-8"?>  
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <settings>  
            <setting name="logImpl" value="LOG4J"/>  
        </settings>  
    
    </configuration>

        ②在log4j.properties文件中指定相应的Mapper文件(也可以是所属包)

    #配置mybatis的日志(下面的的key对应上dao的路径即可)
    #mybatis的日志必须是DEBUG一下的级别才会打印
    og4j.logger.com.kyle.testlog.dao=DEBUG

    五、log4j2的使用

      1.引入jar包

      

     <!-- mybatis、数据库连接池 和mysql驱动相关jar包 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.3.0</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.1.1</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.38</version>
            </dependency>
            <dependency>
                <groupId>commons-dbcp</groupId>
                <artifactId>commons-dbcp</artifactId>
                <version>1.4</version>
            </dependency>
    
            <!-- 日志相关jar包 -->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.8.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.8.2</version>
            </dependency>
             <!-- 发送日志邮件所需jar包 -->
            <dependency>
                <groupId>javax.mail</groupId>
                <artifactId>mail</artifactId>
                <version>1.4</version>
            </dependency>
            <dependency>
                <groupId>javax.activation</groupId>
                <artifactId>activation</artifactId>
                <version>1.1.1</version>
            </dependency>

      2.配置文件log4j2.xml

        

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- status属性是log4j2自身框架的日志级别 
        monitorInterval自动检测配置文件的时间间隔(单位:秒),最小间隔为5秒。log4j 2检测到配置文件有变化,会重新配置自己。
    -->
    <Configuration status="OFF" monitorInterval="60">
        
        <!-- 公共的一些属性的定义,方便下面的配置重复使用如:${LOG_HOME} -->
        <properties>
            <property name="LOG_HOME">/logs</property>
            <property name="FILE_NAME">log</property>
        </properties>
    
        <Appenders>
            <!-- 输出控制台,总共有3个属性
                 name:Appender的名字
                 target:SYSTEM_OUT 或 SYSTEM_ERR,默认是SYSTEM_OUT
                 layout:如何格式化,如果没有默认是%m%n
            -->
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n" />
            </Console>
            
            <!-- 
                                        日志输出到文件,一直会输出到同一个文件,所以可用性不高
                 name:Appender的名字
                 fileName:日志输出目的文件名
                 layout:如何格式化,如果没有默认是%m%n
                 bufferedIO:是否设置缓冲区
             -->
            <File name="File" fileName="/logs/log4j2.log" bufferedIO="true">
                <!-- 控制日志的输出级别(只输出error及以上的日志) -->
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n" />
            </File>
            
            <!-- 
                                        日志按规则输出到多个文件,只会保留几个日志文件。
                 name:Appender的名字
                 fileName:日志输出目的文件名
                 filePattern:指定打包的日志文件的格式,文件的压缩格式支持的后缀名:".gz",".zip",".bz2",".xz";
                                                                          如果不想压缩的话就不用压缩文件格式结尾就行。
                 layout:如何格式化,如果没有默认是%m%n                    
             -->
            <RollingRandomAccessFile name="RollingFile" fileName="${LOG_HOME}/${FILE_NAME}.log" 
                                        filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log.zip">
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout  charset="UTF-8"  pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/>
                <Policies>
                    <!-- 下面的几种日志打包压缩的方式可以同时配置多个规则,只要有一个满足,日志文件就会被压缩 -->
                    <!-- 基于时间来生成压缩文件, interval属性表示每隔多少个时间间隔,其单位为filePatten定义的文件日期最小单位
                                                            下面的配置表示每隔两分钟生成一个压缩文件 -->
                    <TimeBasedTriggeringPolicy interval="2"/>
                    <!-- 固定大小来压缩日志文件 -->
                    <SizeBasedTriggeringPolicy size="10 KB"/>
                    <!-- 日志文件比当前jvm启动时间更早就会触发压缩日志文件 -->
                    <OnStartupTriggeringPolicy />
                </Policies>
                <DefaultRolloverStrategy>
                     <!-- 日志文件保留的个数-->
                     <Delete basePath="${LOG_HOME}" maxDepth="2">
                        <!-- 要删除的日志的名称匹配 -->
                        <IfFileName glob="*/*.log.zip" />
                        <!-- 保留文件的天数 -->
                        <!-- <IfLastModified age="60d" /> -->
                        <!-- 保留文件的个数 -->
                        <IfAccumulatedFileCount  exceeds="10" />
                    </Delete>
                </DefaultRolloverStrategy>
            </RollingRandomAccessFile>
        
            <!-- 日志发送邮件 -->
            <SMTP name="Mail"   subject="LOG4J2  ERROR LOG" 
                                to="123456@qq.com" 
                                from="abcd@163.com" 
                                smtpUsername="abcd" 
                                smtpPassword="12345" 
                                smtpHost="SMTP.163.com" 
                                smtpDebug="false" 
                                smtpPort="25" bufferSize="100">
                 <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
                 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n" />
            </SMTP>
            
            <!-- 日志写入数据库 -->
            <JDBC name="databaseAppender" tableName="testlog_log4j">  
                <ConnectionFactory class="com.kyle.testlog.util.ConnectionFactory" method="getDatabaseConnection" />
                <Column name="project_name" pattern="testlog4j"/>  
                <Column name="create_date" pattern="%d{yyyy-MM-dd}"/>  
                <Column name="create_time" pattern="%d{yyyy-MM-dd HH:mm:ss}"/>  
                <Column name="log_level" pattern="%p" />  
                <Column name="category" pattern="%C" />  
                <Column name="file_name" pattern="%F" />  
                <Column name="thread_name" pattern="%t" />  
                <Column name="line" pattern="%L" />  
                <Column name="all_category" pattern="%l" />  
                <Column name="message" pattern="%message"/>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            </JDBC>  
    
            <Async name="FileAsyncAppender">
                <AppenderRef ref="File"/>
            </Async>
            <Async name="RollingFileAsyncAppender">
                <AppenderRef ref="RollingFile"/>
            </Async>
            <Async name="MailAsyncAppender">
                <AppenderRef ref="Mail"/>
            </Async>
            
        </Appenders>
    
        <Loggers>
            <!-- 结合mybatis打印sql -->
            <logger name="com.kyle.testlog.dao" level="DEBUG"></logger>
            
            <!-- 自定义logger
                name:如果定义为包名或者类名则匹配的时候会对应包下的类中的日志,
                                                    如果自己随便定义则可以通过LogManager.getLogger("definedLogger")来获取
                level:日志级别,和appender中的日志级别取交集
                additivity:是否继承root  默认是true;如果为true则如果该logger与root有相同的appender,则会相同的日志输出两遍
             --> 
            <Logger name="definedLogger" level="ERROR" additivity="true">  
                <AppenderRef ref="FileAsyncAppender" />  
                <AppenderRef ref="RollingFileAsyncAppender" />  
            </Logger>
            
            <Logger name="mailLogger" level="ERROR" additivity="false">  
                <AppenderRef ref="MailAsyncAppender" />  
            </Logger>
            
            <!-- 这里定义的日志级别和每个appender定义的日志级共同起作用,取二者的交集 -->
            <Root level="INFO">  
                <AppenderRef ref="Console" />
                <!-- <AppenderRef ref="FileAsyncAppender" />
                <AppenderRef ref="RollingFileAsyncAppender" />
                <AppenderRef ref="MailAsyncAppender" /> -->
                <AppenderRef ref="databaseAppender" />
            </Root> 
        </Loggers> 
    
    </Configuration>

      mybatis-config.xml配置

      

    <?xml version="1.0" encoding="UTF-8"?>  
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <settings>  
            <setting name="logImpl" value="LOG4J2"/>  
        </settings>  
    
    </configuration>

      3.相关代码

      

    package com.kyle.testlog.util;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp.DriverManagerConnectionFactory;
    import org.apache.commons.dbcp.PoolableConnectionFactory;
    import org.apache.commons.dbcp.PoolingDataSource;
    import org.apache.commons.pool.impl.GenericObjectPool;
    
    
    public class ConnectionFactory {
    
        private static interface Singleton {
            final ConnectionFactory INSTANCE = new ConnectionFactory();
        }
    
        private final DataSource dataSource;
    
        private ConnectionFactory() {
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                System.exit(0);
            }
    
            Properties properties = new Properties();
            properties.setProperty("user", "root");
            properties.setProperty("password", "root");
    
            GenericObjectPool pool = new GenericObjectPool();
            
            DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
                    "jdbc:mysql://localhost:3306/zmyproject", properties);
    
            new PoolableConnectionFactory(connectionFactory, pool, null, null, false, true);
    
            this.dataSource = new PoolingDataSource(pool);
        }
    
        public static Connection getDatabaseConnection() throws SQLException {
            
            return Singleton.INSTANCE.dataSource.getConnection();
        }
    
    }

      

    @RestController
    @RequestMapping("user")
    public class UserController {
        
        Logger logger = LogManager.getLogger(UserController.class);
        //Logger logger = LogManager.getLogger("definedLogger");
        
        @Autowired
        private UserService userService;
        
        @RequestMapping("getUserInfoById")
        public String getUserInfoById(String id){
            logger.debug("DEBUG____________________入参:id:"+id);
            logger.info("INFO____________________入参:id:"+id);
            logger.warn("WARN____________________入参:id:"+id);
            logger.error("ERROR____________________入参:id:"+id);
            logger.fatal("FATAL____________________入参:id:"+id);
            User user = userService.getUserInfoById(id);
            return user.toString();
        }
    }

      

    六、结合log4j和log4j2来使用

      1.log4j和slf4j

        ①引入jar包:maven项目只需要引入一个jar包代替原来的log4j的jar包即可(要注意版本的问题,可能会导致项目无法启动

         <!-- 日志相关jar包 -->
            <!-- <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency> -->
            <dependency>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                    <version>1.7.7</version>
            </dependency> 

         将项目中的获取logger的方式修改一下即可 Logger logger = Logger.getLogger(UserController.class); 改为 Logger logger = LoggerFactory.getLogger(UserController.class); 即可

       2.log4j2和slf4j

        ①引入jar包

          

     <!-- 日志相关jar包 -->
            <!-- <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.11.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.11.0</version>
            </dependency> -->
            
            <!-- 使用slf4j -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.25</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-slf4j-impl</artifactId>
                <version>2.11.1</version>
            </dependency>

        

        将项目中的获取logger的方式修改一下即可   Logger logger = LogManager.getLogger(UserController.class); 改为 Logger logger = LoggerFactory.getLogger(UserController.class); 即可

       

      

  • 相关阅读:
    iPhone开发应用Sqlite使用手册
    2.23 Apps must follow the iOS Data Storage Guidelines or they will be rejected
    跨浏览器(IE/FF/OPERA)JS代码小结
    c#一次数据库查询,JS实现内容分页
    oracle PLSQL /sqlserver2005基本操作对比
    SqlParameter构造函数的临界边缘
    SQL SERVER 2005分页存储过程
    *自创*可变长度随机数字/字母的生成小结(针对文件上传及验证码)
    Visual Source Safe连接数据文件图解 解决密码缓存问题
    [Ubuntu] Invalid command 'VirtualDocumentRoot', perhaps misspelled or defined by a module not included in the server configuration
  • 原文地址:https://www.cnblogs.com/kyleinjava/p/9590380.html
Copyright © 2020-2023  润新知