老板下了死命令赐写,要把日志系統(tǒng)切換到Logback

Log4j 介紹過了,SLF4J 也介紹過了绎秒,那接下來,你懂的厨喂,Logback 就要隆重地登場了,畢竟它哥仨有一個爹庄呈,那就是巨佬 Ceki Gulcu蜕煌。

就在昨天,老板聽我說完 Logback 有多牛逼之后诬留,徹底動心了斜纪,對我下了死命令,“這么好的日志系統(tǒng)文兑,你還不趕緊點盒刚,把它切換到咱的項目當(dāng)中!”

我們項目之前用的 Log4j绿贞,在我看來因块,已經(jīng)足夠用了,畢竟是小公司籍铁,性能上的要求沒那么苛刻贮聂。

01、Logback 強(qiáng)在哪

1)非常自然地實現(xiàn)了 SLF4J寨辩,不需要像 Log4j 和 JUL 那樣加一個適配層。

2)Spring Boot 的默認(rèn)日志框架使用的是 Logback歼冰。一旦某款工具庫成為了默認(rèn)選項靡狞,那就說明這款工具已經(jīng)超過了其他競品。

注意看下圖(證據(jù)找到了隔嫡,來自 Spring Boot 官網(wǎng)):

也可以通過源碼的形式看得到:

3)支持自動重新加載配置文件甸怕,不需要另外創(chuàng)建掃描線程來監(jiān)視。

4)既然是巨佬的新作腮恩,那必然在性能上有了很大的提升梢杭,不然呢?

02秸滴、Logback 使用示例

第一步武契,在 pom.xml 文件中添加 Logback 的依賴:

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

Maven 會自動導(dǎo)入另外兩個依賴:

logback-core 是 Logback 的核心,logback-classic 是 SLF4J 的實現(xiàn)荡含。

第二步咒唆,來個最簡單的測試用例:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 微信搜「沉默王二」,回復(fù)關(guān)鍵字 PDF
 */
public class Test {
    static Logger logger = LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        logger.debug("logback");
    }
}

Logger 和 LoggerFactory 都來自 SLF4J释液,所以如果項目是從 Log4j + SLF4J 切換到 Logback 的話全释,此時的代碼是零改動的。

運行 Test 類误债,可以在控制臺看到以下信息:

12:04:20.149 [main] DEBUG com.itwanger.Test - logback

在沒有配置文件的情況下浸船,一切都是默認(rèn)的妄迁,Logback 的日志信息會輸出到控制臺±蠲可以通過 StatusPrinter 來打印 Logback 的內(nèi)部信息:

LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);

在 main 方法中添加以上代碼后登淘,再次運行 Test 類,可以在控制臺看到以下信息:

12:59:22.314 [main] DEBUG com.itwanger.Test - logback
12:59:22,261 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:59:22,262 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:59:22,262 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:59:22,268 |-INFO in ch.qos.logback.classic.BasicConfigurator@5e853265 - Setting up default configuration.

也就是說项戴,Logback 會在 classpath 路徑下先尋找 logback-test.xml 文件形帮,沒有找到的話,尋找 logback.groovy 文件周叮,還沒有的話辩撑,尋找 logback.xml 文件,都找不到的話仿耽,就輸出到控制臺合冀。

一般來說,我們會在本地環(huán)境中配置 logback-test.xml项贺,在生產(chǎn)環(huán)境下配置 logback.xml君躺。

第三步,在 resource 目錄下增加 logback-test.xml 文件开缎,內(nèi)容如下所示:

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

Logback 的配置文件非常靈活棕叫,最基本的結(jié)構(gòu)為 <configuration> 元素,包含 0 或多個 <appender> 元素奕删,其后跟 0 或多個 <logger> 元素俺泣,其后再跟最多只能存在一個的 <root> 元素。

1)配置 appender完残,也就是配置日志的輸出目的地伏钠,通過 name 屬性指定名字,通過 class 屬性指定目的地:

  • ch.qos.logback.core.ConsoleAppender:輸出到控制臺谨设。
  • ch.qos.logback.core.FileAppender:輸出到文件熟掂。
  • ch.qos.logback.core.rolling.RollingFileAppender:文件大小超過閾值時產(chǎn)生一個新文件。

除了輸出到本地扎拣,還可以通過 SocketAppender 和 SSLSocketAppender 輸出到遠(yuǎn)程設(shè)備赴肚,通過 SMTPAppender 輸出到郵件。甚至可以通過 DBAppender 輸出到數(shù)據(jù)庫中二蓝。

encoder 負(fù)責(zé)把日志信息轉(zhuǎn)換成字節(jié)數(shù)組尊蚁,并且把字節(jié)數(shù)組寫到輸出流。

pattern 用來指定日志的輸出格式:

  • %d:輸出的時間格式侣夷。
  • %thread:日志的線程名横朋。
  • %-5level:日志的輸出級別,填充到 5 個字符百拓。比如說 info 只有 4 個字符琴锭,就填充一個空格晰甚,這樣日志信息就對齊了。

反例(沒有指定 -5 的情況):

  • %logger{length}:logger 的名稱决帖,length 用來縮短名稱厕九。沒有指定表示完整輸出;0 表示只輸出 logger 最右邊點號之后的字符串地回;其他數(shù)字表示輸出小數(shù)點最后邊點號之前的字符數(shù)量扁远。
  • %msg:日志的具體信息。
  • %n:換行符刻像。
  • %relative:輸出從程序啟動到創(chuàng)建日志記錄的時間畅买,單位為毫秒。

2)配置 root细睡,它只支持一個屬性——level谷羞,值可以為:TRACE、DEBUG溜徙、INFO湃缎、WARN、ERROR蠢壹、ALL嗓违、OFF。

appender-ref 用來指定具體的 appender图贸。

3)查看內(nèi)部狀態(tài)信息蹂季。

可以在代碼中通過 StatusPrinter 來打印 Logback 內(nèi)部狀態(tài)信息,也可以通過在 configuration 上開啟 debug 來打印內(nèi)部狀態(tài)信息求妹。

重新運行 Test 類,可以在控制臺看到以下信息:

13:54:54,718 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/Users/maweiqing/Documents/GitHub/JavaPointNew/codes/logbackDemo/target/classes/logback-test.xml]
13:54:54,826 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
13:54:54,828 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
13:54:54,833 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
13:54:54,850 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
13:54:54,850 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
13:54:54,850 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
13:54:54,851 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@f8c1ddd - Registering current configuration as safe fallback point
13:54:54.853 [main] DEBUG com.itwanger.Test - logback

4)自動重載配置佳窑。

之前提到 Logback 很強(qiáng)的一個功能就是支持自動重載配置制恍,那想要啟用這個功能也非常簡單,只需要在 configuration 元素上添加 scan=true 即可神凑。

<configuration scan="true">
    ...
</configuration>

默認(rèn)情況下净神,掃描的時間間隔是一分鐘一次。如果想要調(diào)整時間間隔溉委,可以通過 scanPeriod 屬性進(jìn)行調(diào)整鹃唯,單位可以是毫秒(milliseconds)、秒(seconds)瓣喊、分鐘(minutes)或者小時(hours)坡慌。

下面這個示例指定的時間間隔是 30 秒:

<configuration scan="true" scanPeriod="30 seconds"
   ...
</configuration>

注意:如果指定了時間間隔,沒有指定時間單位藻三,默認(rèn)的時間單位為毫秒洪橘。

當(dāng)設(shè)置 scan=true 后跪者,Logback 會起一個 ReconfigureOnChangeTask 的任務(wù)來監(jiān)視配置文件的變化。

03熄求、把 log4j.properties 轉(zhuǎn)成 logback-test.xml

