java-日志

日志级别:

        Log4j 中日志级别有8种: 从低至高依次为 ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF。

        Log4j 建议只使用四个级别,优先级从低至高分别是DEBUG、INFO、WARN、ERROR。

 日志框架演化:

  • Log4j

Apache Log4j 是一种基于 Java 的日志记录工具,它是 Apache 软件基金会的一个项目。在 jdk1.3 之前,还没有现成的日志框架,Java 工程师只能使用原始的 System.out.println(), System.err.println() 或者 e.printStackTrace()。通过把 debug 日志写到 StdOut 流,错误日志写到 ErrOut 流,以此记录应用程序的运行状态。这种原始的日志记录方式缺陷明显,不仅无法实现定制化,而且日志的输出粒度不够细。鉴于此,1999 年,大牛 Ceki Gülcü 创建了 Log4j 项目,并几乎成为了 Java 日志框架的实际标准。

  • JUL

Log4j 作为 Apache 基金会的一员,Apache 希望将 Log4j 引入 jdk,不过被 sun 公司拒绝了。随后,sun 模仿 Log4j,在 jdk1.4 中引入了 JUL(java.util.logging)。

  • Commons Logging

为了解耦日志接口与实现,2002 年 Apache 推出了 JCL(Jakarta Commons Logging),也就是 Commons Logging。Commons Logging 定义了一套日志接口,具体实现则由 Log4j 或 JUL 来完成。Commons Logging 基于动态绑定来实现日志的记录,在使用时只需要用它定义的接口编码即可,程序运行时会使用 ClassLoader 寻找和载入底层的日志库,因此可以自由选择由 log4j 或 JUL 来实现日志功能。

  • Slf4j&Logback

大牛 Ceki Gülcü 与 Apache 基金会关于 Commons-Logging 制定的标准存在分歧,后来,Ceki Gülcü 离开 Apache 并先后创建了 Slf4j 和 Logback 两个项目。Slf4j 是一个日志门面,只提供接口,可以支持 Logback、JUL、log4j 等日志实现,Logback 提供具体的实现,它相较于 log4j 有更快的执行速度和更完善的功能。

  • Log4j 2

为了维护在 Java 日志江湖的地位,防止 JCL、Log4j 被 Slf4j、Logback 组合取代 ,2014 年 Apache 推出了 Log4j 2。Log4j 2 与 log4j 不兼容,经过大量深度优化,其性能显著提升。

日志门面与日志系统:

        slf4j(全称是Simple Loging Facade For Java)是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就好像我们经常使用的JDBC一样,只是一种规则而已。因此单独的slf4j是不能工作的,它必须搭配其他具体的日志实现方案,比如apache的org.apache.log4j.Logger,jdk自带的java.util.logging.Logger等等。
        commons-logging和slf4j一样都是日志的接口, log4j,logback等等才是日志的真正实现。当我们调用接口时,接口的工厂会自动寻找恰当的实现,返回一个实现的实例给我服务。这些过程都是透明化的,用户不需要进行任何操作!

日志框架的使用选择:

Logback 和 Slf4j 是同一个作者,其兼容性不言而喻。

依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

        spring-boot 集成 logback,spring-boot-starter-logging依赖包括日志门面和日志系统,并且做了连接适配。额外只需要在yml或properties配置文件中添加如下配置:

logging:
  config: classpath:logback-cbssproxy.xml #日志配置文件地址
  #config: http://${nacos.config.server-addr}/nacos/v1/cs/configs?group=cbss&tenant=${nacos.config.namespace}&dataId=logback-spring.xml
  level:
    com.eastcom: debug #指定包路径下的日志输出级别,默认info,排查问题时可改为debug
    org.springframework.cloud.bus: debug #指定特定类的日志级别,方便查看问题

logback.xml文件具体配置示例:

示例1:

<?xml version="1.0" encoding="UTF-8"?>
<!-- debug:打印logback内部日志信息,实时查看logback的运行状态,默认为false -->
<!-- scan:配置文件如果发生改变,是否被重新加载,默认为true。 -->
<!-- scanPeriod:设置检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒,默认的时间间隔为1分钟,默认为true。 -->
<configuration debug="false" scan="false" scanPeriod="30 seconds">

    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径,可以使用系统变量 此处设置为程序运行目录-->
    <!-- <property name="LOG_HOME" value="${app.home}/log" /> -->
    <property name="LOG_HOME" value="logdir"/>

    <!-- 时间戳定义,timeReference:使用日志产生日期为时间基准 -->
    <timestamp key="byDay" datePattern="yyyy-MM-dd" timeReference="contextBirth"/>

    <springProperty scope="context" name="springApplicationName" source="spring.application.name"/>
    <contextName>${springApplicationName}</contextName>

    <!-- 引入springboot的控制台日志输出格式-彩色便于分析 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符 -->
    <property name="CONSOLE_LOG_PATTERN_1"
              value="%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} %-5level [%thread] [%X{traceId}] %logger{36} - %msg%n"/>
    <property name="CONSOLE_LOG_PATTERN_2"
              value="%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} [%15.15thread] [%X{traceId}] %clr(%-40.40logger{39}){cyan} %clr(:){faint} %msg%n"/>
    <property name="CONSOLE_LOG_PATTERN_3"
              value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <!-- 控制台输出,生产环境将请stdout去掉 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN_2}</pattern>
        </encoder>
    </appender>


    <!-- file for all -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志输出文件 -->
        <file>${LOG_HOME}/logback_${springApplicationName}_${byDay}.log</file>
        <!-- 追加日志到原文件结尾 -->
        <append>true</append>
        <!-- timebasedrollingpolicy:演示时间和大小为基础的日志文件归档 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定。 -->
            <!--可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。 -->
            <!--而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
            <!-- 文件滚动日期格式:每天:.YYYY-MM-dd(默认);每星期:.YYYY-ww;每月:.YYYY-MM -->
            <!-- 每隔半天:.YYYY-MM-dd-a;每小时:.YYYY-MM-dd-HH;每分钟:.YYYY-MM-dd-HH-mm -->
            <fileNamePattern>${LOG_HOME}/logback_${springApplicationName}_%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 控制归档文件的最大数量的保存,删除旧的文件,默认单位天数 -->
            <maxHistory>7</maxHistory>
            <!-- 设置当前日志的文件的大小,决定日志翻滚 -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <!-- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> -->
            <pattern>%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>


    <!--输出到logstash的appender-->
    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <!--可以访问的logstash日志收集端口-->
        <destination>10.8.29.171:9252</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
            <!-- 在elasticsearch的index中追加applicationName字段 -->
            <customFields>{"applicationName":"ahsa"}</customFields>
        </encoder>
    </appender>
        

    <!-- 输出至 logStash 的意愿日志-->
    <logger name="logStashWishLog" level="INFO" additivity="true">     
        <appender-ref ref="LOGSTASH"/>    
    </logger>
    <!-- 输出至 logStash 的策略日志-->
    <logger name="logStashStrategyLog" level="INFO" additivity="true">     
        <appender-ref ref="LOGSTASH"/>    
    </logger>



    <logger name="com.eastcom.*" level="INFO" additivity="false">
        <appender-ref ref="FILE"/>
    </logger>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

示例2:

<?xml version="1.0" encoding="UTF-8"?>
  
<!-- debug:打印logback内部日志信息,实时查看logback的运行状态,默认为false -->  
<!-- scan:配置文件如果发生改变,是否被重新加载,默认为true。 -->  
<!-- scanPeriod:设置检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒,默认的时间间隔为1分钟,默认为true。 -->  

