Python—12.1、logging模块

logging模块

参考资料:https://docs.python.org/zh-cn/3/howto/logging.html#logging-basic-tutorial

logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等。

  • 日志级别
级别levelno场景
DEBUG10程序调试详细信息
INFO20按照正常预期执行
WARNING30告警信息
ERROR40程序部分模块运行错误
CRITICAL50整体严重错误,无法继续执行

日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的;

  • 日志要素
    1)发生事件
    2)发生位置
    3)日志级别
    4)事件内容

命令行方式设置日志级别:

----log=INFO

logging模块的2种日志方式
A:通过 logging提供的模块级别的函数
B:使用Logging日志系统的四大组件

其实,logging所提供的模块级别的日志记录函数也是对logging日志系统相关类的封装。

一、logging模块函数

  • logging.debug(msg, *args, **kwargs)

  • logging.info(msg, *args, **kwargs)

  • logging.warning(msg, *args, **kwargs)

  • logging.error(msg, *args, **kwargs)

  • logging.critical(msg, *args, **kwargs)

  • logging.log(level, *args, **kwargs)

  • logging.basicConfig(**kwargs)

  • 简单实现

# coding=utf-8
"""
@DevTool  : PyCharm
@Author   : xxx
@DateTime : 2022/3/29 9:56
@FileName : test_09.py
"""

import logging

logging.debug("there is debug message!")
logging.info("there is info message!")
logging.warning("there is warning message!")
logging.error("there is error message!")
logging.critical("there is critical message!")

输出结果

D:\SoftWare\Python\python.exe E:/PythonProject/FileOperTest/test_09.py
WARNING:root:there is warning message!
ERROR:root:there is error message!
CRITICAL:root:there is critical message!

Process finished with exit code 0

备注:默认情况下 Python的logging模块只输出日志级别 大于等于 warning的信息;

在代码开始位置新增如下代码:

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s: %(message)s", datefmt = "%Y-%m-%d %H:%M:%S")

输出结果:

D:\SoftWare\Python\python.exe E:/PythonProject/FileOperTest/test_09.py
2022-04-08 16:01:41 DEBUG: there is debug message!
2022-04-08 16:01:41 INFO: there is info message!
2022-04-08 16:01:41 WARNING: there is warning message!
2022-04-08 16:01:41 ERROR: there is error message!
2022-04-08 16:01:41 CRITICAL: there is critical message!

Process finished with exit code 0

1.1、**kwargs 参数

logging.debug()、logging.info()等方法的定义中,除了msg和args参数外,还有一个**kwargs参数,其支持3个关键字参数: exc_info, stack_info, extra,具体用法如下:

  • exc_info

其值为布尔值,如果该参数的值设置为True,则会将异常异常信息添加到日志消息中。如果没有异常信息则添加None到日志信息中。

  • stack_info

其值也为布尔值,默认值为False。如果该参数的值设置为True,栈信息将会被添加到日志信息中。

  • extra

这是一个字典(dict)参数,它可以用来自定义消息格式中所包含的字段,但是它的key不能与logging模块定义的字段冲突。

具体用法

# coding=utf-8
"""
@DevTool  : PyCharm
@Author   : xxx
@DateTime : 2022/3/29 9:56
@FileName : test_09.py
"""

import logging

dict_extra={"user":"robot","ip":"99.99.99.99"}
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s {%(user)s-%(ip)s}: %(message)s", datefmt = "%Y-%m-%d %H:%M:%S")

logging.info("there is info message!",exc_info=True,stack_info=True,extra=dict_extra)
logging.warning("there is warning message!",exc_info=True,stack_info=False,extra=dict_extra)
logging.error("there is error message!",exc_info=False,stack_info=False,extra=dict_extra)

结果输出

D:\SoftWare\Python\python.exe E:/PythonProject/FileOperTest/test_09.py
2022-05-02 21:15:53 INFO {robot-99.99.99.99}: there is info message!
NoneType: None
Stack (most recent call last):
  File "E:/PythonProject/FileOperTest/test_09.py", line 14, in <module>
    logging.info("there is info message!",exc_info=True,stack_info=True,extra=dict_extra)
