Developer warnings, mainly (for example, "an OpenGL call failed ..."). So do we really need to build a sophisticated logging system? Information that developers will want to catch/redirect are those given in case of error, ie. those not given by the logging system.
This is true for trivial systems, but if you have a more complex system, you will usually need a bit more context information in order to efficiently track bugs down than most libraries can and do provide through their error reporting mechanism. Of course you can always utilize a step by step debugger, but the typical enduser doesn't have one and step by step debugging is quite time consuming, so a decent log can spare you time.
- Division into different severity levels: error, warning, info, debug
- Filtering (by level, or even module/functionality-wise)
- Choosing the output streams (not only std::cerr)
- Meta-information: time, source file, line
- Ideally zero performance overhead for loggers disabled at compile time
I think that disabling the log system at compile time isn't that useful, because as a dev/library user you want the ability to read the debug information anyways and as an enduser you probably won't be able to provide useful context information without it as already stated. So I believe it is much more useful to implement a configurable almost zero overhead runtime filtering.
Furthermore I think 3. is far to unspecific; I propose to implement an API that works like so:
#include <SFML/System/Log.hpp>
void logEntryProcessor(void *userData, sf::Log::Channel channel, sf::Log::Level level, const std::string &message)
{
// inject into some log framework or log directly to file/cerr/cout/etc.
}
int main( )
{
sf::Log::Manager::setCustomLogEntryProcessor(&logEntryProcessor, NULL);
// any further log message will be processed by logEntryProcessor
}
This only requires the filtering and logEntryProcessor to be threadsafe. The former could easily be realised through atomics and the latter can use a mutex/async work queue/etc. The default log entry processor doesn't have to be more complicated than just outputting the metadata and the message to std::cerr (or sf::err if you don't want to break current redirection systems), because the preferred backend is strongly user dependent and it's easy for the user to integrate the SFML log with his preferred backend.
Time information is almost always unnecessary and if it is necessary the user can easily add it with a custom log processor method. File and line information are mostly unnecessary too (the log system shouldn't be abused as error reporting system), but since this is more common and can't be done with a custom log processor I would provide an additional macro for this like so:
#define SFML_LI __FILE__ << ":" << __LINE__ << ": " <<
sfLogNetErr(SFML_LI "the acutal error message as usual" << someStreamableVar);
For example, I could imagine a macro which creates an object with overloaded operator<<, that logs messages according to the current log configuration. Having a macro allows the collection of above-mentioned meta-information and complete away-optimization for statically disabled loggers.
SFML_LOG_E("An error with number " << 77)
I propose
std::ostream and friends (or more precisly
std::ostringstream), because they can serve all your needs or can be easily extended to do so, no need to reinvent the wheel.
SFML_LOG_E contradicts the
naming conventions; I propose
sfLogErr or
sfLogSysErr instead which do comply to the naming conventions and are less tedious to type.
Edit: I believe that SFML doesn't need a next generation log system, it just needs to be easy to integrate SFML into one of the various existing ones.