bitz-server  2.0.0
file_sinks.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 #include "../details/file_helper.h"
9 #include "../details/null_mutex.h"
10 #include "../fmt/fmt.h"
11 #include "base_sink.h"
12 
13 #include <algorithm>
14 #include <cerrno>
15 #include <chrono>
16 #include <cstdio>
17 #include <ctime>
18 #include <mutex>
19 #include <string>
20 
21 namespace spdlog {
22 namespace sinks {
23 /*
24  * Trivial file sink with single file as target
25  */
26 template<class Mutex>
27 class simple_file_sink SPDLOG_FINAL : public base_sink<Mutex>
28 {
29 public:
30  explicit simple_file_sink(const filename_t &filename, bool truncate = false)
31  : _force_flush(false)
32  {
33  _file_helper.open(filename, truncate);
34  }
35 
36  void set_force_flush(bool force_flush)
37  {
38  _force_flush = force_flush;
39  }
40 
41 protected:
42  void _sink_it(const details::log_msg &msg) override
43  {
44  _file_helper.write(msg);
45  if (_force_flush)
46  {
47  _file_helper.flush();
48  }
49  }
50 
51  void _flush() override
52  {
53  _file_helper.flush();
54  }
55 
56 private:
57  details::file_helper _file_helper;
58  bool _force_flush;
59 };
60 
61 using simple_file_sink_mt = simple_file_sink<std::mutex>;
62 using simple_file_sink_st = simple_file_sink<details::null_mutex>;
63 
64 /*
65  * Rotating file sink based on size
66  */
67 template<class Mutex>
68 class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex>
69 {
70 public:
71  rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
72  : _base_filename(std::move(base_filename))
73  , _max_size(max_size)
74  , _max_files(max_files)
75  {
76  _file_helper.open(calc_filename(_base_filename, 0));
77  _current_size = _file_helper.size(); // expensive. called only once
78  }
79 
80  // calc filename according to index and file extension if exists.
81  // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
82  static filename_t calc_filename(const filename_t &filename, std::size_t index)
83  {
84  typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
85  if (index != 0u)
86  {
87  filename_t basename, ext;
88  std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
89  w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
90  }
91  else
92  {
93  w.write(SPDLOG_FILENAME_T("{}"), filename);
94  }
95  return w.str();
96  }
97 
98 protected:
99  void _sink_it(const details::log_msg &msg) override
100  {
101  _current_size += msg.formatted.size();
102  if (_current_size > _max_size)
103  {
104  _rotate();
105  _current_size = msg.formatted.size();
106  }
107  _file_helper.write(msg);
108  }
109 
110  void _flush() override
111  {
112  _file_helper.flush();
113  }
114 
115 private:
116  // Rotate files:
117  // log.txt -> log.1.txt
118  // log.1.txt -> log.2.txt
119  // log.2.txt -> log.3.txt
120  // log.3.txt -> delete
121  void _rotate()
122  {
123  using details::os::filename_to_str;
124  _file_helper.close();
125  for (auto i = _max_files; i > 0; --i)
126  {
127  filename_t src = calc_filename(_base_filename, i - 1);
128  filename_t target = calc_filename(_base_filename, i);
129 
130  if (details::file_helper::file_exists(target))
131  {
132  if (details::os::remove(target) != 0)
133  {
134  throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
135  }
136  }
137  if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
138  {
139  throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
140  }
141  }
142  _file_helper.reopen(true);
143  }
144 
145  filename_t _base_filename;
146  std::size_t _max_size;
147  std::size_t _max_files;
148  std::size_t _current_size;
149  details::file_helper _file_helper;
150 };
151 
152 using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
153 using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
154 
155 /*
156  * Default generator of daily log file names.
157  */
159 {
160  // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
161  static filename_t calc_filename(const filename_t &filename)
162  {
163  std::tm tm = spdlog::details::os::localtime();
164  filename_t basename, ext;
165  std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
166  std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
167  w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
168  tm.tm_hour, tm.tm_min, ext);
169  return w.str();
170  }
171 };
172 
173 /*
174  * Generator of daily log file names in format basename.YYYY-MM-DD.ext
175  */
177 {
178  // Create filename for the form basename.YYYY-MM-DD
179  static filename_t calc_filename(const filename_t &filename)
180  {
181  std::tm tm = spdlog::details::os::localtime();
182  filename_t basename, ext;
183  std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
184  std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
185  w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
186  return w.str();
187  }
188 };
189 
190 /*
191  * Rotating file sink based on date. rotates at midnight
192  */
193 template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
194 class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex>
195 {
196 public:
197  // create daily file sink which rotates on given time
198  daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute)
199  : _base_filename(std::move(base_filename))
200  , _rotation_h(rotation_hour)
201  , _rotation_m(rotation_minute)
202  {
203  if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
204  {
205  throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
206  }
207  _rotation_tp = _next_rotation_tp();
208  _file_helper.open(FileNameCalc::calc_filename(_base_filename));
209  }
210 
211 protected:
212  void _sink_it(const details::log_msg &msg) override
213  {
214  if (std::chrono::system_clock::now() >= _rotation_tp)
215  {
216  _file_helper.open(FileNameCalc::calc_filename(_base_filename));
217  _rotation_tp = _next_rotation_tp();
218  }
219  _file_helper.write(msg);
220  }
221 
222  void _flush() override
223  {
224  _file_helper.flush();
225  }
226 
227 private:
228  std::chrono::system_clock::time_point _next_rotation_tp()
229  {
230  auto now = std::chrono::system_clock::now();
231  time_t tnow = std::chrono::system_clock::to_time_t(now);
232  tm date = spdlog::details::os::localtime(tnow);
233  date.tm_hour = _rotation_h;
234  date.tm_min = _rotation_m;
235  date.tm_sec = 0;
236  auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
237  if (rotation_time > now)
238  {
239  return rotation_time;
240  }
241  return {rotation_time + std::chrono::hours(24)};
242  }
243 
244  filename_t _base_filename;
245  int _rotation_h;
246  int _rotation_m;
247  std::chrono::system_clock::time_point _rotation_tp;
248  details::file_helper _file_helper;
249 };
250 
251 using daily_file_sink_mt = daily_file_sink<std::mutex>;
252 using daily_file_sink_st = daily_file_sink<details::null_mutex>;
253 
254 } // namespace sinks
255 } // namespace spdlog
Definition: lib/spdlog/common.h:146
Definition: file_helper.h:25
Definition: format.h:448
Definition: async_logger.h:26
void write(BasicCStringRef< Char > format, ArgList args)
Definition: format.h:3332
Definition: log_msg.h:16
Definition: format.h:3924
Definition: base_sink.h:23