2022-05-02 21:15:53 WARNING {robot-99.99.99.99}: there is warning message!
NoneType: None
2022-05-02 21:15:53 ERROR {robot-99.99.99.99}: there is error message!

Process finished with exit code 0

1.2、basicConfig()参数

参数描述
filename指定日志输出到的目标文件的文件名称
filemode指定日志文件的打开模式,默认为’a’;该选项要在filename指定时才有效;
format指定日志格式字符串,即指定日志输出时所包含的字段信息以及顺序;
datefmt指定日期/时间格式,该选项要在format中包含时间字段%(asctime)s时才有效;
level指定日志器的日志级别;
stream指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream;stream和filename不能同时指定,否则会引发 ValueError异常;
style指定format格式字符串的风格,可取值为’%‘、’{‘和’$‘,默认为’%';
handlers这些handler将会被添加到root logger;注意 filename、stream和handlers这三个配置项只能有一个存在,否则会引发ValueError异常;

1.3、format格式参数

属性引用格式描述
asctime%(asctime)s记录日志发生时间,可以精确到毫秒
name%(name)s日志器名称,默认是’root’,因为默认使用的是 rootLogger
filename%(filename)s调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀
funcName%(funcName)s调用日志输出函数的函数名
levelname%(levelname)s日志的最终等级(被filter修改后的)
message%(message)s日志信息, 日志记录的文本内容
lineno%(lineno)d当前日志的行号, 调用日志输出函数的语句所在的代码行
levelno%(levelno)s该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
pathname%(pathname)s完整路径 ,调用日志输出函数的模块的完整路径名,可能没有
process%(process)s当前进程, 进程ID,可能没有
processName%(processName)s进程名称,Python 3.1新增
thread%(thread)s当前线程, 线程ID,可能没有
threadName%(thread)s线程名称
module%(module)s调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文件名
created%(created)f当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间–时间戳,就是当时调用time.time()函数返回的值
relativeCreated%(relativeCreated)d输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging模块加载时间的相对毫秒数
msecs%(msecs)d日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉asctime中产生的毫秒部分,可以用这个加上

到此为止,Python中简单的日志记录已经可以实现,不过对于更加复杂的场景日志记录则需要如下的日志系统来完成;

二、logging日志系统

logging日志模块四大组件

组件名称对应类名功能描述
日志器Logger提供了应用程序可一直使用的接口
处理器Handler将logger创建的日志记录发送到合适的目的输出
过滤器Filter提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
格式器Formatter决定日志记录的最终输出格式

组件关系

  • 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
  • 不同的处理器(handler)可以将日志输出到不同的位置;
  • 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
  • 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
  • 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。

2.1、Logger 类

logger 对象

logging.getLogger([name])返回一个logger对象,如果没有指定名字,则 name 的值为 “root”;若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用。

Logger对象最常用的方法分为两类:配置方法 和 消息发送方法;

常用配置方法:

方法描述
Logger.setLevel()设置日志器将会处理的日志消息的最低严重级别
Logger.addHandler() 和 Logger.removeHandler()为该logger对象添加 和 移除一个handler对象
Logger.addFilter() 和 Logger.removeFilter()为该logger对象添加 和 移除一个filter对象

日志记录方法:

方法描述
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical()创建一个与它们的方法名对应等级的日志记录
Logger.exception()创建一个类似于Logger.error()的日志消息
Logger.log()需要获取一个明确的日志level参数来创建一个日志记录

logger的层级结构和有效等级

  • logger的名称是一个以".“分割的层级结构,每个”.“后面的logger都是”.”前面的logger的children;
  • logger有一个"有效等级(effective level)"的概念:如果一个logger上没有被明确设置一个level,那么该logger就是使用它parent的level;如果它的parent也没有明确设置level则继续向上查找parent的parent的有效level,依次类推;
  • child loggers在完成对日志消息的处理后,默认会将日志消息传递给与它们的祖先loggers相关的handlers。因此,我们不必为一个应用程序中所使用的所有loggers定义和配置handlers,只需要为一个顶层的logger配置handlers,然后按照需要创建child loggers就可足够了。我们也可以通过将一个logger的propagate属性设置为False来关闭这种传递机制。

2.2、Handler类

Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加零个或者多个handler对象。

因为Handler是一个基类,它只定义了所有handlers都应该有的接口,所以无法直接实例化和使用Handler实例。

常用Handler

Handler描述
logging.StreamHandler将日志消息输出到Stream,如std.out, std.err或任何file-like对象。
logging.FileHandler将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
logging.handlers.RotatingFileHandler将日志消息发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.HTTPHandler将日志消息以GET或POST的方式发送给一个HTTP服务器
logging.handlers.SMTPHandler将日志消息发送给一个指定的email地址
logging.NullHandler该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。

handler常用方法

方法描述
Handler.setLevel()设置handler将会处理的日志消息的最低严重级别
Handler.setFormatter()为handler设置一个格式器对象
Handler.addFilter() 和 Handler.removeFilter()为handler添加 和 删除一个过滤器对象

  • RotatingFileHandler
    类定义
class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None)

