摘要:深入學(xué)習(xí)系列三那些年我們用過(guò)的日志框架目前常見(jiàn)的日志框架和中文似乎不太好翻譯有一下幾種即其中,為同一個(gè)作者所寫(xiě)。如前面所述,在才被引入,在這之前,并沒(méi)有官方的日志庫(kù)供開(kāi)發(fā)者使用。
title: 【Java深入學(xué)習(xí)系列】三. 那些年我們用過(guò)的日志框架
date: 2016-10-16 15:32:50
目前常見(jiàn)的Java日志框架和facades(中文似乎不太好翻譯)有一下幾種:
① log4j
② logback
③ SLF4J
④ commons-logging
⑤ j.u.l (即java.util.logging)
其中,①-③為同一個(gè)作者(Ceki)所寫(xiě)。④被很多開(kāi)源項(xiàng)目所用,⑤是Java原生庫(kù)(以下用j.u.l簡(jiǎn)寫(xiě)來(lái)代替),但是在Java 1.4中才被引入。
這么多的日志庫(kù),我們?cè)撊绾芜x擇呢,我認(rèn)為,這并非一道非此即彼的選擇題,但是在了解它們的歷史淵源和優(yōu)劣以及相互關(guān)系的基礎(chǔ)上才能更好地適配自己的項(xiàng)目。
下面我將上述這些框架串起來(lái)講一下,如有疏漏請(qǐng)見(jiàn)諒。
1. Logging frameworks的上古時(shí)期(Java 1.3及以前)在上古時(shí)期,Java打日志依賴(lài)System.out.println(), System.err.println()或者e.printStackTrace()。Debug日志被寫(xiě)入STDOUT流,錯(cuò)誤日志被寫(xiě)入STDERR流。
這種方式目前小腳本中也依然使用廣泛。但是在生產(chǎn)環(huán)境或大的項(xiàng)目中,Debug日志通常被重定向到/dev/null中: >/dev/null, 錯(cuò)誤日志被重定向到本地文件中: 2>stderr.log??雌饋?lái)很完美,是嗎?實(shí)則不然,這樣打日志有一個(gè)非常大的缺陷:無(wú)法可定制化。
具體來(lái)講,沒(méi)有一個(gè)類(lèi)似開(kāi)關(guān)的東東來(lái)切換是否打印Debug日志,當(dāng)我們定位問(wèn)題時(shí)需要輸出Debug日志到文件去查看,而不是到/dev/null里,是嗎?日志無(wú)法定制化,我們只能硬編碼到代碼里,不需要時(shí)再注釋掉相關(guān)代碼,重新編譯。
還有一些缺陷,比如:無(wú)法更細(xì)粒度地輸出日志,換句話說(shuō),缺少當(dāng)前成熟的日志框架常見(jiàn)的LOG LEVEL控制。
而Java本身也沒(méi)有提供相應(yīng)的Library,在這樣惡劣的境況下,Log4j勇敢地站了出來(lái),拯救勞苦大眾。
Log4j可以說(shuō)是一個(gè)里程碑式的框架,它提出的一些基本理念,深深地影響了后來(lái)者,直至今天,這些理念也依然在被廣泛使用:
Logger
我們來(lái)看下維基百科對(duì)Logger的定義:
A Logger is an object that allows the application to log without regard to where the output is sent/stored. The application logs a message by passing an object or an object and an exception with an optional severity level to the logger object under a given a name/identifier.
Logger是一個(gè)允許應(yīng)用記錄日志的對(duì)象,開(kāi)發(fā)者不必需考慮輸出位置。應(yīng)用可將具體需要打印的信息通過(guò)一個(gè)Object傳遞。每個(gè)Logger互相獨(dú)立,通過(guò)名字或標(biāo)識(shí)符來(lái)區(qū)分。
Appender
每個(gè)appender可獨(dú)立配置記錄日志的設(shè)備,可以是文件、數(shù)據(jù)庫(kù)、消息系統(tǒng)等。
Level
每個(gè)打印日志都可以多帶帶制定日志級(jí)別。外部通過(guò)配置文件來(lái)控制輸出級(jí)別,不同的輸出級(jí)別打印不同的日志信息。
2. J.U.L姍姍來(lái)遲后來(lái),Sun公司開(kāi)始意識(shí)到JDK需要一個(gè)記錄日志的特性。受Log4j的啟發(fā),Sun在Java1.4版本中引入了一個(gè)新的API, 叫java.util.logging, 但是,j.u.l功能遠(yuǎn)不如Log4j完善,如果開(kāi)發(fā)者要使用它,就意味著需要自己寫(xiě)Appenders(Sun稱(chēng)它為Handlers),而且,只有兩個(gè)Handlers可被使用:Console和File,這就意味著,開(kāi)發(fā)者只能將日志寫(xiě)入Console和文件。
如前面所述,j.u.l在Java 1.4才被引入,在這之前,并沒(méi)有官方的日志庫(kù)供開(kāi)發(fā)者使用。于是便有了很多日志相關(guān)的"輪子"。我想這應(yīng)該是當(dāng)前會(huì)有如此多日志框架的一個(gè)很重要的原因。
回顧歷史,一方面,在Java 1.4之前,第三方日志庫(kù)已經(jīng)被廣泛使用了,占得了先機(jī)。另一方面,j.u.l在被引入時(shí)性能和可用性都很差,直到1.5甚至以后才有了顯著提升。
3-1. Logging facades出現(xiàn)及進(jìn)化由于項(xiàng)目的日志打印必然依賴(lài)以上兩個(gè)框架中至少一個(gè),無(wú)論是j.u.l還是log4j,開(kāi)發(fā)者必須去兩個(gè)都配置。這時(shí)候,Apache的commons-logging出現(xiàn)了。本質(zhì)上來(lái)講,commons-logging并非一個(gè)日志打印框架,而是一個(gè)API bridge, 它起到一個(gè)連接和溝通的作用,開(kāi)發(fā)者可以使用它來(lái)兼容logging frameworks(j.u.l和log4j)。有了它,第三方庫(kù)就可以使用commons-logging來(lái)做一個(gè)中間層,去靈活選擇j.u.l或者log4j,而不必強(qiáng)加依賴(lài)。
然而commons-logging對(duì)j.u.l和log4j的配置問(wèn)題兼容得并不好,更糟糕的是,使用commons-logging可能會(huì)遇到類(lèi)加載問(wèn)題,導(dǎo)致NoClassDefFoundError的錯(cuò)誤出現(xiàn)。
最終,log4j的創(chuàng)始人Ceki發(fā)起了另一個(gè)項(xiàng)目,這便是大名鼎鼎的SLF4j 日志框架,該框架可以看成是log4j的升級(jí)版。需要說(shuō)明的是,log4j 2.0已經(jīng)被加入Apache基金會(huì),過(guò)去幾年已經(jīng)被大幅改善,社區(qū)活躍度也非常高,借助開(kāi)源社區(qū)的力量,log4j 2.0目前被加入越來(lái)越多得現(xiàn)代化特性,一定程度上,甚至超越了log4j的升級(jí)版logback(稍后介紹),關(guān)于log4j 2.0的新特性,請(qǐng)參見(jiàn)這篇文章:THE NEW LOG4J 2.0
據(jù)slf4j的作者Ceki說(shuō),首先,slf4j是不僅僅是一個(gè)logging framework, 而且一個(gè)logging facdes, 借助slf4j的log4j adapter, 開(kāi)發(fā)者從slf4j切換到log4j不需要額外改動(dòng)一行代碼,只需要從CLASS_PATH中排除掉slf4j-log4j12.jar。如果想從log4j遷移到logback, 在CLASS_PATH添加slf4j-log4j12.jar, 并將log4j.properties轉(zhuǎn)換為logback.xml即可,這里有一個(gè)在線工具可以自動(dòng)完成轉(zhuǎn)換: logback.xml translator。
slf4j提供了很大的靈活度,開(kāi)發(fā)者可以借助它去靈活選擇底層的日志框架。比如,當(dāng)下更多的開(kāi)發(fā)者比較傾向于使用log4j的升級(jí)版logback,因?yàn)樗哂休^log4j更多更好的特性:
配置文件支持xml和Groovy語(yǔ)法(版本號(hào)>= 0.9.22)
自動(dòng)重載有變更的配置文件
自動(dòng)壓縮歷史日志
打印異常信息時(shí)自動(dòng)包含package名稱(chēng)級(jí)版本號(hào)
Filters
其它一些很棒的特性
需要說(shuō)明的是,logback是slf4j接口的一套具體實(shí)現(xiàn),又是同一個(gè)作者,因而保證了其和log4j相近的使用方式,也具有slf4j的全部特性。
此外,對(duì)于一些大型框架及服務(wù)的開(kāi)發(fā)者,需要考慮客戶端用戶的體驗(yàn)。比如jstorm, 你不能只考慮自己的喜好,或許有人偏好使用slf4j開(kāi)發(fā)jstorm topology, 而另一些人喜歡用logback。這種情況下,你應(yīng)該使用slf4j,把最終logging framework的選擇權(quán)留給用戶。
最后,除了slf4j比j.u.l或者log4j更好用,還有一個(gè)選擇slf4j的現(xiàn)實(shí)原因:Java圈的非常多開(kāi)發(fā)者更鐘情于slf4j作為他們的logging API, 隨大流有時(shí)候能少很多不必要的麻煩。
3-2. 日志參數(shù)化打印的支持(parameterized logging)slf4j除了包含該log4j的全部特性外,還提供了parameterized logging特性。這個(gè)特性非常有用,它允許開(kāi)發(fā)者在打印日志時(shí)借助{}來(lái)實(shí)現(xiàn)參數(shù)化打?。?/p>
logger.debug("The attribute value is {}", fooIns.getAttribute());
logback復(fù)用了slf4j的API,這意味著使用logback實(shí)際上是在使用slf4j的API,不難看出,logback同樣支持parameterized logging特性。
4. 各日志框架時(shí)間線以上日志框架,有些是為了解決現(xiàn)有框架的不足,有些是功能的擴(kuò)展升級(jí),有些是從頭到尾重新寫(xiě)的,根據(jù)各自出現(xiàn)先后次序,可以將它們放在同一時(shí)間線上:
注意箭頭僅代表時(shí)間走向,分支不具有fork的含義。
5. SLF4J使用方法slf4j的使用有兩種方式,一種是混合綁定(concrete-bindings), 另一種是橋接遺產(chǎn)(bridging-legacy).
5.1 混合綁定(concrete-bindings)concrete-bindings模式指在新項(xiàng)目中 即開(kāi)發(fā)者直接使用sl4j的api來(lái)打印日志, 而底層綁定任意一種日志框架,如logback, log4j, j.u.l等.
混合綁定根據(jù)實(shí)現(xiàn)原理,基本上有兩種形式, 分別為有適配器(adapter)的綁定和無(wú)適配器的綁定.
有適配器的混合綁定是指底層沒(méi)有實(shí)現(xiàn)slf4j的接口,而是通過(guò)適配器直接調(diào)用底層日志框架的Logger, 無(wú)適配器的綁定不需要調(diào)用其它日志框架的Logger, 其本身就實(shí)現(xiàn)了slf4j的全部接口.
幾個(gè)混合綁定的包分別是:
slf4j-log4j12-1.7.21.jar(適配器, 綁定log4j, Logger由log4j-1.2.17.jar提供)
slf4j-jdk14-1.7.21.jar(適配器, 綁定l.u.l, Logger由JVM runtime, 即j.u.l庫(kù)提供)
logback-classic-1.0.13.jar(無(wú)適配器, slf4j的一個(gè)native實(shí)現(xiàn))
slf4j-simple-1.7.21.jar(無(wú)適配器,slf4j的簡(jiǎn)單實(shí)現(xiàn), 僅打印INFO及更高級(jí)別的消息, 所有輸出全部重定向到System.err, 適合小應(yīng)用)
以上幾種綁定可以無(wú)縫切換, 不需要改動(dòng)內(nèi)部代碼. 無(wú)論哪種綁定,均依賴(lài)slf4j-api.jar.
此外, 適配器綁定需要一種具體的日志框架, 如log4j綁定slf4j-log4j12-1.7.21.jar依賴(lài)log4j.jar, j.u.l綁定slf4j-jdk14-1.7.21.jar依賴(lài)j.u.l(java runtime提供); 無(wú)適配器的直接實(shí)現(xiàn), logback-classic依賴(lài)logback-core提供底層功能, slf4j-simple則不依賴(lài)其它庫(kù).
以上四種綁定的示例圖如下:
下面來(lái)分析兩個(gè)典型綁定log4j和logback的用法.
①log4j適配器綁定(slf4j-log4j12)
配置:
org.slf4j slf4j-log4j12 1.7.21
注意: 添加上述適配器綁定配置后會(huì)自動(dòng)拉下來(lái)兩個(gè)依賴(lài)庫(kù), 分別是slf4j-api-1.7.21.jar和log4j-1.2.17.jar
基本邏輯: 用戶層 <- 中間層 <- 底層基礎(chǔ)日志框架層
org.slf4j.impl.Log4jLoggerFactory <- StaticLoggerBinder.getSingleton().getLoggerFactory()<- org.sl4j.impl.StaticLoggerBinder.getSingleton().getLoggerFactory() <- 具體的日志框庫(kù)的Logger
其中org.slf4j.impl.Log4jLoggerFactory在應(yīng)用層調(diào)用, StaticLoggerBinder在中間層實(shí)現(xiàn), 獲取具體的日志框庫(kù)的Logger
綁定實(shí)例圖如下:
應(yīng)用層(slf4j-api-1.7.21.jar)
用戶調(diào)用org.slf4j.impl.Log4jLoggerFactory.getLogger獲取底層具體的Logger:
// Foo.java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Foo { private final static Logger logger = LoggerFactory.getLogger(CommuteNaviInfoParser.class); public static void main() { logger.info("info:{}..", "hello, sl4j"); } }
適配層(slf4j-log4j12-1.7.21.jar)
由應(yīng)用層org.slf4j.impl.Log4jLoggerFactory.getLogger內(nèi)部創(chuàng)建適配層的StaticLoggerBinder:
public static Logger getLogger(Class> clazz) { return StaticLoggerBinder.getSingleton().getLoggerFactory(); }
接下來(lái)直接由StaticLoggerBinder獲取具體的Logger:
private StaticLoggerBinder() { loggerFactory = new Log4jLoggerFactory(); } public Log4jLoggerFactory() { // force log4j to initialize org.apache.log4j.LogManager.getRootLogger(); }
注意, 各個(gè)StaticLoggerBinder均在適配層實(shí)現(xiàn), 放在org.slf4j.impl中.
② slf4j綁定到logback-classic上
配置:
ch.qos.logback logback-classic 1.1.7
注意: 添加上述適配器綁定配置后會(huì)自動(dòng)拉下來(lái)兩個(gè)依賴(lài)庫(kù), 分別是slf4j-api-1.7.21.jar和logback-core-1.0.13.jar
logback-classic沒(méi)有適配器層, 而是在logback-classic-1.0.13.jar的ch.qos.logback.classic.Logger直接實(shí)現(xiàn)了slf4j的org.slf4j.Logger, 并強(qiáng)依賴(lài)ch.qos.logback.core中的大量基礎(chǔ)類(lèi):
import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.spi.LocationAwareLogger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.classic.util.LoggerNameUtil; import ch.qos.logback.core.Appender; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.spi.AppenderAttachable; import ch.qos.logback.core.spi.AppenderAttachableImpl; import ch.qos.logback.core.spi.FilterReply; public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable, Serializable {}
綁定示例圖:
5.2 橋接遺產(chǎn)(bridging-legacy)橋接遺產(chǎn)用法主要針對(duì)歷史遺留項(xiàng)目, 不論是用log4j寫(xiě)的, j.c.l寫(xiě)的,還是j.u.l寫(xiě)的, 都可以在不改動(dòng)代碼的情況下具有另外一種日志框架的能力.
比如,你的項(xiàng)目使用java提供的原生日志庫(kù)j.u.l寫(xiě)的, 使用slf4j的bridging-legacy模式,便可在不改動(dòng)一行代碼的情況下瞬間具有l(wèi)og4j的全部特性.
說(shuō)得更直白一些,就是你的項(xiàng)目代碼可能是5年前寫(xiě)的, 當(dāng)時(shí)由于沒(méi)得選擇, 用了一個(gè)比較垃圾的日志框架, 有各種缺陷和問(wèn)題, 如不能按天存儲(chǔ), 不能控制大小, 支持的appender很少, 無(wú)法存入數(shù)據(jù)庫(kù)等. 你很想對(duì)這個(gè)已完工并在線上運(yùn)行的項(xiàng)目進(jìn)行改造, 顯然, 直接改代碼, 把舊的日志框架替換掉是不現(xiàn)實(shí)的, 因?yàn)楹苡锌赡芤氩豢深A(yù)期的bug.
那么,如何在不修改代碼的前提下, 替換掉舊的日志框架,引入更優(yōu)秀且成熟的日志框架如如log4j和logback呢? slf4j的bridging-legacy模式便是為了解決這個(gè)痛點(diǎn).
slf4j以slf4j-api為中間層, 將上層舊日志框架的消息轉(zhuǎn)發(fā)到底層綁定的新日志框架上.
基于不同的底層框架,以SLF4J作為中轉(zhuǎn)層,有如下幾種組合用法:
基于j.u.l的facade使用
上述facade將slf4j-api.jar綁定到底層基礎(chǔ)日志庫(kù)j.u.l(jvm runtime)上. slf4j-api和底層日志庫(kù)的Logger通過(guò)適配器連接.
基于logback-classic的facade使用
基于log4j的facade使用
舉例說(shuō)明上述facade的使用, 以便于大家理解.
假如我有一個(gè)已完成的使用了舊日志框架commons-loggings的項(xiàng)目,現(xiàn)在想把它替換成log4j以獲得更多更好的特性.
項(xiàng)目的maven舊配置如下:
commons-logging commons-logging 1.2
項(xiàng)目代碼:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Created by xialeizhou on 16/9/20. */ public class MainTest { private static Log logger = LogFactory.getLog(MainTest.class); public static void main(String[] args) throws InterruptedException { logger.info("hello,world"); } }
項(xiàng)目打印的基于commons-logging的日志顯示在console上,具體如下:
十月 23, 2016 6:52:00 下午 MainTest main 信息: hello,world
下面我們對(duì)項(xiàng)目改造, 將commongs-logging框架的日志轉(zhuǎn)發(fā)到log4j上. 改造很簡(jiǎn)單, 我們將commongs-logging依賴(lài)刪除, 替換為相應(yīng)的facade(此處為jcl-over-slf4j.jar), 并在facade下面掛一個(gè)5.1的混合綁定即可.
具體來(lái)講, 將commons-logging.jar替換成jcl-over-slf4j.jar, 并加入適配器slf4j-log412.jar(注意, 加入slf4j-log412.jar后會(huì)自動(dòng)pull下來(lái)另外兩個(gè)jar包), 所以實(shí)際最終只需添加facadejcl-over-slf4j.jar和5.1節(jié)混合綁定中相同的jar包slf4j-log412.jar即可.
改造后的maven配置:
org.slf4j jcl-over-slf4j 1.7.21 org.slf4j slf4j-log4j12 1.7.21
現(xiàn)在, 我們的舊項(xiàng)目在沒(méi)有改一行代碼的情況下具有了log4j的全部特性, 下面進(jìn)行測(cè)試.
在resources/下新建一個(gè)log4j.properties文件, 對(duì)commongs-logging庫(kù)的日志輸出進(jìn)行定制化:
# Root logger option log4j.rootLogger=INFO, stdout, fout # Redirect log messages to console log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.Threshold = INFO log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n # add a FileAppender to the logger fout log4j.appender.fout=org.apache.log4j.FileAppender # create a log file log4j.appender.fout.File=royce-testing.log log4j.appender.fout.layout=org.apache.log4j.PatternLayout # use a more detailed message pattern log4j.appender.fout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
重新編譯運(yùn)行, console輸出變?yōu)?
2016-10-23 19:26:15 INFO MainTest:11 - hello,world
同時(shí)在當(dāng)前目錄生成了一個(gè)日志文件:
% cat royce-testing.log INFO 2016-10-23 19:26:15,341 0 MainTest [main] hello,world
可見(jiàn), 基于facade的日志框架橋接已經(jīng)生效, 我們?cè)俨桓膭?dòng)代碼的前提下,讓commons-logging日志框架具有了log4j12的全部特性.
6. 參考文獻(xiàn)http://stackoverflow.com/ques...
http://logback.qos.ch/reasons...
http://stackoverflow.com/ques...
http://stackoverflow.com/ques...
http://stackoverflow.com/ques...
https://blog.frankel.ch/thoug...
https://en.wikipedia.org/wiki...
http://slf4j.org/faq.html
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66034.html
摘要:用戶態(tài)不能干擾內(nèi)核態(tài)所以指令就有兩種特權(quán)指令和非特權(quán)指令不同的狀態(tài)對(duì)應(yīng)不同的指令。非特權(quán)指令所有程序均可直接使用。用戶態(tài)常態(tài)目態(tài)執(zhí)行非特權(quán)指令。 這是我今年從三月份開(kāi)始,主要的大廠面試經(jīng)過(guò),有些企業(yè)面試的還沒(méi)來(lái)得及整理,可能有些沒(méi)有帶答案就發(fā)出來(lái)了,還請(qǐng)各位先思考如果是你怎么回答面試官?這篇文章會(huì)持續(xù)更新,請(qǐng)各位持續(xù)關(guān)注,希望對(duì)你有所幫助! 面試清單 平安產(chǎn)險(xiǎn) 飛豬 上汽大通 浩鯨科...
摘要:開(kāi)頭正式開(kāi)啟我入職的里程,現(xiàn)在已是工作了一個(gè)星期了,這個(gè)星期算是我入職的過(guò)渡期,算是知道了學(xué)校生活和工作的差距了,總之,盡快習(xí)慣這種生活吧。當(dāng)時(shí)是看的廖雪峰的博客自己也用做爬蟲(chóng)寫(xiě)過(guò)幾篇博客,不過(guò)有些是在前人的基礎(chǔ)上寫(xiě)的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開(kāi)頭 2017.08.21 正式開(kāi)啟我...
摘要:前提好幾周沒(méi)更新博客了,對(duì)不斷支持我博客的童鞋們說(shuō)聲抱歉了。熟悉我的人都知道我寫(xiě)博客的時(shí)間比較早,而且堅(jiān)持的時(shí)間也比較久,一直到現(xiàn)在也是一直保持著更新?tīng)顟B(tài)。 showImg(https://segmentfault.com/img/remote/1460000014076586?w=1920&h=1080); 前提 好幾周沒(méi)更新博客了,對(duì)不斷支持我博客的童鞋們說(shuō)聲:抱歉了!。自己這段時(shí)...
摘要:如果應(yīng)用發(fā)生了內(nèi)存泄漏問(wèn)題,就會(huì)進(jìn)行檢測(cè)生成報(bào)告,并且提供切實(shí)可行的方案去掉這個(gè)問(wèn)題。主要特性實(shí)時(shí)的內(nèi)存泄漏檢測(cè)和告警一份包含時(shí)間,內(nèi)存大小,速度以及泄漏事件的重要級(jí)別的報(bào)告。 在這篇文章中我們決定收集制作一個(gè)關(guān)于這類(lèi)工具的簡(jiǎn)略名單,他們中的大多數(shù)工具只是最近推出的。其中一些工具是為Java定制的,但也有一些是支持其他語(yǔ)言。但對(duì)于Java項(xiàng)目而言,他們都是非常好的,并且擁有同一個(gè)愿景:...
閱讀 1330·2021-11-24 09:38
閱讀 3268·2021-11-22 12:03
閱讀 4203·2021-11-11 10:59
閱讀 2334·2021-09-28 09:36
閱讀 1043·2021-09-09 09:32
閱讀 3434·2021-08-05 10:00
閱讀 2541·2021-07-23 15:30
閱讀 2985·2019-08-30 13:12