2015-09-09 116 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

相关问题