简体   繁体   English

支持__LINE__宏等的C ++样式记录器

[英]C++ style Logger that supports __LINE__ macro and others

I want to make a Logger that can be used like std::cout , but I want to log some extra data like date, time, __LINE__ , __func__ , and __FILE__ which should be saved to the file automatically. 我想要的是可以像使用的记录器std::cout ,但我想记录,如日期,时间,一些额外的数据__LINE____func____FILE__应自动保存到文件中。

Example

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

Expected output 预期产量

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

Inadequate solution 解决方案不足

To do this I have to put macros like __LINE__ direct in the line where I call my logger, otherwise the macros won't work correct. 为此,我必须将__LINE__宏直接放在调用记录器的行中,否则宏将无法正常工作。 I found that I can replace std::endl with my macro that will do this black magic like this: 我发现我可以用我的宏替换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

The macro logendl uses static variables from my ToolLogger class to save the values of __LINE__ , __func__ and __FILE__ needed later. logendl使用静态变量从我ToolLogger类保存的值__LINE____func____FILE__以后需要。 So actually using the logger will looks like this: 因此,实际使用记录器将如下所示:

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

In the class i have to overload the operator<< to get this to work, and I need two of them. 在课堂上,我必须重载operator<<才能使它正常工作,并且我需要两个。 One for taking the normal values like std::string or int , and the other to take the std::endl manipulator. 一种用于获取标准值,如std::stringint ,另一种用于获取std::endl操纵器。 Here is the most important things from my class: 这是我班上最重要的事情:

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;

Problem 问题

The problem in this solution is that I can use my logger in two ways: 该解决方案中的问题在于,我可以通过两种方式使用记录器:

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

So actually I need to remove the operator<< from my class that takes std::endl manipulator, and solve it other way, but how to do it? 所以实际上我需要从使用std::endl机械手的类中删除operator<< ,并以其他方式解决它,但是该怎么做呢? I was thinking about changing std::endl in logendl macro to other custom manipulator, and then this custom manipulator will do the work that is actually doing the operator<< , but I have no idea how to do it. 我当时正在考虑将logendl宏中的std::endl更改为其他自定义操纵器,然后此自定义操纵器将完成实际上正在执行operator<< ,但是我不知道该怎么做。 I'm looking for other solution, any suggestions? 我正在寻找其他解决方案,有什么建议吗?

Here's what I do. 这是我的工作。 It kind of skirts your question. 这有点像你的问题。 That is, is does away with having to define an endl . 也就是说,无需定义endl What I do is separate out a Logger class (which just takes strings and outputs then to wherever you need them to go) from a LogMessage class which builds a message. 我要做的是从建立消息的LogMessage类中分离出Logger类(该类只接受字符串并输出,然后到需要的地方)。

The benefits are: 好处是:

  • Each class, on it's own, is pretty simple. 每个类都非常简单。

  • Very simple macros. 非常简单的宏。 I don't define the macro below but it's easy enough to do. 我没有在下面定义宏,但这很容易做到。

  • No need to define an endl . 无需定义endl The message ends at the semicolon when the LogMessage class destructs LogMessage类破坏时,消息以分号结尾

Let me know what you think: 让我知道你的想法:

#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;
}

You might have a LoggerAt class with a LoggerAt(const char*filename, int lineno) constructor (perhaps a subclass of std::ostringstream , etc...), then define 您可能有一个LoggerAt类,并带有LoggerAt(const char*filename, int lineno)构造函数(也许是std::ostringstream等的子类,等等),然后定义

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

In some of my C++ projects I have coded: 在我的一些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)

And using something like MOM_INFORM("x=" << " point:" << pt); 并使用MOM_INFORM("x=" << " point:" << pt); where you could imagine the usual Point pt; 您可以想象通常的Point pt; example with appropriate std::ostream& operator << (std::ostream&out, const Point&point) function. 具有适当的std::ostream& operator << (std::ostream&out, const Point&point)函数的示例。

Notice that to use conveniently __FILE__ and __LINE__ you'll better use macros. 注意,要方便地使用__FILE____LINE__ ,最好使用宏。

I have solved my own problem. 我已经解决了自己的问题。 Other answers posted here may be better than main, but I wanted to use logger in a simple way just like in C++ std::cout is used. 此处发布的其他答案可能比main更好,但是我想以简单的方式使用logger,就像在使用C ++ std::cout Also my solution may not be optimal and may lead to other problems, but it meets my requirements. 同样,我的解决方案可能不是最佳解决方案,并且可能导致其他问题,但是它满足了我的要求。

I have added a custom std::ostream 我添加了一个自定义的std::ostream

class CustomOstream : public std::ostream
{
  public:

    static CustomOstream& endl( CustomOstream& out )
    {
        return out;
    }
};

and changed macro to use the endl function from CustomOstream 并将宏更改为使用CustomOstreamendl函数

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

Also the operator<< from the main class has been changed 此外,主类的operator<<也已更改

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

Now the logger can be used just like I wanted 现在可以像我想要的那样使用记录器

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM