winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

AppInstallerLogging.h (8178B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #pragma once
      4 #include <AppInstallerLanguageUtilities.h>
      5 #include <chrono>
      6 #include <filesystem>
      7 #include <memory>
      8 #include <sstream>
      9 #include <string>
     10 #include <string_view>
     11 #include <type_traits>
     12 #include <vector>
     13 
     14 #define AICLI_LOG_DIRECT(_logger_,_channel_,_level_,_outstream_) \
     15     do { \
     16         auto _aicli_log_channel = AppInstaller::Logging::Channel:: _channel_; \
     17         auto _aicli_log_level = AppInstaller::Logging::Level:: _level_; \
     18         auto& _aicli_log_log = _logger_; \
     19         if (_aicli_log_log.IsEnabled(_aicli_log_channel, _aicli_log_level)) \
     20         { \
     21             AppInstaller::Logging::LoggingStream _aicli_log_strstr; \
     22             _aicli_log_strstr _outstream_; \
     23             _aicli_log_log.Write(_aicli_log_channel, _aicli_log_level, _aicli_log_strstr.str()); \
     24         } \
     25     } while (0, 0)
     26 
     27 #define AICLI_LOG(_channel_,_level_,_outstream_) AICLI_LOG_DIRECT(AppInstaller::Logging::Log(),_channel_,_level_,_outstream_)
     28 
     29 // Consider using this macro when the string might be larger than 4K.
     30 // The normal macro has some buffering that occurs; it can cut off larger strings and is slower.
     31 #define AICLI_LOG_LARGE_STRING(_channel_,_level_,_headerStream_,_largeString_) \
     32     do { \
     33         auto _aicli_log_channel = AppInstaller::Logging::Channel:: _channel_; \
     34         auto _aicli_log_level = AppInstaller::Logging::Level:: _level_; \
     35         auto& _aicli_log_log = AppInstaller::Logging::Log(); \
     36         if (_aicli_log_log.IsEnabled(_aicli_log_channel, _aicli_log_level)) \
     37         { \
     38             AppInstaller::Logging::LoggingStream _aicli_log_strstr; \
     39             _aicli_log_strstr _headerStream_; \
     40             _aicli_log_log.Write(_aicli_log_channel, _aicli_log_level, _aicli_log_strstr.str()); \
     41             _aicli_log_log.WriteDirect(_aicli_log_channel, _aicli_log_level, _largeString_); \
     42         } \
     43     } while (0, 0)
     44 
     45 namespace AppInstaller::Logging
     46 {
     47     // The channel that the log is from.
     48     // Channels enable large groups of logs to be enabled or disabled together.
     49     enum class Channel : uint32_t
     50     {
     51         Fail = 0x1,
     52         CLI = 0x2,
     53         SQL = 0x4,
     54         Repo = 0x8,
     55         YAML = 0x10,
     56         Core = 0x20,
     57         Test = 0x40,
     58         Config = 0x80,
     59         Workflow = 0x100,
     60         None = 0,
     61         All = 0xFFFFFFFF,
     62         Defaults = All & ~(SQL | Workflow),
     63     };
     64 
     65     DEFINE_ENUM_FLAG_OPERATORS(Channel);
     66 
     67     // Gets the channel's name as a string.
     68     std::string_view GetChannelName(Channel channel);
     69 
     70     // Gets the channel from it's name.
     71     Channel GetChannelFromName(std::string_view channel);
     72 
     73     // Gets the maximum channel name length in characters.
     74     size_t GetMaxChannelNameLength();
     75 
     76     // The level of the log.
     77     enum class Level
     78     {
     79         Verbose,
     80         Info,
     81         Warning,
     82         Error,
     83         Crit,
     84     };
     85 
     86     // The interface that a log target must implement.
     87     struct ILogger
     88     {
     89         virtual ~ILogger() = default;
     90 
     91         // Gets the name of the logger for internal use.
     92         virtual std::string GetName() const = 0;
     93 
     94         // Informs the logger of the given log.
     95         virtual void Write(Channel channel, Level level, std::string_view message) noexcept = 0;
     96 
     97         // Informs the logger of the given log with the intention that no buffering occurs (in winget code).
     98         virtual void WriteDirect(Channel channel, Level level, std::string_view message) noexcept = 0;
     99     };
    100 
    101     // This type contains the set of loggers that diagnostic logging will be sent to.
    102     // Each binary that leverages it must configure any loggers and filters to their
    103     // desired level, as nothing is enabled by default.
    104     struct DiagnosticLogger
    105     {
    106         DiagnosticLogger() = default;
    107 
    108         ~DiagnosticLogger() = default;
    109 
    110         DiagnosticLogger(const DiagnosticLogger&) = delete;
    111         DiagnosticLogger& operator=(const DiagnosticLogger&) = delete;
    112 
    113         DiagnosticLogger(DiagnosticLogger&&) = delete;
    114         DiagnosticLogger& operator=(DiagnosticLogger&&) = delete;
    115 
    116         // Gets the singleton instance of this type.
    117         static DiagnosticLogger& GetInstance();
    118 
    119         // NOTE: The logger management functionality is *SINGLE THREAD SAFE*.
    120         //       This includes with logging itself.
    121         //       As it is not expected that adding/removing loggers is an
    122         //       extremely frequent operation, no care has been made to protect
    123         //       it from modifying loggers while logging may be occurring.
    124 
    125         // Adds a logger to the active set.
    126         void AddLogger(std::unique_ptr<ILogger>&& logger);
    127 
    128         // Determines if a logger with the given name is present.
    129         bool ContainsLogger(const std::string& name);
    130 
    131         // Removes a logger from the active set, returning it.
    132         std::unique_ptr<ILogger> RemoveLogger(const std::string& name);
    133 
    134         // Removes all loggers.
    135         void RemoveAllLoggers();
    136 
    137         // Enables the given channel(s), in addition to the currently enabled channels.
    138         void EnableChannel(Channel channel);
    139 
    140         // The given channel mask will become the only enabled channels.
    141         void SetEnabledChannels(Channel channel);
    142 
    143         // Disables the given channel.
    144         void DisableChannel(Channel channel);
    145 
    146         // Sets the enabled level.
    147         // All levels above this level will be enabled.
    148         // For example; SetLevel(Verbose) will enable all logs.
    149         void SetLevel(Level level);
    150 
    151         // Gets the enabled level.
    152         Level GetLevel() const;
    153 
    154         // Checks whether a given channel and level are enabled.
    155         bool IsEnabled(Channel channel, Level level) const;
    156 
    157         // Writes a log line, if the given channel and level are enabled.
    158         void Write(Channel channel, Level level, std::string_view message);
    159 
    160         // Writes a log line, if the given channel and level are enabled.
    161         // Use to make large logs more efficient by writing directly to the output streams.
    162         void WriteDirect(Channel channel, Level level, std::string_view message);
    163 
    164     private:
    165 
    166         std::vector<std::unique_ptr<ILogger>> m_loggers;
    167         Channel m_enabledChannels = Channel::None;
    168         Level m_enabledLevel = Level::Info;
    169     };
    170 
    171     DiagnosticLogger& Log();
    172 
    173     // Calls the various stream format functions to produce an 8 character hexadecimal output.
    174     std::ostream& SetHRFormat(std::ostream& out);
    175 
    176     // This type allows us to override the default behavior of output operators for logging.
    177     struct LoggingStream
    178     {
    179         // Force use of the UTF-8 string from a file path.
    180         // This should not be necessary when we move to C++20 and convert to using u8string.
    181         friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, const std::filesystem::path& path)
    182         {
    183             out.m_out << path.u8string();
    184             return out;
    185         }
    186 
    187         // Enums
    188         template <typename T>
    189         friend std::enable_if_t<std::is_enum_v<std::decay_t<T>>, AppInstaller::Logging::LoggingStream&>
    190             operator<<(AppInstaller::Logging::LoggingStream& out, T t)
    191         {
    192             out.m_out << ToIntegral(t);
    193             return out;
    194         }
    195 
    196         // Everything else.
    197         template <typename T>
    198         friend std::enable_if_t<!std::disjunction_v<std::is_same<std::decay_t<T>, std::filesystem::path>, std::is_enum<std::decay_t<T>>>, AppInstaller::Logging::LoggingStream&>
    199             operator<<(AppInstaller::Logging::LoggingStream& out, T&& t)
    200         {
    201             out.m_out << std::forward<T>(t);
    202             return out;
    203         }
    204 
    205         std::string str() const { return m_out.str(); }
    206 
    207     private:
    208         std::stringstream m_out;
    209     };
    210 }
    211 
    212 namespace std
    213 {
    214     std::ostream& operator<<(std::ostream& out, const std::chrono::system_clock::time_point& time);
    215     std::ostream& operator<<(std::ostream& out, const GUID& guid);
    216 }