From 0b59e5a94c3551cf550196db1ee7175972e69d10 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Mon, 12 Jun 2023 16:59:28 -0700 Subject: [PATCH] Documented log class (#4711) * Documented log class * cspell --- .../azure/core/internal/diagnostics/log.hpp | 133 +++++++++++++++++- sdk/core/azure-core/src/logger.cpp | 11 +- 2 files changed, 137 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core/inc/azure/core/internal/diagnostics/log.hpp b/sdk/core/azure-core/inc/azure/core/internal/diagnostics/log.hpp index dcedc6113..7fc7e0365 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/diagnostics/log.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/diagnostics/log.hpp @@ -12,6 +12,47 @@ #include namespace Azure { namespace Core { namespace Diagnostics { namespace _internal { + + /** @brief Internal Log class used for generating diagnostic logs. + * + * When components within the Azure SDK wish to emit diagnostic log messages, they should use the + * Azure::Core::Diagnostics::_internal::Log class to generate those messages. + * + * The Log class methods integrate with the public diagnostic logging infrastructure to generate + * log messages which will be captured by either the default logger or a customer provided logger. + * + * Usage: + * + * There are two primary interfaces to the Log class. The first (and most common) is to use the + * Log::Write method to write a string to the configured logger. For example: + * + * ```cpp + * using namespace Azure::Core::Diagnostics::_internal; + * using namespace Azure::Core::Diagnostics; + * : + * : + * Log::Write(Logger::Level::Verbose, "This is a diagnostic message"); + * ``` + * + * this will pass the string "This is a diagnostic message" to the configured logger at the + * "Verbose" error level. + * + * The second interface is to use the Log::Stream() class to stream a string to the configured + * logger. For example: + * + * ```cpp + * using namespace Azure::Core::Diagnostics::_internal; + * using namespace Azure::Core::Diagnostics; + * : + * : + * int resultCode = 500; + * Log::Stream(Logger::Level::Error) << "An error has occurred " << resultCode; + * ``` + * + * this will pass the string "An error has occurred 500" to the configured logger at the "Error" + * error level. + * + */ class Log final { static_assert( std::is_same::type>::value == true, @@ -27,20 +68,35 @@ namespace Azure { namespace Core { namespace Diagnostics { namespace _internal { Log() = delete; ~Log() = delete; + /** @brief String buffer for use in the logger. + * + * A specialization of std::stringbuf for use in the logger. + * + * This function primarily exists to implement the sync() function which triggers a call to + * Log::Write(). + */ class LoggerStringBuffer : public std::stringbuf { public: + /** @brief Configure a LogStringBuffer with the specified logging level. */ LoggerStringBuffer(Logger::Level level) : m_level{level} {} LoggerStringBuffer(LoggerStringBuffer&& that) = default; LoggerStringBuffer& operator=(LoggerStringBuffer&& that) = default; ~LoggerStringBuffer() override = default; + /** @brief Implementation of std::basic_streambuf::sync. + * + * @returns 0 on success, -1 otherwise. + */ virtual int sync() override; private: Logger::Level m_level; }; - public: + /** @brief Logger Stream used internally by the GetStream() private method. + * + * Stream class used to wrap a LoggerStringBuffer stringbuf object. + */ class LoggerStream : public std::basic_ostream { public: LoggerStream(Logger::Level level) : std::ostream(&m_stringBuffer), m_stringBuffer{level} {} @@ -50,30 +106,105 @@ namespace Azure { namespace Core { namespace Diagnostics { namespace _internal { LoggerStringBuffer m_stringBuffer; }; + public: + /** @brief Stream class used to enable using iomanip operators on an I/O stream. + * Usage: + * + * ```cpp + * using namespace Azure::Core::Diagnostics::_internal; + * using namespace Azure::Core::Diagnostics; + * : + * : + * int resultCode = 500; + * Log::Stream(Logger::Level::Error) << "An error has occurred " << resultCode; + * ``` + * + * this will pass the string "An error has occurred 500" to the configured logger at the "Error" + * error level. + * + * @remarks The Log::Stream() construct creates a temporary Stream object whose lifetime ends a + * the end of the statement creating the Stream object. In the destructor for the stream, the + * underlying stream object is flushed thus ensuring that the output is generated at the end of + * the statement, even if the caller does not insert the std::endl object. + */ class Stream { public: + /** @brief Construct a new Stream object with the configured I/O level. + * + * @param level - Represents the desired diagnostic level for the operation. + */ Stream(Logger::Level level) : m_stream(GetStream(level)) {} + + /** @brief Called when the Stream object goes out of scope. */ ~Stream() { m_stream.flush(); } Stream(Stream const&) = delete; Stream& operator=(Stream const&) = delete; + /** @brief Insert an object of type T into the output stream. + * + * @tparam T Type of the object being inserted. + * @param val value to be inserted into the underlying stream. + */ template std::ostream& operator<<(T val) { return m_stream << val; } private: LoggerStream& m_stream; }; + /** @brief Returns true if the logger would write a string at the specified level. + * + * This function primarily exists to enable callers to avoid expensive computations if the + * currently configured log level doesn't support logging at the specified level. + * + * @param level - log level to check. + * @returns true if the logger will write at that log level. + */ static bool ShouldWrite(Logger::Level level) { return g_isLoggingEnabled && level >= g_logLevel; } + /** @brief Write a string to the configured logger at the specified log level. + * + * Expected usage: + * + * ```cpp + * using namespace Azure::Core::Diagnostics::_internal; + * using namespace Azure::Core::Diagnostics; + * : + * : + * Log::Write(Logger::Level::Verbose, "This is a diagnostic message"); + * ``` + * + * this will pass the string "This is a diagnostic message" to the configured logger at the + * "Verbose" error level. + * + * @param level - log level to use for the message. + * @param message - message to write to the logger. + * + */ static void Write(Logger::Level level, std::string const& message); + /** @brief Enable logging. + * + * @param isEnabled - true if logging should be enabled, false if it should be disabled. + */ static void EnableLogging(bool isEnabled); + + /** @brief Set the current log level globally. + * + * Note that this overrides any customer configuration of the log level and should generally be + * avoided. + * + * @param logLevel - New global log level. + */ static void SetLogLevel(Logger::Level logLevel); private: + static LoggerStream g_verboseLogger; + static LoggerStream g_informationalLogger; + static LoggerStream g_warningLogger; + static LoggerStream g_errorLogger; static LoggerStream& GetStream(Logger::Level level); }; }}}} // namespace Azure::Core::Diagnostics::_internal diff --git a/sdk/core/azure-core/src/logger.cpp b/sdk/core/azure-core/src/logger.cpp index 1a93cd917..7277db6b6 100644 --- a/sdk/core/azure-core/src/logger.cpp +++ b/sdk/core/azure-core/src/logger.cpp @@ -61,12 +61,11 @@ int Log::LoggerStringBuffer::sync() str(std::string()); return 0; } -namespace { -static Log::LoggerStream g_verboseLogger{Logger::Level::Verbose}; -static Log::LoggerStream g_informationalLogger{Logger::Level::Informational}; -static Log::LoggerStream g_warningLogger{Logger::Level::Warning}; -static Log::LoggerStream g_errorLogger{Logger::Level::Error}; -} // namespace + +Log::LoggerStream Log::g_verboseLogger{Logger::Level::Verbose}; +Log::LoggerStream Log::g_informationalLogger{Logger::Level::Informational}; +Log::LoggerStream Log::g_warningLogger{Logger::Level::Warning}; +Log::LoggerStream Log::g_errorLogger{Logger::Level::Error}; /** Returns a custom ostream implementation with a logger based stream buffer. * @param level The level of the log message.