Livox SDK API  V2.2.0
rotating_file_sink.h
Go to the documentation of this file.
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 #ifndef SPDLOG_H
9 #include "spdlog/spdlog.h"
10 #endif
11 
14 #include "spdlog/fmt/fmt.h"
15 #include "spdlog/sinks/base_sink.h"
16 
17 #include <cerrno>
18 #include <chrono>
19 #include <ctime>
20 #include <mutex>
21 #include <string>
22 #include <tuple>
23 
24 namespace spdlog {
25 namespace sinks {
26 
27 //
28 // Rotating file sink based on size
29 //
30 template<typename Mutex>
31 class rotating_file_sink final : public base_sink<Mutex>
32 {
33 public:
34  rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open=false)
35  : base_filename_(std::move(base_filename))
36  , max_size_(max_size)
37  , max_files_(max_files)
38  {
39  file_helper_.open(calc_filename(base_filename_, 0));
40  current_size_ = file_helper_.size(); // expensive. called only once
41  if (rotate_on_open && current_size_ > 0)
42  {
43  rotate_();
44  }
45  }
46 
47  // calc filename according to index and file extension if exists.
48  // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
49  static filename_t calc_filename(const filename_t &filename, std::size_t index)
50  {
51  typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
52  if (index != 0u)
53  {
54  filename_t basename, ext;
55  std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
56  fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
57  }
58  else
59  {
60  fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
61  }
62  return fmt::to_string(w);
63  }
64 
65  const filename_t &filename() const
66  {
67  return file_helper_.filename();
68  }
69 
70 protected:
71  void sink_it_(const details::log_msg &msg) override
72  {
73  fmt::memory_buffer formatted;
74  sink::formatter_->format(msg, formatted);
75  current_size_ += formatted.size();
76  if (current_size_ > max_size_)
77  {
78  rotate_();
79  current_size_ = formatted.size();
80  }
81  file_helper_.write(formatted);
82  }
83 
84  void flush_() override
85  {
86  file_helper_.flush();
87  }
88 
89 private:
90  // Rotate files:
91  // log.txt -> log.1.txt
92  // log.1.txt -> log.2.txt
93  // log.2.txt -> log.3.txt
94  // log.3.txt -> delete
95  void rotate_()
96  {
98  file_helper_.close();
99  for (auto i = max_files_; i > 0; --i)
100  {
101  filename_t src = calc_filename(base_filename_, i - 1);
103  {
104  continue;
105  }
106  filename_t target = calc_filename(base_filename_, i);
107 
108  if (!rename_file(src, target))
109  {
110  // if failed try again after a small delay.
111  // this is a workaround to a windows issue, where very high rotation
112  // rates can cause the rename to fail with permission denied (because of antivirus?).
114  if (!rename_file(src, target))
115  {
116  file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
117  current_size_ = 0;
118  throw spdlog_ex(
119  "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
120  }
121  }
122  }
123  file_helper_.reopen(true);
124  }
125 
126  // delete the target if exists, and rename the src file to target
127  // return true on success, false otherwise.
128  bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
129  {
130  // try to delete the target file in case it already exists.
131  (void)details::os::remove(target_filename);
132  return details::os::rename(src_filename, target_filename) == 0;
133  }
134 
135  filename_t base_filename_;
136  std::size_t max_size_;
137  std::size_t max_files_;
138  std::size_t current_size_;
139  details::file_helper file_helper_;
140 };
141 
144 
145 } // namespace sinks
146 
147 //
148 // factory functions
149 //
150 
151 template<typename Factory = default_factory>
152 inline std::shared_ptr<logger> rotating_logger_mt(
153  const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open=false)
154 {
155  return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files, rotate_on_open);
156 }
157 
158 template<typename Factory = default_factory>
159 inline std::shared_ptr<logger> rotating_logger_st(
160  const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
161 {
162  return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files, rotate_on_open);
163 }
164 } // namespace spdlog
std::shared_ptr< logger > rotating_logger_mt(const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open=false)
void sleep_for_millis(int milliseconds) noexcept
Definition: os.h:351
void open(const filename_t &fname, bool truncate=false)
Definition: file_helper.h:42
std::shared_ptr< logger > rotating_logger_st(const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open=false)
std::unique_ptr< spdlog::formatter > formatter_
Definition: sink.h:50
static filename_t calc_filename(const filename_t &filename, std::size_t index)
void sink_it_(const details::log_msg &msg) override
Definition: format.h:297
void reopen(bool truncate)
Definition: file_helper.h:60
const filename_t & filename() const
std::size_t size() const
Definition: core.h:250
Definition: async.h:27
static std::tuple< filename_t, filename_t > split_by_extension(const spdlog::filename_t &fname)
Definition: file_helper.h:125
basic_memory_buffer< char > memory_buffer
Definition: format.h:553
int remove(const filename_t &filename) noexcept
Definition: os.h:169
void write(const fmt::memory_buffer &buf)
Definition: file_helper.h:83
std::string filename_t
Definition: common.h:205
const filename_t & filename() const
Definition: file_helper.h:102
int rename(const filename_t &filename1, const filename_t &filename2) noexcept
Definition: os.h:178
static bool file_exists(const filename_t &fname)
Definition: file_helper.h:107
#define SPDLOG_FILENAME_T(s)
Definition: os.h:369
std::enable_if< is_contiguous< Container >::value &&internal::is_string< S >::value, std::back_insert_iterator< Container > >::type format_to(std::back_insert_iterator< Container > out, const S &format_str, const Args &...args)
Definition: core.h:1430
std::string to_string(const T &value)
Definition: format.h:3209
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open=false)
std::string filename_to_str(const filename_t &filename)
Definition: os.h:370