Best way to implement a logging feature
I currently have 5 subsytems like input.cpp, system.cpp, sound.cpp etc
Id like to implement a logging feature for all of them, but all of the subsytems need to use it. Iv tried to make a class with ofstream, but found it hard to let all the other instances use the same stream
sigh this is annoying me now, please help me!
You might want to look into the "singleton pattern". I use it for the log manager in my game. Basically, it restricts a certain class to having at most one instantiation. Sometimes people overuse singletons, but for a LogManager, one instance is all you would ever want. So when I need to log a message, I just call LogManager::instance().log("blah") and it deals with the stream internally, and I can log messages from any part of my game that I need to without passing around a stream object.
Why dont you just pass a pointer to the same ofstream, found in the parent class of all of them, to each ones constructor?
I use a global instance of a small logging class for every log file I want. Don't know if that's the "best" or "cleanest" way, but it's very simple and works.
Please do not use a singleton (google will provide many reasons).
Personally, I tend to use the strategy pattern (akin to Crazyfool's suggestion) if doing anything moderately complex. Otherwise std::cerr is sufficient.
Personally, I tend to use the strategy pattern (akin to Crazyfool's suggestion) if doing anything moderately complex. Otherwise std::cerr is sufficient.
<IMHO>
While I'm not big on singletons generally, passing around a pointer to all classes that may possibly need to log something (ie. everything) is probably a bit overkill/fugly/annoying. :)
However, doing the whole Logger::instance().log("blah") is also pretty fugly, so what I'm doing for my logger is have a global instance (*gasp*), rather than using a singleton (which is just a global in fancy dress anyway). In fact, I have several instances, one for each level of "severity".
</IMHO>
Here's an example of what I do:
My Logger class overloads << (using ostringstream internally) so i can just do:
Log::Normal << "Level of pie: " << pieLevel << Log::End;
It's probably worth pointing out that the Logger class is pretty much a stub class that's pretty empty of functionality besides the stream operator. Once Log::End is streamed in, the Logger class calls a callback that was setup by the application on startup. It's up to the application to log to file, print to game console, etc.
Okay, I better stop rambling and get back to work. Hope this helps :)
While I'm not big on singletons generally, passing around a pointer to all classes that may possibly need to log something (ie. everything) is probably a bit overkill/fugly/annoying. :)
However, doing the whole Logger::instance().log("blah") is also pretty fugly, so what I'm doing for my logger is have a global instance (*gasp*), rather than using a singleton (which is just a global in fancy dress anyway). In fact, I have several instances, one for each level of "severity".
</IMHO>
Here's an example of what I do:
in Log.h:namespace Log {class Logger { }extern Logger Normal;extern Logger Warning;extern Logger Error;extern Logger Debug;}in Log.cppLogger Normal(kSeverity_Normal);Logger Warning(kSeverity_Warning);etc...
My Logger class overloads << (using ostringstream internally) so i can just do:
Log::Normal << "Level of pie: " << pieLevel << Log::End;
It's probably worth pointing out that the Logger class is pretty much a stub class that's pretty empty of functionality besides the stream operator. Once Log::End is streamed in, the Logger class calls a callback that was setup by the application on startup. It's up to the application to log to file, print to game console, etc.
Okay, I better stop rambling and get back to work. Hope this helps :)
Personally I use a singleton for my logger (it's one of the few places where I feel the singleton pattern actually has a use), along with a helper macro for working with it:
The Logger::startEntry() function returns a helper object that acts as a std::stringstream, and on destruction of the helper outputs the complete log entry (the format of which is determined by a LogFormatter object) to the log file.
extern Singleton<Logger> g_logger;#define LOG(level) g_logger::get().startEntry(level, __FILE__, __LINE__)void func(){ LOG(WARNING) << "This is warning number " << 5;}int main(){ g_logger::get().open("app.log"); g_logger::get().set_formatter(new LogFormatterXML); func();}
The Logger::startEntry() function returns a helper object that acts as a std::stringstream, and on destruction of the helper outputs the complete log entry (the format of which is determined by a LogFormatter object) to the log file.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement