bitz-server  2.0.3
file_helper.h
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 // Throw spdlog_ex exception on errors
11 
12 #include "../details/log_msg.h"
13 #include "../details/os.h"
14 
15 #include <cerrno>
16 #include <chrono>
17 #include <cstdio>
18 #include <string>
19 #include <thread>
20 #include <tuple>
21 
22 namespace spdlog {
23 namespace details {
24 
26 {
27 
28 public:
29  const int open_tries = 5;
30  const int open_interval = 10;
31 
32  explicit file_helper() = default;
33 
34  file_helper(const file_helper &) = delete;
35  file_helper &operator=(const file_helper &) = delete;
36 
37  ~file_helper()
38  {
39  close();
40  }
41 
42  void open(const filename_t &fname, bool truncate = false)
43  {
44  close();
45  auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
46  _filename = fname;
47  for (int tries = 0; tries < open_tries; ++tries)
48  {
49  if (!os::fopen_s(&_fd, fname, mode))
50  {
51  return;
52  }
53 
54  details::os::sleep_for_millis(open_interval);
55  }
56 
57  throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
58  }
59 
60  void reopen(bool truncate)
61  {
62  if (_filename.empty())
63  {
64  throw spdlog_ex("Failed re opening file - was not opened before");
65  }
66  open(_filename, truncate);
67  }
68 
69  void flush()
70  {
71  std::fflush(_fd);
72  }
73 
74  void close()
75  {
76  if (_fd != nullptr)
77  {
78  std::fclose(_fd);
79  _fd = nullptr;
80  }
81  }
82 
83  void write(const log_msg &msg)
84  {
85  size_t msg_size = msg.formatted.size();
86  auto data = msg.formatted.data();
87  if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
88  {
89  throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
90  }
91  }
92 
93  size_t size() const
94  {
95  if (_fd == nullptr)
96  {
97  throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
98  }
99  return os::filesize(_fd);
100  }
101 
102  const filename_t &filename() const
103  {
104  return _filename;
105  }
106 
107  static bool file_exists(const filename_t &fname)
108  {
109  return os::file_exists(fname);
110  }
111 
112  //
113  // return file path and its extension:
114  //
115  // "mylog.txt" => ("mylog", ".txt")
116  // "mylog" => ("mylog", "")
117  // "mylog." => ("mylog.", "")
118  // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
119  //
120  // the starting dot in filenames is ignored (hidden files):
121  //
122  // ".mylog" => (".mylog". "")
123  // "my_folder/.mylog" => ("my_folder/.mylog", "")
124  // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
125  static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
126  {
127  auto ext_index = fname.rfind('.');
128 
129  // no valid extension found - return whole path and empty string as extension
130  if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
131  {
132  return std::make_tuple(fname, spdlog::filename_t());
133  }
134 
135  // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
136  auto folder_index = fname.rfind(details::os::folder_sep);
137  if (folder_index != fname.npos && folder_index >= ext_index - 1)
138  {
139  return std::make_tuple(fname, spdlog::filename_t());
140  }
141 
142  // finally - return a valid base and extension tuple
143  return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
144  }
145 
146 private:
147  FILE *_fd{nullptr};
148  filename_t _filename;
149 };
150 } // namespace details
151 } // namespace spdlog
Definition: lib/spdlog/common.h:146
Definition: file_helper.h:25
const Char * data() const FMT_NOEXCEPT
Definition: format.h:3280
Definition: async_logger.h:26
std::size_t size() const
Definition: format.h:3271
Definition: log_msg.h:16