外链论坛

 找回密码
 立即注册
搜索
查看: 1|回复: 0

忽略日志吃大亏,手把手教你玩转 SpringBoot 日志

[复制链接]

2565

主题

4794

回帖

9957万

积分

论坛元老

Rank: 8Rank: 8

积分
99570772
发表于 8 小时前 | 显示全部楼层 |阅读模式

1、日志重要吗

程序中的日志重要吗? 在回答这个问题前,笔者先说个事例:

笔者印象尤深的便是去年某个同事,收到了客户反馈的紧急bug。尽管申请到了日志文件,但由于非常多关键过程打印日志,引起排查进度很慢,数个小时都没能排查到问题,没法给出处理对策。引起了客户程序始终阻断,最后产生了不少损失。 事后,经过仔细推敲,成功复现了这个bug,其实是一个很不起眼的数据转换引起的。可由于日志内容的匮乏,排查起来难度很大。其实只要在数据转换前后进行日志输出,这个问题便是一眼的事。但可惜没倘若,故事的最后,研发分部还是遭到了客户的投诉,影响到了分部绩效

针对刚学习编程的朋友非常多人都对日志满不在乎,咱们在做code review的时候,经常发掘有些朋友爱好一个办法写得很长,而后中间的注释和日志都少的可怜。

坦白的说,这是很欠好的习惯,这寓意着日后办法出了bug,需要迭代,要花费海量时间来理清办法的思路。千万别迷信什么“办法名、字段名起的见明知意,就能够不写注释与日志”,那是她们的业务场景不足繁杂。以笔者为例,繁杂的场景触及非常多公式、奇特的规定,不写注释与日志,后续没人能守护得了

因此请务必记住,日志在研发过程中非常重要。它能够帮忙研发人员认识程序中出现了什么,以及在某些状况为何出现错误或反常经过查看日志,研发人员能够容易地定位并处理问题,并且能够更好地监控和调节应用程序的性能,在必要时进行故障排除和安全检测

2、日志分级

起始的日志分级是由于Syslog的研发者Eric Allman在1981年提出的。之后,这个级别分级系统被广泛应用于各样行业的日志记录和信息处理中。下面咱们就来介绍下常用的日志等级

TRACE

是最低级别的日志记录,用于输出最仔细的调试信息,一般用于研发调试目的。在生产环境中,应该关闭 TRACE 级别的日志记录,以避免输出太多无用信息。

DEBUG

是用于输出程序中的有些调试信息,一般用于研发过程中。像 TRACE 同样,在生产环境中应该关闭 DEBUG 级别的日志记录。

INFO

用于输出程序正常运行时的有些关键信息,例如程序的起步、运行日志等。一般在生产环境中开启 INFO 级别的日志记录。

WARN

是用于输出有些警告信息,提示程序可能会显现有些反常错误。在应用程序中,WARN 级别的日志记录一般用于记录有些非致命性反常信息,以便能够即时发掘并处理这些问题。

ERROR

是用于输出程序运行时的有些错误信息,一般暗示程序显现有些不可预料的错误。在应用程序中,ERROR 级别的日志记录一般用于记录有些致命性的反常信息,以便能够即时发掘并处理这些问题。

当然,除了这五种级别以外,还有有些日志框架定义了其他级别,例如 Python 中的 CRITICAL、PHP 中的 FATAL 等。CRITICAL 和 FATAL 都是用于暗示程序显现了致命性错误反常,即不可恢复的错误。当然,针对咱们今天要说的内容,晓得以上五种日志等级就够了。

3、常用日志插件

Log4j(1999年诞生)

Log4j 是Java行业中最早的流行日志框架之一。它由Ceki Gülcü研发,并后来由Apache软件基金会接管。Log4j 供给了灵活的配置选项、多种输出目的地、日志级别和分层日志体系。尽管Log4j 1在其时代取得了巨大的成功,但在性能和某些功能方面存在限制,因此呢后来演化为Log4j 2。

SLF4J(2004年诞生)

