logback 从入门到精通 超详细配置说明
logback简介官网http://logback.qos.chlogback主要由三个模块构成:logback-core,logback-classic及logback-access。logback-core为基础核心,另外两个均依赖它。其中logback-classic实现了简单日志门面SLF4J;logback-access主要作为一个与Servlet容器交互的模块,提供与HTTP访问相关的
logback简介
logback主要由三个模块构成:logback-core,logback-classic及logback-access。
logback-core为基础核心,另外两个均依赖它。其中logback-classic实现了简单日志门面SLF4J;logback-access主要作为一个与Servlet容器交互的模块,提供与HTTP访问相关的一些功能。
logback取代log4j的理由
- 更快的实现:Logback的内核重写了,在一些关键执行路径上性能提升10倍以上。而且logback不仅性能提升了,初始化内存加载也更小了。
- 非常充分的测试:Logback经过了几年,数不清小时的测试。Logback的测试完全不同级别的。
- Logback-classic非常自然实现了SLF4j:Logback-classic实现了SLF4j。在使用SLF4j中,你都感觉不到logback-classic。而且因为logback-classic非常自然地实现了slf4j,所以切换到log4j或者其他,非常容易,只需要提供成另一个jar包就OK,根本不需要去动那些通过SLF4JAPI实现的代码。
- 自动重新加载配置文件,当配置文件修改了,Logback-classic能自动重新加载配置文件。扫描过程快且安全,它并不需要另外创建一个扫描线程。
- 谨慎的模式和非常友好的恢复,在谨慎模式下,多个FileAppender实例跑在多个JVM下,能够安全地写道同一个日志文件。RollingFileAppender会有些限制。Logback的FileAppender和它的子类包括RollingFileAppender能够非常友好地从I/O异常中恢复。
- 配置文件可以处理不同的情况,一个配置文件就可以适应多个环境。
- Filters(过滤器)有些时候,需要诊断一个问题。在log4j,只有降低日志级别,不过这样会打出大量的日志,会影响应用性能。在Logback,你可以继续保持那个日志级别而除掉某种特殊情况,如alice这个用户登录,她的日志将打在DEBUG级别而其他用户可以继续打在WARN级别。要实现这个功能只需加4行XML配置。可以参考MDCFIlter。
- 自动压缩已经打出来的log:RollingFileAppender在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩是个异步过程,所以甚至对于大的日志文件,在压缩过程中应用不会受任何影响。
- 堆栈树带有包版本:Logback在打出堆栈树日志时,会带上包的数据。
- 自动去除旧的日志文件:可以控制已经产生日志文件的最大数量。
自定义logback配置
在SpringBoot中,默认支持四种命名的日志文件
logback-spring.xmllogback.xmllogback-spring.groovylogback.groovy
而Spring Boot官方推荐优先使用带有-spring的文件名配置(如有logback-spring.xml,则不会使用logback.xml)。
若需要对配置文件名进行修改,或者希望把放到其它目录下,可以在application中通过logging.config属性来指定,如logging.config=classpath:config/my-log-config.xml。
logback-spring.xml详解

