2015-09-09 147 views
4

我正在使用第三方,有時會發生內部錯誤,發生第三方寫入stderr時,我可以在控制檯中看到它。如何將stderr/stdout重定向到我在C++中的日誌?

(我做檢查的第三方函數的返回值,看看他是失敗了,但我想他寫至標準錯誤的信息)

我有一個記錄器,以我所編寫使用的方法我有

SendLog(string log); 

我想以某種方式捕捉我的第三方寫給stderr的東西(也許聽stderr一些如何),然後把這些東西寫到我的日誌中。

我該怎麼辦? 我需要它,因爲我的用戶無法看到控制檯,只能看到日誌。

重要的是要注意,我的程序不會在這些錯誤之後崩潰或存在,它會繼續工作。

編輯: 我的問題是不相似的涉嫌類似的問題,我想避免使用鏈接器黑客(如在類似的問題中使用過)。

+0

@BenjaminBannier:關閉不夠的 - 有答案存在不依賴於'.rdbuf'背後'cout'。 – MSalters

+0

你正在參考的答案不回答我的問題,它使用醜陋的鏈接黑客。我正在尋找一種通過代碼實現的方法。 – OopsUser

+0

鏈接問題中有多個答案。我會同意鏈接攻擊是醜陋的,但是有時候會出現醜陋的解決方案:在沒有非醜陋的解決方案時。 – MSalters

回答

0

您可以使用std :: stringstream的

std::stringstream log; 
std::streambuf *buf = std::cerr.rdbuf(log).rdbuf()); 
std::cerr << "Error Msg" << std::endl; 
std::string errMsg(log.str()); 

ERRMSG將是 「錯誤消息」。

2

一種解決方案是將寫入cerr的所有內容複製到例如文件中。

這是輔助類:

class CTee { 
public: 
    // Use ostream &s2 if you want to duplicate to an ostream, pass other 
    // stuff you need if you have other logging mechanisms. 
    CTee(ostream &s1, ostream &s2) : m_s1(s1), m_s1OrigBuf(s1.rdbuf()), m_teebuf(s1.rdbuf(), s2.rdbuf()) { s1.rdbuf(&m_teebuf); } 
    ~CTee() { m_s1.rdbuf(m_s1OrigBuf); } 

private: 
    CTee &operator =(CTee &rhs); // not implemented 

    class CTeeBuf : public streambuf { 
    public: 
     // Use streambuf *sb2 if you want to duplicate to an ostream/streambuf. 
     // Pass other Information if you want to log to something different. 
     CTeeBuf(streambuf* sb1, streambuf* sb2) : m_sb1(sb1), m_sb2(sb2) {} 

    protected: 
     virtual int_type overflow(int_type c) { 
      if(streambuf::traits_type::eq_int_type(c, streambuf::traits_type::eof())) 
       return c; 
      else { 
       // Put char to cerr/stream to duplicate 
       m_sb1->sputc((streambuf::char_type)c); 
       // Put char to duplicate stream. If you want to duplicate to something 
       // different, then write the char whereever you want to. 
       return m_sb2->sputc((streambuf::char_type)c); 
      } 
     } 
     virtual int sync() { 
      m_sb1->pubsync(); 
      return m_sb2->pubsync(); 
     } 

     // Store streambuf *m_sb2 if you want to duplicate to streambuf. 
     // Store anything else if you want to duplicate to something different. 
     streambuf *m_sb1, *m_sb2; 
    }; 

    ostream &m_s1; 
    streambuf * const m_s1OrigBuf; 
    CTeeBuf m_teebuf; 
}; 

CTee需要一個ostream複製和ostream複製到。它需要重複的ostream,並用CTeeBuf(見CTee ctor)替換它寫入的rdbuf,streambuf。 CTeeBuf獲取寫入它的char,並將它們轉發給ostreams(請參閱CTeeBuf :: overflow和CTeeBuf :: sync)的streambuf。 CTee dtor將已更改的streambuf恢復爲其原始值。

而且它使用的是這樣的:

char logfilename[] = "myfile.log"; 
ofstream logfile(logfilename, ios_base::out | ios_base::app); 
CTee tee(cerr, logfile); 

從現在開始,一切都寫入CERR將被複制(三通的壽命期間)到日誌文件。因此,此消息將被寫入CERR,也記錄到logfile:

cerr << "error occured: ..." << endl; 

也可以寫信給其他ostreams比日誌文件。如果你不想複製到另一個ostream而只是其他的東西,只需用一個實現來替換CTeeBuf :: overflow即可。請參閱http://www.cs.technion.ac.il/~imaman/programs/teestream.htmlhttp://wordaligned.org/articles/cpp-streambufs

+0

你能寫一些解釋給「tee」類和CTeeBuf類嗎? – OopsUser

+0

如何調用函數並將當前寫入buf的字符串傳遞給它? – OopsUser

+0

@OopsUser我在代碼中調整了我的答案,在代碼中需要進行更改。你首先改變溢出函數來調用你想要通知每個字符的函數(對不起,只適用於字符,不適用於字符串)。您將在CTeeBuf中看到您需要的成員以及如何調整ctor。 –

1

一種方法是使用stringstream。如果庫使用C++流編寫將會起作用。

class redirect_stream 
{ 
public: 
    redirect_stream(std::ostreamstream& stream, std::ostream& oldstream) : 
    stream_(stream), oldstream_(oldstream) 
    { 
     oldbuf_ = oldstream_.rdbuf(); 
     oldstream_.rdbuf(stream_.rdbuf()); 
    } 
    ~redirect_stream() 
    { 
     const std::string str = stream_.str(); 
     if (!str.empty()) 
     { 
     SendLog(str); 
     } 
     oldstream_.rdbuf(oldbuf_); 
    } 
private: 
    std::ostringstream& stream_; 
    std::ostream& olstream_; 
    std::streambuf* oldbuf_; 
}; 

和第三方庫只是使用前:

std::ostringstream oss; 
redirect_stream redirecter(oss, std::cerr); 

,或者您不能打印信息登錄在析構函數,只是與第三方庫工作結束後,打印oss.str()

Simple usage example

+0

你認爲我的程序存在錯誤之後?情況並非如此。我在節目的整個生命週期中使用第三方。而它運行時,我想獲取日誌 – OopsUser

+0

@OopsUser爲什麼退出?這只是析構函數,對象重定向器將在範圍之後被銷燬。 – ForEveR

相關問題