【實(shí)戰(zhàn)指南】輕松自研嵌入式日志框架俗或,6大功能亮點(diǎn)一文讀懂

【實(shí)戰(zhàn)指南】輕松自研嵌入式日志框架,6大功能亮點(diǎn)一文讀懂

[TOC]

引言

? 日志系統(tǒng)雖非項(xiàng)目直接功能岁忘,卻是開發(fā)者背后的強(qiáng)大輔助辛慰。優(yōu)秀的日志設(shè)計(jì)如同給程序安裝了北斗定位,讓問(wèn)題排查變得直觀快捷干像,極大提升開發(fā)效率與項(xiàng)目維護(hù)體驗(yàn)昆雀。本文旨在深入探討并詳細(xì)記載自主研發(fā)日志框架的具體技術(shù)和實(shí)施策略。

概述

? 日志框架作為一種普遍應(yīng)用于軟件開發(fā)領(lǐng)域的關(guān)鍵工具蝠筑,已發(fā)展出眾多成熟案例狞膘,諸如Android平臺(tái)的logcatglog什乙、Log4cpp等挽封。汲取這些成熟的框架的經(jīng)驗(yàn),本篇主要從需求分析臣镣、設(shè)計(jì)方案辅愿、實(shí)現(xiàn)細(xì)節(jié)和難點(diǎn)智亮、測(cè)試、總結(jié)部分記錄自研嵌入式框架的細(xì)節(jié)点待。

需求分析

? 在使用者的角度阔蛉,對(duì)于日志功能的需求主要概括如下:

  1. 日志分級(jí)管理
    實(shí)現(xiàn)包括DEBUGINFO癞埠、WARNING状原、ERROR在內(nèi)的多級(jí)別日志輸出接口,并允許用戶靈活配置和動(dòng)態(tài)切換日志輸出級(jí)別閾值苗踪。
  2. 異步處理與并發(fā)安全性
    系統(tǒng)應(yīng)具備異步日志記錄能力颠区,確保在多線程或并發(fā)環(huán)境下,日志信息記錄的完整性及正確性通铲,且內(nèi)部實(shí)現(xiàn)需保證線程安全毕莱。
  3. 詳盡上下文信息記錄
  • 日志條目應(yīng)包含精確的時(shí)間戳、進(jìn)程ID颅夺、代碼行號(hào)以及模塊標(biāo)識(shí)等關(guān)鍵上下文信息朋截,以便于進(jìn)行問(wèn)題定位和性能診斷。
  • 各模塊在使用日志接口時(shí)吧黄,傳入對(duì)應(yīng)的模塊標(biāo)識(shí)质和,方便調(diào)試過(guò)濾關(guān)鍵日志。
  1. 滾動(dòng)日志歸檔策略
  • 實(shí)時(shí)存儲(chǔ)與文件滾動(dòng):日志應(yīng)即時(shí)被寫入到本地文件稚字,確保信息的完整留存和持久化饲宿。
  • 文件大小限制與自動(dòng)分卷:?jiǎn)蝹€(gè)日志文件具有預(yù)設(shè)的最大容量限制,當(dāng)達(dá)到上限時(shí)胆描,系統(tǒng)自動(dòng)創(chuàng)建新文件進(jìn)行存儲(chǔ)瘫想。
  • 有序歸檔命名規(guī)則:采用“文件名.log.序號(hào)”形式命名歷史日志文件,如sparrow.log.1昌讲、sparrow.log.2等国夜;當(dāng)前活動(dòng)日志文件統(tǒng)一命名為sparrow.log,不帶序號(hào)短绸,以此保持最新日志的訪問(wèn)便捷性车吹。
  1. 高效資源利用
    設(shè)計(jì)上注重輕量化,確保日志系統(tǒng)占用較低的內(nèi)存和CPU資源醋闭,在不影響系統(tǒng)性能的前提下完成日志記錄工作窄驹。
  2. 便捷API接口
    提供一套簡(jiǎn)潔明了、易于集成的API接口证逻,使得開發(fā)人員能夠輕松地在代碼中添加和使用日志功能乐埠。

