#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_SUPPORT_TEXT_ROLLINGLOGGER_H #define NUCLEX_SUPPORT_TEXT_ROLLINGLOGGER_H #include "Nuclex/Support/Config.h" #include "Nuclex/Support/Text/Logger.h" #include "Nuclex/Support/Text/LexicalAppend.h" // used by templated Append() method #include // for std::vector namespace Nuclex { namespace Support { namespace Text { // ------------------------------------------------------------------------------------------- // /// Logger that buffers lines cheaply in memory until they're needed /// /// /// This implementation of a logger is intended to be light on CPU time and constantly /// collect log output in a circular buffer in the background. It will not cause /// hard drive accesses and even avoids memory allocations if you avoid external string /// formatting (i.e. you rely on Append() instead of std::format() or lexical_cast) /// and keep you log lines below 100 characters. /// /// /// When an error happens, you can use the GetLines() method of the rolling logger to /// obtain the last 1024 lines from the log's history and display this as technical /// error information, save it to an error report file or upload it in a reporting tool. /// /// /// /// logger.Inform(u8"Saving configuration {"); /// { /// Logger::IndentationScope indentConfigurationScope(logger); /// /// logger.Append(u8"ResolutionX = "); /// logger.Append(1920); /// logger.Inform(std::string()); /// /// logger.Append(u8"ResolutionY = "); /// logger.Append(1080); /// logger.Inform(std::string()); /// } /// logger.Inform(u8"}"); /// /// /// class NUCLEX_SUPPORT_TYPE RollingLogger : public Logger { /// Initializes a new rolling logger /// Number of lines the logger will keep /// Length the logger expects most lines to stay under public: NUCLEX_SUPPORT_API RollingLogger( std::size_t historyLineCount = 1024U, std::size_t lineSizeHint = 100U ); /// Frees all resources owned by the logger public: NUCLEX_SUPPORT_API virtual ~RollingLogger() = default; /// Advises the logger that all successive output should be indented /// /// /// This method is provided because logging often involves printing status in /// multiple lines. Having an official method to indent output in the basic logger /// interface helps keep output readable. /// /// /// This method can be called any number of times and will apply increasing /// indentation to all log output performed. It needs to be followed by an equal /// number of calls to the Unindent() method eventually. /// /// public: NUCLEX_SUPPORT_API void Indent() override; /// Advises the logger to go back up by one level of indentation /// /// /// This is the counterpart to the method. It needs to be /// called exactly one time for each call to the method. /// In order to ensure the logger isn't accumulating indentation levels, use /// the nested class provided by the logger interface. /// /// public: NUCLEX_SUPPORT_API void Unindent() override; /// Whether the logger is actually doing anything with the log messages /// True if the log messages are processed in any way, false otherwise /// /// Forming the log message strings may be non-trivial and cause memory allocations, too, /// so by checking this method just once, you can skip all logging in case your messages /// would be discarded anyway. /// public: NUCLEX_SUPPORT_API bool IsLogging() const override; /// Logs a diagnostic message /// Message the operation wishes to log /// /// Use this for diagnostic output that may help with debugging or verifying that /// things are indeed happening the way you intended to. These messages typically /// go into some log, a details window or are discarded outright. /// public: NUCLEX_SUPPORT_API void Inform(const std::string &message) override; /// Logs a warning /// Warning the operation wishes to log /// /// /// Use this if your operation encounters a problem that isn't fatal but means /// that the outcome will not be as intended. Also use if your operation discovers /// something that isn't the way it should be (i.e. a filename doesn't follow /// conventions, data uses deprecated format, etc.) /// /// /// Logged warnings may be displayed to the user, for example as a summary after /// the operation completed with warnings. /// /// public: NUCLEX_SUPPORT_API void Warn(const std::string &warning) override; /// Logs an error /// Error the operation wishes to log /// /// /// Only use this if the operation is bound to fail. An exception should be thrown /// from the operation as a result. /// /// /// The error logger may provide additional information beyond the exception /// message and may be displayed to the user, for example in an error dialog after /// the operation has failed. /// /// public: NUCLEX_SUPPORT_API void Complain(const std::string &error) override; /// Appends something to the log line currently being formed /// /// Value that will be appended to the line-in-progress as text. /// Must be a primitive type or std::string /// /// /// This method appends the specified value to the logger's internal line buffer. /// When you call , or , /// the line will be closed and appear in the log history. /// public: template inline void Append(const TValue &value) { lexical_append(*this->currentLine, value); } /// Appends text from a buffer to the line currently being formed /// Buffer holding the characters that will be appended /// Number of bytes to append from the buffer public: NUCLEX_SUPPORT_API void Append(const char *buffer, std::size_t count); /// Removes all history and clears the line currently being formed public: NUCLEX_SUPPORT_API void Clear(); /// Returns a vector holding all lines currently in the log history /// A vector of all lines in the log history /// /// /// The rolling logger is designed as a logger you can feed all the time at a low /// performance price (achieved by efficient append operations and by not writing /// anything to a file). /// /// /// If and when an error happens, you can log it and then use this method to obtain /// the recent log history. This will let you save the error details themselves as /// well as the actions leading up to it when needed. /// /// public: NUCLEX_SUPPORT_API std::vector GetLines() const; #if 0 // Convenient, but usually people want to save additional information to the file... /// Saves the current contents of the log into a file /// Path of the file to which the log will be written public: NUCLEX_SUPPORT_API void SaveToFile(const std::string &path) const; #endif /// Called each time a new line is added to the rolling log /// The full contents of the line /// /// You can override this method if you wish to live-print log lines to console /// windows or terminals of some kind. /// protected: NUCLEX_SUPPORT_API virtual void OnLineAdded(const std::string &line) { (void)line; } /// Advances to the next line private: void advanceLine(); /// Updates the time stamp stored in the line with the specified index /// Index of the line in which the time stamp will be stored /// /// Assumes that the line is long enough have the time stamp written into it. /// private: static void updateTimeInLine(std::string &line); /// Index of the line that is currently being formed private: std::size_t nextLineIndex; /// Index of the oldest line in the ring buffer private: std::size_t oldestLineIndex; /// Ring buffer holding the log history as strings that get reused private: std::vector lines; /// String from the lines array with index nextLineIndex private: std::string *currentLine; /// Number of spaces the current line is indented by private: std::size_t indentationCount; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Support::Text #endif // NUCLEX_SUPPORT_TEXT_ROLLINGLOGGER_H