当前被写入的文件总是 app.log。
如果 maxBytes 或 backupCount 两者之一的值为零,日志文件不会发生轮换;
当 backupCount 为 5 而基本文件名为 app.log 时,你将得到 app.log、app.log.1、app.log.2 直至 app.log.5。

  • TimedRotatingFileHandler
    类定义
class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None, errors=None)

日志配置

[handler_timedRotatingFileHandler]
class=logging.handlers.TimedRotatingFileHandler
level=INFO
formatter=commonFormatter
args=("logTrack.log","D",1,180,"utf-8")

说明:此处因为报错TimedRotatingFileHandler 没有定义,所以使用全路径来描述类名称;

2.3、Formater类

Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。

类构造方法:

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

一般直接用 logging.Formatter(fmt, datefmt)

对象获取

2.4、Filter类

Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。

2.5、流处理流程

1、创建一个根 logger对象;
2、设置根 logger对象的日志等级;
3、创建需要的Handler(FileHandler要有路径);
4、设置下每个Handler对象的日志等级;
5、创建下日志的fmt格式和 datefmt格式;
6、设置每个 Handler对象的 formatter 的格式
7、将每个 Handler对象添加到logger对象中;
8、打印输出logger.debug\logger.info\logger.warning\logger.error\logger.critical日志信息;

代码实现

功能描述:按照不同的日志级别,将日志信息分别记录到不同的日志文件;

# coding=utf-8
"""
@DevTool  : PyCharm
@Author   : xxx
@DateTime : 2022/5/2 0:34
@FileName : test_14.py
"""
import logging

logger = logging.getLogger("Tracker")
logger.setLevel(logging.DEBUG)

allHandler = logging.FileHandler("all_log.txt",mode="a+",encoding="utf-8")
allHandler.setLevel(logging.DEBUG)

warnHandler = logging.FileHandler("warn_log.txt",mode="a+",encoding="utf-8")
warnHandler.setLevel(logging.WARNING)

errorHandler = logging.FileHandler("error_log.txt",mode="a+",encoding="utf-8")
errorHandler.setLevel(logging.ERROR)

criticalHandler = logging.FileHandler("critical_log.txt",mode="a+",encoding="utf-8")
criticalHandler.setLevel(logging.CRITICAL)

formatter = logging.Formatter(fmt="%(asctime)s {%(name)s}-[%(levelname)s] %(message)s",datefmt = "%Y-%m-%d %H:%M:%S")

# set formatter
allHandler.setFormatter(formatter)
warnHandler.setFormatter(formatter)
errorHandler.setFormatter(formatter)
criticalHandler.setFormatter(formatter)

# add handlers
logger.addHandler(allHandler)
logger.addHandler(warnHandler)
logger.addHandler(errorHandler)
logger.addHandler(criticalHandler)


# write log messages
logger.debug("there is debug message!")
logger.info("there is info message!")
logger.warning('{} is {} years old.'.format('Tom', 10))
logger.error("there is error message!")
logger.critical("there is critical message!")

all_log.txt

2022-05-02 00:59:13 {Tracker}-[DEBUG] there is debug message!
2022-05-02 00:59:13 {Tracker}-[INFO] there is info message!
2022-05-02 00:59:13 {Tracker}-[WARNING] Tom is 10 years old.
2022-05-02 00:59:13 {Tracker}-[ERROR] there is error message!
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!

