WireCellToolkit
Wire Cell Simulation, Signal Process and Reconstruction Toolki for Liquid Argon Detectors
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)
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  }
42 
43  // calc filename according to index and file extension if exists.
44  // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
45  static filename_t calc_filename(const filename_t &filename, std::size_t index)
46  {
47  typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
48  if (index != 0u)
49  {
50  filename_t basename, ext;
51  std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
52  fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
53  }
54  else
55  {
56  fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
57  }
58  return fmt::to_string(w);
59  }
60 
61 protected:
62  void sink_it_(const details::log_msg &msg) override
63  {
64  fmt::memory_buffer formatted;
65  sink::formatter_->format(msg, formatted);
66  current_size_ += formatted.size();
67  if (current_size_ > max_size_)
68  {
69  rotate_();
70  current_size_ = formatted.size();
71  }
72  file_helper_.write(formatted);
73  }
74 
75  void flush_() override
76  {
77  file_helper_.flush();
78  }
79 
80 private:
81  // Rotate files:
82  // log.txt -> log.1.txt
83  // log.1.txt -> log.2.txt
84  // log.2.txt -> log.3.txt
85  // log.3.txt -> delete
86  void rotate_()
87  {
89  file_helper_.close();
90  for (auto i = max_files_; i > 0; --i)
91  {
92  filename_t src = calc_filename(base_filename_, i - 1);
94  {
95  continue;
96  }
97  filename_t target = calc_filename(base_filename_, i);
98 
99  if (!rename_file(src, target))
100  {
101  // if failed try again after a small delay.
102  // this is a workaround to a windows issue, where very high rotation
103  // rates can cause the rename to fail with permission denied (because of antivirus?).
105  if (!rename_file(src, target))
106  {
107  file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
108  current_size_ = 0;
109  throw spdlog_ex(
110  "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
111  }
112  }
113  }
114  file_helper_.reopen(true);
115  }
116 
117  // delete the target if exists, and rename the src file to target
118  // return true on success, false otherwise.
119  bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
120  {
121  // try to delete the target file in case it already exists.
122  (void)details::os::remove(target_filename);
123  return details::os::rename(src_filename, target_filename) == 0;
124  }
125 
126  filename_t base_filename_;
127  std::size_t max_size_;
128  std::size_t max_files_;
129  std::size_t current_size_;
130  details::file_helper file_helper_;
131 };
132 
135 
136 } // namespace sinks
137 
138 //
139 // factory functions
140 //
141 
142 template<typename Factory = default_factory>
143 inline std::shared_ptr<logger> rotating_logger_mt(
144  const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
145 {
146  return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
147 }
148 
149 template<typename Factory = default_factory>
150 inline std::shared_ptr<logger> rotating_logger_st(
151  const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
152 {
153  return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
154 }
155 } // namespace spdlog
basic_memory_buffer< char > memory_buffer
Definition: format.h:553
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)
void open(const filename_t &fname, bool truncate=false)
Definition: file_helper.h:42
std::unique_ptr< spdlog::formatter > formatter_
Definition: sink.h:55
static filename_t calc_filename(const filename_t &filename, std::size_t index)
void sink_it_(const details::log_msg &msg) override
If we are still before C++14, supply the fodder for doing the "indices trick".
Definition: format.h:297
void reopen(bool truncate)
Definition: file_helper.h:60
Definition: async.h:27
static std::tuple< filename_t, filename_t > split_by_extension(const spdlog::filename_t &fname)
Definition: file_helper.h:125
int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
Definition: os.h:178
void write(const fmt::memory_buffer &buf)
Definition: file_helper.h:83
std::string filename_t
Definition: common.h:202
int remove(const filename_t &filename) SPDLOG_NOEXCEPT
Definition: os.h:169
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::shared_ptr< logger > rotating_logger_st(const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
std::string to_string(const T &value)
Definition: format.h:3209
static bool file_exists(const filename_t &fname)
Definition: file_helper.h:107
#define SPDLOG_FILENAME_T(s)
Definition: os.h:369
void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
Definition: os.h:351
std::string filename_to_str(const filename_t &filename)
Definition: os.h:370
basic_memory_buffer< wchar_t > wmemory_buffer
Definition: format.h:554
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
type
Definition: core.h:530