【问题标题】:C++ style Logger that supports __LINE__ macro and others支持 __LINE__ 宏等的 C++ 风格 Logger
【发布时间】:2016-02-16 05:14:38
【问题描述】:

我想制作一个可以像std::cout 一样使用的记录器,但我想记录一些额外的数据,例如日期、时间、__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&lt;&lt; 才能让它工作,我需要其中两个。一个用于获取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&lt;&lt;,并以其他方式解决它,但是该怎么做呢?我正在考虑将logendl 宏中的std::endl 更改为其他自定义操纵器,然后这个自定义操纵器将完成实际上正在执行operator&lt;&lt; 的工作,但我不知道该怎么做。我正在寻找其他解决方案,有什么建议吗?

【问题讨论】:

    标签: c++ logging operator-overloading endl


    【解决方案1】:

    这就是我所做的。它有点绕开你的问题。也就是说,无需定义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;
    }
    

    【讨论】:

    • 嗯,我想这是一些解决方案,我会考虑的,谢谢!
    【解决方案2】:

    您可能有一个带有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=" &lt;&lt; " point:" &lt;&lt; pt); 之类的东西,您可以想象通常的Point pt; 示例和适当的std::ostream&amp; operator &lt;&lt; (std::ostream&amp;out, const Point&amp;point) 函数。

    注意方便使用__FILE____LINE__你最好使用宏。

    【讨论】:

    • 谢谢,我会检查你的建议
    【解决方案3】:

    我已经解决了我自己的问题。此处发布的其他答案可能比主要答案更好,但我想以简单的方式使用记录器,就像在 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&lt;&lt; 也已更改

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

    现在记录器可以按照我的意愿使用

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-21
      • 2013-01-15
      • 2015-04-04
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多