2011-10-25 76 views
5

我想添加一個日誌到我的應用程序。我選擇了一個日誌記錄庫,但我希望能夠切換到不同的庫,而無需更改任何使用日誌記錄的代碼。C++日誌封裝設計

因此,我需要某種形式的日誌記錄包裝的足夠靈活地利用幾乎任何下屬記錄庫的功能。

對於這種包裝設計的任何建議?

編輯:我必須在此包裝中的一個功能是組件標記。我希望我的算法類在其日誌行之前出現「X:」,並且我的經理類出現「Y:」。如何將這些標籤傳播到底下的日誌以及如何構建組件標籤命名機制是一個主要的設計問題。

+2

你真的*需要包裝嗎?問問你自己。是否真的有可能轉換你的記錄器(可能是多次),或者你會利用多個日誌記錄框架?編寫一個包裝器需要資源(時間),如果你不真的需要包裝器,它們可以放入其他東西。 – Xeo

+0

謝謝,你可以信任我真的這樣做。爲了簡單起見,我願意用我提到的標記功能來解決一個非常簡化的日誌版本。 – Leo

回答

2

最好的選擇是儘可能簡化界面。將日誌記錄用戶的界面與記錄實際執行的方式完全分開。

橫切關注點始終是維護費用昂貴,所以使任何事情更復雜會讓你討厭的生活。

某些庫只想要簡單的東西是這樣的:

void logDebug(const std::string &msg); 
void logWarning(const std::string &msg); 
void logError(const std::string &msg); 

他們不應該添加或指定任何更多的上下文。無論如何,沒有人可以使用這些信息,所以不要過度設計它。

如果你開始添加到您的日誌的更多信息電話就更難重用使用它的客戶端代碼。通常,當組件在不同的抽象層次上使用時,你會看到這個表面。特別是當一些低級代碼提供僅與更高級別相關的調試信息時。

這不會強迫你的日誌實現(或連界面日誌實現符合!)到任何東西,所以你可以改變它時。

UPDATE:

只要該標記,這是一個高層次的關注。我會推測它不屬於日誌,但那不是在這裏也不在那裏。

將其保留在日誌消息規範之外。低層次的代碼不應該讓你或你的經理是飛行卡車。

我不知道你如何指定你的榜樣XY。你如何做到這一點從我們給出的描述中並不是很明顯。我將只使用一個字符串進行演示,但如果可能的話,您應該將其替換爲安全類型。

如果這總是開啓,那麼僅僅有一個實例上下文(可能是全局變量)可能是合適的。登錄時,設置上下文並忘記它。如果它沒有設置,拋出極端的偏見。如果在未設置時不能拋出,則不會始終打開。

void setLoggingContext("X:"); 

如果這在不同的抽象層次上發生變化,我會考慮基於堆棧的RAII實現。

LoggingTag tag("X:"); 

我不確定在不同的堆棧幀以不同的值傳遞時,您的要求是什麼。我可以看到堆棧的頂部或底部對於不同的用例是合理的。

void foo() { 
    LoggingTag tag("X:"); 
    logWarning("foo"); 
    bar(); 
    baz(); 
} 

void bar() { 
    LoggingTag tag("Y:"); 
    logWarning("bar"); 
    baz(); 
} 

void baz() { 
    logWarning("baz"); 
} 

無論哪種方式,這應該不會影響您如何向日志添加消息。 baz函數沒有指定LoggingTag的上下文。出於這個原因,使用logWarning不知道標籤是非常重要的。

如果你想基於某種類型進行標記,你可以做一些簡單的事情。

struct LoggingTag { 
    LoggingTag(const std::string &tag_) : tag(tag_) {} 
    template<typename T> 
    static LoggingTag ByType() { 
     return LoggingTag(typeid(T).name()); 
    } 
    std::string tag; 
}; 

void foo() { 
    LoggingTag tag = LogginTag::ByType<int>(); 
} 

這不會有人使用typeid(T).name(),如果他們不想,而是給你的便利。

+0

我必須考慮的一個問題是不同的組件標籤。 – Leo

+0

我在腦海裏有這樣的語法:Log log = GetLogger(MyClassName); log.Warn(...)。我不確定MyClassName應該是什麼,如果它是一個字符串,我從哪裏得到它?如果將類名轉換爲字符串是一些竅門,那怎麼辦呢? – Leo

+0

@ user991339我不明白你在問什麼。你想記錄一個類型嗎? –

1

我喜歡這種方法:

class Log { 
public: 
    virtual logString(const std::string&)=0; 
}; 

template <typename T> 
Log& operator<<(Log& logger, const T& object) { 
     std::stringstream converter; 
     converter << object; 
     logger.logString(converter.str()); 
     return logger; 
} 

簡單快捷!你所需要做的就是重新實現logString方法...

+0

這是一個好主意。你能建議一種實現標記功能的方法嗎? – Leo

+0

@CatPlusPlus它很容易修改使用模板... –

+0

@ user991339我會添加一個標記屬性到記錄器和一個布爾表明一個開始行,所以在第一次日誌它獲得標記戳。你可以專門爲<< endl case <<操作符,所以你知道下一個輸入是行的開始。 –

0

看看zf_log庫。它非常小(〜2000k行,編譯時〜10KB)和快速(見README.md中的對照表)。這與您所描述的封裝非常接近。它爲您提供了一個抽象API,您可以在您的項目中使用它,並允許指定要使用的實際日誌記錄實施。請參見custom_output.c示例,其中syslog用作輸出工具。它也可以在圖書館內私下使用,而不會與其他可能使用此庫的代碼發生衝突(請參閱ZF_LOG_LIBRARY_PREFIX定義以獲取更多信息)。 即使它不是你正在尋找的東西,我想這可能是你的包裝的一個很好的例子。