-
Notifications
You must be signed in to change notification settings - Fork 0
/
mlogger.hpp
316 lines (291 loc) · 9.69 KB
/
mlogger.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
//
// Created by Michael Soulier on 2015-09-26.
// Copyright © 2015 Michael Soulier. All rights reserved.
//
#ifndef mlogger_hpp
#define mlogger_hpp
#define BUFSIZE 1024
#define DATETIMESIZE 14 // ie. 20220409181030
#include <iostream>
#include <string>
#include <sstream>
#include <mutex>
#include <vector>
#include <stdio.h>
#include <stdarg.h>
#include <tuple>
#include <errno.h>
#include <type_traits>
#include <memory>
#include "to_string.hpp"
#include "mlog.h"
#include "mdebug.h"
#define MLOGGER_BUFSIZE 4096
enum MLoggerVerbosity {
trace=0,
debug=10,
info=20,
warning=30,
error=40,
critical=50
};
//////////////////////////////////////////////////////////////////////
// MLogger - A second pass at a C++, thread-safe logger. //
// Note that it returns MLoggerEmitter objects to do the actual //
// logging. //
//////////////////////////////////////////////////////////////////////
/*
* This is the base class for all logging handlers.
*/
class MLoggerHandler
{
public:
MLoggerHandler();
virtual ~MLoggerHandler() = default;
// For iostream operations to the handler (ie. logs)
void operator<< (std::string buffer);
friend std::ostream& operator<< (std::ostream& os, const MLoggerHandler& me);
virtual std::string print(void);
private:
virtual void handle(std::string buffer);
virtual std::string identify(void) const;
};
/*
* A stderr logger implementation.
*/
class MLoggerStderrHandler: public MLoggerHandler
{
public:
MLoggerStderrHandler();
~MLoggerStderrHandler();
std::string print(void);
private:
void handle(std::string buffer);
std::string identify(void) const;
};
/*
* A rotating file handler.
*/
class MLoggerFileHandler: public MLoggerHandler
{
public:
MLoggerFileHandler(std::string path,
size_t rotation_filesize,
size_t rotation_filetime,
bool post_compress);
~MLoggerFileHandler();
std::string print(void);
private:
// The path to the log symlink
std::string m_path;
// The path to the current open logfile
std::string m_curpath;
size_t m_rotation_filesize;
time_t m_rotation_filetime;
bool m_post_compress;
FILE *m_logfile;
size_t m_max_path_size;
struct tm m_start_time;
size_t m_bytes_written;
void handle(std::string buffer);
std::string identify(void) const;
std::string rotation_filesize2s(void);
std::string gettimesuffix(void);
std::string getfilename(void);
void setup(void);
bool validate_path(std::string path);
void rotate(void);
int symlink_exists(void);
void pop_start_time(const char *pathbuf);
void pop_bytes_written(const char *pathbuf);
};
class MLoggerEmitter
{
public:
MLoggerEmitter(std::stringstream& buffer,
std::mutex& mutex,
std::ostream& ostream,
MLoggerVerbosity threshold,
std::string prefix,
std::vector<std::unique_ptr<MLoggerHandler>> &handlers);
~MLoggerEmitter();
void setLevel(MLoggerVerbosity level);
template <class T>
// For handling << from any object.
// Here we add to the m_buffer. We are not printing it until we see an std::endl
MLoggerEmitter& operator <<(T input) {
// Only log if the level is set above our threshold.
if (m_threshold >= m_level) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_buffer.str().length() == 0) {
m_buffer << localDateTime() << " " << m_prefix << ": " << input;
}
else {
m_buffer << input;
}
}
return *this;
}
// For handling std::endl
// This should trigger flushing of the m_buffer to the ostream.
std::ostream& operator <<(std::ostream& (*f)(std::ostream&)) {
// Only log if the level is set above our threshold.
if (m_threshold >= m_level) {
std::lock_guard<std::mutex> lock(m_mutex);
// Loop on the handlers and send the buffer to each.
for (auto&& handler : m_handlers) {
*handler << m_buffer.str();
}
// Clear the buffer
m_buffer.str("");
}
return m_ostream;
}
private:
// The thread-safe buffer where the logs are composed.
std::stringstream& m_buffer;
// A mutex passed in from the main logger for synchronization.
std::mutex& m_mutex;
// The logging level.
MLoggerVerbosity m_level;
// The output stream.
std::ostream& m_ostream;
// The threshold for logging for this handler.
MLoggerVerbosity m_threshold;
// The string prefix for logging.
std::string m_prefix;
// Return the current date and time as a localized string.
const std::string localDateTime();
// A reference to the MLogger class' vector of handlers.
std::vector<std::unique_ptr<MLoggerHandler>>& m_handlers;
};
/*
* The MLogger (Mike-logger) is a thread-safe C++ logger using the iostream
* operators. To use it, you must invoke a logging level handler which will
* return an MLoggerEmitter reference, and then terminate your line with
* std::endl to ensure that the buffer is flushed and the line terminated
* with a newline.
*/
class MLogger
{
public:
MLogger();
MLogger(std::string name);
~MLogger();
// Block copy constructor and assignment operator.
//MLogger(MLogger& source) = delete;
//MLogger& operator=(const MLogger& source) = delete;
// Set the current logging level
void setLevel(MLoggerVerbosity level);
// Get the current logging level
MLoggerVerbosity getLevel();
// Convenience methods for trace level log with iostream.
MLoggerEmitter& trace();
// Convenience methods for debug level log with iostream.
MLoggerEmitter& debug();
// Convenience methods for info level log with iostream.
MLoggerEmitter& info();
// Convenience methods for warning level log with iostream.
MLoggerEmitter& warning();
// Convenicence methods for error level log with iostream.
MLoggerEmitter& error();
// Convenicence methods for critical level log with iostream.
MLoggerEmitter& critical();
// Static method for mikelibc to call.
static void Callback(logseverity_t severity, char *message, void *data);
// Log printf style at called level.
template <typename ...Args>
void trace(Args&&... args) {
m_trace_emitter << this->print(std::forward<Args>(args)...)
<< std::endl;
}
template <typename ...Args>
void debug(Args&&... args) {
m_debug_emitter << this->print(std::forward<Args>(args)...)
<< std::endl;
}
template <typename ...Args>
void info(Args&&... args) {
m_info_emitter << this->print(std::forward<Args>(args)...)
<< std::endl;
}
template <typename ...Args>
void warning(Args&&... args) {
m_warning_emitter << this->print(std::forward<Args>(args)...)
<< std::endl;
}
template <typename ...Args>
void error(Args&&... args) {
m_error_emitter << this->print(std::forward<Args>(args)...)
<< std::endl;
}
template <typename ...Args>
void critical(Args&&... args) {
m_critical_emitter << this->print(std::forward<Args>(args)...)
<< std::endl;
}
template <typename T, typename ...Args>
void addHandler(Args &&...args);
// Clear all handlers.
void clearHandlers();
// Initialize the logger with default settings.
void setDefaults();
// Just for debugging.
void printHandlers();
private:
// The logger name.
std::string m_name;
// The current log level.
MLoggerVerbosity m_level;
// The output stream for the logger.
std::ostream& m_ostream;
// The mutex used for synchronization.
std::mutex m_mutex;
// Trace handler
MLoggerEmitter m_trace_emitter;
// Debug handler
MLoggerEmitter m_debug_emitter;
// Info handler
MLoggerEmitter m_info_emitter;
// Warn handler
MLoggerEmitter m_warning_emitter;
// Error handler
MLoggerEmitter m_error_emitter;
// Error handler
MLoggerEmitter m_critical_emitter;
// The thread-safe buffer where the logs are composed.
std::stringstream m_buffer;
// A vector of std::unique_ptr<MLoggerHandler> objects.
std::vector<std::unique_ptr<MLoggerHandler>> m_handlers;
// The underlying mikelibc logger handler.
mlog_handle_t m_handle;
template<typename ...Args>
std::string print(const char *fmt, Args &&...args) {
//mdbgf("in logger print with fmt '%s'\n", fmt);
std::tuple t = std::make_tuple(to_string<Args>(args)...);
return std::apply([fmt](auto ...args) {
//printf(fmt, std::forward<decltype(args)>(args)...);
//printf("\n");
char buffer[MLOGGER_BUFSIZE];
int rv = snprintf(buffer,
MLOGGER_BUFSIZE,
fmt,
std::forward<decltype(args)>(args)...);
if (rv < 0) {
perror("sprintf");
return std::string("");
} else {
if (rv > MLOGGER_BUFSIZE) {
fprintf(stderr, "MLogger::printf: output truncated\n");
}
return std::string(buffer);
}
}, t);
}
};
template <typename T, typename ...Args>
void MLogger::addHandler(Args &&...args) {
//m_handlers.push_back(new T(std::forward<Args>(args)...));
m_handlers.push_back(std::make_unique<T>(std::forward<Args>(args)...));
}
#endif /* mlogger_hpp */