用于日志记录目的的重载 cout

Overloading cout for logging purposes?

本文关键字:重载 cout 日志 记录 用于      更新时间:2023-10-16

我想定义一些类似新cout的东西,我可以用来记录我的数据:

some_type cout2( Message_type message ){
    cout << message;
    logfile.save(message);
}

所以我会用它

cout2 << "some message" << endl;

到目前为止,我无法找出上面的代码必须是什么样子的。

感谢您的帮助。

您可以创建自己的记录器,如下所示:

class Logger {
public:
  Logger(std::string const& filename)
    : stream_(filename, std::ofstream::out | std::ofstream::app)
  {
    if (!stream_) {
      // Error...
    }
  }
  Logger& operator<<(std::string const& str) {
    std::cout << str;
    stream_ << str;
    return *this;
  }
private:
  std::ofstream stream_;
};

一般来说,来自标准库C++类不是为派生而设计的,对于流类也是如此。

所以恕我直言,最好指定cout2对象需要什么方法,然后:

  • 设计一个包含 ostream& 对象的类,在 CTOR 中初始化
  • 将实际输出委托给该内部对象
  • 在方法中执行所需的任何日志

应使用模板化operator <<以便能够轻松处理std::ostream可以处理的任何类。

class LogStream {
    std::ostream& out;
    Logfile logfile;
    LogStream(std::ostream& out, /* param for logfile initialization */ ...)
        : out(out), logfile(...) {}
    ... // other useful methods for state
};
template<typename T>
LogStream& operator << (LogStream& out, T val) {
    out.out << message;
    // should first test whether T is  manipulator, and choose whether and how it should be logged
    logfile.save(message);
}

您不想修改std::cout .

相反,您希望创建一个专用std::streambuf,用于写入两个缓冲区而不是一个缓冲区。 例如;

#include <streambuf>
template <typename char_type,
          typename traits = std::char_traits<char_type> >
class basic_teebuf:
    public std::basic_streambuf<char_type, traits>
{
public:
    typedef typename traits::int_type int_type;
    basic_teebuf(std::basic_streambuf<char_type, traits> * sb1,
                 std::basic_streambuf<char_type, traits> * sb2)
      : sb1(sb1)
      , sb2(sb2)
    {
    }
protected:      // override virtuals inherited from std::basic_streambuf
    virtual int sync()
    {
        int const r1 = sb1->pubsync();
        int const r2 = sb2->pubsync();
        return r1 == 0 && r2 == 0 ? 0 : -1;
    }
    virtual int_type overflow(int_type c)
    {
        int_type const eof = traits::eof();
        if (traits::eq_int_type(c, eof))
        {
            return traits::not_eof(c);
        }
        else
        {
            char_type const ch = traits::to_char_type(c);
            int_type const r1 = sb1->sputc(ch);
            int_type const r2 = sb2->sputc(ch);
            return
                traits::eq_int_type(r1, eof) ||
                traits::eq_int_type(r2, eof) ? eof : c;
        }
    }
private:
    std::basic_streambuf<char_type, traits> * sb1;
    std::basic_streambuf<char_type, traits> * sb2;
};
typedef basic_teebuf<char> teebuf;

然后,您需要创建一个使用此类缓冲区的专用ostream

#include <ostream>
class teestream : public std::ostream
{
public:
    // Construct an ostream which tees output to the supplied
    // ostreams.
    teestream(std::ostream & o1, std::ostream & o2);
private:
    teebuf tbuf;
};
teestream::teestream(std::ostream & o1, std::ostream & o2)
  : std::ostream(&tbuf)
  , tbuf(o1.rdbuf(), o2.rdbuf())
{
}

以上所做的只是创建一个使用我们专用缓冲区的专用std::ostream,而专用缓冲区又使用两个缓冲区。

现在,我们的teestream需要使用两个流进行初始化。 例如

#include <fstream>
#include <iostream>
//   include the preceding definition of teestream here
int main()
{
    std::ofstream logfile("hello-world.log");
    teestream tee(std::cout, logfile);
        //  tee is now a stream that writes the same output to std::cout and logfile
    tee << "Hello, world!n";
    return 0; 
}

这样做的好处是所有流插入(运算符 << (都可以使用我们的teestream - 即使对于具有重载版本的类也是如此。

main()返回时,溪流也将干净地关闭。

我已经将规范的streambuf编写为模板(std::streambuf是名为std::basic_streambuf的模板化类的专业化(。 为了通用性,最好对流做同样的事情(使用std::ostream也是模板化std::basic_ostream的专用化这一事实(。 我把这种概括作为一种练习。

我见过的基于这个想法构建的日志记录系统看起来像这样:

#define MY_PRINT(x, ...) 
        { 
            fprintf(logFile, x, ##__VA_ARGS__); 
            fflush(acuLogFile); 
        }

你会像这样使用它:

MY_PRINT("this is just a testn");

尽管这是 C 的做事方式,但它非常通用,也可以在C++中工作。

您只需要在代码中的任何位置使用新定义的打印函数即可。

也许你应该有一个基于实例的记录器,它有一个.log()方法,根据实现可以记录到文件,写入stdout等,而不是尝试从命名空间重载std自由函数?

你的意图会更清晰,它会更面向对象。

事实上,这是我最近写的一个非常千篇一律的事件记录器的例子,如果这有助于你理解我所说的那种事情。我的设计允许依赖注入,以及在记录器中保留关于如何格式化为输出以及应该去哪里(stdout 或文件等(的无聊决策。

当然,

您可以定义自己的cout。首先,您需要一个可以处理<<运算符并包含 fstream 和 ostream (如 cout(的类。

class logstream
{
private:
    fstream *filestream;
    ostream *cout;
public:
    logstream(fstream* filestream, ostream* cout)
    {
        this->cout = cout;
        this->filestream = filestream;
    }
    string operator<< (char* s)
    {
        *cout << s;
        *filestream << s;
        return s;
    }
};

要使用它,只需使用 cout 和 fstream 初始化它。

fstream lout = fstream("Filename", ios::app);
logstream cout = logstream(&lout, &std::cout);

现在你得到了你想要的。

cout << "message";

编辑:不要忘记包括

#include <iostream>
#include <fstream>

using namespace std;