I'm creating my own logging utility for my project, I want to create a function like iostream's std::cout, to log to a file and print to the console as well.
Here's what i want:
enum
{
debug, error, warning, info
};
LOG(level) << "test"; // level - from the above enum
The result should be like this:
int iPlayerID = 1337;
LOG(info) << "Player " << iPlayerID << "Connected";
[Thu Jan 29 18:32:11 2015] [info] Player 1337 Connected
std::cout
is not a function, it's an object of typestd::ostream
which overloadsoperator<<
.A quick sketch of how you could do it:
Ant then use it like
You could define an
enum
likethen have a global variable:
and provide some way to set it (e.g. from program arguments).
At last, define a macro (in a global header)
and use it like
Feel free to improve
MY_LOG
macro to output more things (date, etc...)You could define instead
but that does not play nice with code like
So I strongly suggest something like
MY_LOG
There's only one decent solution, and it's not simple, but it gets
LOG(log_info) << "line 1\nline 2" << std::endl;
correct.You must implement a custòm
std::ostreambuf
. It's the only class that can reformat output after all the usualoperator<<
functions have been applied.In particular, your stream buffer method
overflow
is called when you've got a bunch of characters to work on. You can now add your loglevel filtering, and reliably check for newlines in the formatted character stream.The trick is for your
LOG(level)
to return a special type which contains a pointer to anstd::ostream
, and defines the<<
operator. Something like:The
LOG(level)
macro creats an instance of one, something like:Of course, the
getLogStream
may insert any information it wants (like a timestamp) at the moment it is called.You might want to add a flush in the destructor of
LogStream
.There are two problems I see above. The first is forking your message (to both a file, and the console). The second is wrapping what is written with some extra stuff.
meta_stream
handles theoperator<<
overloading. It uses CRTP to statically dispatch to its child type:I had to override
<<
twice because of howstd::endl
and other modifiers work -- they are the name of an overloaded function.This solves the problem of outputing the same string to two different ostreams:
note the use of CRTP via
meta_stream
. I just have to implementwrite_to
.First, write your 4 loggers to this array:
giving each a pointer to a
std::cout
and a pointer to a (stored elsewhere) stream wrapping a file you want to save the log to. You can passnullptr
if you don't want that level to be logged to that output stream (say, in release, skip debug logs), and you can log stuff to different log file (debug to one file, info to another).now
log(debug) << "hello " << "world\n"
will write your message for you.You can do more fancy stuff if you don't want to write the newline at the end of the log message, but I doubt it is worth it. Just write the newline.
If you really want that feature:
but I don't think it is worth it.
I will not enter coding details here, but I will provide you some quick guidelines :
Create a singleton object pool (for loggers is ok to create a singleton) or a namespace or a that returns a certain log class according to the enum :
Logger& SingletonLoggersManager::GetLoggerForLevel(eLogLevel);
Override the "<<" operator for your class in order to allow outputting accoridng to your needs in your Logger class
https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx
Define a macro in order to be able to make a quick call inside your code :
#define LOG(x) SingletonLogger::GetLoggerForLevel(eLogLoevel);
Now when you use inside your code
It will expand to :