Photon 1.0.0
Loading...
Searching...
No Matches
daily_file_sink.h
Go to the documentation of this file.
1// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
2// Distributed under the MIT License (http://opensource.org/licenses/MIT)
3
4#pragma once
5
6#include <spdlog/common.h>
9#include <spdlog/fmt/fmt.h>
10#include <spdlog/fmt/chrono.h>
12#include <spdlog/details/os.h>
15
16#include <chrono>
17#include <cstdio>
18#include <ctime>
19#include <mutex>
20#include <string>
21
22namespace spdlog
23{
24 namespace sinks
25 {
26
27 /*
28 * Generator of daily log file names in format basename.YYYY-MM-DD.ext
29 */
31 {
32 // Create filename for the form basename.YYYY-MM-DD
33 static filename_t calc_filename(const filename_t& filename, const tm& now_tm)
34 {
35 filename_t basename, ext;
36 std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
37 return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}")), basename, now_tm.tm_year + 1900,
38 now_tm.tm_mon + 1, now_tm.tm_mday, ext);
39 }
40 };
41
42 /*
43 * Generator of daily log file names with strftime format.
44 * Usages:
45 * auto sink = std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);"
46 * auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour, minute)"
47 *
48 */
50 {
51 static filename_t calc_filename(const filename_t& filename, const tm& now_tm)
52 {
53#ifdef SPDLOG_USE_STD_FORMAT
54 // adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546
55
56 filename_t tm_format;
57 tm_format.append(filename);
58 // By appending an extra space we can distinguish an empty result that
59 // indicates insufficient buffer size from a guaranteed non-empty result
60 // https://github.com/fmtlib/fmt/issues/2238
61 tm_format.push_back(' ');
62
63 const size_t MIN_SIZE = 10;
64 filename_t buf;
65 buf.resize(MIN_SIZE);
66 for (;;)
67 {
68 size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm);
69 if (count != 0)
70 {
71 // Remove the extra space.
72 buf.resize(count - 1);
73 break;
74 }
75 buf.resize(buf.size() * 2);
76 }
77
78 return buf;
79#else
80 // generate fmt datetime format string, e.g. {:%Y-%m-%d}.
81 filename_t fmt_filename = fmt::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{{:{}}}")), filename);
82
83 // MSVC doesn't allow fmt::runtime(..) with wchar, with fmtlib versions < 9.1.x
84#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) && FMT_VERSION < 90101
85 return fmt::format(fmt_filename, now_tm);
86#else
87 return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
88#endif
89
90#endif
91 }
92
93 private:
94#if defined __GNUC__
95#pragma GCC diagnostic push
96#pragma GCC diagnostic ignored "-Wformat-nonliteral"
97#endif
98
99 static size_t strftime(char* str, size_t count, const char* format, const std::tm* time)
100 {
101 return std::strftime(str, count, format, time);
102 }
103
104 static size_t strftime(wchar_t* str, size_t count, const wchar_t* format, const std::tm* time)
105 {
106 return std::wcsftime(str, count, format, time);
107 }
108
109#if defined(__GNUC__)
110#pragma GCC diagnostic pop
111#endif
112 };
113
114 /*
115 * Rotating file sink based on date.
116 * If truncate != false , the created file will be truncated.
117 * If max_files > 0, retain only the last max_files and delete previous.
118 */
119 template <typename Mutex, typename FileNameCalc = daily_filename_calculator>
120 class daily_file_sink final : public base_sink<Mutex>
121 {
122 public:
123 // create daily file sink which rotates on given time
124 daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {})
125 : base_filename_(std::move(base_filename)), rotation_h_(rotation_hour), rotation_m_(rotation_minute), file_helper_{event_handlers}, truncate_(truncate), max_files_(max_files), filenames_q_()
126 {
127 if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
128 {
129 throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
130 }
131
132 auto now = log_clock::now();
133 auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
136
137 if (max_files_ > 0)
138 {
140 }
141 }
142
144 {
145 std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
146 return file_helper_.filename();
147 }
148
149 protected:
150 void sink_it_(const details::log_msg& msg) override
151 {
152 auto time = msg.time;
153 bool should_rotate = time >= rotation_tp_;
154 if (should_rotate)
155 {
156 auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
159 }
160 memory_buf_t formatted;
161 base_sink<Mutex>::formatter_->format(msg, formatted);
162 file_helper_.write(formatted);
163
164 // Do the cleaning only at the end because it might throw on failure.
165 if (should_rotate && max_files_ > 0)
166 {
167 delete_old_();
168 }
169 }
170
171 void flush_() override
172 {
174 }
175
176 private:
178 {
180
182 std::vector<filename_t> filenames;
183 auto now = log_clock::now();
184 while (filenames.size() < max_files_)
185 {
186 auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
187 if (!path_exists(filename))
188 {
189 break;
190 }
191 filenames.emplace_back(filename);
192 now -= std::chrono::hours(24);
193 }
194 for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
195 {
196 filenames_q_.push_back(std::move(*iter));
197 }
198 }
199
200 tm now_tm(log_clock::time_point tp)
201 {
202 time_t tnow = log_clock::to_time_t(tp);
204 }
205
206 log_clock::time_point next_rotation_tp_()
207 {
208 auto now = log_clock::now();
209 tm date = now_tm(now);
210 date.tm_hour = rotation_h_;
211 date.tm_min = rotation_m_;
212 date.tm_sec = 0;
213 auto rotation_time = log_clock::from_time_t(std::mktime(&date));
214 if (rotation_time > now)
215 {
216 return rotation_time;
217 }
218 return {rotation_time + std::chrono::hours(24)};
219 }
220
221 // Delete the file N rotations ago.
222 // Throw spdlog_ex on failure to delete the old file.
224 {
227
228 filename_t current_file = file_helper_.filename();
229 if (filenames_q_.full())
230 {
231 auto old_filename = std::move(filenames_q_.front());
233 bool ok = remove_if_exists(old_filename) == 0;
234 if (!ok)
235 {
236 filenames_q_.push_back(std::move(current_file));
237 throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
238 }
239 }
240 filenames_q_.push_back(std::move(current_file));
241 }
242
246 log_clock::time_point rotation_tp_;
249 uint16_t max_files_;
251 };
252
257
258 } // namespace sinks
259
260 //
261 // factory functions
262 //
263 template <typename Factory = spdlog::synchronous_factory>
264 inline std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {})
265 {
266 return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
267 }
268
269 template <typename Factory = spdlog::synchronous_factory>
270 inline std::shared_ptr<logger> daily_logger_format_mt(const std::string& logger_name, const filename_t& filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {})
271 {
272 return Factory::template create<sinks::daily_file_format_sink_mt>(
273 logger_name, filename, hour, minute, truncate, max_files, event_handlers);
274 }
275
276 template <typename Factory = spdlog::synchronous_factory>
277 inline std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {})
278 {
279 return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
280 }
281
282 template <typename Factory = spdlog::synchronous_factory>
283 inline std::shared_ptr<logger> daily_logger_format_st(const std::string& logger_name, const filename_t& filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {})
284 {
285 return Factory::template create<sinks::daily_file_format_sink_st>(
286 logger_name, filename, hour, minute, truncate, max_files, event_handlers);
287 }
288} // namespace spdlog
Definition circular_q.h:16
void pop_front()
Definition circular_q.h:103
const T & front() const
Definition circular_q.h:70
void push_back(T &&item)
Definition circular_q.h:53
bool full() const
Definition circular_q.h:113
Definition file_helper.h:19
const filename_t & filename() const
Definition file_helper-inl.h:142
void flush()
Definition file_helper-inl.h:88
void write(const memory_buf_t &buf)
Definition file_helper-inl.h:123
static std::tuple< filename_t, filename_t > split_by_extension(const filename_t &fname)
Definition file_helper-inl.h:160
void open(const filename_t &fname, bool truncate=false)
Definition file_helper-inl.h:35
Definition base_sink.h:22
Definition daily_file_sink.h:121
void delete_old_()
Definition daily_file_sink.h:223
void init_filenames_q_()
Definition daily_file_sink.h:177
void flush_() override
Definition daily_file_sink.h:171
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate=false, uint16_t max_files=0, const file_event_handlers &event_handlers={})
Definition daily_file_sink.h:124
bool truncate_
Definition daily_file_sink.h:248
uint16_t max_files_
Definition daily_file_sink.h:249
log_clock::time_point rotation_tp_
Definition daily_file_sink.h:246
log_clock::time_point next_rotation_tp_()
Definition daily_file_sink.h:206
details::circular_q< filename_t > filenames_q_
Definition daily_file_sink.h:250
int rotation_h_
Definition daily_file_sink.h:244
tm now_tm(log_clock::time_point tp)
Definition daily_file_sink.h:200
int rotation_m_
Definition daily_file_sink.h:245
void sink_it_(const details::log_msg &msg) override
Definition daily_file_sink.h:150
details::file_helper file_helper_
Definition daily_file_sink.h:247
filename_t filename()
Definition daily_file_sink.h:143
filename_t base_filename_
Definition daily_file_sink.h:243
std::basic_string< Char > format(const text_style &ts, const S &format_str, const Args &... args)
Definition color.h:646
#define SPDLOG_FMT_STRING(format_string)
Definition common.h:60
#define SPDLOG_FILENAME_T(s)
Definition common.h:132
#define SPDLOG_FMT_RUNTIME(format_string)
Definition common.h:59
constexpr auto count() -> size_t
Definition core.h:1538
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
Definition os-inl.h:399
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
Definition os-inl.h:177
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
Definition os-inl.h:102
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
Definition os-inl.h:192
daily_file_sink< details::null_mutex, daily_filename_format_calculator > daily_file_format_sink_st
Definition daily_file_sink.h:256
daily_file_sink< details::null_mutex > daily_file_sink_st
Definition daily_file_sink.h:254
daily_file_sink< std::mutex > daily_file_sink_mt
Definition daily_file_sink.h:253
daily_file_sink< std::mutex, daily_filename_format_calculator > daily_file_format_sink_mt
Definition daily_file_sink.h:255
Definition async.h:26
std::shared_ptr< logger > daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour=0, int minute=0, bool truncate=false, uint16_t max_files=0, const file_event_handlers &event_handlers={})
Definition daily_file_sink.h:277
std::shared_ptr< logger > daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour=0, int minute=0, bool truncate=false, uint16_t max_files=0, const file_event_handlers &event_handlers={})
Definition daily_file_sink.h:264
std::shared_ptr< logger > daily_logger_format_mt(const std::string &logger_name, const filename_t &filename, int hour=0, int minute=0, bool truncate=false, uint16_t max_files=0, const file_event_handlers &event_handlers={})
Definition daily_file_sink.h:270
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
Definition common-inl.h:75
std::string filename_t
Definition common.h:131
std::shared_ptr< logger > daily_logger_format_st(const std::string &logger_name, const filename_t &filename, int hour=0, int minute=0, bool truncate=false, uint16_t max_files=0, const file_event_handlers &event_handlers={})
Definition daily_file_sink.h:283
fmt::basic_memory_buffer< char, 250 > memory_buf_t
Definition common.h:173
Definition uuid.h:926
Definition log_msg.h:14
log_clock::time_point time
Definition log_msg.h:24
Definition common.h:329
Definition daily_file_sink.h:31
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
Definition daily_file_sink.h:33
static size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
Definition daily_file_sink.h:99
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
Definition daily_file_sink.h:51
static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time)
Definition daily_file_sink.h:104
time
Definition tag_strings.h:53