深入理解 SpringBoot 日志框架:从入门到高级应用——(二)Logback配置文件详解

Configuration 配置项

logback.xml 是一个用于配置日志记录器的文件,它是 Logback 日志框架的配置文件。通过 logback.xml 文件,可以配置日志的输出格式、日志级别、日志文件路径等信息。在 Java 应用程序中,Logback 是一个常用的日志框架,它可以帮助开发人员记录应用程序中的各种事件和错误信息,以便于问题的排查和调试。logback.xml 日志配置文件中的配置项包括:

配置项描述
<configuration>根标签,所有的配置都在该标签内进行
<appender>定义日志输出的目的地,可以配置控制台输出、文件输出等
<logger>定义日志记录器,指定日志输出的级别、输出目的地等
<root>根日志记录器,即全局日志配置,用于定义默认的日志输出级别和输出目的地
<pattern>指定日志输出的格式,可以自定义输出格式

Pattern 日志格式

指定日志输出的格式,样例如下:

<pattern>%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n</pattern>

常用占位符如下:

占位符描述
%d表示输出到毫秒的时间,例如%d{yyyy-MM-dd HH:mm:ss.SSS}
%-5level输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
%n输出换行符
%thread输出日志的线程名称,例如:main
%logger输出日志记录器名称,例如:com.example.MyClass
%msg输出日志的消息内容
%X输出 MDC(Mapped Diagnostic Context)中的键值对,例如:%X{user}
%class输出日志的类名,例如:com.example.MyClass
%method输出日志的方法名,例如:doSomething
%file输出日志的文件名,例如:MyClass.java
%line例如:10
%color()颜色:%black、%shired、%green、%yellow、%blue、%magenta、%cyan、%white、%gray、%boldRed、%boldGreen、%boldYellow、%boldBlue、%boldMagenta、%boldCyan、%boldWhite、%highlight

以上是一些常用的占位符,可以根据需要自行组合使用。

Appender 附加器

Logback 的Appender 是用来定义日志输出的目的地。它可以向控制台、文件、数据库等不同的目的地输出日志。例如,ConsoleAppender 用来输出日志到控制台,FileAppender 用来输出日志到一个文件中,SMTPAppender 用来将日志以电子邮件的形式发送出去,等等。

在 Logback 中,可以使用多个 Appender 来输出同一条日志到不同的目的地,也可以为每个 Logger 都设置不同的 Appender,实现不同的输出效果。Appender 是 Logback 中比较重要的一个组件,通过灵活的配置,可以实现各种复杂的日志输出方式。

附加器描述
ConsoleAppender将日志输出到控制台
FileAppender将日志输出到指定文件
RollingFileAppender将日志输出到滚动的文件中,可以设置滚动的方式和文件大小
SocketAppender将日志输出到远程的日志服务器
SMTPAppender将日志以邮件的形式发送给指定的收件人
DBAppender将日志输出到数据库中
SyslogAppender将日志输出到 Syslog 服务器

ConsoleAppender

Logback的ConsoleAppender用于将日志输出到控制台。它可以配置在logback.xml 或 logback-spring.xml 文件中。以下是 ConsoleAppender 的配置示例:

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  <!-- 日志输出格式 -->
  <encoder>
    <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
  </encoder>
</appender>

解析:

  • name: Appender 的名称,可自定义。
  • class: 指定 Appender 的类,这里是 ConsoleAppender。
  • encoder: Encoder 用于将日志事件转换为输出字符串的格式,可以根据需要自定义。
  • pattern: 输出格式,这里使用了时间戳、日志级别、Logger 名称、消息文本等信息。

FileAppender

Logback 的 FileAppender 可以将日志输出到文件中。以下是一个简单的 FileAppender 的配置示例:

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <!-- 日志输出路径 -->
  <file>/logs/file.log</file>
  <!-- 日志输出格式 -->
  <encoder>
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  </encoder>
</appender>

在上面的配置中,我们创建了一个名为 FILE 的 FileAppender。

  • <file>:元素指定了日志文件的路径和名称。
  • <encoder>:元素定义了将日志转换为字符串的方式。在这个示例中,我们使用了一个简单的模式:时间戳、线程名、日志级别、日志记录器名称和日志消息。

RollingFileAppender

RollingFileAppender 是 Logback 中常用的日志输出组件之一,可以按照一定的条件对日志文件进行滚动输出。以下是 RollingFileAppender 的配置示例:

<!--RollingFileAppender 文件滚动输出-->
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">

  <!-- 日志输出的路径 -->
  <file>logs/myapp.log</file>

  <!-- 基于时间和文件大小的滚动策略 -->
  <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <!-- 基于时间创建日志文件 -->
    <fileNamePattern>logs/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <!-- 最大文件大小,超过该大小则进行日志滚动 -->
    <maxFileSize>10MB</maxFileSize>
    <!-- 最大的历史日志文件数量 -->
    <maxHistory>7</maxHistory>
  </rollingPolicy>

  <!-- 基于时间的滚动策略 -->
  <!--        
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>logs/%d{yyyy-MM-dd}.log</fileNamePattern>
    <maxHistory>7</maxHistory>
  </rollingPolicy>
  -->

  <!-- 日志输出格式 -->
  <encoder>
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n</pattern>
  </encoder>
</appender>

其中,上述配置的含义如下:

配置项说明
nameAppender 的名称,必须唯一
classAppender 的类名,对应的类名为 ch.qos.logback.core.rolling.RollingFileAppender
file默认日志输出的路径
rollingPolicy滚动策略,控制日志文件的滚动方式
encoder控制日志往文件里的输出格式

Logback 提供的控制日志文件的滚动方式如下:

滚动策略属性说明
TimeBasedRollingPolicy
  • fileNamePattern:日志文件名的格式,其中%d{yyyy-MM-dd}表示日期,%i表示文件序号,触发时间条件时日志会转存到下一个文件,%i的值就会增加)
  • maxHistory:最大的历史日志文件数量,超过该数量时会删除旧的日志文件
  • totalSizeCap:备份日志文件的总大小上限。当备份文件的总大小达到此上限时,最早的备份文件将被删除
按时间来滚动日志文件,可以指定时间间隔、文件名模式等
SizeAndTimeBasedRollingPolicy
  • fileNamePattern:日志文件名的格式,其中%d{yyyy-MM-dd}表示日期,%i表示文件序号(当日志文件大小超过maxFileSize时,转存到下一个文件,%i的值就会增加)
  • maxFileSize:最大的日志文件大小,达到该大小时进行滚动
  • maxHistory:最大的历史日志文件数量,超过该数量时会删除旧的日志文件
  • totalSizeCap:备份日志文件的总大小上限。当备份文件的总大小达到此上限时,最早的备份文件将被删除
基于时间和文件大小来滚动日志文件,可以指定时间间隔、文件大小等

SMTPAppender

Logback 的 SMTPAppender 允许日志信息以电子邮件形式发送。以下是 SMTPAppender 的配置示例:

<!-- name的值是变量的名称,source为 yaml 配置文件中的变量。定义后,可以使“${}”来使用变量。 -->
<springProperty scope="context" name="user.host" source="spring.mail.host" defaultValue="smtp.qq.com"/>
<springProperty scope="context" name="user.email" source="spring.mail.username"/>
<springProperty scope="context" name="user.email.password" source="spring.mail.password"/>

<!--发QQ邮件-->
<appender name="QQEMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
  <!-- SMTP server的地址 -->
  <smtpHost>${user.host}</smtpHost>
  <!-- SMTP server的端口地址 -->
  <smtpPort>465</smtpPort>
  <!--发件人账号-->
  <username>${user.email}</username>
  <!--发件人密码-->
  <password>${user.email.password}</password>
  <!--SSL连接到日志服务器,默认值:false-->
  <SSL>true</SSL>
  <!--异步发送-->
  <asynchronousSending>true</asynchronousSending>
  <!--收件人账号,多个用逗号隔开-->
  <to>${user.email}</to>
  <!-- 发件人名称 -->
  <from>${user.email}</from>
  <!-- emial的标题 -->
  <subject>【Error】:%logger{0}</subject>
  <!-- 编码 -->
  <charsetEncoding>UTF-8</charsetEncoding>
  
  <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
    <!-- 每个电子邮件只发送一个日志条目 -->
    <bufferSize>10</bufferSize>
  </cyclicBufferTracker>
  
  <!--HTML展示-->
  <layout class="ch.qos.logback.classic.html.HTMLLayout" />
  <!--文本展示-->
  <!--<layout class="ch.qos.logback.classic.layout.TTLLLayout"/>-->
  
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <!--错误级别(只会提示大于该级别的错误)-->
    <level>ERROR</level>
  </filter>
</appender>

解释一下 SMTPAppender 的配置参数:

配置项说明
nameSMTPAppender的名称
classSMTPAppender的类名
smtpHostSMTP服务器主机名
smtpPortSMTP服务器端口号
STARTTLS是否启用TLS
usernameSMTP服务器的用户名
passwordSMTP服务器的密码
to接收日志邮件的邮箱地址
from发件人的邮箱地址
subject邮件主题
layout日志输出格式
cyclicBufferTracker防止日志过多,可以设置日志的缓冲大小,超过指定数量时将会自动删除旧的日志
filter日志过滤条件,只有满足条件的日志才会被发送邮件