严格来讲,SLF4J(Simple Logging Facade for Java)并不算一个插件,而是Ceki Gülcü研发的一个日志门面接口。它为Java应用程序供给了统一的日志抽象,使研发人员能够运用一致的API进行日志记录,而不需要直接依赖于特定的日志实现。SLF4J 能够与多种底层日志框架(如Logback、Log4j 2、java.util.logging等)结合运用

Logback(2009年诞生)

Logback 是Ceki Gülcü研发的日志框架,他是Log4j的作者。Logback 是Log4j 1的后续版本,旨在供给更高性能、更灵活的配置和现代化的日志处理方法。Logback 支持异步日志记录、多种输出格式、灵活的配置以及与SLF4J紧密集成。

Log4j 2(2014年诞生)

Log4j 2 是Apache软件基金会研发的Log4j的下一代版本。它引入了许多新特性,如异步日志记录、插件支持、丰富的过滤器等,旨在供给更好的性能和灵活性。Log4j 2 在设计上思虑了Log4j 1的局限性,并且支持多种配置方式。

小故事

不难重视到,一个有意思的小故事是,前三款日志插件都是Ceki Gülcü研发的,但 Log4j 2 并不是,虽然此刻非常多人以为log4j2是他写的,但咱们在github上能够看到其个人说明 “Unaffiliated with log4j 2.x.” (与 log4j 2.x 无关),因此log4j2 和 logback 都自叫作是log4j 的后续版本,到底谁才算正统续作呢?这就留给各位读者自己玩味了

4、外观模式与SLF4J

在讲解更加多插件详情之前,咱们先来瞧瞧运用最多的SLF4J ,咱们前面说了 SLF4J(Simple Logging Facade for Java)是Ceki Gülcü研发的一个日志门面接口,那样很显然这儿就用到了门面模式(即Facade 或 外观模式),笔者比较习惯说成是外观模式,后续就叫作为外观模式。

1. 外观模式

定义:外观模式是一种结构型设计模式,它供给了一个简单的接口,封装了底层繁杂的子系统,使得客户端能够更方便地运用这个子系统

目的:外观模式的目的是隐匿底层系统的繁杂性,降低拜访成本。

倘若说看定义有些抽象,那咱们能够以生活中的例子来讲咱们都知道此刻越来越流行智能家居,便是家庭内装了非常多智能家电,从电视、空调、到廊灯乃至窗帘都是智能的。这类家庭常常会有一个掌控中心,咱们不需要手动去开电视,只需要对着掌控中心说:“小A小A,帮我打开电视,音量调到30%”,电视就会应声打开并调节音量

那样这般的话,咱们不需要晓得怎么开电视,怎么调音量。通通都能用最简单的话语来调节。同理,此刻手机上的拍照功能:感光度,对焦,白平衡这些细节都给你自动完成为了因此这些繁杂的内容你此刻基本不消管,只需要猛按拍照键就可

便是外观模式的道理外观模式便是为了隐匿系统的繁杂性而设计出来的,让客户端只对接触到一个外观类,而不会接触到系统内部的繁杂规律

2. SLF4J 的诞生

初期运用日志框架时,应用程序一般需要直接与详细的日志框架进行耦合,这就引起了以下几个问题:

代码依赖性

应用程序需要直接引用详细的日志框架,从而引起代码与日志框架强耦合,难以满足应用程序对日志框架的灵活配置。

日志框架不统一

运用区别的日志框架时,应用程序需要按照详细的日志框架来编写代码,这不仅会增多研发难度,况且在多种日志框架中切换时需要进行海量的代码改动。

性能问题

在日志输出频繁的状况下,因为日志框架的实现方式和API设计区别,可能会引起性能问题。

认识决这些问题,SLF4J供给了一套通用的日志门面接口,让应用程序能够经过这些接口来记录日志信息,而不需要直接引用详细的日志框架。这般,应用程序就能够区别的日志框架之间进行灵活配置和切换,同期能够得到更好的性能表现。因此,我剧烈意见各位运用SLF4J, 而不是直接对接某个详细的日志框架。

3. SLF4J 的运用

首要咱们需要在工程内引入包,然则倘若你用了springboot,各样 spring-boot-starter 起步已然引用过了,因此引用前最好确认下:

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

