我创建了自己的类似std::cout
的对象,该对象同时写入std::cout
和日志文件。
我目前在一个头文件中这样定义它,但我收到了未使用的变量警告。
头文件<MyLib/Log.h>
static LOut { };
static LOut lo;
template<typename T> inline LOut& operator<<(LOut& mLOut, const T& mValue)
{
std::string str{toStr(mValue)};
std::cout << str;
getLogStream() << str;
return mLOut;
}
用法:
#include <MyLib/Log.h>
...
lo << "hello!" << std::endl;
lo
应该是static
吗?lo
应该是extern
吗?
感谢您解释了声明类cout
对象的正确方法,并展示了主要标准库实现是如何做到这一点的
Edit:对于类似cout
的对象,我指的是在包含相应的头之后始终可用的全局变量。
std::cout
简单地声明如下:
namespace std {
extern ostream cout;
}
它是一个正则的全局变量;你自己也可以做同样的事情。将变量的extern
声明放在头中;然后在源文件中定义相同的变量,并将其链接到您的应用程序:
// mylog.h
extern MyLog mylog;
// mylog.cpp
MyLog mylog(someparams);
首先,我不太确定类似cout
的对象是什么意思?也许是std::ostream
。
无论如何,通常的方法是使用筛选streambuf。只需写一个streambuf转发到日志文件,除了通常的地方,把它插在你曾经的地方想要:
class LoggingOutputStreambuf : public std::streambuf
{
std::streambuf* myDest;
std::ofstreambuf myLogFile;
std::ostream* myOwner;
protected:
int overflow( int ch )
{
myLogFile.sputc( ch ); // ignores errors...
return myDest->sputc( ch );
}
public:
LoggingOutputStreambuf(
std::streambuf* dest,
std::string const& logfileName )
: myDest( dest )
, myLogFile( logfileName.c_str(), std::ios_base::out )
, myOwner( nullptr )
{
if ( !myLogFile.is_open() ) {
// Some error handling...
}
}
LoggingOutputStreambuf(
std::ostream& dest,
std::string const& logfileName )
: LoggingOutputStreambuf( dest.rdbuf(), logfileName )
{
dest.rdbuf( this );
myOwner = &dest;
}
~LoggingOutputStreambuf()
{
if ( myOwner != nullptr ) {
myOwner->rdbuf( myDest );
}
}
};
(这是C++11,但修改它应该不难C++03.)
要使用,您可以使用以下内容:
LoggingOutputStreambuf logger( std::cout );
// ...
std::cout
的所有输出将被记录,直到logger
熄灭范围内。
在实践中,您可能会使用比filebuf
用于日志记录,因为您可能需要插入时间戳在每条线的起点,或在每条线。(过滤streambufs可以解决这些问题)
类似std::cout的对象,它同时写入std::cout和日志文件
也许boost.iostreams就足够了?
#include <iostream>
#include <fstream>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/tee.hpp>
namespace io = boost::iostreams;
int main()
{
typedef io::tee_device<std::ostream, std::ofstream> teedev;
typedef io::stream<teedev> LOut;
std::ofstream outfile("test.txt");
teedev logtee(std::cout, outfile);
LOut mLOut(logtee);
mLOut << "hello!" << std::endl;
}
简单地将输入值直接发送到cout对我来说不起作用,因为我想在日志输出中添加标题和信息。
此外,我有一个静态Debug类,用于包装日志流。
这就是我设法做到的,我希望它有用。不知怎么的,我是c++的新手,所以如果有什么问题,请随时告诉我:)
#include <iostream>
#include <sstream>
#include <ostream>
enum class DebugLevel {
INFO,
WARNING,
ERROR
};
class Debug {
public:
/* other Debug class methods/properties
...
*/
// out stream object
static struct OutStream {
std::ostringstream stream;
DebugLevel level = DebugLevel::INFO;
public:
// here you can add parameters to the object, every line log
OutStream& operator()(DebugLevel l) {
level = l;
return *this;
}
// this overload receive the single values to append via <<
template<typename T>
OutStream& operator<<(T&& value) {
stream << value;
return *this;
}
// this overload intercept std::endl, to flush the stream and send all to std::cout
OutStream& operator<<(std::ostream& (*os)(std::ostream&)) {
// here you can build the real console log line, add colors and infos, or even write out to a log file
std::cout << __TIME__ << " [" << (int)level << "] " << stream.str() << os;
stream.str(""); // reset the string stream
level = DebugLevel::INFO; // reset the level to info
return *this;
}
} Log;
};
Debug::OutStream Debug::Log; // need to be instantiaded only because I use a static Debug class
int main() {
Debug::Log(DebugLevel::ERROR) << "Hello Log! " << 2 << " " << __FUNCTION__ << std::endl;
Debug::Log << "Hello Log! " << 0xFA << std::endl; // NB: this way the debugLevel is default
return 0;
}
在我的一个项目中,我为std::cout
编写了包装器。
它看起来像这样:
struct out_t {
template<typename T>
out_t&
operator << (T&& x) {
std::cout << x;
// log << x;
return *this;
};
};
out_t out;
out << 1;
有关完整的代码,请在io.h
中查找struct out