java日志篇(5)-logback

慢慢来比较快,虚心学技术

前言:Logback是由log4j创始人设计的又一个开源日志组件。logback当前分成三个模块:logback-core,logback-classic和logback-access。
logback-core :其它两个模块的基础模块
logback-classic:log4j的一个改良版本(此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging)
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能

一、logback+slf4j简单使用示例

①获取logback的jar包,最新jar包从官网https://logback.qos.ch/download.html下载,将其放在lib包目录下,并引入项目中;如果是maven项目,在pom文件中引入如下:

<dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-core</artifactId>
     <version>1.1.7</version>
</dependency>
<dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-access</artifactId>
     <version>1.1.7</version>
</dependency>
<dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-classic</artifactId>
     <version>1.1.7</version>
</dependency>

②获取slf4j的jar包,因为logback完整实现了slf4j的接口,所以需要依赖于slf4j实现功能。最新jar包从官网https://www.slf4j.org/download.html下载,如果是maven项目,在pom文件中引入如下:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.7</version>
</dependency>

③代码编写

package SimpleTest;

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

/**
 * 简单日志获取
 */
public class SimpleTest {

    public static void main(String[] args) {
        //获取logger对象
        Logger logger = LoggerFactory.getLogger(SimpleTest.class);

        logger.info("info.....");
        logger.debug("debug...");
        logger.error("error...");
        logger.warn("warn...");

    }
}

④执行结果

22:14:47.606 [main] INFO SimpleTest.SimpleTest - info.....
22:14:47.611 [main] DEBUG SimpleTest.SimpleTest - debug...
22:14:47.611 [main] ERROR SimpleTest.SimpleTest - error...
22:14:47.611 [main] WARN SimpleTest.SimpleTest - warn...

二、logback加载过程

当我们使用logback-classic.jar时,应用启动,logback会按照如下顺序进行扫描:

  • 在系统配置文件System Properties中寻找是否有logback.configurationFile对应的value
  • 在classpath下寻找是否有logback.groovy(即logback支持groovy与xml两种配置方式)
  • 在classpath下寻找是否有logback-test.xml
  • 在classpath下寻找是否有logback.xml

以上任何一项找到之后都会停止,如果全部都不满足,则默认使用ch.qos.logback.classic.BasicConfigurator的configure方法,构造一个ConsoleAppender用于向控制台输出日志,默认日志输出格式为”%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} – %msg%n”。如本文简单实例所示

三、logback配置文件讲解

分析logbac配置文件节点如下:

