-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
logger.hpp
339 lines (275 loc) · 13.4 KB
/
logger.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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2013-2023 plan44.ch / Lukas Zeller, Zurich, Switzerland
//
// Author: Lukas Zeller <[email protected]>
//
// This file is part of p44utils.
//
// p44utils is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// p44utils is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with p44utils. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __p44utils__logger__
#define __p44utils__logger__
#include "p44utils_minimal.hpp"
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef ESP_PLATFORM
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
#else
#include <syslog.h>
#endif
#ifndef __printflike
#define __printflike(...)
#endif
#if REDUCED_FOOTPRINT
#define IFNOTREDUCEDFOOTPRINT(lvl) (0)
#else
#define IFNOTREDUCEDFOOTPRINT(lvl) (lvl)
#endif
#ifndef ENABLE_LOG_COLORS
#define ENABLE_LOG_COLORS 1
#endif
#include "p44obj.hpp"
#include <boost/function.hpp>
// global object independent logging
#define LOGENABLED(lvl) globalLogger.logEnabled(lvl)
#define LOG(lvl,...) { if (globalLogger.logEnabled(lvl)) globalLogger.log(lvl,##__VA_ARGS__); }
#define SETLOGLEVEL(lvl) globalLogger.setLogLevel(lvl)
#define SETERRLEVEL(lvl, dup) globalLogger.setErrLevel(lvl, dup)
#define SETDELTATIME(dt) globalLogger.setDeltaTime(dt)
#define LOGLEVEL (globalLogger.getLogLevel())
#define SETLOGHANDLER(lh,allowother) globalLogger.setLogHandler(lh,allowother)
#define DAEMONMODE globalLogger.getDaemonMode()
#define SETDAEMONMODE(d) globalLogger.setDaemonMode(d)
#if ENABLE_LOG_COLORS
#define SETLOGSYMBOLS(s) globalLogger.setSymbols(s)
#define SETLOGCOLORING(c) globalLogger.setColoring(c)
#else
#define SETLOGSYMBOLS(s)
#define SETLOGCOLORING(c)
#endif
// logging from within a P44LoggingObj (messages prefixed with object's logContextPrefix(), object's logoffset applied)
#define OLOGENABLED(lvl) logEnabled(lvl)
#define OLOG(lvl,...) { if (logEnabled(lvl)) log(lvl,##__VA_ARGS__); }
// logging via a specified P44LoggingObj (messages prefixed with object's logContextPrefix(), object's logoffset applied)
#define SOLOGENABLED(obj,lvl) ((obj).logEnabled(lvl))
#define SOLOG(obj,lvl,...) { if ((obj).logEnabled(lvl)) (obj).log(lvl,##__VA_ARGS__); }
// logging via a pointed-to P44LoggingObj (messages prefixed with object's logContextPrefix(), object's logoffset applied)
#define POLOGENABLED(obj,lvl) ((obj) ? (obj)->logEnabled(lvl) : LOGENABLED(lvl))
#define POLOG(obj,lvl,...) { if (POLOGENABLED(obj,lvl)) { if (obj) (obj)->log(lvl,##__VA_ARGS__); else globalLogger.log(lvl,##__VA_ARGS__); }}
// debug build extra logging (not included in release code unless ALWAYS_DEBUG is set)
#if defined(DEBUG) || ALWAYS_DEBUG
#define DEBUGLOGGING 1
#define DBGLOGENABLED(lvl) LOGENABLED(lvl)
#define DBGLOG(lvl,...) LOG(lvl,##__VA_ARGS__)
#define DBGFOCUSLOG FOCUSLOG
#define DBGFOCUSOLOG FOCUSOLOG
#define DBGFOCUSSOLOG FOCUSSOLOG
#define DBGFOCUSPOLOG FOCUSPOLOG
#define DBGOLOGENABLED(lvl) OLOGENABLED(lvl)
#define DBGOLOG(lvl,...) OLOG(lvl,##__VA_ARGS__)
#define DBGSOLOGENABLED(obj,lvl) SOLOGENABLED(obj,lvl)
#define DBGSOLOG(obj,lvl,...) SOLOG(obj,lvl,##__VA_ARGS__)
#define LOGGER_DEFAULT_LOGLEVEL LOG_DEBUG
#else
#define DEBUGLOGGING 0
#define DBGLOGENABLED(lvl) false
#define DBGLOG(lvl,...)
#define DBGFOCUSLOG(...)
#define DBGFOCUSOLOG(...)
#define DBGFOCUSSOLOG(...)
#define DBGFOCUSPOLOG(...)
#define DBGOLOGENABLED(lvl) false
#define DBGOLOG(lvl,...)
#define DBGSOLOGENABLED(obj,lvl) false
#define DBGSOLOG(obj,lvl,...)
#define LOGGER_DEFAULT_LOGLEVEL LOG_NOTICE
#endif
// "focus" logging during development, additional logging that can be enabled per source file
// (define FOCUSLOGLEVEL before including logger.hpp)
#if FOCUSLOGLEVEL
#define FOCUSLOG(...) LOG(FOCUSLOGLEVEL,##__VA_ARGS__)
#define FOCUSOLOG(...) OLOG(FOCUSLOGLEVEL,##__VA_ARGS__)
#define FOCUSSOLOG(obj,...) SOLOG(obj,FOCUSLOGLEVEL,##__VA_ARGS__)
#define FOCUSPOLOG(obj,...) POLOG(obj,FOCUSLOGLEVEL,##__VA_ARGS__)
#define FOCUSLOGGING 1
#define FOCUSLOGENABLED LOGENABLED(FOCUSLOGLEVEL)
#define FOCUSOLOGENABLED OLOGENABLED(FOCUSLOGLEVEL)
#define FOCUSPLOGENABLED PLOGENABLED(FOCUSLOGLEVEL)
#if !(defined(DEBUG) || ALWAYS_DEBUG || FOCUSLOGLEVEL>=7)
#warning "**** FOCUSLOGLEVEL<7 enabled in non-DEBUG build ****"
#endif
#else
#define FOCUSLOGGING 0
#define FOCUSLOG(...)
#define FOCUSOLOG(...)
#define FOCUSSOLOG(obj,...)
#define FOCUSPOLOG(obj,...)
#define FOCUSLOGENABLED false
#define FOCUSOLOGENABLED false
#define FOCUSPLOGENABLED false
#endif
using namespace std;
namespace p44 {
/// callback for logging lines somewhere else than stderr/stdout
/// @param aLevel the log level
/// @param aLinePrefix the line prefix (date and loglevel in square brackets, or indent for multilines)
/// @param aLogMessage the log message itself
typedef boost::function<void (int aLevel, const char *aLinePrefix, const char *aLogMessage)> LoggerCB;
class Logger : public P44Obj
{
pthread_mutex_t mReportMutex;
struct timeval mLastLogTS; ///< timestamp of last log line
int mLogLevel; ///< log level
int mStderrLevel; ///< lowest level that also goes to stderr
bool mDeltaTime; ///< if set, log timestamps will show delta time relative to previous log line
bool mErrToStdout; ///< if set, even log lines that go to stderr are still shown on stdout as well
bool mDaemonMode; ///< if set, normal log goes to stdout and log<=mStderrLevel goes to stderr. If cleared, all log goes to stderr according to mLogLevel
LoggerCB mLoggerCB; ///< custom logger output function to use (instead or in addition of stderr/stdout, see mLogCBOnly)
bool mAllowOther; ///< if set, setting CB or file will still allow other logging to happen in parallel
FILE *mLogFILE; ///< file to log to (instead of stderr/stdout)
#if ENABLE_LOG_COLORS
bool mLogColors; ///< if set, logger uses ANSI colors to differentiate levels
bool mLogSymbols; ///< if set, logger uses UTF-8 color dots to differentiate levels
#endif
public:
Logger();
virtual ~Logger();
/// test if log is enabled at a given level
/// @param aLogLevel level to check
/// @param aLevelOffset if aLogLevel is in the 5..7 (LOG_NOTICE..LOG_DEBUG) range, aLevelOffset is subtracted
/// and the result is limited to the 5..7 range.
/// This parameter can be fed from a property of a logging object to elevate (positive aLevelOffset)
/// or silence (negative aLevelOffset) its logging selectively.
/// @return true if any logging (stderr or stdout) is enabled at the specified level
bool logEnabled(int aLogLevel, int aLevelOffset = 0);
/// test if log to std out is enabled at a given level
/// @param aErrLevel level to check
/// @return true if logging to stdout is enabled at this level.
/// Note: logging might still occur on stderr, even if this function returns false
bool stdoutLogEnabled(int aErrLevel);
/// log a message if logging is enabled for the specified aErrLevel
/// @param aErrLevel error level of the message
/// @param aFmt ... printf style error message
void log(int aErrLevel, const char *aFmt, ... ) __printflike(3,4);
/// log a message
/// @param aErrLevel error level of the message
/// @param aAlways if set, always log the message without checking current loglevel, otherwise
/// only if logging is enabled for the specified aErrLevel
/// @param aFmt printf-style format string
/// @param aArgs va_list of argumments
void logV(int aErrLevel, bool aAlways, const char *aFmt, va_list aArgs);
/// log a message unconditionally (even if aErrLevel is not enabled)
/// @param aErrLevel error level of the message, for inclusion into log message prefix
/// @param aFmt ... printf style error message
void log_always(int aErrLevel, const char *aFmt, ... ) __printflike(3,4);
/// log a message (unconditionally)
/// @param aErrLevel error level of the message, for inclusion into log message prefix
/// @param aMessage the message string
void logStr_always(int aErrLevel, std::string aMessage);
/// log a message with context prefix (unconditionally)
/// @param aErrLevel error level of the message, for inclusion into log message prefix
/// @param aContext a context string, to be inserted before the message itself
/// @param aMessage the message string
void contextLogStr_always(int aErrLevel, const std::string& aContext, const std::string& aMessage);
/// set log file
/// @param aLogFilePath file to write log to instead of stdout
/// @param aAllowOther if set, stdout/stderr still happens
/// @note if a callback is set with setLogHandler, it overrides logging to a file if aAllowOther is not set
void setLogFile(const char *aLogFilePath, bool aAllowOther = false);
/// set log level
/// @param aLogLevel the new log level
/// @note even if aLogLevel is set to suppress messages, messages that qualify for going to stderr
/// (see setErrorLevel) will still be shown on stderr, but not on stdout.
void setLogLevel(int aLogLevel);
/// return current log level
/// @return current log level
int getLogLevel() { return mLogLevel; }
/// @return true if log symbols are enabled
bool logSymbols() { return mLogSymbols; }
/// set level required to send messages to stderr
/// @param aStderrLevel any messages with this or a lower (=higher priority) level will be sent to stderr (default = LOG_ERR)
/// @param aErrToStdout if set, messages that qualify for stderr will STILL be duplicated to stdout as well (default = true)
void setErrLevel(int aStderrLevel, bool aErrToStdout);
/// set handler for outputting log lines
/// @param aLoggerCB callback to be called whenever
/// @param aAllowOther if set, other log methods (file and stdout/stderr still happen)
void setLogHandler(LoggerCB aLoggerCB, bool aAllowOther = false);
/// set delta time display
/// @param aDeltaTime if set, time passed since last log line will be displayed
void setDeltaTime(bool aDeltaTime) { mDeltaTime = aDeltaTime; };
// @return true if in daemonmode (log goes to stdout, only higher importance errors to stderr)
bool getDaemonMode() { return mDaemonMode; }
// @param true to enable daemon mode (on by default)
void setDaemonMode(bool aDaemonMode) { mDaemonMode = aDaemonMode; }
/// @param aSymbols if set, logger uses UTF-8 symbols to differentiate levels and separate prefix from actual log content
void setSymbols(bool aSymbols) { mLogSymbols = aSymbols; };
/// @param aColoring if set, ANSI terminal colors are used to differentiate levels and separate prefix from actual log content
void setColoring(bool aColoring) { mLogColors = aColoring; };
private:
void logOutput_always(int aLevel, const char *aLinePrefix, const char *aLogMessage);
};
class P44LoggingObj : public P44Obj
{
typedef P44Obj inherited;
protected:
int logLevelOffset; ///< will be subtracted from log level for checking (in 7..5 range only)
public:
P44LoggingObj();
/// @return the prefix to be used for logging from this object
virtual string logContextPrefix();
/// @return name (usually user-defined) of the context object
virtual string contextName() const;
/// @return type (such as: device, element, vdc, trigger) of the context object
virtual string contextType() const;
/// @return id identifying the context object
virtual string contextId() const;
/// test if log is enabled from this object at a given level
/// @param aLogLevel level to check
bool logEnabled(int aLogLevel);
/// log a message from this object if logging is enabled for the specified aErrLevel adjusted by local logLevelOffset
/// @param aErrLevel error level of the message
/// @param aFmt ... printf style error message
void log(int aErrLevel, const char *aFmt, ... ) __printflike(3,4);
/// @return the per-instance log level offset
/// @note is virtual because some objects might want to use the log level offset of another object
virtual int getLogLevelOffset();
/// @return always locally stored offset, even when getLogLevelOffset() returns something else
int getLocalLogLevelOffset() { return logLevelOffset; }
/// set the log level offset on this logging object (and possibly contained sub-objects)
/// @param aLogLevelOffset the new log level offset
virtual void setLogLevelOffset(int aLogLevelOffset);
};
} // namespace p44
extern p44::Logger globalLogger;
#endif /* defined(__p44utils__logger__) */