而后咱们要打印日志的类里加上一行 ;private static final Logger logger = LoggerFactory.getLogger(XXXX.class); 就可运用,如下:

import

 org.slf4j.Logger;

import

 org.slf4j.LoggerFactory;

public class MyClass 

{

    private static final Logger log = LoggerFactory.getLogger(MyClass.class)

;

    //...    public static void main(String[] args) 

{

        log.info("This is an info message."

);

    }

}

倘若咱们引用了lombok的话,能够运用lombok的注解@Slf4j 代替上面那句话来运用 SLF4J ,如下:

import

 lombok.extern.slf4j.Slf4j;

@Slf

4j

public class MyClass 

{

    public static void main(String[] args) 

{

        log.info("This is an info message."

);

    }

}

然则咱们晓得SLF4J仅仅是个门面,换句话说,仅有接口而实现,倘若此刻咱们直接运行,打印日志是用处的

因此咱们倘若要运行,咱们必须要给 SLF4J 安排上实现,而日前最常用的便是 logback 和 log4j2 了,就让咱们接着往下看

5、双雄之争

其实关于 Logback 和 Log4j 2,网络上有非常多评测,就不需赘述了,重点是围绕性能方面的,从日前大众的反馈看,Log4j 2 晚显现好几年,还是有后发优良的,性能会比 Logback 好。当然, Logback 本身性能很强,针对都数场景,完全是够用的,况且配置比较直观,是spring-boot 默认运用的日志插件。

因此,选谁都能够倘若不想费神,能够直接运用spring-boot自带的Logback,倘若对日志性能需求很高,运用log4j2更保险,咱们接下来分别介绍两者。

1. Logback1. 引用

因为 Logback 为 spring-boot 默认日志框架,因此无需再引用,但针对非spring - boot 项目,能够做如下引用

<dependency>    <groupId>ch.qos.logback</groupId>    <artifactId>logback-classic</artifactId>    <version>1.2.12</version></dependency>

Logback 的核心模块为 logback-classic,它供给了一个 SLF4J 的实现,兼容 Log4j API,能够无缝地替换 Log4j。它自己已然包括了 logback-core 模块,而 logback-core,顾名思义便是 logback 的核心功能,包含日志记录器、Appender、Layout 等。其他 logback 模块都依赖于该模块

2. 配置

logback 能够经过 XML Groovy 配置。下面以 XML 配置为例。logback 的 XML 配置文件名叫作一般为 logback.xml logback-spring.xml(在 Spring Boot 中),需要安置在 classpath 的根目录下,

<?xml version="1.0" encoding="UTF-8"?><configuration>    <!--定义日志文件的存储位置运用Spring的属性文件配置方式-->    <springProperty scope="context" name="log.home" source="log.home" defaultValue="logs"/>    <!--定义日志文件的路径-->    <property name="LOG_PATH" value="${log.home}"/>    <!--定义掌控台输出-->    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">        <encoder>            <pattern>%-5relative [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <!--定义 INFO 及以上级别信息输出到掌控台-->    <root level="INFO">        <appender-ref ref="console"/>    </root>    <!--定义所有组件的日志级别,如所有 DEBUG-->    <logger name="com.example" level="DEBUG"/>    <!-- date 格式定义 -->    <property name="LOG_DATEFORMAT" value="yyyy-MM-dd"/>    <!-- 定义日志归档文件名叫作格式,每日生成一个日志文件 -->    <property name="ARCHIVE_PATTERN" value="${LOG_PATH}/%d{${LOG_DATEFORMAT}}/app-%d{${LOG_DATEFORMAT}}-%i.log.gz"/>    <!--定义文件输出,会按照定义的阈值进行切割,支持自动归档压缩过期日志-->    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">        <file>${LOG_PATH}/app.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">            <!--定义日志文件切割的阈值,本例是 50MB-->            <maxFileSize>50MB</maxFileSize>            <!--定义日志文件保存时间,本例是每日生成一个日志文件-->            <fileNamePattern>${ARCHIVE_PATTERN}</fileNamePattern>            <maxHistory>30</maxHistory>            <!-- zip 压缩生成的归档文件 -->            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">                <maxFileSize>50MB</maxFileSize>            </timeBasedFileNamingAndTriggeringPolicy>            <!-- 删除过期文件 -->            <cleanHistoryOnStart>true</cleanHistoryOnStart>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>        </encoder>    </appender>    <!--定义 ERROR 级别以上信息输出到文件-->    <logger name="com.example.demo" level="ERROR" additivity="false">        <appender-ref ref="file"/>    </logger>    <!--异步输出日志信息-->    <appender name="asyncFile" class="ch.qos.logback.classic.AsyncAppender">        <discardingThreshold>0</discardingThreshold>        <queueSize>256</queueSize>        <appender-ref ref="file"/>    </appender>    <!--定义INFO及以上级别信息异步输出到文件-->    <logger name="com.example" level="INFO" additivity="false">        <appender-ref ref="asyncFile"/>    </logger></configuration>

