C++风格的记录器,支持__LINE_宏和其他

C++ style Logger that supports __LINE__ macro and others

本文关键字:LINE 其他 支持 风格 记录器 C++      更新时间:2023-10-16

我想制作一个可以像std::cout一样使用的Logger,但我想记录一些额外的数据,如日期、时间、__LINE____func____FILE__,这些数据应该自动保存到文件中。

示例

ToolLogger log;
log << "some data" << std::endl;

预期输出

[14.11.2015 21:10:12.344 (main.cpp) (main,14): some data

解决方案不充分

要做到这一点,我必须将像__LINE__这样的宏直接放在我调用记录器的行中,否则宏将无法正常工作。我发现我可以用我的宏来代替std::endl,它会像这样变魔术:

#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl 
    ((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") 
    << ((ToolLogger::line = __LINE__) ? "" : "") 
    << ((ToolLogger::function = __func__).empty() ? "" : "") 
    << std::endl

logendl使用我的ToolLogger类中的静态变量来保存稍后需要的__LINE____func____FILE__的值。所以实际使用记录器将看起来像这样:

ToolLogger log;
log << "some data" << logendl;

在课堂上,我必须重载operator<<才能使其工作,我需要其中的两个。一个用于取std::stringint等法线值,另一个用于获取std::endl操纵器。以下是我班上最重要的东西:

class ToolLogger
{
  public:
    // standard operator<< //
    template<typename T>
    ToolLogger& operator<< (const T& str)
    {
        out << str;
        return *this;
    }
    // operator<< for taking the std::endl manipulator //
    typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
    typedef CoutType& (*StandardEndLine)(CoutType&);
    ToolLogger& operator<<(StandardEndLine manip)
    {
        // save fileName, line and function to the file //
        // and all what is already in stringstream //
        // clear stringstream //
        return *this;
    }
    static string fileName;
    static int line;
    static string function;
  private:
    ofstream file;
    std::stringstream out;
};
string ToolLogger::fileName;
int ToolLogger::line;
string ToolLogger::function;

问题

这个解决方案中的问题是,我可以通过两种方式使用我的记录器:

log << "some data" << logendl;   // correct //
log << "some data" << std::endl; // compiles -> wrong /

所以实际上我需要从使用std::endl操纵器的类中删除operator<<,并用其他方法解决它,但如何做到呢?我正在考虑将logendl宏中的std::endl更改为其他自定义操纵器,然后这个自定义操纵器将完成实际执行operator<<的工作,但我不知道如何完成。我正在寻找其他解决方案,有什么建议吗?

我是这么做的。这有点回避你的问题。也就是说,它不必定义endl。我所做的是从构建消息的LogMessage类中分离出一个Logger类(它只接受字符串并输出到需要它们的地方)。

好处是:

  • 每个类都非常简单。

  • 非常简单的宏。我没有在下面定义宏,但它很容易做到。

  • 无需定义endl。LogMessage类销毁时,消息以分号结尾

让我知道你的想法:

#include <iostream>
#include <sstream>
#include <string>
// logger class
// this is not complete, it exists just to illustrate the LogIt function
class Logger
{
public:
    void LogIt(const std::string & s)
    {
        std::cout << s << std::endl;
    }
};
// builds a logging message; outputs it in the destructor
class LogMessage
{
public:
    // constructor
    // takes identifying info of message.  You can add log level if needed
    LogMessage(const char * file, const char * function, int line)
    {
        os << file << ": " << function << '('  << line << ") ";
    }
    // output operator
    template<typename T>
    LogMessage & operator<<(const T & t)
    {
        os << t;
        return *this;
    }
    // output message to Logger
    ~LogMessage()
     {
         Logger logger; // get logger here (perhaps it's a singleton?)
         logger.LogIt(os.str());
     }
private:
     std::ostringstream os;
};
int main()
{
// example usage
// typically this is invoked via a simple macro to reduce typing of the LogMessage constructor
LogMessage(__FILE__, __func__, __LINE__) << "this is an int " << 5;
}

您可能有一个带有LoggerAt(const char*filename, int lineno)构造函数的LoggerAt类(可能是std::ostringstream的子类,等等),然后定义

#define LOG(Out) do {LoggerAt(__FILE__,__LINE__) 
  << Out << std::endl; }while(0)

在我的一些C++项目中,我已经编码了:

void mom_inform_at(const char*fil, int lin, std::ostringstream& out)
{ out.flush(); 
  std::clog << fil << ":" << lin 
            << " INFORM: " << out.str() << std::endl ;
}
#define MOM_INFORM_AT(Fil,Lin,Output) do {      
      std::ostringstream out_##Lin;               
        out_##Lin << mom_outlog << Output ;       
        mom_inform_at(Fil,Lin,out_##Lin);         
    } while(0)
  #define MOM_INFORM_AT_BIS(Fil,Lin,Output) 
    MOM_INFORM_AT(Fil,Lin,Output)
  #define MOM_INFORM(Out)                         
    MOM_INFORM_AT_BIS(__FILE__,__LINE__,Out)

使用类似MOM_INFORM("x=" << " point:" << pt);的东西,您可以想象具有适当std::ostream& operator << (std::ostream&out, const Point&point)函数的常见Point pt;示例。

请注意,要方便地使用__FILE____LINE__,您最好使用宏。

我已经解决了自己的问题。这里发布的其他答案可能比main更好,但我想以一种简单的方式使用logger,就像在C++中使用std::cout一样。此外,我的解决方案可能不是最佳的,可能会导致其他问题,但它符合我的要求。

我添加了一个自定义std::ostream

class CustomOstream : public std::ostream
{
  public:
    static CustomOstream& endl( CustomOstream& out )
    {
        return out;
    }
};

并将宏更改为使用CustomOstream 中的endl函数

#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl 
    ((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") 
    << ((ToolLogger::line = __LINE__) ? "" : "") 
    << ((ToolLogger::function = __func__).empty() ? "" : "") 
    << ToolLogger::CustomOstream::endl

此外,主类的operator<<已更改

ToolLogger& operator<< (CustomOstream& (*f)(CustomOstream&))
{
    // do something //
    return *this;
}

现在记录器可以像我想要的一样使用

log << "some data" << logendl;   // correct //
log << "some data" << std::endl; // won't compile -> correct //
相关文章: