winget-cli

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

FileLogger.cpp (5051B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/AppInstallerFileLogger.h"
      5 
      6 #include "Public/AppInstallerRuntime.h"
      7 #include "Public/AppInstallerStrings.h"
      8 #include "Public/AppInstallerDateTime.h"
      9 #include <corecrt_io.h>
     10 
     11 
     12 namespace AppInstaller::Logging
     13 {
     14     using namespace std::string_view_literals;
     15     using namespace std::chrono_literals;
     16 
     17     static constexpr std::string_view s_fileLoggerDefaultFilePrefix = "WinGet"sv;
     18     static constexpr std::string_view s_fileLoggerDefaultFileExt = ".log"sv;
     19 
     20     FileLogger::FileLogger() : FileLogger(s_fileLoggerDefaultFilePrefix) {}
     21 
     22     FileLogger::FileLogger(const std::filesystem::path& filePath)
     23     {
     24         m_name = GetNameForPath(filePath);
     25         m_filePath = filePath;
     26         OpenFileLoggerStream();
     27     }
     28 
     29     FileLogger::FileLogger(const std::string_view fileNamePrefix)
     30     {
     31         m_name = "file";
     32         m_filePath = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation);
     33         m_filePath /= fileNamePrefix.data() + ('-' + Utility::GetCurrentTimeForFilename() + s_fileLoggerDefaultFileExt.data());
     34         OpenFileLoggerStream();
     35     }
     36 
     37     FileLogger::~FileLogger()
     38     {
     39         m_stream.flush();
     40         // When std::ofstream is constructed from an existing File handle, it does not call fclose on destruction
     41         // Only calling close() explicitly will close the file handle.
     42         m_stream.close();
     43     }
     44 
     45     std::string FileLogger::GetNameForPath(const std::filesystem::path& filePath)
     46     {
     47         using namespace std::string_literals;
     48         return "file :: "s + filePath.u8string();
     49     }
     50 
     51     std::string_view FileLogger::DefaultPrefix()
     52     {
     53         return s_fileLoggerDefaultFilePrefix;
     54     }
     55 
     56     std::string_view FileLogger::DefaultExt()
     57     {
     58         return s_fileLoggerDefaultFileExt;
     59     }
     60 
     61     std::string FileLogger::GetName() const
     62     {
     63         return m_name;
     64     }
     65 
     66     void FileLogger::Write(Channel channel, Level, std::string_view message) noexcept try
     67     {
     68         // Send to a string first to create a single block to write to a file.
     69         std::stringstream strstr;
     70         strstr << std::chrono::system_clock::now() << " [" << std::setw(GetMaxChannelNameLength()) << std::left << std::setfill(' ') << GetChannelName(channel) << "] " << message;
     71         m_stream << strstr.str() << std::endl;
     72     }
     73     catch (...)
     74     {
     75         // Just eat any exceptions here; better than losing logs
     76     }
     77 
     78     void FileLogger::WriteDirect(Channel, Level, std::string_view message) noexcept try
     79     {
     80         m_stream << message << std::endl;
     81     }
     82     catch (...)
     83     {
     84         // Just eat any exceptions here; better than losing logs
     85     }
     86 
     87     void FileLogger::Add()
     88     {
     89         Log().AddLogger(std::make_unique<FileLogger>());
     90     }
     91 
     92     void FileLogger::Add(const std::filesystem::path& filePath)
     93     {
     94         Log().AddLogger(std::make_unique<FileLogger>(filePath));
     95     }
     96 
     97     void FileLogger::Add(std::string_view fileNamePrefix)
     98     {
     99         Log().AddLogger(std::make_unique<FileLogger>(fileNamePrefix));
    100     }
    101 
    102     void FileLogger::BeginCleanup()
    103     {
    104         BeginCleanup(Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation));
    105     }
    106 
    107     void FileLogger::BeginCleanup(const std::filesystem::path& filePath)
    108     {
    109         std::thread([filePath]()
    110             {
    111                 try
    112                 {
    113                     auto now = std::filesystem::file_time_type::clock::now();
    114 
    115                     // Remove all files that are older than 7 days from the standard log location.
    116                     for (auto& file : std::filesystem::directory_iterator{ filePath })
    117                     {
    118                         if (file.is_regular_file() &&
    119                             now - file.last_write_time() > (7 * 24h))
    120                         {
    121                             std::filesystem::remove(file.path());
    122                         }
    123                     }
    124                 }
    125                 // Just throw out everything
    126                 catch (...) {}
    127             }).detach();
    128     }
    129 
    130     void FileLogger::OpenFileLoggerStream() 
    131     {
    132         // Prevent other writers to our log file, but allow readers
    133         FILE* filePtr = _wfsopen(m_filePath.wstring().c_str(), L"w", _SH_DENYWR);
    134 
    135         if (filePtr)
    136         {
    137             auto closeFile = wil::scope_exit([&]() { fclose(filePtr); });
    138 
    139             // Prevent inheritance to ensure log file handle is not opened by other processes
    140             THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(filePtr))), HANDLE_FLAG_INHERIT, 0));
    141 
    142             m_stream = std::ofstream{ filePtr };
    143             closeFile.release();
    144         }
    145         else
    146         {
    147             AICLI_LOG(Core, Error, << "Failed to open log file " << m_filePath.u8string());
    148             throw std::system_error(errno, std::generic_category());
    149         }
    150     }
    151 }