warn_log.txt

2022-05-02 00:59:13 {Tracker}-[WARNING] Tom is 10 years old.
2022-05-02 00:59:13 {Tracker}-[ERROR] there is error message!
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!

error_log.txt

2022-05-02 00:59:13 {Tracker}-[ERROR] there is error message!
2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!

critical_log.txt

2022-05-02 00:59:13 {Tracker}-[CRITICAL] there is critical message!

2.6、日志重复记录

当重复调用一个 同名的 logger 时,将重复 添加相同 Handler,导致日志的重复记录;
解决方式,即在添加之前 判断 logger 的 handlers 是否为空;

实现代码

# coding=utf-8
"""
@DevTool  : PyCharm
@Author   : xxx
@DateTime : 2022/5/2 0:34
@FileName : test_14.py
"""
import logging


def getConfigedLogger():

    logger = logging.getLogger("Tracker")
    logger.setLevel(logging.DEBUG)

    warnHandler = logging.FileHandler("warn_log.txt", mode="a+", encoding="utf-8")
    warnHandler.setLevel(logging.WARNING)

    errorHandler = logging.FileHandler("error_log.txt", mode="a+", encoding="utf-8")
    errorHandler.setLevel(logging.ERROR)

    formatter = logging.Formatter(fmt="%(asctime)s {%(name)s}-[%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")

    # set formatter
    warnHandler.setFormatter(formatter)
    errorHandler.setFormatter(formatter)

    if not logger.handlers:
        logger.addHandler(warnHandler)
        logger.addHandler(errorHandler)

    return logger



realLogger = getConfigedLogger()

# write log messages
realLogger.debug("there is debug message!")
realLogger.info("there is info message!")
realLogger.warning('{} is {} years old.'.format('Tom', 10))
realLogger.error("there is error message!")
realLogger.critical("there is critical message!")

三、logging日志配置

Python日志常用的3种配置方式如下:

1、使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数;
2、创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;
3、创建一个包含配置信息的dict,然后把它传递个dictConfig()函数;

参考资料:https://www.cnblogs.com/yyds/p/6885182.html

3.2、配置文件

通过配置文件实现日志记录,实现了配置和代码的分离,同时开发者可以轻松修改日志记录属性;

  • 配置文件
[loggers]
keys=root,tracker

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=commonFormatter

[logger_root]
level=INFO
handlers=consoleHandler

[logger_tracker]
level=INFO
handlers=consoleHandler,fileHandler
qualname=tracker
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=commonFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=commonFormatter
args=("logTrack.log","a+","utf-8")

[formatter_commonFormatter]
format=%(asctime)s -[%(name)s-%(levelname)s]: %(message)s
datefmt=%Y-%m-%d %H:%M:%S

当使用配置文件进行日志的记录时,有些关键规则:

0)在配置文件中,首先包含了三大主要模块,loggers、handlers、formatters。对于三个主要模块其包含的内容都是通过keys进行指定,然后通过logger_key/handler_key/formatter_key对里面的key进行具体的设置。

1)配置logger信息,必须包含一个名字叫做root的logger,当使用无参函数logging.getLogger()时,默认返回root这个logger;其他自定义logger可以通过 logging.getLogger(“loggername”) 方式进行调用;

2)对loggers中声明的logger进行逐个配置,且必须指定level和handlers这两个选项;对于非roothandler,还需要添加一些额外的option,其中qualname表示它在logger层级中的名字;
handlers可以指定多个,中间用逗号隔开,比如handlers=fileHandler,consoleHandler,同时制定使用控制台和文件输出日志;

3)在handler中,必须指定class和args这两个option,常用的class包括 StreamHandler、FileHandler、RotaRotatingFileHandler;
args表示传递给class所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;里面指定输出路径,比如输出的文件名称等。
level与logger中的level一样,而formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息;

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('Tracker')

四、添加上下文

除了传递给日志记录函数的参数外,有时候需要日志输出中包含一些额外的上下文信息,如客户端的IP地址和用户信息等,实现方式如下:

  • 通过向日志记录函数传递一个extra参数引入上下文信息
  • 使用LoggerAdapters引入上下文信息
  • 使用Filters引入上下文信息