<configuration debug="false" scan="false" scanPeriod="30 seconds">

    <contextName>Application</contextName>
	
    <!-- 时间戳定义,timeReference:使用日志产生日期为时间基准 -->  
    <timestamp key="byDay" datePattern="yyyy-MM-dd" timeReference="contextBirth" />  
  
    <!-- 定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径,可以使用系统变量 -->  
    <!-- <property name="LOG_HOME" value="${app.home}/log" /> --> 
	<!-- 日志会放在项目根路径下的 logdir 目录 -->  	
    <property name="LOG_HOME" value="logdir" />  
  
    <!-- 控制台输出,生产环境将请stdout去掉 -->  
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">  
        <encoder>  
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符 -->  
            <pattern>
				%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n  
            </pattern>  
        </encoder>  
    </appender>  
  
  
    <!-- file for all 打印所有日志 -->  
    <appender name="FILE-ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">  
        <!-- 日志输出文件 -->  
        <file>${LOG_HOME}/LoggingBack-${byDay}.log</file>  
        <!-- 追加日志到原文件结尾 -->  
        <append>true</append>  
        <!-- timebasedrollingpolicy:演示时间和大小为基础的日志文件归档 -->  
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定。 -->  
            <!--可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。 -->  
            <!--而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->  
            <!-- 文件滚动日期格式:每天:.YYYY-MM-dd(默认);每星期:.YYYY-ww;每月:.YYYY-MM -->  
            <!-- 每隔半天:.YYYY-MM-dd-a;每小时:.YYYY-MM-dd-HH;每分钟:.YYYY-MM-dd-HH-mm -->  
            <fileNamePattern>${LOG_HOME}/log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>  
            <!-- 控制归档文件的最大数量的保存,删除旧的文件,默认单位天数 -->  
            <maxHistory>7</maxHistory>  
            <!-- 设置当前日志的文件的大小,决定日志翻滚 -->  
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">  
                <!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->  
                <maxFileSize>10MB</maxFileSize>  
            </timeBasedFileNamingAndTriggeringPolicy>  
        </rollingPolicy>  
        <encoder>  
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n  
            </pattern>  
        </encoder>  
    </appender>  
    
	
    <!-- file for info 只打印 info 级别的日志(通过过滤器实现)-->
    <appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">  
        <!-- 这里添加一个过滤器 -->  
        <file>${LOG_HOME}/LoggingBack-info.log</file>  
        <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_HOME}/LOG-INFO-%d{yyyy-MM-dd}.%i.log</fileNamePattern>  
            <maxHistory>7</maxHistory>  
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">  
                <maxFileSize>10MB</maxFileSize>  
            </timeBasedFileNamingAndTriggeringPolicy>  
        </rollingPolicy>  
        <encoder>  
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n  
            </pattern>  
        </encoder>  
    </appender>  
  
  
    <!-- file for error 只打印 error 级别的日志-->
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">  
        <file>${LOG_HOME}/LoggingBack-error.log</file>  
        <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">  
            <fileNamePattern>${LOG_HOME}/LOG-ERROR-%d{yyyy-MM-dd}.%i.log</fileNamePattern>  
            <maxHistory>7</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">  
                <maxFileSize>10MB</maxFileSize>  
            </timeBasedFileNamingAndTriggeringPolicy>  
        </rollingPolicy>  
        <encoder>  
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n  
            </pattern>  
        </encoder>  
    </appender>


    <!--输出到logstash的appender-->
    <appender name="LOGSTASH"         class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <!--可以访问的logstash日志收集端口-->
        <destination>10.8.29.171:9252</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
            <!-- 在elasticsearch的index中追加applicationName字段 -->
            <customFields>{"applicationName":"ahsa"}</customFields>
        </encoder>
    </appender>

    <!-- 输出至 logStash 的意愿日志-->
    <logger name="logStashWishLog" level="INFO" additivity="true">     
        <appender-ref ref="LOGSTASH"/>    
    </logger>
    <!-- 输出至 logStash 的策略日志-->
    <logger name="logStashStrategyLog" level="INFO" additivity="true">     
        <appender-ref ref="LOGSTASH"/>    
    </logger>




    <!-- 
		业务日志级别:项目com.eastcom包下的所有日志输出到以下appender。
		additivity 为false代表日志在以下 appender 不会重复	
	-->  
	
    <!-- <logger name="com.eastcom" level="INFO" additivity="false">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="FILE-INFO" />  
        <appender-ref ref="FILE-ERROR" />  
    </logger>   -->
   
    <!-- additivity 为false 表示不向 root 标签传递日志打印信息,即日志不会重复打印。
    为true,则 root 标签下的 appender 都会重复打印该日志-->
    <logger name="playLog" level="INFO" additivity="true">
        <appender-ref ref="playLog" />
    </logger>

    <!-- root,所有logger标签的父类,若 logger标签中的additivity=false,则日志不重复输出,以子类为准。 -->
    <root level="INFO">  
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="FILE-INFO" />  
        <appender-ref ref="FILE-ERROR" />  
    </root>  
   
</configuration>

示例3:(最新)