<configuration>(总配置)
    |----------------<jmxConfigurator>(动态日志级别)
    |----------------<property>(定义变量值)
    |----------------<timestamp>(获取时间戳字符串)
    |----------------<contextName>(设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default)
    |----------------<appender>(负责写日志的组件)
            |------------------<encoder>(对日志进行格式化)
                    |----------------<pattern>(指定日志格式化格式)           
            |------------------<target>(输出为System.out或System.err)
            |------------------<file>(被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。)
            |------------------<append>(是否增加到文件末尾)
            |------------------<prudent>(如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false)
            |------------------<filter>(日志输出过滤器)
                |-----------------<level> (目标日志级别)
                |-----------------<onMatch>(符合输出条件的的日志行为)
                |-----------------<onMismatch>(不符合日志级别的日志输出行为)
            |------------------<rollingPolicy>(当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名)
                |-----------------<fileNamePattern>(被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。)
                |-----------------<maxHistory>(控制保留的归档文件的最大数量,超出数量就删除旧文件)
                |-----------------<maxFileSize>(活动文件的大小,默认值是10MB)
                |-----------------<prudent>(当为true时,不支持FixedWindowRollingPolicy)
                |-----------------<triggeringPolicy >(告知 RollingFileAppender 何时激活滚动)
                |-----------------<encorder>(对记录事件进行格式化)
    |----------------<root>(所有<loger>(的上级)
            |-------------<appender-ref>(标识这个appender将会添加到这个loger)
    |----------------<logger>(用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>)
            |-------------<appender-ref>(标识这个appender将会添加到这个loger)

1.<configuration> 属性讲解

scan="true" //当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true
scanPeriod="60 seconds" //设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug="false" //当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
...........
</configuration>
  1. <property>属性讲解
    name="变量名" //定义变量的名称,引用处可以使用"{变量名}"引用
    value="变量值" //定义变量的值
<configuration  scan="true" scanPeriod="60 seconds" debug="false">
     <property name="log_dir" value="./Logback-Modle/src/main/logs" />
</configuration>

3.<appender>属性及其子节点属性讲解

name="appender名称" //定义appender的名称
class="ch.qos.logback.core.ConsoleAppender" //logback的默认appender实现类

稍微讲解logback的集中appender类所具备的子节点及属性
①ch.qos.logback.core.ConsoleAppender 将日志输出到控制台

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
<!-- ConsoleAppender 控制台输出日志 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                <!-- 设置日志输出格式 -->
                %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n
            </pattern>
        </encoder>
        <!--日志输出方式,默认为System.out-->
        <target>System.err</target>
    </appender>
</configuration>

②ch.qos.logback.core.FileAppender 将日志输出到日志文件

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
   <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <!--指定日志输出文件-->
            <file>testFile.log</file> 
            <!--指定是否追尾-->
            <append>true</append> 
             <!--指定日志输出格式-->
            <encoder> 
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>                                 
            </encoder> 
   </appender> 
</configuration>

③ch.qos.logback.core.RollingFileAppender 将日志滚动输出到日志文件中

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
    <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志输出位置 可相对、和绝对路径 -->
            <fileNamePattern>${log_dir}/warn/%d{yyyy-MM-dd}/warn-log.log</fileNamePattern>
            <!--日志留存天数-->
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 这是活动文件的大小,默认值是10MB -->
                <maxFileSize>1KB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>
</configuration>

④<logger>及其子节点和属性讲解

name="com.data.test" 用来指定受此loger约束的某一个包或者具体的某一个类

level="debug" 用来设置打印级别,大小写无关

addtivity=true 是否向上级loger传递打印信息。默认是true

子节点<appender-ref> 指定受logger约定的appender

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
   <!--如无限定level,将自动继承父级logger,即rootLogger-->
    <logger name="java" additivity="false" />
    <logger name="java.lang" level="warn">
        <appender-ref ref="STDOUT" />
    </logger>
</configuration>

⑤<root> 讲解

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
   <!-- root级别 DEBUG -->
    <root>
        <!-- 打印debug级别日志及以上级别日志 -->
        <level value="debug" />
        <!--指定使用的appender-->
        <appender-ref ref="console" />
        <appender-ref ref="haha" />
        <appender-ref ref="test" />
    </root>
</configuration>

四、Filter 等级过滤器

<filter>是<appender>的一个子节点,表示在当前给到的日志级别下再进行一次过滤,最基本的Filter有ch.qos.logback.classic.filter.LevelFilter和ch.qos.logback.classic.filter.ThresholdFilter

我们看一下LevelFilter,其过滤策略是针对指定级别的日志是否显示,有三个子节点,

<level> 指定当前过滤的目标级别

<onMath> 指定当日志级别符合当前目标级别时的行为,有值ACCEPT接收,和DENY拒绝输出

<onMismatch> 指定当日志级别不符合当前目标级别时的行为,值同上

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录ERROR级别的日志 -->
        <!-- 如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置过滤级别 -->
            <level>ERROR</level>
            <!-- 用于配置符合过滤条件的操作 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            .....
        </rollingPolicy>
    </appender>
</configuration>

我们再看一下 ThresholdFilter ,其过滤策略为针对指定级别以下的日志全部不显示,配置如下:

<configuration  scan="true" scanPeriod="60 seconds" debug="false">
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!-- 设置过滤级别 -->
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            .....
        </rollingPolicy>
    </appender>
</configuration>

五、进阶Demo

综合案例如下:
使用网上现成可用的配置logback-test.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 级别从高到低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志 -->
<!-- scan 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 -->
<!-- scanPeriod 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 动态日志级别 -->
    <jmxConfigurator />
    <!-- 定义日志文件 输出位置 -->
    <property name="log_dir" value="./Logback-Modle/src/main/logs" />
    <!-- 日志最大的历史 30天 -->
    <property name="maxHistory" value="30" />

    <!-- ConsoleAppender 控制台输出日志 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                <!-- 设置日志输出格式 -->
                %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n
            </pattern>
        </encoder>
        <target>System.err</target>
    </appender>

    <!-- ERROR级别日志 -->
    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender -->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录ERROR级别的日志 -->
        <!-- 如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置过滤级别 -->
            <level>ERROR</level>
            <!-- 用于配置符合过滤条件的操作 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${log_dir}/error/error-log.log</file>
        <!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志输出位置 可相对、和绝对路径 -->
            <fileNamePattern>
                ${log_dir}/error/error-log.%d.%i.log
            </fileNamePattern>
            <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6, 则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除 -->
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
                <maxFileSize>1KB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>
                <!-- 设置日志输出格式 -->
                %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n
            </pattern>
        </encoder>
    </appender>

    <!-- WARN级别日志 appender -->
    <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录WARN级别的日志 -->
        <!-- 果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置过滤级别 -->
            <level>WARN</level>
            <!-- 用于配置符合过滤条件的操作 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志输出位置 可相对、和绝对路径 -->
            <fileNamePattern>${log_dir}/warn/%d{yyyy-MM-dd}/warn-log.log</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- INFO级别日志 appender -->
    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/info/%d{yyyy-MM-dd}/info-log.log</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- DEBUG级别日志 appender -->
    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/debug/%d{yyyy-MM-dd}/debug-log.log</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- TRACE级别日志 appender -->
    <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/trace/%d{yyyy-MM-dd}/trace-log.log</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- root级别 DEBUG -->
    <root>
        <!-- 打印debug级别日志及以上级别日志 -->
        <level value="debug" />
        <!-- 控制台输出 -->
        <appender-ref ref="console" />
        <!-- 文件输出 -->
        <appender-ref ref="ERROR" />
        <appender-ref ref="INFO" />
        <appender-ref ref="WARN" />
        <appender-ref ref="DEBUG" />
        <appender-ref ref="TRACE" />
    </root>
</configuration>

程序代码:

package SimpleTest;

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

/**
 * 简单日志获取
 */
public class SimpleTest {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(SimpleTest.class);

        logger.info("info.....");
        logger.debug("debug...");
        logger.error("error...");
        logger.warn("warn...");
    }
}

输出结果如下:

控制台输出

2019-02-04 16:48:43.703 INFO  SimpleTest.SimpleTest - info.....
2019-02-04 16:48:43.703 DEBUG SimpleTest.SimpleTest - debug...
2019-02-04 16:48:43.703 ERROR SimpleTest.SimpleTest - error...
2019-02-04 16:48:43.703 WARN  SimpleTest.SimpleTest - warn...

日志文件输出
2019-02-04 14:39:04.496 DEBUG SimpleTest.SimpleTest - debug...

输出日志目录:


log目录结构

总结

1.logback完整实现了slf4j的接口,所以依赖于slf4j实现功能

2.logback的配置需要配置输出源appender,输出日志的logger和根logger root

3.logback通过filter实现对特定日志级别的过滤输出

参考文档

【1】logback官方文档

【2】http://www.importnew.com/28541.html

【3】https://www.cnblogs.com/warking/p/5710303.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272

推荐阅读更多精彩内容