如果你的項目以前用的 Log4j渣玲,那么可以通過下面這個網(wǎng)址把 log4j.properties 轉(zhuǎn)成 logback-test.xml:

http://logback.qos.ch/translator/

把之前 log4j.properties 的內(nèi)容拷貝一份:

### 設(shè)置###
log4j.rootLogger = debug,stdout,D,E

### 輸出信息到控制臺 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 輸出DEBUG 級別以上的日志到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 輸出ERROR 級別以上的日志到=error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

粘貼到該網(wǎng)址的文本域:

點擊「Translate」,可以得到以下內(nèi)容:

<?xml version="1.0" encoding="UTF-8"?>

<!-- For assistance related to logback-translator or configuration  -->
<!-- files in general, please contact the logback user mailing list -->
<!-- at http://www.qos.ch/mailman/listinfo/logback-user             -->
<!--                                                                -->
<!-- For professional support please see                            -->
<!--    http://www.qos.ch/shop/products/professionalSupport         -->
<!--                                                                -->
<configuration>
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <Target>System.out</Target>
    <encoder>
      <pattern>[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n</pattern>
    </encoder>
  </appender>
  <appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
    <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
    <!--for further documentation-->
    <Append>true</Append>
    <File>debug.log</File>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>DEBUG</level>
    </filter>
  </appender>
  <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
    <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
    <!--for further documentation-->
    <File>error.log</File>
    <Append>true</Append>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>ERROR</level>
    </filter>
  </appender>
  <root level="debug">
    <appender-ref ref="stdout"/>
    <appender-ref ref="D"/>
    <appender-ref ref="E"/>
  </root>
</configuration>

可以確認(rèn)一下內(nèi)容弟晚,發(fā)現(xiàn)三個 appender 都在忘衍。

但是呢,轉(zhuǎn)換后的文件并不能直接使用卿城,需要稍微做一些調(diào)整枚钓,因為:

第一,日志的格式化有細(xì)微的不同藻雪,Logback 中沒有 %l秘噪。

第二,RollingFileAppender 需要指定 RollingPolicy 和 TriggeringPolicy勉耀,前者負(fù)責(zé)日志的滾動功能指煎,后者負(fù)責(zé)日志滾動的時機(jī)。如果 RollingPolicy 也實現(xiàn)了 TriggeringPolicy 接口便斥,那么只需要設(shè)置 RollingPolicy 就好了至壤。

TimeBasedRollingPolicy 和 SizeAndTimeBasedRollingPolicy 是兩種最常用的滾動策略。

TimeBasedRollingPolicy 同時實現(xiàn)了 RollingPolicy 與 TriggeringPolicy 接口枢纠,因此使用 TimeBasedRollingPolicy 的時候就可以不指定 TriggeringPolicy像街。

TimeBasedRollingPolicy 可以指定以下屬性:

  • fileNamePattern,用來定義文件的名字(必選項)晋渺。它的值應(yīng)該由文件名加上一個 %d 的占位符镰绎。%d 應(yīng)該包含 java.text.SimpleDateFormat 中規(guī)定的日期格式,缺省是 yyyy-MM-dd木西。滾動周期是通過 fileNamePattern 推斷出來的畴栖。

  • maxHistory,最多保留多少數(shù)量的日志文件(可選項)八千,將會通過異步的方式刪除舊的文件吗讶。比如,你指定按月滾動恋捆,指定 maxHistory = 6照皆,那么 6 個月內(nèi)的日志文件將會保留,超過 6 個月的將會被刪除沸停。

  • totalSizeCap膜毁,所有日志文件的大小(可選項)。超出這個大小時爽茴,舊的日志文件將會被異步刪除葬凳。需要配合 maxHistory 屬性一起使用,并且是第二條件室奏。

來看下面這個 RollingFileAppender 配置:

<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>debug.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--            按天滾動 -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--            保存 30 天的歷史記錄火焰,最大大小為 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

基于按天滾動的文件策略,最多保留 30 天胧沫,最大大小為 30G昌简。

SizeAndTimeBasedRollingPolicy 比 TimeBasedRollingPolicy 多了一個日志文件大小設(shè)定的屬性:maxFileSize,其他完全一樣绒怨。

基于我們對 RollingPolicy 的了解纯赎,可以把 logback-test.xml 的內(nèi)容調(diào)整為以下內(nèi)容:

<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Target>System.out</Target>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <Append>true</Append>
    <File>debug.log</File>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--            按天輪轉(zhuǎn) -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--            保存 30 天的歷史記錄,最大大小為 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>
    <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--            按天輪轉(zhuǎn) -->
            <fileNamePattern>error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--            保存 30 天的歷史記錄南蹂,最大大小為 30GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>
    <root level="debug">
        <appender-ref ref="stdout"/>
        <appender-ref ref="D"/>
        <appender-ref ref="E"/>
    </root>
</configuration>

修改 Test 類的內(nèi)容:

public class Test {
    static Logger logger = LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        logger.debug("logback");
        logger.error("logback");
    }
}

