支持__LINE__宏等的C ++样式记录器 [英] C++ style Logger that supports __LINE__ macro and others

查看:100
本文介绍了支持__LINE__宏等的C ++样式记录器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

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

预期产量

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

解决方案不足

为此,我必须将像__LINE__这样的宏直接放在调用记录器的行中,否则这些宏将无法正常工作.我发现我可以用自己的宏替换std::endl,它将像下面这样处理黑魔法:

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. I found that I can replace std::endl with my macro that will do this black magic like this:

#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__的值.因此,实际上使用记录器将如下所示:

The macro logendl uses static variables from my ToolLogger class to save the values of __LINE__, __func__ and __FILE__ needed later. So actually using the logger will looks like this:

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

在课堂上,我必须重载operator<<才能使它正常工作,我需要两个.一个用于获取正常值,例如std::stringint,另一个用于获取std::endl机械手.这是我班上最重要的事情:

In the class i have to overload the operator<< to get this to work, and I need two of them. One for taking the normal values like std::string or int, and the other to take the std::endl manipulator. 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 /

所以实际上我需要从使用std::endl机械手的类中删除operator<<,并以其他方式解决它,但是该怎么做呢?我当时正在考虑将logendl宏中的std::endl更改为其他自定义操纵器,然后此自定义操纵器将完成实际上正在执行operator<<的工作,但是我不知道该怎么做.我在寻找其他解决方案,有什么建议吗?

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? 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. I'm looking for other solution, any suggestions?

推荐答案

这就是我要做的.这有点像你的问题.也就是说,无需定义endl.我要做的是从LogMessage类中分离出一个Logger类(该类只接收字符串并输出,然后输出到需要它们去的任何地方).

Here's what I do. It kind of skirts your question. That is, is does away with having to define an 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.

好处是:

  • 每个类都非常简单.

  • 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.

无需定义endl.当LogMessage类破坏时,消息以分号结束

No need to define an endl. The message ends at the semicolon when the LogMessage class destructs

让我知道您的想法:

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

这篇关于支持__LINE__宏等的C ++样式记录器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