其中,重点包含以下配置:

springProperty定义了 log 文件的存储路径,能够通过 Spring 的属性文件配置方式进行设置,倘若配置则默认存储在 logs 目录下。appender 定义了日志输出的目的这儿包含掌控台输出和文件输出两种,详细能够按照需要进行配置。root定义了默认的日志级别和输出目的,默认状况下,INFO 级别以上的日志信息会输出到掌控台,能够按照实质需求进行修改。logger 定义了区别组件的日志级别和输出目的,例如,这儿定义了 com.example 这个组件的日志级别为 DEBUG,而 com.example.demo 这个组件的日志级别为 ERROR,并将其输出到文件中。rollingPolicy定义了日志文件的切割规则和归档策略,此处定义了日志文件每一个 50MB 进行切割,每日生成一个日志文件,并且压缩和删除过期文件,最多保存 30 天的日志文件。encoder 定义了日志信息的输出格式,详细的格式能够自动定义。asyncAppender 定义了异步输出日志的方式,针对高并发时,能够运用异步输出来加强系统的性能。discardingThreshold 定义了异步输出队列的阈值,当队列中的数据量超过此值时,会丢弃最早放入的数据,此处设置为 0 暗示队列不会丢弃任何数据。queueSize 定义了异步输出队列的体积,当队列满时,会等待队列中的数据被消费后再将数据放入队列中,此处设置为 256。3. 演示

咱们新建一个普通工程(非spring工程),引用Logback后,把以上配置文件复制进logback.xml,而后将工程结构设置成如下模式

其中两个类的代码如下:

public class Main 

{

    private static final Logger log = LoggerFactory.getLogger(Main.class)

;

    public static void main(String[] args) 

{

        log.trace("This is a Main trace message."

);

        log.debug("This is a Main debug message."

);

        log.info("This is a Main info message."

);

        log.warn("This is a Main warn message."

);

        log.error("This is a Main error message."

);

        Slave.main(args);

    }

}

public class Slave 

{

    private static finalLogger log = LoggerFactory.getLogger(Slave.class)

;

    public static void main(String[] args) 

{

        log.trace("This is a Slave trace message."

);

        log.debug("This is a Slave debug message."

);

        log.info("This is a Slave info message."

);

        log.warn("This is a Slave warn message."

);

        log.error("This is a Slave error message."

);

    }

}

我们想实现这般的效果,首要日志要同期 输出到掌控台 及 日志文件,且区别层级的代码,输出的日志层级区别那样咱们能够以上的xml做出有些调节

由于是非Spring项目,因此 springProperty 这般的标签就不要用了,咱们直接写死一个日志文件位置就可

<!--定义日志文件的路径--><property name="LOG_PATH" value="D://logs/log"/>

去掉原有的哪些root、logger标签,咱们自己新建两个logger,用于两个区别的层级。咱们想里层输出 debug 级别,外层输出info 级别,咱们能够这么设置。并且同期输出到掌控台及日志文件

<logger name="com.zhanfu" level="INFO">    <appender-ref ref="file" />    <appender-ref ref="console"/></logger><logger name="com.zhanfu.child" level="DEBUG" additivity="false">    <appender-ref ref="file" />    <appender-ref ref="console"/></logger>

咱们运行Main.main的时候,就能够得到以下日志,slave 能输出debug级别,Main 只能输出 info及以上级别

4. 细节点

其实咱们上面的演示,有两个细节点,需要重视一下。一个便是咱们

 <logger name="com.zhanfu.child" level="DEBUG" additivity="false">

运用了一个 additivity="false" 的属性,这其实是由于 logger 这个标签在锁定某个目录时,可能会出现层级关系。例如咱们的两个 logger, 一个针对的目录是 com.zhanfu 另一个是 com.zhanfu.child ,后者就会被前者包括

咱们的 com.zhanfu.child.Slave 打印日志时,当然会运用后者(更精确)的设置,但前者的设置还运用吗?就依赖于 additivity=“false”,此处倘若咱们把 additivity="false" (该属性默认值为true)去掉,再来打印日志

就会发掘,Slave 的日志打了两遍,况且连 debug 级别的都打了两遍,咱们能够把这种规律理解为继承,子类执行一遍,父类还能在执行一遍,但 leve 属性还是会采用子类而非父类的。

另一点便是咱们把 root 标签删除了,root 其实是一个顶级的 logger , 其他的logger都能够视为它的子类,倘若哪些logger存在没涵盖的地区,或其指定 additivity="false" ,那最后root的设置就会被运用例如咱们将设置改为如下:

<logger name="com.zhanfu" level="INFO">    <appender-ref ref="file" />    <appender-ref ref="console"/></logger><logger name="com.zhanfu.child" level="DEBUG">    <appender-ref ref="file" />    <appender-ref ref="console"/></logger><root level="WARN">    <appender-ref ref="console"/></root>

结果掌控台的输出日志,Main会重复两次,Slave 会重复三次,如下

然则由于咱们的 root 只配置了掌控台输出,因此日志文件里还是不会变的

2. Log4j 21. 引用

针对spring-boot项目,除了引用 Log4j 2 咱们还需要先剔除 Logback 的引用,针对普通项目,咱们只需直接引用就可。但重视咱们的原则,经过 SLF4J 来运用 Log4j2,因此引用下面这个包

<dependency>     <groupId>org.apache.logging.log4j</groupId>     <artifactId>log4j-slf4j-impl</artifactId>     <version>2.13.3</version></dependency>

其内包括 Log4j2 的实现,和 SLF4J 的 API,如下:

2. 配置

Log4j2 的配置规律和 logback 是类似的,仅有些细节区别例如Logger 的首字母大写等等,最后咱们写下这般一个 log4j2.xml

<?xml version="1.0" encoding="UTF-8"?><Configuration status="INFO" monitorInterval="30">  <Properties>    <Property name="logPath">logs</Property>  </Properties>  <Appenders>    <Console name="Console" target="SYSTEM_OUT">      <PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n" />    </Console>    <RollingFile name="File" fileName="${logPath}/example.log"                 filePattern="${logPath}/example-%d{yyyy-MM-dd}-%i.log">      <PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n"/>      <Policies>        <SizeBasedTriggeringPolicy size="10 MB"/>      </Policies>      <DefaultRolloverStrategy max="4"/>    </RollingFile>  </Appenders>  <Loggers>    <Logger name="com.zhanfu.child" level="DEBUG">      <AppenderRef ref="File"/>      <AppenderRef ref="Console"/>    </Logger>    <Logger name="com.zhanfu" level="INFO">      <AppenderRef ref="File"/>      <AppenderRef ref="Console"/>    </Logger>    <Root level="WARN">      <AppenderRef ref="Console" />    </Root>  </Loggers></Configuration>Properties

部分定义了一个 logPath 属性,方便在其他地区引用。

Appenders

定义了两个 Appender:Console 和 RollingFile,分别将日志输出到掌控台和文件中。RollingFile 运用了 RollingFileAppender,并设置了日志滚动策略和默认的备份文件数量。

Loggers

定义了三个 Logger:com.zhanfu.child 的日志级别为 DEBUG,com.zhanfu 的日志级别为INFO,Root Logger 的日志级别为 WARN。并指定了两个 Appender:Console 和 File。

