My current approach is to use some kind of injection of dependence, using Strength Strength instead of magic. This is not required for anything specific to C ++ 11 (except that __thread , which is an extension, can be replaced with thread_local if you want to be standard).
class LoggerEngine { public: static LoggerEngine* Current() { return CurrentE; } virtual bool isActive(Level) { return true; } virtual void log(char const* function, char const* file, int line, std::string message) = 0;
And then, specify the macros (to capture the function, file, and line):
#define LOG(L_, Message_) \ do { if (LoggerEngine* e = LoggerEngine::Current() and e->isActive(L_)) { \ std::ostringstream _28974986589657165; \ _28974986589657165 << Message_; \ e->log(__func__, __FILE__, __LINE__, _28974986589657165.str()); \ }} while(0);
However, this can be done better if spacers are used instead, because although it prevents any calculations if the level is not active, it still requires formatting the complete message (and the necessary memory allocation), even if it is going to in any case, truncate the message (for example, because it uses fixed-size buffers) and does not allow you to easily configure formatting.
The combination of stacking mechanisms (and automatically dropping them using RAII) using local streams is really very neat. In most cases, only the interface sees the interface, without having to scroll it (cool when you have 4/5 different engines), and any level of the stack can switch the engine to something more suitable.
There is one caveat, as this does not occur before the first engine is determined. For this reason, I often thought that I would not write to the console if no engine was configured, but ... I basically changed my style to avoid calculation before main is called, since I can not impose a dependency at this stage (and this is inconvenient if an exception occurs ...)
Usage looks like this:
void benchmark() { LOG(INFO, "Hello, World!"); Timer t; { MySinkLogger const _; (void)_; // a logger with "isActive" always false for (size_t i = 0; i != 10000; ++i) { LOG(INFO, "Flood!"); } } LOG(INFO, "Elapsed: " << t.elapsed()); } int main() { MyFileLoggerEngine const _("somefile.log"); (void)_; // a file logger benchmark(); }
And usually this can create a file "somefile.log" containing:
2013-10-03T18:38:04.645512 mylaptop INFO <test.cpp#42> Hello, World! 2013-10-03T18:38:04.865765 mylaptop INFO <test.cpp#47> Elapsed: 0.220213s