3
3
4
4
#include < chrono>
5
5
#include < cstdint>
6
+ #include < filesystem>
6
7
#include < iomanip>
7
8
#include < iostream>
8
9
#include < sstream>
@@ -54,15 +55,35 @@ void WriteTerminationLog(std::string message);
54
55
55
56
const size_t LevelPaddingWidth = 7 ;
56
57
57
- class LogMessage {
58
+ /* *
59
+ * Interface for log headers.
60
+ *
61
+ * This class defines how log headers should behave and provides some
62
+ * basic, reusable methods.
63
+ */
64
+ class ILogHeader {
58
65
public:
59
- LogMessage (const char * file, int line, bool throttled, LogLevel level)
60
- : file_(file), line_(line), level_(level), throttled_(throttled) {
61
- // if in debug mode, output file names associated with log messages
62
- include_file_ = CheckLogLevel (LogLevel::DEBUG);
66
+ ILogHeader (const char * file, int line, LogLevel level)
67
+ : line_(line), level_(level) {
68
+ std::filesystem::path p (file);
69
+ if (p.has_filename ()) {
70
+ file_ = p.filename ().string ();
71
+ } else {
72
+ file_ = p.string ();
73
+ }
63
74
}
64
75
65
- ~LogMessage () {
76
+ ILogHeader (const ILogHeader&) = delete ;
77
+ ILogHeader (ILogHeader&&) = delete ;
78
+ ILogHeader& operator =(const ILogHeader&) = delete ;
79
+ ILogHeader& operator =(ILogHeader&&) = delete ;
80
+ virtual ~ILogHeader () = default ;
81
+
82
+ virtual void PrintHeader () = 0;
83
+ inline LogLevel GetLogLevel () const { return level_; }
84
+
85
+ protected:
86
+ void PrintPrefix () {
66
87
auto now = std::chrono::system_clock::to_time_t (std::chrono::system_clock::now ());
67
88
auto nowTm = gmtime (&now);
68
89
@@ -71,25 +92,106 @@ class LogMessage {
71
92
<< std::left << std::setw (LevelPaddingWidth) << GetLogLevelName (level_)
72
93
<< " " << std::put_time (nowTm, " %Y/%m/%d %H:%M:%S" )
73
94
<< " ] " ;
95
+ }
74
96
75
- if (throttled_) {
76
- std::cerr << " [Throttled] " ;
97
+ void PrintFile () {
98
+ if (CheckLogLevel (LogLevel::DEBUG)) {
99
+ std::cerr << " (" << file_ << " :" << line_ << " ) " ;
77
100
}
101
+ }
102
+
103
+ private:
104
+ std::string file_;
105
+ int line_;
106
+ LogLevel level_;
107
+ };
108
+
109
+ /* *
110
+ * Basic log header.
111
+ *
112
+ * Most log lines will use this header.
113
+ *
114
+ * The format when running in debug level will be:
115
+ * [LEVEL YYYY/MM/DD HH:mm:SS] (file:line)
116
+ *
117
+ * Other log levels exclude the (file:line) part of the header.
118
+ */
119
+ class LogHeader : public ILogHeader {
120
+ public:
121
+ LogHeader (const char * file, int line, LogLevel level)
122
+ : ILogHeader(file, line, level) {}
123
+
124
+ void PrintHeader () override {
125
+ PrintPrefix ();
126
+ PrintFile ();
127
+ }
128
+ };
129
+
130
+ /* *
131
+ * Throttled log header.
132
+ *
133
+ * When the same log message is triggered multiple times, this header
134
+ * helps prevent flooding the logs and instead counts the occurrences
135
+ * of the message, which will be output after a given time window
136
+ * expires.
137
+ *
138
+ * The format when running in debug level will be:
139
+ * [LEVEL YYYY/MM/DD HH:mm:SS] [Throttled COUNT message] (file:line)
140
+ *
141
+ * Other log levels exclude the (file:line) part of the header.
142
+ */
143
+ class ThrottledLogHeader : public ILogHeader {
144
+ public:
145
+ ThrottledLogHeader (const char * file, int line, LogLevel level, std::chrono::duration<unsigned int > interval)
146
+ : ILogHeader(file, line, level), interval_(interval) {}
147
+
148
+ void PrintHeader () override {
149
+ PrintPrefix ();
150
+
151
+ std::cerr << " [Throttled " << count_ << " messages] " ;
152
+ count_ = 0 ;
153
+
154
+ PrintFile ();
155
+ }
78
156
79
- if (include_file_) {
80
- const char * basename = strrchr (file_, ' /' );
81
- if (!basename) {
82
- basename = file_;
83
- } else {
84
- ++basename;
85
- }
86
- std::cerr << " (" << basename << " :" << line_ << " ) " ;
157
+ /* *
158
+ * Check if the log message should be suppressed.
159
+ *
160
+ * Throttled logs only output a message every interval_ time windows.
161
+ * Every time this method is called, we increment count_ so the next
162
+ * time the log is printed we can add the amount of times the log has
163
+ * happened.
164
+ *
165
+ * @returns true if the log has to be suppressed.
166
+ */
167
+ bool Suppress () {
168
+ std::chrono::duration elapsed = std::chrono::steady_clock::now () - last_log_;
169
+ count_++;
170
+ if (elapsed < interval_) {
171
+ return true ;
87
172
}
88
173
174
+ last_log_ = std::chrono::steady_clock::now ();
175
+ return false ;
176
+ }
177
+
178
+ private:
179
+ std::chrono::steady_clock::time_point last_log_;
180
+ std::chrono::duration<unsigned int > interval_;
181
+ unsigned long count_{};
182
+ };
183
+
184
+ class LogMessage {
185
+ public:
186
+ LogMessage (ILogHeader& ls) : ls_(ls) {}
187
+
188
+ ~LogMessage () {
189
+ ls_.PrintHeader ();
190
+
89
191
std::cerr << buf_.str ()
90
192
<< std::endl;
91
193
92
- if (level_ == LogLevel::FATAL) {
194
+ if (ls_. GetLogLevel () == LogLevel::FATAL) {
93
195
WriteTerminationLog (buf_.str ());
94
196
exit (1 );
95
197
}
@@ -102,32 +204,35 @@ class LogMessage {
102
204
}
103
205
104
206
private:
105
- const char * file_;
106
- int line_;
107
- LogLevel level_;
207
+ ILogHeader& ls_;
108
208
std::stringstream buf_;
109
- bool include_file_;
110
- bool throttled_;
111
209
};
112
210
113
211
} // namespace logging
114
212
115
213
} // namespace collector
116
214
215
+ // Helpers for creating unique variables within a compilation unit.
216
+ #define CLOG_CAT_ (a, b ) a##b
217
+ #define CLOG_CAT (a, b ) CLOG_CAT_(a, b)
218
+ #define CLOG_VAR (a ) CLOG_CAT(a, __LINE__)
219
+
117
220
#define CLOG_ENABLED (lvl ) (collector::logging::CheckLogLevel(collector::logging::LogLevel::lvl))
118
221
119
222
#define CLOG_IF (cond, lvl ) \
223
+ static collector::logging::LogHeader CLOG_VAR (_clog_stmt_)( \
224
+ __FILE__, __LINE__, collector::logging::LogLevel::lvl); \
120
225
if (collector::logging::CheckLogLevel(collector::logging::LogLevel::lvl) && (cond)) \
121
- collector::logging::LogMessage (__FILE__, __LINE__, false , collector::logging::LogLevel::lvl )
226
+ collector::logging::LogMessage (CLOG_VAR(_clog_stmt_) )
122
227
123
228
#define CLOG (lvl ) CLOG_IF(true , lvl)
124
229
125
230
#define CLOG_THROTTLED_IF (cond, lvl, interval ) \
126
- static std::chrono::steady_clock::time_point _clog_lastlog_##__LINE__; \
231
+ static collector::logging::ThrottledLogHeader CLOG_VAR (_clog_stmt_)( \
232
+ __FILE__, __LINE__, collector::logging::LogLevel::lvl, interval); \
127
233
if (collector::logging::CheckLogLevel(collector::logging::LogLevel::lvl) && (cond) && \
128
- (std::chrono::steady_clock::now() - _clog_lastlog_##__LINE__ >= interval)) \
129
- _clog_lastlog_##__LINE__ = std::chrono::steady_clock::now(), \
130
- collector::logging::LogMessage (__FILE__, __LINE__, true , collector::logging::LogLevel::lvl)
234
+ !CLOG_VAR(_clog_stmt_).Suppress()) \
235
+ collector::logging::LogMessage (CLOG_VAR(_clog_stmt_))
131
236
132
237
#define CLOG_THROTTLED (lvl, interval ) CLOG_THROTTLED_IF(true , lvl, interval)
133
238
0 commit comments