DBAppender

要使用 Logback 的 DBAppender,需要更改 Logback 的配置文件以引入 DBAppender,并配置连接数据库的相关参数。

以下是一个简单的 Logback 配置文件示例,使用 MySQ 作为数据库:

<!-- 日志输出 MySQL -->
<appender name="MYSQL" class="ch.qos.logback.classic.db.DBAppender">
  <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
    <!--数据库驱动-->
    <driverClass>com.mysql.cj.jdbc.Driver</driverClass>
    <!--数据库 Url-->
    <url>jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8</url>
    <!--数据库用户名-->
    <user>root</user>
    <!--数据库密码-->
    <password>root</password>
  </connectionSource>
</appender>

参数如下:

配置项说明
nameDBAppender 的名称
classDBAppender 的类名
connectionSource存储源
connectionSource.classDriverManagerConnectionSource 类,用于在运行时从 DriverManager 获取数据库连接
connectionSource.driverClass数据库驱动类
connectionSource.url数据库 Url
connectionSource.user数据库用户名
connectionSource.password数据库密码

Logger 日志记录器

Logback 中的 Logger 是一种记录日志的对象,其作用是在程序运行时记录日志信息,它可以将日志记录到文件、控制台或其他目标。Logger 是 Logback中最基本的组件,它把日志信息传递给 Logback 框架。Logger 有多个级别,从高到低分别是TRACE、DEBUG、INFO、WARN和ERROR,表示打印日志的不同程度。使用 Logger 可以对不同级别的日志进行控制和分离,方便开发人员在不同场景下进行调试和查看日志信息。同时,Logger 还可以通过添加 Appender 来控制日志信息的输出目标,比如输出到控制台或文件。

一个简单的样例如下:

<logger name="com.example" level="DEBUG">
  <appender-ref ref="CONSOLE" />
</logger>

Logger 的配置项如下:

配置项说明
name用于唯一标识一个 Logger 实例。通过设置 logger 的 name 属性,可以根据不同的 logger 名称来区分不同的日志记录器,从而实现更细粒度的日志记录
levelLogger 的日志级别,可用的取值包括 TRACE、DEBUG、INFO、WARN、ERROR和OFF
additivity是否继承父Logger的日志输出,默认为 true
appender-ref指定要使用的 Appender,这里是 CONSOLE。多个 Appender 可以用逗号分隔,可以关联多个 Appender

Root 根节点

在 Logback 中,root 是指最高级别的 Logger,它是所有 Logger 的父 Logger。所有未被其他日志记录器匹配的日志事件都会由 root Logger 处理。可以通过设置 root 的日志级别和输出目的地来控制应用程序的整个日志记录。

在配置文件中,可以使用“”标签来定义root Logger的属性和行为。例如,以下是一个配置文件中root Logger的定义:

<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  <root level="info">
    <appender-ref ref="CONSOLE" />
  </root>
  <!--对应 logger-->
  <!--
  <logger name="sys-info" level="info">
    <appender-ref ref="CONSOLE"/>
  </logger>
  -->
</configuration>

Filter 过滤器

过滤器是附加器的一个组件,它用于决定附加器是否输出日志。一个附加器可以包含一个或多个过滤器。每个过滤器部会返回一个枚举值:DENY、NEUTRAL、ACCEPT。附加器根据过滤器返回值判断是否输出日志:

  • DENY:不输出日志
  • ACCEPT:输出日志
  • NEUTRAL:中立,即不决定是否输出日志

Logback 过滤器是一种可插拔的模块,可以在记录日志消息之前或之后以编程的方式更改或过滤消息。Logback 提供了多个预定义过滤器,常用过滤器如下:

过滤器说明
LevelFilter级别过滤器:根据日志级别过滤,只接受指定级别的事件
ThresholdFilter阈值过滤器:根据日志级别过滤,只接受大于等于指定级别的事件
EvaluatorFilter求值过滤器:评估、鉴别日志是否符合指定条件
MarkerFilter根据一个标记来过滤日志,例如根据日志中的异常类型来过滤
TimeFilter根据时间范围来过滤日志
TurboFilter可以在运行时修改Logback的配置,例如动态地修改日志级别
RegexFilter根据一个正则表达式来过滤日志

LevelFilter

LevelFilter是级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据 onMath 和 onMismatch 接收或拒绝日志。以下是一个使用 LevelFilter 过滤器的样例:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
  </encoder>
  <filter class="ch.qos.logback.classic.filter.LevelFilter">
    <level>INFO</level>
    <onMatch>ACCEPT</onMatch>
    <onMismatch>DENY</onMismatch>
  </filter>