TimeBasedRollingPolicy 和 SizeAndTimeBasedFNATP 合并为 SizeAndTimeBasedRollingPolicy,SizeAndTimeBasedFNATP 已弃用。

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志输出文件 -->
        <file>${LOG_HOME}/logback_${springApplicationName}_${byDay}.log</file>
        <!-- 追加日志到原文件结尾 -->
        <append>true</append>
        <!-- timebasedrollingpolicy:演示时间和大小为基础的日志文件归档 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定。 -->
            <!--可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。 -->
            <!--而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
            <!-- 文件滚动日期格式:每天:.YYYY-MM-dd(默认);每星期:.YYYY-ww;每月:.YYYY-MM -->
            <!-- 每隔半天:.YYYY-MM-dd-a;每小时:.YYYY-MM-dd-HH;每分钟:.YYYY-MM-dd-HH-mm -->
            <fileNamePattern>${LOG_HOME}/logback_${springApplicationName}_%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 控制归档文件的最大数量的保存,删除旧的文件,默认单位天数 -->
            <maxHistory>7</maxHistory>
            <!-- 设置当前日志的文件的大小,决定日志翻滚 -->
             <!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>
        <encoder>
            <!-- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> -->
            <pattern>%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

LoggerFactory.getLogger("name") 和  LoggerFactory.getLogger(类.class) 的区别: 

        使用LoggerFactory.getLogger("name") 获取日志,需要在 name属性中填写需要获取的日志的名称。

        注意:若logback.xml文件中未配置appender和logger,获者只添加了appender,而没有该名称的logger标签,则日志未指定输出目的地,按root指定输出。

若只添加了logger,并且里面指定了未添加的appender,则启动时会报找不到appender的错误。

<logger name="name" level="INFO" additivity="true">
    <appender-ref ref="LOGSTASH"/>
</logger>

        使用LoggerFactory.getLogger(类.class) 获取日志,需要在 name属性中填写该类的全限定名称。注意:若该类的包路径发生改变时,对应的日志也要做改动。

<logger name="com.ryxx.User" level="INFO" additivity="true">
    <appender-ref ref="LOGSTASH"/>
</logger>

        

appender标签:

作用:

        ①控制打印日志的地方、②打印日志的输出格式。

root标签:

        是所有logger的祖先元素,所有的logger都继承与root元素,相当于java中的object对象。

        每一个logger标签都可以指定一个级别(TRACE,DEBUG,INFO,WARN,ERROR五种,他们是在 ch.qos.logback.classic.Level class中定义的)。

         如果一个指定的logger标签没有指定规则的话,就会继承离他最近的祖先的级别。

        因为所有的logger都是继承root标签,所以为了确保所有的logger都有一个级别,root标签有一个默认的级别,默认值为DEBUG。

logger标签:

概述:

        会继承距离自己最近的 root 标签。

        任何一个logger对象都有五种级别,所以就对应有五种打印日志方法:logger.info(字符串),logger.debug(字符串)。

        如果使用logger.info(字符串)的话,那么logger打印级别就是INFO。

        有效级别是最低级别,高于有效级别的信息都能打印出来,低于有效级别都不能打印出来。

结构:

        属性:
            name:必填属性,含义是指定包名或者类名的全路径(包含包名)。若为类表示范围只是该类,若为包名,则表示该包以下的全部类。
            level:选填属性,可以是8种日志级别中任何一个,并且不区分大小写。如果没有写的话就会继承离他最近的祖先的级别。
            additivity:选填属性,是否向上级loger传递打印信息。默认为true(累加)。若不需要将该处的日志累加到 root ,设置为false。
        子标签:
            appender-ref(一个或多个):
                属性:
                    ref:必填属性,值是被引用的appender的name。

示例:

   
    <!-- additivity 为false 表示不向 root 标签传递日志打印信息,即日志不会重复打印。
    为true,则 root 标签下的 appender 都会重复打印该日志-->
    <logger name="playLog" level="INFO" additivity="true">
        <appender-ref ref="playLog" />
    </logger>

    <!-- root,所有logger标签的父类,若 logger标签中的additivity=false,则日志不重复输出,以子类为准。 -->
    <root level="INFO">  
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="FILE-INFO" />  
        <appender-ref ref="FILE-ERROR" />  
    </root>  

参考:

全面梳理 Java 日志框架 - 知乎 (zhihu.com)

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值