3. 演示

因为咱们的配置规律没变,因此日志的结果还是同样的:

3. 对比

Log4j2和Logback都是Java应用程序中最流行的日志框架之一。它们均具备高度的可配置性和运用灵活性,并供给了一系列有用的功能,例如异步日志记录和日志过滤等。下面从配置遍历性、功能性、性能等方面进行比较和总结。

配置遍历性

Logback的配置文件格式相对简单,易于阅读和修改。它支持符号来引用变量、属性和环境变量等。另外,它还支持要求日志记录(按照日志级别、日志记录器名叫作或时间等),以及滚动文件的体积或日期等。

Log4j2的配置文件格式较繁杂,但它在配置文件中供给海量的选项来掌控日志记录。它支持在配置文件中直接声明上下文参数、过滤器、输出器和Appender等,这使得它的配置更加灵活。另外,Log4j2还支持异步日志记录、日志事件序列化和性能优化等。

总体来讲,两者都很好地支持了配置遍历性,但Log4j2供给更加多的选项和更高的灵活性。

功能性

Logback供给了一系列基本的日志记录功能,例如异步Appender、滚动文件和GZIP压缩等。它还支持与SLF4J一块运用能够很容易地与其他日志框架集成。

Log4j2供给更加多的高级功能,例如异步日志记录、性能优化和日志事件序列化等。它还支持Lambda表达式,能够使日志记录器更加简洁和易读。另外,Log4j2还支持Flume和Kafka等大数据处理框架,能够方便地将日志记录发送到这些框架中。

总体来讲,Log4j2供给更加多的高级功能,并且能够更好地与大数据处理框架集成。

性能

Logback的性能很好,能够处理高吞吐量的日志记录。它采用了异步记录器,利用了多线程来加强性能。

Log4j2在性能方面更加强大。它运用了异步记录器和多线程,还引入了RingBuffer数据结构和Disruptor库来加速日志事件的传递和处理。这使得它比Logback拥有更高的吞吐量和更低的延迟。

综上所述,Log4j2在配置灵活性、功能性和性能方面都比Logback更为强大。但倘若需要轻量级的日志框架只需要基本的日志记录功能,Logback是一个不错的选取

倘若咱们同期引用了这两者,会报错吗?还是会运用其中的某一个?

 <dependency>    <groupId>ch.qos.logback</groupId>    <artifactId>logback-classic</artifactId>    <version>1.2.12</version></dependency><dependency>    <groupId>org.apache.logging.log4j</groupId>    <artifactId>log4j-slf4j-impl</artifactId>    <version>2.13.3</version></dependency>

能够看到,SLF4J 发掘了系统中同期存在两个插件框架,并最后选取运用 Logback

总结

学习完本文,你应当对此刻这几个常用框架的有所认识,并能基本应用了。此次咱们讲源码,深入的讲其配置及进阶运用,这些咱们会在后面慢慢学习。但此刻期盼你能晓得的是。必定要写好日志,必定要写好日志,必定要写好日志。重要的事情说三遍!这是区别新人和老鸟的一个重要依据,是让自己排查问题更容易的不二法门!

另一此刻非常多中间件都自己引用了日志插件,咱们做为一个整体工程在运用中间件时,要即时发掘处理插件冲突,避免咱们自己的日志配置失效,这是一个程序员该重视的点。

源自:blog.csdn.net/u011709538/article/details/132363370

13 秒插进 30 万条数据,这才是批量插进正确的姿势!

和 XShell 说再见,这款 SSH 工具足够惊艳,还支持网页版!

我进入银行科技部半年,已然丧失跳槽的能力了!

程序员:有那些话一听就晓得对方很水 (段子)

23 届校招技术岗薪资汇总

面试官问我 ,try catch 应该在 for 循环里面还是外面?

再见了 shiro

近期面试BAT,整理一份面试资料Java面试BAT通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666领取,更加多内容持续奉上。明天见(。・ω・。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站点统计|Archiver|手机版|小黑屋|外链论坛 ( 非经营性网站 )|网站地图

GMT+8, 2024-10-3 17:30 , Processed in 0.076463 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.