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