1 // 2 // Copyright(c) 2015 Gabi Melman. 3 // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 // 5 6 #pragma once 7 8 // Helper class for file sink 9 // When failing to open a file, retry several times(5) with small delay between the tries(10 ms) 10 // Can be set to auto flush on every line 11 // Throw spdlog_ex exception on errors 12 13 #include <spdlog/details/os.h> 14 #include <spdlog/details/log_msg.h> 15 16 #include <chrono> 17 #include <cstdio> 18 #include <string> 19 #include <thread> 20 #include <cerrno> 21 22 namespace spdlog 23 { 24 namespace details 25 { 26 27 class file_helper 28 { 29 30 public: 31 const int open_tries = 5; 32 const int open_interval = 10; 33 file_helper()34 explicit file_helper() : 35 _fd(nullptr) 36 {} 37 38 file_helper(const file_helper&) = delete; 39 file_helper& operator=(const file_helper&) = delete; 40 ~file_helper()41 ~file_helper() 42 { 43 close(); 44 } 45 46 47 void open(const filename_t& fname, bool truncate = false) 48 { 49 50 close(); 51 auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); 52 _filename = fname; 53 for (int tries = 0; tries < open_tries; ++tries) 54 { 55 if (!os::fopen_s(&_fd, fname, mode)) 56 return; 57 58 std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); 59 } 60 61 throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); 62 } 63 reopen(bool truncate)64 void reopen(bool truncate) 65 { 66 if (_filename.empty()) 67 throw spdlog_ex("Failed re opening file - was not opened before"); 68 open(_filename, truncate); 69 70 } 71 flush()72 void flush() 73 { 74 std::fflush(_fd); 75 } 76 close()77 void close() 78 { 79 if (_fd) 80 { 81 std::fclose(_fd); 82 _fd = nullptr; 83 } 84 } 85 write(const log_msg & msg)86 void write(const log_msg& msg) 87 { 88 89 size_t msg_size = msg.formatted.size(); 90 auto data = msg.formatted.data(); 91 if (std::fwrite(data, 1, msg_size, _fd) != msg_size) 92 throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); 93 } 94 size()95 size_t size() 96 { 97 if (!_fd) 98 throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); 99 return os::filesize(_fd); 100 } 101 filename()102 const filename_t& filename() const 103 { 104 return _filename; 105 } 106 file_exists(const filename_t & name)107 static bool file_exists(const filename_t& name) 108 { 109 110 return os::file_exists(name); 111 } 112 113 private: 114 FILE* _fd; 115 filename_t _filename; 116 }; 117 } 118 } 119