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 | |
---|
12 | #include "spdlog/details/file_helper.h" |
---|
13 | #include "spdlog/details/null_mutex.h" |
---|
14 | #include "spdlog/fmt/fmt.h" |
---|
15 | #include "spdlog/sinks/base_sink.h" |
---|
16 | |
---|
17 | #include <chrono> |
---|
18 | #include <cstdio> |
---|
19 | #include <ctime> |
---|
20 | #include <mutex> |
---|
21 | #include <string> |
---|
22 | |
---|
23 | namespace spdlog { |
---|
24 | namespace sinks { |
---|
25 | |
---|
26 | /* |
---|
27 | * Generator of daily log file names in format basename.YYYY-MM-DD.ext |
---|
28 | */ |
---|
29 | struct daily_filename_calculator |
---|
30 | { |
---|
31 | // Create filename for the form basename.YYYY-MM-DD |
---|
32 | static filename_t calc_filename(const filename_t &filename, const tm &now_tm) |
---|
33 | { |
---|
34 | filename_t basename, ext; |
---|
35 | std::tie(basename, ext) = details::file_helper::split_by_extension(filename); |
---|
36 | std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; |
---|
37 | fmt::format_to( |
---|
38 | w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext); |
---|
39 | return fmt::to_string(w); |
---|
40 | } |
---|
41 | }; |
---|
42 | |
---|
43 | /* |
---|
44 | * Rotating file sink based on date. rotates at midnight |
---|
45 | */ |
---|
46 | template<typename Mutex, typename FileNameCalc = daily_filename_calculator> |
---|
47 | class daily_file_sink final : public base_sink<Mutex> |
---|
48 | { |
---|
49 | public: |
---|
50 | // create daily file sink which rotates on given time |
---|
51 | daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false) |
---|
52 | : base_filename_(std::move(base_filename)) |
---|
53 | , rotation_h_(rotation_hour) |
---|
54 | , rotation_m_(rotation_minute) |
---|
55 | , truncate_(truncate) |
---|
56 | { |
---|
57 | if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) |
---|
58 | { |
---|
59 | throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); |
---|
60 | } |
---|
61 | auto now = log_clock::now(); |
---|
62 | file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_); |
---|
63 | rotation_tp_ = next_rotation_tp_(); |
---|
64 | } |
---|
65 | |
---|
66 | protected: |
---|
67 | void sink_it_(const details::log_msg &msg) override |
---|
68 | { |
---|
69 | |
---|
70 | if (msg.time >= rotation_tp_) |
---|
71 | { |
---|
72 | file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_); |
---|
73 | rotation_tp_ = next_rotation_tp_(); |
---|
74 | } |
---|
75 | fmt::memory_buffer formatted; |
---|
76 | sink::formatter_->format(msg, formatted); |
---|
77 | file_helper_.write(formatted); |
---|
78 | } |
---|
79 | |
---|
80 | void flush_() override |
---|
81 | { |
---|
82 | file_helper_.flush(); |
---|
83 | } |
---|
84 | |
---|
85 | private: |
---|
86 | tm now_tm(log_clock::time_point tp) |
---|
87 | { |
---|
88 | time_t tnow = log_clock::to_time_t(tp); |
---|
89 | return spdlog::details::os::localtime(tnow); |
---|
90 | } |
---|
91 | |
---|
92 | log_clock::time_point next_rotation_tp_() |
---|
93 | { |
---|
94 | auto now = log_clock::now(); |
---|
95 | tm date = now_tm(now); |
---|
96 | date.tm_hour = rotation_h_; |
---|
97 | date.tm_min = rotation_m_; |
---|
98 | date.tm_sec = 0; |
---|
99 | auto rotation_time = log_clock::from_time_t(std::mktime(&date)); |
---|
100 | if (rotation_time > now) |
---|
101 | { |
---|
102 | return rotation_time; |
---|
103 | } |
---|
104 | return {rotation_time + std::chrono::hours(24)}; |
---|
105 | } |
---|
106 | |
---|
107 | filename_t base_filename_; |
---|
108 | int rotation_h_; |
---|
109 | int rotation_m_; |
---|
110 | log_clock::time_point rotation_tp_; |
---|
111 | details::file_helper file_helper_; |
---|
112 | bool truncate_; |
---|
113 | }; |
---|
114 | |
---|
115 | using daily_file_sink_mt = daily_file_sink<std::mutex>; |
---|
116 | using daily_file_sink_st = daily_file_sink<details::null_mutex>; |
---|
117 | |
---|
118 | } // namespace sinks |
---|
119 | |
---|
120 | // |
---|
121 | // factory functions |
---|
122 | // |
---|
123 | template<typename Factory = default_factory> |
---|
124 | inline std::shared_ptr<logger> daily_logger_mt( |
---|
125 | const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) |
---|
126 | { |
---|
127 | return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate); |
---|
128 | } |
---|
129 | |
---|
130 | template<typename Factory = default_factory> |
---|
131 | inline std::shared_ptr<logger> daily_logger_st( |
---|
132 | const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) |
---|
133 | { |
---|
134 | return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate); |
---|
135 | } |
---|
136 | } // namespace spdlog |
---|