設(shè)計(jì)方案

? 基于上述日志功能需求分析,以下是設(shè)計(jì)方案的概要框架:

  1. 日志分級(jí)管理設(shè)計(jì)
  • 日志級(jí)別定義:
    設(shè)計(jì)一組枚舉類型表示不同日志級(jí)別(DEBUGINFO丈咐、WARNING瑞眼、ERROR),并構(gòu)建相應(yīng)的日志輸出接口棵逊。
    配置模塊可讀取環(huán)境變量或配置文件伤疙,動(dòng)態(tài)設(shè)置日志級(jí)別閾值,低于此閾值的日志將不會(huì)被記錄辆影。
  • 日志過(guò)濾器
    在日志輸出前增加過(guò)濾邏輯徒像,根據(jù)當(dāng)前設(shè)置的日志級(jí)別決定是否需要輸出指定級(jí)別的日志消息。
  1. 異步處理與并發(fā)安全性設(shè)計(jì)
  • 構(gòu)建日志共享緩存區(qū)
    映射一片共享內(nèi)存秸歧,通過(guò)環(huán)形buffer的形式管理厨姚,用于實(shí)時(shí)緩存產(chǎn)生的日志衅澈。模塊使用時(shí)键菱,只會(huì)實(shí)時(shí)的將日志丟到共享內(nèi)存中,而不會(huì)參與日志存儲(chǔ)業(yè)務(wù)今布,提升日志的寫入效率经备。
  • 創(chuàng)建獨(dú)立的日志管理進(jìn)程LogManager
    此進(jìn)程負(fù)責(zé)實(shí)時(shí)讀取緩存區(qū)的日志,并寫入到本地文件中部默。同時(shí)侵蒙,此進(jìn)程負(fù)責(zé)實(shí)現(xiàn)日志文件的管理等功能。
  • 通過(guò)信號(hào)量傅蹂,實(shí)現(xiàn)并發(fā)同步
    多進(jìn)程在寫入共享內(nèi)存時(shí)纷闺,通過(guò)信號(hào)量實(shí)現(xiàn)進(jìn)程間同步,避免日志寫入錯(cuò)亂份蝴。
  1. 詳盡上下文信息記錄設(shè)計(jì)
  • 日志輸出格式封裝
    將時(shí)間戳犁功、進(jìn)程ID、代碼行號(hào)婚夫、模塊名等信息封裝到日志中浸卦,伴隨每一條日志消息一起輸出。
  1. 滾動(dòng)日志歸檔策略設(shè)計(jì)
    為確保日志文件的有效管理和存儲(chǔ)案糙,設(shè)計(jì)了一套文件滾動(dòng)機(jī)制限嫌。當(dāng)當(dāng)前日志文件sparrow.log的大小超出預(yù)設(shè)閾值時(shí),系統(tǒng)將自動(dòng)執(zhí)行回滾操作:
  • 容量監(jiān)測(cè)階段
    實(shí)施動(dòng)態(tài)監(jiān)控sparrow.log文件大小时捌,一旦觸及預(yù)先設(shè)定的容量上限怒医,即刻觸發(fā)回滾流程。
  • 文件遷移過(guò)程
    將現(xiàn)有的sparrow.log文件通過(guò)原子操作進(jìn)行重命名奢讨,新增后綴".1"變?yōu)閟parrow.log.1裆熙,以保存歷史日志內(nèi)容。
  • 新文件初始化
    立即創(chuàng)建一個(gè)新的sparrow.log文件,用于接收接下來(lái)產(chǎn)生的實(shí)時(shí)日志信息入录。
  • 滾動(dòng)策略連續(xù)執(zhí)行
    當(dāng)sparrow.log文件再度達(dá)到閾值時(shí)蛤奥,延續(xù)相同邏輯,依次將sparrow.log更新為sparrow.log.n+1(n為歷史文件序號(hào))僚稿,并創(chuàng)建新的sparrow.log凡桥。
  • 歷史日志管理
    設(shè)定保留的歷史日志文件數(shù)量上限,超出限額的最老日志文件將被適時(shí)清理蚀同,確保存儲(chǔ)空間的有效利用缅刽。
  • 日志文件壓縮設(shè)計(jì)
    可選功能。若文件存儲(chǔ)閾值定義較大蠢络,產(chǎn)生歷史文件時(shí)衰猛,將其壓縮存儲(chǔ)。
  1. 資源效率優(yōu)化設(shè)計(jì)
  • 共享環(huán)形緩沖區(qū)管理
    1)將實(shí)時(shí)日志先存入共享內(nèi)存中刹孔,避免大量的文件讀寫操作啡省。
    2)引入環(huán)形緩沖區(qū)管理共享內(nèi)存,實(shí)現(xiàn)共享內(nèi)存的循環(huán)使用髓霞,避免創(chuàng)建大容量共享內(nèi)存卦睹。
    1. 控制緩存大小,環(huán)形設(shè)計(jì)避免共享內(nèi)存寫入溢出方库。
  1. 便捷API接口設(shè)計(jì)
    將日志接口封裝成宏函數(shù)结序,方便各模塊調(diào)用。