参考资料:https://www.cnblogs.com/yyds/p/6897964.html

五、项目引用

5.1、日志封装

5.2、项目引入

  • 项目结构
    项目根目录tree /f 显示目录结构
E:.
│  appprog.py
│
├─comTool
│      comFun.py
│
├─conf
│      logging.conf
│
├─logs
│      logTrack.log
│
└─__pycache__

logging.conf 日志配置文件内容:

[loggers]
keys=root,tracker

# 和上面部分一样
...

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=commonFormatter
args=("./logs/logTrack.log","a+","utf-8")


[formatter_commonFormatter]
format=[%(asctime)s]-[%(name)s-%(levelname)s]-->[%(filename)s]: %(message)s
datefmt=%Y-%m-%d %H:%M:%S

主程序 appprog.py 内容如下:

# -*- coding= utf-8 -*-
"""
@DevTool  : PyCharm
@Author   : xxx
@DateTime : 2023/6/15 13:41
@FileName : appprog.py
"""
import logging
import logging.config
from comTool import comFun

if __name__ == "__main__":
    print("the main app is running!")
    logging.config.fileConfig('./conf/logging.conf')
    logger = logging.getLogger("tracker")

    logger.info("the main app is running!")
    comFun.recordTest()

公共程序 comFun.py 内容如下:

# -*- coding= utf-8 -*-
"""
@DevTool  : PyCharm
@Author   : xxx
@DateTime : 2023/6/15 14:12
@FileName : comFun.py
"""
import logging


def recordTest():
    print("current step is in function recordTest!")
    logger = logging.getLogger("tracker")
    logger.info("current step is in function recordTest!")

在公共程序中,logger 可以直接使用在主程序配置的;

控制台输出内容:

the main app is running!
[2023-06-15 14:48:58]-[tracker-INFO]-->[appprog.py]: the main app is running!
current step is in function recordTest!
[2023-06-15 14:48:58]-[tracker-INFO]-->[comFun.py]: current step is in function recordTest!

Process finished with exit code 0

日志输出内容:

[2023-06-15 14:40:43]-[tracker-INFO]-->[appprog.py]: the main app is running!
[2023-06-15 14:40:43]-[tracker-INFO]-->[comTool.py]: current step is in function recordTest!

================================ over ========================================

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python logger 是一个内置的日志记录模块,它可以帮助我们在程序中记录和管理日志信息。使用 Python logger,我们可以将程序的运行状态、错误信息等输出到日志文件或者控制台。 要使用 Python logger,我们首先需要导入 logging 模块。然后,我们可以创建一个 logger 对象,并设置它的日志级别、输出格式等属性。接下来,我们可以使用 logger 对象进行日志记录,包括调用不同级别的方法,如 debug、info、warning、error 等。 下面是一个简单的示例: ```python import logging # 创建一个 logger 对象 logger = logging.getLogger('my_logger') # 设置日志级别 logger.setLevel(logging.DEBUG) # 创建一个文件处理器 file_handler = logging.FileHandler('my_log.log') # 设置文件处理器的日志级别 file_handler.setLevel(logging.DEBUG) # 创建一个控制台处理器 console_handler = logging.StreamHandler() # 设置控制台处理器的日志级别 console_handler.setLevel(logging.INFO) # 创建一个日志格式器 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 设置文件处理器和控制台处理器的格式化器 file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # 将处理器添加到 logger 对象中 logger.addHandler(file_handler) logger.addHandler(console_handler) # 记录日志 logger.debug('This is a debug message') logger.info('This is an info message') logger.warning('This is a warning message') logger.error('This is an error message') ``` 在上面的示例中,我们创建了一个名为 'my_logger' 的 logger 对象,并设置了它的日志级别为 DEBUG。然后,我们创建了一个文件处理器和一个控制台处理器,并设置它们的日志级别分别为 DEBUG 和 INFO。接着,我们创建了一个日志格式器,并将它设置给文件处理器和控制台处理器。最后,我们使用 logger 对象记录了不同级别的日志信息。 通过使用 Python logger,我们可以更好地跟踪程序的运行状态和调试信息,从而方便地进行错误排查和日志分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值