如何编写类似iostream的接口到日志库?

时间:2016-10-26 23:36:42

标签: c++ c++11 logging iostream

我想为我非常简单的日志库编写一个方便的界面。以下两段代码。第一个是我现在所做的,第二个是我对直观界面的想法:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancelTapped:)];
}

std::ostringstream stream;
stream<<"Some text "<<and_variables<<" formated using standard string stream"
logger.log(stream.str()); //then passed to the logger

我的思想设计过程背后是从logger.convinient_log()<<"Same text "<<with_variables<<" but passed directly"; 函数返回某种类似临时字符串流的对象。破坏的对象(我希望它发生在行尾或类似的,方便的地方)将从自身收集字符串并调用实际的logger.convinient_log()。关键是我想整个处理它,而不是逐个处理,以便log()可以添加例如。前缀和sufix到整行文本。

我非常清楚,如果没有一些重大的魔力,它可能是完全不可能或不可能的。如果是这样的话,那么这样做几乎是方便的方式以及如何实现它?我打赌传递一些特殊的变量会强制收集调用 - logger.log()操作。

如果您不知道确切的答案,也欢迎有关该主题的资源(例如扩展字符串流)。

3 个答案:

答案 0 :(得分:5)

这就是Boost.Log的工作原理。基本思路很简单:

struct log
{
    log() {
        uncaught = std::uncaught_exceptions();
    }

    ~log() {
        if (uncaught >= std::uncaught_exceptions()) {
            std::cout << "prefix: " << stream.str() << " suffix\n";
        }
    }

    std::stringstream stream;
    int uncaught;
};

template <typename T>
log& operator<<(log& record, T&& t) {
    record.stream << std::forward<T>(t);
    return record;
}

template <typename T>
log& operator<<(log&& record, T&& t) {
    return record << std::forward<T>(t);
}

// Usage:
log() << "Hello world! " << 42;

std::uncaught_exceptions()用于避免在中间抛出异常时记录不完整的消息。

答案 1 :(得分:4)

这是我前一段时间的课程。这听起来像你正在寻找的是这个。我能够在没有任何令人生畏的继承ostreams,stream_buf或其他任何东西的情况下实现它。无论何时捕获到同花,您都可以写入文件,控制台,套接字或其他任何内容。

它不适用于ostream_iterators,但可以很好地处理所有io_manip函数。

用法:

Logger log;

int age = 32;
log << "Hello, I am " << age << " years old" << std::endl;
log << "That's " << std::setbase(16) << age << " years in hex" << std::endl;
log(Logger::ERROR) << "Now I'm logging an error" << std::endl;
log << "However, after a flush/endl, the error will revert to INFO" << std::end;

实施

#include <iostream>
#include <sstream>
#include <string>

class Logger
{
public:
    typedef std::ostream&  (*ManipFn)(std::ostream&);
    typedef std::ios_base& (*FlagsFn)(std::ios_base&);

    enum LogLevel
    {
        INFO,
        WARN,
        ERROR
    };

    Logger() : m_logLevel(INFO) {}

    template<class T>  // int, double, strings, etc
    Logger& operator<<(const T& output)
    {
        m_stream << output;
        return *this;
    }

    Logger& operator<<(ManipFn manip) /// endl, flush, setw, setfill, etc.
    { 
        manip(m_stream);

        if (manip == static_cast<ManipFn>(std::flush)
         || manip == static_cast<ManipFn>(std::endl ) )
            this->flush();

        return *this;
    }

    Logger& operator<<(FlagsFn manip) /// setiosflags, resetiosflags
    {
        manip(m_stream);
        return *this;
    }

    Logger& operator()(LogLevel e)
    {
        m_logLevel = e;
        return *this;
    }

    void flush() 
    {
    /*
      m_stream.str() has your full message here.
      Good place to prepend time, log-level.
      Send to console, file, socket, or whatever you like here.
    */      

        m_logLevel = INFO;

        m_stream.str( std::string() );
        m_stream.clear();
    }

private:
    std::stringstream  m_stream;
    int                m_logLevel;
};

答案 2 :(得分:1)

创建一个派生自std::basic_streambuf的自定义类以写入您的记录器,例如:

class LoggerBuf : public std::stringbuf
{
private:
    Logger logger;
public:
    LoggerBuf(params) : std::stringbuf(), logger(params) {
        ...
    }

    virtual int sync() {
        int ret = std::stringbuf::sync();
        logger.log(str());
        return ret;
    }
};

然后你可以实例化一个std::basic_ostream对象,给它一个指向LoggerBuf对象的指针,例如:

LoggerBuf buff(params);
std::ostream stream(&buf);
stream << "Some text " << and_variables << " formated using standard string stream";
stream << std::flush; // only if you need to log before the destructor is called

或者,从std::basic_ostream派生自定义类以包装您的LoggerBuf类,例如:

class logger_ostream : public std::ostream
{
private:
    LoggerBuf buff;
public:
    logger_ostream(params) : std:ostream(), buff(params)
    {
        init(&buff);
    }
};

std::logger_ostream logger(params);
logger << "Some text " << and_variables << " formated using standard string stream";
logger << std::flush; // only if you need to log before the destructor is called