實(shí)現(xiàn)細(xì)節(jié)

  1. 日志框架組成
    基于上述設(shè)計(jì)方案的概括纵潦,日志框架主要?jiǎng)澐譃槿齻€(gè)相互協(xié)作的部分徐鹤,在實(shí)際環(huán)境運(yùn)行示意圖如下:
LogManager.jpg

注:箭頭指示日志數(shù)據(jù)的傳輸路徑及其流向

  • 對(duì)外接口(API)
    此組件專為應(yīng)用開發(fā)者設(shè)計(jì),嵌入于使用者進(jìn)程內(nèi)部邀层,承擔(dān)著將產(chǎn)生的日志實(shí)時(shí)推送至共享緩存區(qū)的責(zé)任返敬,通過(guò)簡(jiǎn)潔高效的API接口,確保開發(fā)者能夠輕松被济、靈活地在不同模塊中記錄不同級(jí)別的日志救赐。
  • 日志核心管理(LogManagerSrv)
    作為獨(dú)立運(yùn)行的日志管理進(jìn)程,核心組件專注于日志的存儲(chǔ)和文件管理任務(wù)只磷。它負(fù)責(zé)從共享緩存區(qū)讀取日志信息经磅,執(zhí)行日志文件的滾動(dòng)歸檔策略,以及對(duì)文件大小钮追、歷史日志數(shù)量進(jìn)行有效控制预厌,確保日志數(shù)據(jù)的持久化存儲(chǔ)和系統(tǒng)資源的高效利用。
  • 調(diào)試輸出(終端Debug)
    調(diào)試輸出進(jìn)程主要用于開發(fā)和調(diào)試場(chǎng)景元媚。在終端手動(dòng)執(zhí)行后轧叽,它實(shí)時(shí)監(jiān)聽共享緩存區(qū)中的日志數(shù)據(jù)苗沧,并將它們立即顯示在終端界面,便于開發(fā)人員實(shí)時(shí)觀測(cè)和分析程序運(yùn)行狀況炭晒。
  1. 對(duì)外接口(API)
    這部分代碼相對(duì)簡(jiǎn)潔待逞,其核心在于對(duì)日志使用接口進(jìn)行了一層邏輯封裝,并進(jìn)一步通過(guò)宏定義的形式轉(zhuǎn)化為易于使用的宏接口网严,旨在為開發(fā)者提供更為便捷的日志調(diào)用方式识樱。
#define LOGD(tag, fmt, args...)     SprLog::GetInstance()->d(tag, "%4d " fmt, __LINE__, ##args)
#define LOGI(tag, fmt, args...)     SprLog::GetInstance()->i(tag, "%4d " fmt, __LINE__, ##args)
#define LOGW(tag, fmt, args...)     SprLog::GetInstance()->w(tag, "%4d " fmt, __LINE__, ##args)
#define LOGE(tag, fmt, args...)     SprLog::GetInstance()->e(tag, "%4d " fmt, __LINE__, ##args)
  1. 日志核心管理(Core)
    此核心模塊承載了日志功能的核心實(shí)現(xiàn)邏輯,面對(duì)的主要挑戰(zhàn)集中在如下幾個(gè)關(guān)鍵技術(shù)環(huán)節(jié):
  • 實(shí)時(shí)存儲(chǔ)至本地
    負(fù)責(zé)實(shí)現(xiàn)日志數(shù)據(jù)從內(nèi)存到磁盤文件的實(shí)時(shí)高效寫入震束,確保日志信息的完整性和可靠性怜庸。
int LogManager::MainLoop()
{
    while (mRunning)
    {
        if (pLogMCacheMem->AvailData() <= 0) {
            usleep(10000);
            continue;
        }

        int32_t len = 0;
        int ret = pLogMCacheMem->read(&len, sizeof(int32_t));
        if (ret != 0 || len < 0) {
            SPR_LOGE("read memory failed! len = %d, ret = %d\n", len, ret);
            usleep(10000);
            continue;
        }

        std::string value;
        value.resize(len);
        char* data = const_cast<char*>(value.c_str());
        ret = pLogMCacheMem->read(data, len);
        if (ret != 0) {
            SPR_LOGE("read failed! len = %d\n", len);
        }

        RotateLogsIfNecessary(len);
        WriteToLogFile(value);
    }

    return 0;
}

MainLoop中,不停讀取環(huán)形共享內(nèi)存數(shù)據(jù)垢村,并寫入本地文件中割疾。在寫入過(guò)程中,發(fā)現(xiàn)長(zhǎng)度超過(guò)文件閾值嘉栓,則觸發(fā)日志文件回滾策略宏榕。回滾策略業(yè)務(wù)在RotateLogsIfNecessary實(shí)現(xiàn)胸懈。

  • 日志文件滾動(dòng)
    設(shè)計(jì)并執(zhí)行一套有效的日志文件回滾機(jī)制担扑,當(dāng)單個(gè)日志文件達(dá)到預(yù)設(shè)大小時(shí)恰响,需自動(dòng)創(chuàng)建新的日志文件并遷移日志趣钱,同時(shí)維護(hù)好歷史日志文件的有序命名和存儲(chǔ)。
// E.g: sparrow.log sparrow.log.1 sparrow.log.2 ...
int LogManager::RotateLogsIfNecessary(uint32_t logDataSize)
{
    uint32_t curFileSize = static_cast<uint32_t>(mLogFileStream.tellp());
    if (curFileSize + logDataSize > mMaxFileSize) {
        mLogFileStream.close();

        UpdateSuffixOfAllFiles();
        mLogFileStream.open(mLogsDirPath + '/' + mCurrentLogFile, std::ios_base::app | std::ios_base::out);
        if (!mLogFileStream.is_open()) {
            SPR_LOGE("Open %s failed!", mCurrentLogFile.c_str());
        }
    }

    return 0;
}

在回滾過(guò)程中胚宦,涉及到歷史日志文件遷移首有,在UpdateSuffixOfAllFiles實(shí)現(xiàn),篇幅有限暫不列舉枢劝。文末獲取代碼方法井联,需要自取。

  • 共享環(huán)形緩存區(qū)管理
    此部分實(shí)現(xiàn)了一個(gè)基于共享內(nèi)存的環(huán)形緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)您旁,其核心功能與常規(guī)環(huán)形緩沖區(qū)保持一致烙常,但特別之處在于它位于可供多個(gè)進(jìn)程共同訪問(wèn)的共享內(nèi)存區(qū)域中。該模塊提供的接口服務(wù)于日志數(shù)據(jù)的高效暫存和交換鹤盒,確保在多進(jìn)程環(huán)境下蚕脏,日志信息能安全、順暢地從生成者進(jìn)程傳遞至消費(fèi)者進(jìn)程(如日志管理進(jìn)程)侦锯,并在此過(guò)程中有效地避免數(shù)據(jù)沖突和丟失驼鞭。
class SharedRingBuffer
{
public:
    /**
     * @brief Constructs a master Shared Ring Buffer object.
     * @param path  The path to the shared memory.
     * @param capacity The buffer's capacity.
     *
     * Intended for use in master mode with shared memory refreshing.
     */
    SharedRingBuffer(std::string path, uint32_t capacity);

    /**
     * @brief Constructs a slave Shared Ring Buffer object
     * @param path The path to the shared memory.
     *
     * This constructor creates an instance of a slave Shared Ring Buffer, typically used by client applications.
     * It facilitates access and utilization of the shared buffer by referencing it through the specified path.
     */
    SharedRingBuffer(std::string path);
    ~SharedRingBuffer();

    bool    IsReadable()    const noexcept;
    bool    IsWriteable()   const noexcept;
    int     write(const void* data, int32_t len);
    int     read(void* data, int32_t len);
    int     DumpBuffer(void* data, int32_t len) const noexcept;

    int32_t AvailSpace()    const noexcept;
    int32_t AvailData()     const noexcept;


private:
    void    AdjustPosIfOverflow(uint32_t* pos, int32_t size) const noexcept;
    void    SetRWStatus(ECmdType type) const noexcept;
    void    DumpMemory(const char* pAddr, uint32_t size);
    void    DumpErrorInfo();

private:
    Root*       mRoot;
    void*       mData;
    uint32_t    mCapacity;
    std::mutex  mMutex;
    std::string mShmPath;
};
  1. 調(diào)試輸出(Debug)
    ? 這部分實(shí)現(xiàn)主要是將存儲(chǔ)在緩存區(qū)的日志實(shí)時(shí)顯示在終端上,便于調(diào)試時(shí)尺碰,觀察實(shí)時(shí)打印挣棕∫氚考慮到終端通過(guò)執(zhí)行tail -f sparrow.log 能達(dá)到同樣效果,故暫不實(shí)現(xiàn)洛心。

測(cè)試

  1. 測(cè)試終端實(shí)時(shí)日志打印
    本地觸發(fā)10ms一次的定時(shí)器固耘,觀察終端輸出的日志間隔是否為10ms
$ tail -f /tmp/sprlog/sparrow.log
04-12 23:53:26.048  51958       TimerM D:   76 [0x0 -> 0x5] msg.GetMsgId() = SIG_ID_SYSTEM_TIMER_NOTIFY
04-12 23:53:26.048  51958       TimerM D:   76 [0x0 -> 0x0] msg.GetMsgId() = SIG_ID_TIMER_START_SYSTEM_TIMER
04-12 23:53:26.057  51958       TimerM D:   76 [0x0 -> 0x5] msg.GetMsgId() = SIG_ID_SYSTEM_TIMER_NOTIFY
04-12 23:53:26.058  51958       TimerM D:   76 [0x0 -> 0x0] msg.GetMsgId() = SIG_ID_TIMER_START_SYSTEM_TIMER
04-12 23:53:26.068  51958       TimerM D:   76 [0x0 -> 0x5] msg.GetMsgId() = SIG_ID_SYSTEM_TIMER_NOTIFY
04-12 23:53:26.068  51958       TimerM D:   76 [0x0 -> 0x0] msg.GetMsgId() = SIG_ID_TIMER_START_SYSTEM_TIMER
04-12 23:53:26.078  51958       TimerM D:   76 [0x0 -> 0x5] msg.GetMsgId() = SIG_ID_SYSTEM_TIMER_NOTIFY
04-12 23:53:26.078  51958       TimerM D:   76 [0x0 -> 0x0] msg.GetMsgId() = SIG_ID_TIMER_START_SYSTEM_TIMER
04-12 23:53:26.088  51958       TimerM D:   76 [0x0 -> 0x5] msg.GetMsgId() = SIG_ID_SYSTEM_TIMER_NOTIFY
04-12 23:53:26.088  51958       TimerM D:   76 [0x0 -> 0x0] msg.GetMsgId() = SIG_ID_TIMER_START_SYSTEM_TIMER
04-12 23:53:26.098  51958       TimerM D:   76 [0x0 -> 0x5] msg.GetMsgId() = SIG_ID_SYSTEM_TIMER_NOTIFY

通過(guò)觀測(cè),證實(shí)了每隔10毫秒的時(shí)間間隔均能得到預(yù)期反饋词身,確認(rèn)該間隔時(shí)間設(shè)置準(zhǔn)確無(wú)誤玻驻。

  1. 測(cè)試日志回滾
    為了方便驗(yàn)證,日志文件閾值暫設(shè)置為1M偿枕。
$ ls /tmp/sprlog/ -lh
total 10M
-rw-r--r-- 1 dx dx 995K Apr 12 23:56 sparrow.log
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:55 sparrow.log.1
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:54 sparrow.log.2
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:54 sparrow.log.3
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:53 sparrow.log.4
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:52 sparrow.log.5
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:51 sparrow.log.6
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:50 sparrow.log.7
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:50 sparrow.log.8
-rw-r--r-- 1 dx dx 1.0M Apr 12 23:49 sparrow.log.9

觀察結(jié)果顯示璧瞬,日志回滾機(jī)制正在正常運(yùn)作,表現(xiàn)為sparrow.log文件大小隨著新日志的實(shí)時(shí)寫入而動(dòng)態(tài)更新渐夸,始終保持存儲(chǔ)最新內(nèi)容嗤锉。

總結(jié)

  • 對(duì)于項(xiàng)目來(lái)說(shuō),移植并使用像Android logcat這樣的成熟日志框架至關(guān)重要墓塌,本文的實(shí)現(xiàn)正是以此為參考依據(jù)瘟忱。
  • 雖然日志系統(tǒng)看似與實(shí)際功能模塊無(wú)直接關(guān)聯(lián),但其重要性不可忽視苫幢;真正動(dòng)手實(shí)現(xiàn)一套完善的日志系統(tǒng)访诱,對(duì)個(gè)人技術(shù)水平的成長(zhǎng)有著極大的提升。
  • 在實(shí)現(xiàn)日志系統(tǒng)框架時(shí)韩肝,發(fā)現(xiàn)了共享內(nèi)存應(yīng)用的一種獨(dú)特方式:通過(guò)與特定數(shù)據(jù)結(jié)構(gòu)的有機(jī)結(jié)合触菜,能實(shí)現(xiàn)數(shù)據(jù)在意外斷電情況下的記憶保留,這種方式頗具趣味性和實(shí)用性哀峻。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涡相,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剩蟀,更是在濱河造成了極大的恐慌催蝗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件育特,死亡現(xiàn)場(chǎng)離奇詭異丙号,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)缰冤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門犬缨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人锋谐,你說(shuō)我怎么就攤上這事遍尺。” “怎么了涮拗?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乾戏,是天一觀的道長(zhǎng)迂苛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鼓择,這世上最難降的妖魔是什么三幻? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮呐能,結(jié)果婚禮上念搬,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布报慕。 她就那樣靜靜地躺著膘侮,像睡著了一般据块。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音温亲,去河邊找鬼。 笑死杯矩,一個(gè)胖子當(dāng)著我的面吹牛栈虚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播史隆,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼魂务,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了逆害?” 一聲冷哼從身側(cè)響起头镊,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚣驼,失蹤者是張志新(化名)和其女友劉穎魄幕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颖杏,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纯陨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了留储。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翼抠。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖获讳,靈堂內(nèi)的尸體忽然破棺而出阴颖,到底是詐尸還是另有隱情,我是刑警寧澤丐膝,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布量愧,位于F島的核電站钾菊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏偎肃。R本人自食惡果不足惜煞烫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望累颂。 院中可真熱鬧滞详,春花似錦、人聲如沸紊馏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)朱监。三九已至稀火,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赌朋,已是汗流浹背凰狞。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沛慢,地道東北人赡若。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像团甲,于是被迫代替她去往敵國(guó)和親逾冬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容