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 }