運行后犬金,可以在 target 目錄下看到兩個文件:debug.log 和 errror.log。

到此為止六剥,項目已經(jīng)從 Log4j 切換到 Logback 了晚顷,過程非常的絲滑順暢,嘿嘿疗疟。

04该默、Logback 手冊

Logback 的官網(wǎng)上是有一份手冊的,非常詳細(xì)策彤,足足 200 多頁栓袖,只不過是英文版的。小伙伴們可以看完我這篇文章入門實操的 Logback 教程后店诗,到下面的地址看官方手冊裹刮。

http://logback.qos.ch/manual/index.html

如果英文閱讀能力有限的話,可以到 GitHub 上查看雷鋒翻譯的中文版:

https://github.com/itwanger/logback-chinese-manual

當(dāng)然了庞瘸,還有一部分小伙伴喜歡看離線版的 PDF捧弃,我已經(jīng)整理好了:

鏈接:https://pan.baidu.com/s/16FrbwycYUUIfKknlLhRKYA 密碼:bptl

實話實話吧,白嫖的感覺就是舒服恕洲,趕緊去下載吧塔橡!日常求個三連梅割,謝謝你勤勞的手指霜第,嘿嘿。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末户辞,一起剝皮案震驚了整個濱河市泌类,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖刃榨,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弹砚,死亡現(xiàn)場離奇詭異,居然都是意外死亡枢希,警方通過查閱死者的電腦和手機(jī)桌吃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苞轿,“玉大人茅诱,你說我怎么就攤上這事“嶙洌” “怎么了瑟俭?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長契邀。 經(jīng)常有香客問我摆寄,道長,這世上最難降的妖魔是什么坯门? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任微饥,我火速辦了婚禮,結(jié)果婚禮上田盈,老公的妹妹穿的比我還像新娘畜号。我一直安慰自己,他們只是感情好允瞧,可當(dāng)我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布简软。 她就那樣靜靜地躺著,像睡著了一般述暂。 火紅的嫁衣襯著肌膚如雪痹升。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天畦韭,我揣著相機(jī)與錄音疼蛾,去河邊找鬼。 笑死艺配,一個胖子當(dāng)著我的面吹牛察郁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播转唉,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皮钠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赠法?” 一聲冷哼從身側(cè)響起麦轰,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后款侵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體末荐,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年新锈,在試婚紗的時候發(fā)現(xiàn)自己被綠了甲脏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡妹笆,死狀恐怖剃幌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晾浴,我是刑警寧澤负乡,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站脊凰,受9級特大地震影響抖棘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狸涌,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一切省、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帕胆,春花似錦朝捆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脸秽,卻和暖如春儒老,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背记餐。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工驮樊, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人片酝。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓囚衔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雕沿。 傳聞我的和親對象是個殘疾皇子练湿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,665評論 2 354