Elegant approach to reporting errors in a C ++ 11 application?

I am working on a small C ++ 11 application (SDL2 game) and it is difficult for me to port some of my object-oriented knowledge from PHP / Java to C ++. For example, to create an elegant approach to reporting errors, I would create a class with various adapters and centralize the logging there. I've done it in C ++, but I have no idea how my classes must use the class Logger .

In Java and PHP, I would use dependency injection and put Logger as a class member variable in them. But in C ++, what is right? I really don't think static behavior would be nice.

+6
source share
4 answers

About a human.

For me, logging is like processing date and time: the main case is trivial, but something more than trivial is extremely difficult: no middle ground.

Let me advise you to study a general-purpose reference library such as Pantheios or Boost.Log .

The reason I advise this approach rather than doing “your own efforts” is because I know first hand how the “registration situation” happens:

  • you start with a simple "write to file" or "write to screen"
  • then you also need to log in on another device
  • then you want to filter severity levels
  • then you want to send your magazines through channels
  • then you want to disable logging

And things get really, really complicated, and the log classes are starting to pollute your code.

So, as I said: based on my limited experience, I would advise you to study the proposed libraries.

Good luck.

Edit: Boost.Log examples

For completeness only ( see page for details ).

Trivial case:

 #include <boost/log/trivial.hpp> int main(int, char*[]) { BOOST_LOG_TRIVIAL(trace) << "A trace severity message"; BOOST_LOG_TRIVIAL(debug) << "A debug severity message"; BOOST_LOG_TRIVIAL(info) << "An informational severity message"; BOOST_LOG_TRIVIAL(warning) << "A warning severity message"; BOOST_LOG_TRIVIAL(error) << "An error severity message"; BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message"; return 0; } 
+3
source

One approach is to pass a link to a log object around function calls. However, registration is a kind of orthogonal aspect for application logic, so explicitly transferring this registrar and including it as a member quickly becomes a nuisance and only adds artificial complexity.

I prefer to have one global registrar in the application. Modules can create their own registrars as child registrars of the main registrar, forming a hierarchy (I think this looks like a Python logging module) and independently process its output table and verbosity if necessary.

+2
source

I always use something like this:

 class Log { public: Log() : m_filename( "dafault.log" ) {} // if you wanna give other names eventually... Log( const std::string & p_filename ) : m_filename( p_filename ) {} virtual ~Log() { // implement your writeToFile() with std::ofstream writeToFile( m_filename, m_stream, true ); } template< typename T > Log & operator<<( const T & p_value ) { m_stream << p_value; return *this; } private: std::string m_filename; std::ostringstream m_stream; }; 

This way I can write like this:

 Log() << "My message in the log with numbers " << 1 << 2 << 3 << " and so on..."; Log( "other.log" ) << "Log in another file eventually..."; 
0
source

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; // cuz' decorators rock LoggerEngine* previous() const { return _previous; } protected: LoggerEngine(): _previous(CurrentE) { CurrentE = this; } ~LoggerEngine() { CurrentE = _previous; } private: static __thread LoggerEngine* CurrentE; LoggerEngine* const _previous; }; // class LoggerEngine // in some .cpp file: __thread LoggerEngine* LoggerEngine::CurrentE = 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 
0
source

Source: https://habr.com/ru/post/955205/


All Articles