configuration是根元素,必须的;logger和root可视为同一类,都是日志组件;logger定义日志从哪里(包)获取以及级别;appender配置日志格式、如何过滤、文件处理等。property和contextName元素,分别用来定义变量和应用上下文名称,非必须。
下面是一段简单的配置,大家可以大致看下他的层级结构
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO"/>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
1、根节点 <configuration>
包含3个属性:
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
范例:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!--其他配置省略-->
</configuration>
2、子节点 <contextName>
设置上下文名称,每一个日志组件(logger)都会关联到日志上下文,默认上下文名称是’default’,用于标识应用,如果多个应用输出到同一个地方,就有必要使用%contextName来区别。
3、子节点<property>与<springProperty>
用来定义变量值,它有两个属性name和value,通过定义的值会被插入到logger上下文中,可以使“${}”来使用变量。作用类似于代码中的常量字符串,定义之后公共地方便可以统一使用。如日志文件名称前缀、日志路径、日志输出格式等。
name: 变量的名称value: 的值时变量定义的值
范例
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="LOG_HOME" value="logs/logback" />
<contextName>${LOG_HOME}</contextName>
<!--其他配置省略-->
</configuration>
如果是在Spring或SpringBoot项目当中,想让value值是通过配置文件获取,可使用springProperty来定义。
source:指定的是在application.xml或application.properties配置文件中配置变量name:变量的名称scope:规定设置属性的作用域,该属性有两个值:context 和 local。
local 是设置到 interpretaionContext 的属性map中,局部使用;
context 是设置到 interpretaionContext 中的上下文(是log的上下文),全局可使用
<springProperty scope="context" name="app.name" source="spring.application.name"/>
4、子节点<appender>
负责写日志的组件,它有两个必要属性name和class。
name:appender名称class:指定appender的全限定名
appender的结构如下:
class属性指定要实例化的appender类的完全限定名称。appender类默认有以下几种:
ConsoleAppender:日志输出到控制台,类名ch.qos.logback.core.ConsoleAppender。FileAppender:日志输入到文件,类名ch.qos.logback.core.FileAppender。RollingFileAppender:滚动记录文件,FileAppender的子类,当符合条件(大小、时间),日志进行切分处理。类名:ch.qos.logback.core.rolling.RollingFileAppender。
4.1 ConsoleAppender
把日志输出到控制台,有以下子节点:
<encoder>:对日志进行格式化。(具体参数稍后讲解 )<target>:字符串System.out(默认)或者System.err<filter>:过滤器 (后面具体讲解)
例如:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4.2 FileAppender
把日志添加到文件,有以下子节点:
<file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。<append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。<encoder>:对记录事件进行格式化。(具体参数稍后讲解 )<prudent>:如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false。
例如:把>=DEBUG级别的日志都输出到testFile.log
<configuration>
<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>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
4.2 RollingFileAppender
RollingFileAppender是FileAppender的子类,扩展了FileAppender,具有翻转日志文件的功能。
例如RollingFileAppender可以记录到名为log.txt文件的文件,并且一旦满足某个条件,就将其日志记录目标更改为另一个文件。
有以下子节点:
<file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。<append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。<rollingPolicy>:当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。<triggeringPolicy>:告知 RollingFileAppender 合适激活滚动。
特别注意:
一般情况下,<rollingPolicy> 和 <triggeringPolicy> 都可以用来配置滚动策略,两个节点使用其中一种就行。有一种特殊情况,当使用<rollingPolicy>节点并配置 FixedWindowRollingPolicy滚动策略,就需要<triggeringPolicy>来配合使用,<triggeringPolicy>节点可以配置成SizeBasedTriggeringPolicy滚动策略。
4.2.1 滚动策略介绍(4种)
4.2.1.1 基于时间的滚动策略 TimeBasedRollingPolicy
- class:
ch.qos.logback.core.rolling.TimeBasedRollingPolicy - 基本属性:
fileNamePattern:必要节点,包含文件名及“%d”转换符。“%d”可以包含一个java.text.SimpleDateFormat指定的时间格式,如:%d{yyyy-MM}。如果直接使用 %d,默认格式是 yyyy-MM-dd。<maxHistory>:可选属性,控制保留的归档文件的最大数量,超出数量就删除旧文件。<totalSizeCap>:可选属性,用来控制所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。使用这个属性时还需要设置 maxHistory 属性。而且,maxHistory 将会被作为第一条件,该属性作为第二条件。<cleanHistoryOnStart>:boolean类型,如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为 false。归档文件的删除通常在轮转期间执行。但是,有些应用的存活时间可能等不到轮转触发。对于这种短期应用,可以通过设置该属性为 true,在 appender 启动的时候执行删除操作。
范例:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天轮转 -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保存 30 天的历史记录,最大大小为 3GB -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
4.2.1.2 基于文件大小和时间滚策略 SizeAndTimeBasedRollingPolicy
- class:
ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy - 基本属性:
fileNamePattern:必要节点,%d 之外还有 %i。这两个占位符都是强制要求的,在当前时间还没有到达周期轮转之前,日志文件达到了maxFileSize 指定的大小,会进行归档,递增索引从 0 开始。<maxHistory>:可选属性<totalSizeCap>:可选属性<cleanHistoryOnStart><maxFileSize>:活动文件的大小,默认值是10MB。
范例
<configuration debug="true">
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 基于时间和日志文件大小的轮转策略 -->
<fileNamePattern>logFile-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize><!--单个文件最大为100MB-->
<maxHistory>60</maxHistory><!--保存60天的历史记录-->
<totalSizeCap>3GB</totalSizeCap><!--归档文件总大小3G-->
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
4.2.1.3 基于文件个数的滚动策略 FixedWindowRollingPolicy
- class:
ch.qos.logback.core.rolling.FixedWindowRollingPolicy - 基本属性:
<minIndex>:窗口索引最小值<maxIndex>:窗口索引最大值,当用户指定的窗口过大时,会自动将窗口设置为20。<fileNamePattern>:必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者 没有log%i.log.zip
需要和 <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> 配合使用
范例:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>log.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
4.2.1.4 基于文件大小的触发策略 SizeAndTimeBasedRollingPolicy
- class:
ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy - 基本属性:
<maxFileSize>:活动文件的大小,默认值是10MB。
需要和<rollingPolicy>节点并配置 FixedWindowRollingPolicy滚动策略来搭配使用
4.3 <encoder>日志的输出格式及编码
转换符 作用:
c{length }/lo{length}/log{length}:输出日志的logger名,可有一个整形参数,功能是缩短logger名,设置为0表示只输入logger最右边点符号之后的字符串C{length}/class {length}:输出执行记录请求的调用者的全限定名。参数与上面的一样。尽量避免使用,除非执行速度不造成任何问题。contextName:输出上下文名称。date{pattern}/d{pattern}:输出日期,模式语法与java.text.SimpleDateFormat 兼容。F/file:输出执行记录请求的java源文件名。尽量避免使用,除非执行速度不造成任何问题。L/line:输出执行日志请求的行号。尽量避免使用,除非执行速度不造成任何问题。m/msg/message:输出应用程序提供的信息。M/method:输出执行日志请求的方法名。尽量避免使用,除非执行速度不造成任何问题。n:输出分行符“\n”或者“\r\n”。p/le/level:输出日志级别。r/relative:输出从程序启动到创建日志记录的时间,单位是毫秒。t/thread:输出产生日志的线程名。replace(p ){r, t}:p 为日志内容,r 是正则表达式,将p 中符合r 的内容替换为t 。例如, “%replace(%msg){’\s’, ‘’}”
配色版:%black, %red, %green,%yellow,%blue, %magenta,%cyan, %white, %gray,%boldRed,%boldGreen, %boldYellow, %boldBlue, %boldMagenta,%boldCyan, %boldWhite,%highlight
例如:
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%red(%date{yyyy-MM-dd HH:mm:ss}) %highlight(%-5level) %red([%thread]) %boldMagenta(%logger{50}) %cyan(%msg%n)</pattern>
</encoder>
就会打印出找个效果:
4.4 <filter> 过滤器
下面是几个常用的过滤器:
- LevelFilter: 级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。有以下子节点:
<level>:设置过滤级别<onMatch>:用于配置符合过滤条件的操作<onMismatch>:用于配置不符合过滤条件的操作
<onMatch>和<onMismatch>的属性值说明:
DENY:如果方法返回DENY(拒绝),则跳出过滤链,而该 logging event 也会被抛弃。
NRUTRAL:如果返回NRUTRAL(中立),则继续过滤链中的下一个过滤器。
ACCEPT:如果返回ACCEPT(通过),则跳出过滤链
例如:将过滤器的日志级别配置为INFO,所有INFO级别的日志交给appender处理,非INFO级别的日志,被过滤掉
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
- ThresholdFilter: 临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。
例如:过滤掉所有低于INFO级别的日志。
<configuration>
<appender name="CONSOLE"
class="ch.qos.logback.core.ConsoleAppender">
<!-- 过滤掉 TRACE 和 DEBUG 级别的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
- EvaluatorFilter: 求值过滤器,评估、鉴别日志是否符合指定条件。有以下子节点:
<evaluator>: 鉴别器,常用的鉴别器是JaninoEventEvaluato,也是默认的鉴别器,它以任意的java布尔值表达式作为求值条件,求值条件在配置文件解释过成功被动态编译,布尔值表达式返回true就表示符合过滤条件。evaluator有个子标签<expression>,用于配置求值条件。<onMatch>:用于配置符合过滤条件的操作<onMismatch>:用于配置不符合过滤条件的操作
例如:过滤掉所有日志消息中不包含“billing”字符串的日志。
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator> <!-- 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
<expression>return message.contains("billing");</expression>
</evaluator>
<OnMatch>ACCEPT </OnMatch>
<OnMismatch>DENY</OnMismatch>
</filter>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4.5 <fileNamePattern> 滚动属性介绍
- /log/test.%d :
每天轮转(晚上零点)。由于省略了指定 %d 的日期格式,所以默认为 yyyy-MM-dd。 - /log/%d{yyyy/MM}/test.txt:
每个月开始的时候轮转。 - /log/test.%d{yyyy-ww}.log:
每周的第一天(取决于时区)轮转。 - /log/test%d{yyyy-MM-dd_HH}.log:
每小时轮转。 - /log/test%d{yyyy-MM-dd_HH-mm}.log:
每分钟轮转。 - /log/test%d{yyyy-MM-dd_HH-mm, UTC}.log:
每分钟轮转,时间格式为UTC。
任何斜杆或者反斜杠够会被当作文件夹分隔符。任何必要的文件夹都会在有需要的时候创建。你可以轻松的将日志文件放在单独的文件夹中。
TimeBasedRollingPolicy支持文件自动压缩。如果 fileNamePattern以 .gz 或者 .zip结尾,将会启动这个特性。
例如:/log/test.%d.gz:每天轮转(晚上零点),自动将归档文件压缩成 GZIP 格式。
5、子节点<logger>
logger用来设置某一个类或者某个包的日志输出级别、以及关联的appender。
logger包含三个属性:
name:要输出日志的包名或者类名,比如com.secbro2。必选项。level:设置日志级别,允许一个不区分大小写的字符串值TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF。如果未设置,则logger会向上继承最近一个非空级别。可选项。additivity:是否将日志向上级传递,默认为 true。可选项。
logger通过1个或多个子节点appender-ref来控制日志的输出。
以下的示例对指定包指定appender进行日志控制,由于设置了info级别,additivity为true,而且关联CONSOLE的appender,因此info以上级别的日志会输出到控制台。
同时会把日上传到父级,即root。若root也有配置CONSOLE的输出的话,会在控制台输出两次。additivity为false,则不会。
<logger name="com.secbro2" level="info" additivity="true">
<appender-ref ref="CONSOLE" />
</logger>
6、子节点<root>
它也是<logger>元素,但是它是根logger,是所有<logger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。
包含一个属性:
level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,不能设置为INHERITED或者同义词NULL。 默认是DEBUG。
例如:
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE-ROOT"/>
</root>
7、demo
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="LOG_HOME" value="logs/logback" />
<property name="CONSOLE_LOG_PATTERN"
value="%red(%date{yyyy-MM-dd HH:mm:ss}) %highlight(%-5level) %red([%thread]) %boldMagenta(%logger{50}) %cyan(%msg%n)"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender" additivity="false">
<file>${LOG_HOME}/logback.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--每个文件限制 50MB,每日滚动,最多30个文件,归档文件限制5G-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<MaxFileSize>50MB</MaxFileSize>
<fileNamePattern>${LOG_HOME}/logback.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</triggeringPolicy>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE-ROOT" class="ch.qos.logback.core.rolling.RollingFileAppender" additivity="false">
<file>${LOG_HOME}/logback-root.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
</filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--每个文件限制 50MB,每日滚动,最多30个文件,归档文件限制5G-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<MaxFileSize>50MB</MaxFileSize>
<fileNamePattern>${LOG_HOME}/logback-root.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy >
</appender>
<logger name="com.cx.learn" level="warn" additivity="false">
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT" />
</logger>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE-ROOT"/>
</root>
</configuration>
更多推荐

所有评论(0)