spdlog
Loading...
Searching...
No Matches
hourly_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>
11#include <spdlog/details/os.h>
14
15#include <chrono>
16#include <cstdio>
17#include <ctime>
18#include <mutex>
19#include <string>
20
21namespace spdlog {
22namespace sinks {
23
24/*
25 * Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
26 */
28{
29 // Create filename for the form basename.YYYY-MM-DD-H
30 static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
31 {
32 filename_t basename, ext;
33 std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
34 return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}{:02d}{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1,
35 now_tm.tm_mday, now_tm.tm_hour, ext);
36 }
37};
38
39/*
40 * Rotating file sink based on time.
41 * If truncate != false , the created file will be truncated.
42 * If max_files > 0, retain only the last max_files and delete previous.
43 */
44template<typename Mutex, typename FileNameCalc = hourly_filename_calculator>
45class hourly_file_sink final : public base_sink<Mutex>
46{
47public:
48 // create hourly file sink which rotates on given time
49 hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0)
50 : base_filename_(std::move(base_filename))
51 , truncate_(truncate)
52 , max_files_(max_files)
53 , filenames_q_()
54 {
55 auto now = log_clock::now();
56 auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
59
60 if (max_files_ > 0)
61 {
63 }
64 }
65
71
72protected:
73 void sink_it_(const details::log_msg &msg) override
74 {
75 auto time = msg.time;
76 bool should_rotate = time >= rotation_tp_;
77 if (should_rotate)
78 {
79 auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
82 }
83 memory_buf_t formatted;
84 base_sink<Mutex>::formatter_->format(msg, formatted);
85 file_helper_.write(formatted);
86
87 // Do the cleaning only at the end because it might throw on failure.
88 if (should_rotate && max_files_ > 0)
89 {
91 }
92 }
93
94 void flush_() override
95 {
97 }
98
99private:
101 {
103
105 std::vector<filename_t> filenames;
106 auto now = log_clock::now();
107 while (filenames.size() < max_files_)
108 {
109 auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
110 if (!path_exists(filename))
111 {
112 break;
113 }
114 filenames.emplace_back(filename);
115 now -= std::chrono::hours(1);
116 }
117 for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
118 {
119 filenames_q_.push_back(std::move(*iter));
120 }
121 }
122
123 tm now_tm(log_clock::time_point tp)
124 {
125 time_t tnow = log_clock::to_time_t(tp);
127 }
128
129 log_clock::time_point next_rotation_tp_()
130 {
131 auto now = log_clock::now();
132 tm date = now_tm(now);
133 date.tm_min = 0;
134 date.tm_sec = 0;
135 auto rotation_time = log_clock::from_time_t(std::mktime(&date));
136 if (rotation_time > now)
137 {
138 return rotation_time;
139 }
140 return {rotation_time + std::chrono::hours(1)};
141 }
142
143 // Delete the file N rotations ago.
144 // Throw spdlog_ex on failure to delete the old file.
146 {
149
150 filename_t current_file = file_helper_.filename();
151 if (filenames_q_.full())
152 {
153 auto old_filename = std::move(filenames_q_.front());
154 filenames_q_.pop_front();
155 bool ok = remove_if_exists(old_filename) == 0;
156 if (!ok)
157 {
158 filenames_q_.push_back(std::move(current_file));
159 SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
160 }
161 }
162 filenames_q_.push_back(std::move(current_file));
163 }
164
166 log_clock::time_point rotation_tp_;
169 uint16_t max_files_;
171};
172
175
176} // namespace sinks
177
178//
179// factory functions
180//
181template<typename Factory = spdlog::synchronous_factory>
183 const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
184{
185 return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files);
186}
187
188template<typename Factory = spdlog::synchronous_factory>
190 const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
191{
192 return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files);
193}
194} // namespace spdlog
const filename_t & filename() const
void write(const memory_buf_t &buf)
static std::tuple< filename_t, filename_t > split_by_extension(const filename_t &fname)
void open(const filename_t &fname, bool truncate=false)
log_clock::time_point rotation_tp_
tm now_tm(log_clock::time_point tp)
hourly_file_sink(filename_t base_filename, bool truncate=false, uint16_t max_files=0)
details::circular_q< filename_t > filenames_q_
log_clock::time_point next_rotation_tp_()
void sink_it_(const details::log_msg &msg) override
#define SPDLOG_FILENAME_T(s)
Definition common.h:107
#define SPDLOG_THROW(ex)
Definition common.h:87
T emplace_back(T... args)
T mktime(T... args)
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
Definition os-inl.h:387
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
Definition os-inl.h:172
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
Definition os-inl.h:97
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
Definition os-inl.h:187
Definition async.h:25
std::shared_ptr< logger > hourly_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate=false, uint16_t max_files=0)
std::shared_ptr< logger > hourly_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate=false, uint16_t max_files=0)
fmt::basic_memory_buffer< char, 250 > memory_buf_t
Definition common.h:116
T rbegin(T... args)
T rend(T... args)
T size(T... args)
log_clock::time_point time
Definition log_msg.h:22
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
T tie(T... args)