</appender>

在此示例中,LevelFilter 过滤器用于仅记录 INFO 级别的消息。LevelFilter 的配置项如下:

配置项说明
level设置过滤级别
onMatch配置符合过滤条件的操作
onMismatch配置不符合过滤条件的操作

ThresholdFilter

ThresholdFilter 是临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回 NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

以下是一个基于logback的ThresholdFilter过滤器的样例:

<!-- 定义一个Logger -->
<logger name="com.example" level="DEBUG" additivity="false">
  <appender-ref ref="stdout" />
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <level>INFO</level>
  </filter>
</logger>

在这个样例中,我们定义了一个基于 ThresholdFilter 的过滤器,它会阻止 com.example 包下面的 DEBUG 级别的日志消息输出到控制台,只允许INFO级别和更高级别的日志消息通过,其他的日志消息将被过滤掉。

使用这个过滤器的方式非常简单。只需要在 Logback 的配置文件中,为指定 Logger 定义一个 ThresholdFilter ,并设置它的阈值级别即可。

EvaluatorFilter

以下是一个基于EvaluatorFilter的示例:

<root level="DEBUG">
  <appender-ref ref="STDOUT" />
  <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">
    <evaluator>
      <expression>return message.contains("error")</expression>
    </evaluator>
    <onMatch>DENY</onMatch>
    <onMismatch>NEUTRAL</onMismatch>
  </filter>
</root>

在这个示例中,EvaluatorFilter 被用作根日志记录器的过滤器。它的 evaluator 子元素定义了一个表达式,该表达式自动返回一个布尔值,表示日志消息中是否包含“error”字符串。如果是,则过滤器的 onMatch 属性指示日志事件被拒绝。如果不是,则 onMismatch 属性指示日志事件将继续向下传递。最终,只有那些不包含“error”字符串的日志消息才会被输出到控制台,其他日志消息将被过滤掉。

Logback-spring.xml 样例

通用配置样例如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 日志存放路径 -->
    <property name="log.path" value="logs"/>
    <!-- 日志输出格式 -->
    <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
    <!-- 日志控制台输出格式 -->
    <property name="log.console.pattern" value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>

    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.console.pattern}</pattern>
        </encoder>
    </appender>

    <!--INFO 文件输出-->
    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径/文件名 -->
        <file>${log.path}/myapp-info.log</file>
        <!-- 基于时间和文件大小的滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 基于时间创建日志文件 -->
            <fileNamePattern>${log.path}/info/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 最大文件大小,超过该大小则进行日志滚动 -->
            <maxFileSize>100MB</maxFileSize>
            <!-- 最大的历史日志文件数量 -->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 系统日志输出 debug-->
    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径/文件名 -->
        <file>${log.path}/myapp-debug.log</file>
        <!-- 基于时间和文件大小的滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 基于时间创建日志文件 -->
            <fileNamePattern>${log.path}/debug/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 最大文件大小,超过该大小则进行日志滚动 -->
            <maxFileSize>100MB</maxFileSize>
            <!-- 最大的历史日志文件数量 -->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>DEBUG</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 系统日志输出 error-->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径/文件名 -->
        <file>${log.path}/myapp-error.log</file>
        <!-- 基于时间和文件大小的滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 基于时间创建日志文件 -->
            <fileNamePattern>${log.path}/error/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 最大文件大小,超过该大小则进行日志滚动 -->
            <maxFileSize>100MB</maxFileSize>
            <!-- 最大的历史日志文件数量 -->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 用户访问日志输出  -->
    <appender name="USER" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件路径/文件名 -->
        <file>${log.path}/myapp-user.log</file>
        <!-- 基于时间和文件大小的滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 基于时间创建日志文件 -->
            <fileNamePattern>${log.path}/user/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 最大文件大小,超过该大小则进行日志滚动 -->
            <maxFileSize>100MB</maxFileSize>
            <!-- 最大的历史日志文件数量 -->
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!--系统操作日志-->
    <root level="info">
        <!-- 全局日志输出级别,info 以上输出到控制台  -->
        <appender-ref ref="CONSOLE"/>
        <!--appender将会添加到logger-->
        <appender-ref ref="INFO"/>
        <appender-ref ref="DEBUG"/>
        <appender-ref ref="ERROR"/>
    </root>

    <!--系统用户操作日志-->
    <logger name="sys-user" level="info">
        <appender-ref ref="USER"/>
    </logger>

</configuration>
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@赵士杰

如果对你有用,可以进行打赏,感

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值