Dear habrovchane, I was born a publication - a question. I am pleased to hear your criticism in the comments.
The project had to sketch a small wrapper over the existing logging system and, in addition to its main purpose, I wanted to use it in the style of working with the output stream.
Omitting unnecessary details, the wrapper looks like this:
class LoggerWrap: public RequiredInterface { public: void write(const std::string& str) const; // /* ... RequiredInterface ... */ };
And I also want to use something like this:
LoggerWrap log; log << " 1: " << std::setw(10) << someValue << "; 2:" << std::setw(15) << anotherValue;
In the process of thinking, I came up with several decisions that I want to discuss.
Overload the operator <<, the data received by him to send to the local stream ostringstream. At the end of writing to the log a special object - a sign of the end of the line.
The solution might look something like this:
class LoggerWrap { public: void write(const std::string& str) const { // std::cout << "[Log]: " << str << std::endl; } /* ... ... */ struct Flush {}; // template<typename T> LoggerWrap& operator<< (const T& data) { buf << data; return *this; } LoggerWrap& operator<< (const Flush&) { write(buf.str()); buf.str(""); buf.flags(defFmtFlags); return *this; } private: thread_local static std::ostringstream buf; const thread_local static std::ios::fmtflags defFmtFlags; }; thread_local std::ostringstream LoggerWrap::buf; const thread_local std::ios::fmtflags LoggerWrap::defFmtFlags(buf.flags());
Using:
LoggerWrap logger; logger << "#" << 1 << ": " << 1.2 << ", text again" << LoggerWrap::Flush(); logger << "#" << 2 << ": " << std::scientific << 2.3 << ", text again" << LoggerWrap::Flush(); logger << "string #" << 3 << ": " << 10.5 << LoggerWrap::Flush();
the console will print:
[Log]: #1: 1.2, text again [Log]: #2: 2.300000e+00, text again [Log]: #3: 10.5
For me, the disadvantage of this option is the need to write LoggerWrap :: Flush (). You can forget to write it and then try to understand for a long time what the hell is happening in the log.
How to determine that the line for logging is complete, without explicit reference to this? I decided to rely on the lifetime of the temporary object. When a function returns an object, then it lives while there is a link to it. In this way, you can create a temporary object with your operator <<, which returns a reference to this object, and its destructor will call the LoggerWrap :: write method.
It turns out the following:
class LoggerWrap { public: void write(const std::string& str) const { // std::cout << "[Log]: " << str << std::endl; } /* ... ... */ class TmpLog { friend class LoggerWrap; public: ~TmpLog() { if (flush) { logger.write(buf.str()); buf.str(""); } } template<typename T> TmpLog& operator<< (const T& data) { buf << data; return *this; } TmpLog(const TmpLog&) = delete; private: TmpLog(const LoggerWrap& logger, std::ostringstream& buf) : logger(logger), buf(buf) { } TmpLog(TmpLog&& that): logger(that.logger), buf(that.buf), flush(that.flush) { that.flush = false; } const LoggerWrap& logger; std::ostringstream& buf; bool flush = true; }; template<typename T> TmpLog operator<< (const T& data) { buf.flags(defFmtFlags); TmpLog tmlLog(*this, buf); return std::move(tmlLog << data); } private: thread_local static std::ostringstream buf; const thread_local static std::ios::fmtflags defFmtFlags; }; thread_local std::ostringstream LoggerWrap::buf; const thread_local std::ios::fmtflags LoggerWrap::defFmtFlags(buf.flags());
and use:
LoggerWrap logger; logger << "#" << 1 << ": " << 1.2 << ", text again"; logger << "#" << 2 << ": " << std::scientific << 2.3 << ", text again"; logger << "#" << 3 << ": " << 10.5;
console output will be similar to the first solution.
Logging levels are not specified here (Warning, Error, Info, etc.) as our logging system does not distinguish between them, but adding it to the wrapper is not difficult. For example, define LoggerWrap :: operator (), taking as an argument the desired level for the string to be output.
I prefer the second solution, since I don’t need to add a certain magic word after each line. And when adding logging levels, this information can be stored in a temporary object for the current line.
Thank you for reading, I hope to see your opinion in the comments.
Source: https://habr.com/ru/post/342160/
All Articles