-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathxlog.h
358 lines (293 loc) · 12.5 KB
/
xlog.h
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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#pragma once
#ifndef _XLOG_H
#define _XLOG_H
#include <errno.h>
#include <string.h>
#include <string>
#include <stdexcept>
#include <string_view>
#include <type_traits>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <fmt/core.h>
#ifdef XLOG_USE_SOURCE_LOCATION_IF_AVAILABLE
#ifdef __cpp_lib_source_location
#define XLOG_LOGGING_USE_SOURCE_LOCATION
#include <source_location>
#endif
#endif
#ifdef XLOG_ENABLE_EXTERNAL_LOG_CONTROL
namespace XLog
{
struct ExternalLogControlSettings
{
// Is external log control enabled at runtime?
bool enabled = true;
// Is the log socket modified so that anyone can connect to it?
bool allow_anyone_access = true;
// Is failure to setup the log socket fatal to the application?
bool setup_failure_is_fatal = false;
};
}
#endif // XLOG_ENABLE_EXTERNAL_LOG_CONTROL
#ifdef XLOG_USE_SYSLOG_LOG
#include <boost/log/sinks/syslog_constants.hpp>
namespace XLog
{
struct SyslogSettings
{
// Is syslog logging enabled at runtime?
bool enabled = true;
// Syslog facility
boost::log::sinks::syslog::facility facility = boost::log::sinks::syslog::facility::user;
};
}
#endif // XLOG_USE_SYSLOG_LOG
#ifdef XLOG_USE_JOURNAL_LOG
namespace XLog
{
struct JournalSettings
{
// Is journal logging enabled at runtime?
bool enabled = true;
};
}
#endif // XLOG_USE_JOURNAL_LOG
namespace XLog
{
/*
* Why are there DEBUGS, WARNS, and ERRORS where we don't care about source location?
*
* Well sometimes a log is VERY unique so we don't need it since it's
* clear where to look for it!
*
* While I do sometimes find source location annoying, I recognize that a blanket
* option for ON/OFF isn't really suitable, so having multiple macros that
* allow you to conditionally apply that is useful in my opinion.
*
* Of course if you aren't using C++ 20 then it means nothing, but perhaps it can
* be argued that using the correct macros makes the eventual transition easier?
*
* I doubt I'll ever allow FATAL to not have a source location, they should be fairly rare
* outside of very specific situations where the error basically stops the program from
* executing, so even though that satisfies the "uniqueness" criteria I mentioned before,
* I still think source location is 'correct' (whatever that means...)
*
*/
enum class Severity
{
INFO = 0,
DEBUG,
DEBUG2, // For debugs where we don't really need to know the source location (if enabled)
WARNING,
WARNING2, // For warnings where we don't really need to know the source location (if enabled)
ERROR,
ERROR2, // For errors where we don't really need to know the source location (if enabled)
FATAL,
INTERNAL // For xlog itself, can never be disabled (knowing why your logger failed is *really* important)
};
struct LogSettings
{
Severity s_default_level = Severity::INFO;
#ifdef XLOG_ENABLE_EXTERNAL_LOG_CONTROL
ExternalLogControlSettings s_external_control;
#endif // XLOG_ENABLE_EXTERNAL_LOG_CONTROL
#ifdef XLOG_USE_SYSLOG_LOG
SyslogSettings s_syslog;
#endif // XLOG_USE_SYSLOG_LOG
#ifdef XLOG_USE_JOURNAL_LOG
JournalSettings s_journal;
#endif // XLOG_USE_JOURNAL_LOG
};
typedef boost::log::sources::severity_channel_logger_mt<Severity, std::string> LoggerType;
std::string GetSeverityString(Severity sev) noexcept;
LoggerType& GetNamedLogger(const std::string_view channel) noexcept;
void InitializeLogging(LogSettings settings = {});
void ShutownLogging(int signal = -1);
void SetGlobalLoggingLevel(Severity sev);
bool SetLoggingLevel(Severity sev, const std::string_view channel);
Severity GetGlobalLoggingLevel();
Severity GetLoggingLevel(const std::string_view channel);
std::unordered_map<std::string, Severity> GetAllLoggingLevels();
std::vector<std::string> GetAllLogHandles();
}
// Set attribute and return the new value
// Pretty sure I found this on stackoverflow, but can't recall where
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
auto attr = boost::log::attribute_cast<boost::log::attributes::mutable_constant<ValueType>>(boost::log::core::get()->get_global_attributes()[name]);
attr.set(value);
return attr.get();
}
#ifdef XLOG_LOGGING_USE_SOURCE_LOCATION
#define CUSTOM_LOG_SEV_SLOC(logger, sev, sloc) \
BOOST_LOG_STREAM_WITH_PARAMS( \
(logger), \
(set_get_attrib("SourceLocation", sloc)) \
(::boost::log::keywords::severity = (sev)) \
)
#define CUSTOM_LOG_SEV(logger, sev) CUSTOM_LOG_SEV_SLOC(logger, sev, std::source_location::current())
#else
#define CUSTOM_LOG_SEV(logger, sev) BOOST_LOG_SEV(logger, sev)
#endif
#define PRINT_ENUM(var) static_cast<std::underlying_type_t<decltype(var)>>(var)
#define GET_LOGGER(name) static XLog::LoggerType& __logger = XLog::GetNamedLogger(name);
#define ERRC_STREAM(errc) errc.message() << std::endl
inline std::string get_errno_string()
{
constexpr int BUFFER_SIZE = 256;
char buffer[BUFFER_SIZE];
char* realBuffer = ::strerror_r(errno, buffer, BUFFER_SIZE - 1);
return std::string(realBuffer);
}
#define ERRNO_STREAM get_errno_string()
#define LOG_INFO() CUSTOM_LOG_SEV(__logger, XLog::Severity::INFO)
#define LOG_DEBUG() CUSTOM_LOG_SEV(__logger, XLog::Severity::DEBUG)
#define LOG_DEBUG2() CUSTOM_LOG_SEV(__logger, XLog::Severity::DEBUG2)
#define LOG_WARN() CUSTOM_LOG_SEV(__logger, XLog::Severity::WARNING)
#define LOG_WARN2() CUSTOM_LOG_SEV(__logger, XLog::Severity::WARNING2)
#define LOG_ERROR() CUSTOM_LOG_SEV(__logger, XLog::Severity::ERROR)
#define LOG_ERROR2() CUSTOM_LOG_SEV(__logger, XLog::Severity::ERROR2)
#define CODE_INFO(errc) LOG_INFO() << ERRC_STREAM(errc)
#define CODE_DEBUG(errc) LOG_DEBUG() << ERRC_STREAM(errc)
#define CODE_DEBUG2(errc) LOG_DEBUG2() << ERRC_STREAM(errc)
#define CODE_WARN(errc) LOG_WARN() << ERRC_STREAM(errc)
#define CODE_WARN2(errc) LOG_WARN2() << ERRC_STREAM(errc)
#define CODE_ERROR(errc) LOG_ERROR() << ERRC_STREAM(errc)
#define CODE_ERROR2(errc) LOG_ERROR2() << ERRC_STREAM(errc)
#define LOG_INFO_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::INFO)
#define LOG_DEBUG_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::DEBUG)
#define LOG_DEBUG2_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::DEBUG2)
#define LOG_WARN_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::WARNING)
#define LOG_WARN2_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::WARNING2)
#define LOG_ERROR_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::ERROR)
#define LOG_ERROR2_INPLACE(name) CUSTOM_LOG_SEV(XLog::GetNamedLogger(name), XLog::Severity::ERROR2)
#define CODE_INFO_INPLACE(name, errc) LOG_INFO_INPLACE(name) << ERRC_STREAM(errc)
#define CODE_DEBUG_INPLACE(name, errc) LOG_DEBUG_INPLACE(name) << ERRC_STREAM(errc)
#define CODE_DEBUG2_INPLACE(name, errc) LOG_DEBUG2_INPLACE(name) << ERRC_STREAM(errc)
#define CODE_WARN_INPLACE(name, errc) LOG_WARN_INPLACE(name) << ERRC_STREAM(errc)
#define CODE_WARN2_INPLACE(name, errc) LOG_WARN2_INPLACE(name) << ERRC_STREAM(errc)
#define CODE_ERROR_INPLACE(name, errc) LOG_ERROR_INPLACE(name) << ERRC_STREAM(errc)
#define CODE_ERROR2_INPLACE(name, errc) LOG_ERROR2_INPLACE(name) << ERRC_STREAM(errc)
#define ERRNO_INFO() LOG_INFO() << ERRNO_STREAM
#define ERRNO_DEBUG() LOG_DEBUG() << ERRNO_STREAM
#define ERRNO_DEBUG2() LOG_DEBUG2() << ERRNO_STREAM
#define ERRNO_WARN() LOG_WARN() << ERRNO_STREAM
#define ERRNO_WARN2() LOG_WARN2() << ERRNO_STREAM
#define ERRNO_ERROR() LOG_ERROR() << ERRNO_STREAM
#define ERRNO_ERROR2() LOG_ERROR2() << ERRNO_STREAM
#define ERRNO_INFO_INPLACE(name) LOG_INFO_INPLACE(name) << ERRNO_STREAM
#define ERRNO_DEBUG_INPLACE(name) LOG_DEBUG_INPLACE(name) << ERRNO_STREAM
#define ERRNO_DEBUG2_INPLACE(name) LOG_DEBUG2_INPLACE(name) << ERRNO_STREAM
#define ERRNO_WARN_INPLACE(name) LOG_WARN_INPLACE(name) << ERRNO_STREAM
#define ERRNO_WARN2_INPLACE(name) LOG_WARN2_INPLACE(name) << ERRNO_STREAM
#define ERRNO_ERROR_INPLACE(name) LOG_ERROR_INPLACE(name) << ERRNO_STREAM
#define ERRNO_ERROR2_INPLACE(name) LOG_ERROR2_INPLACE(name) << ERRNO_STREAM
namespace XLog
{
class fatal_exception : public std::runtime_error
{
public:
#ifdef XLOG_LOGGING_USE_SOURCE_LOCATION
explicit fatal_exception(XLog::LoggerType& logger, const std::string& what_arg, const std::source_location sloc = std::source_location::current());
explicit fatal_exception(XLog::LoggerType& logger, const char* what_arg, const std::source_location sloc = std::source_location::current());
template<typename... FormatArgs>
explicit fatal_exception(XLog::LoggerType& logger, const std::source_location sloc, std::string_view format, FormatArgs&&... args) : std::runtime_error(fmt::vformat(format, fmt::make_format_args(std::forward<FormatArgs>(args)...)))
{
print_fatal(logger, sloc);
}
#else
explicit fatal_exception(XLog::LoggerType& logger, const std::string& what_arg);
explicit fatal_exception(XLog::LoggerType& logger, const char* what_arg);
template<typename... FormatArgs>
explicit fatal_exception(XLog::LoggerType& logger, std::string_view format, FormatArgs&&... args) : std::runtime_error(fmt::vformat(format, fmt::make_format_args(std::forward<FormatArgs>(args)...)))
{
print_fatal(logger);
}
#endif
private:
#ifdef XLOG_LOGGING_USE_SOURCE_LOCATION
void print_fatal(XLog::LoggerType& logger, const std::source_location sloc) const;
#else
void print_fatal(XLog::LoggerType& logger) const;
#endif
};
}
#define ERRC_MSG(errc) errc.message()
/*
* This should work with std::error_code & boost::system::error_code
* TODO - Expand
*/
#define FATAL(msg) throw XLog::fatal_exception(__logger, msg);
#define CODE_FATAL(errc) FATAL(ERRC_MSG(errc))
#define FATAL_NAMED(name, msg) throw XLog::fatal_exception(XLog::GetNamedLogger(name), msg);
#define CODE_FATAL_NAMED(name, errc) FATAL_NAMED(name, ERRC_MSG(errc))
#define FATAL_GLOBAL(msg) FATAL_NAMED("Global", msg)
#define CODE_FATAL_GLOBAL(errc) FATAL_GLOBAL(ERRC_MSG(errc))
/*
* Log ERRNO messages
* TODO - Expand
*/
#define ERRNO_MESSAGE get_errno_string()
#define ERRNO_FATAL() FATAL(ERRNO_MESSAGE)
#define ERRNO_FATAL_NAMED(name) FATAL_NAMED(name, ERRNO_MESSAGE)
#define ERRNO_FATAL_GLOBAL() FATAL_GLOBAL(ERNNO_MESSAGE)
#ifdef XLOG_LOGGING_USE_SOURCE_LOCATION
#define FATAL_FMT(fmt, ...) throw XLog::fatal_exception(__logger, std::source_location::current(), fmt, __VA_ARGS__);
#define FATAL_NAMED_FMT(name, fmt, ...) throw XLog::fatal_exception(XLog::GetNamedLogger(name), std::source_location::current(), fmt, __VA_ARGS__);
#define FATAL_GLOBAL_FMT(fmt, ...) FATAL_NAMED_FMT("Global", std::source_location::current(), fmt, __VA_ARGS__)
#else
#define FATAL_FMT(fmt, ...) throw XLog::fatal_exception(__logger, fmt, __VA_ARGS__);
#define FATAL_NAMED_FMT(name, fmt, ...) throw XLog::fatal_exception(XLog::GetNamedLogger(name), fmt, __VA_ARGS__);
#define FATAL_GLOBAL_FMT(fmt, ...) FATAL_NAMED_FMT("Global", fmt, __VA_ARGS__)
#endif
/*
* Default log formatter
*/
namespace XLogFormatters
{
void default_formatter(const boost::log::record_view& rec, boost::log::formatting_ostream& stream);
}
/*
* String hashes to allow different "strings" to
* be used as keys for the logger map (see source file)
*/
namespace XLog
{
struct StringHash
{
using hash_type = std::hash<std::string_view>;
using is_transparent = void;
inline size_t operator()(const char* str) const
{
return hash_type{}(str);
}
inline size_t operator()(std::string_view str) const
{
return hash_type{}(str);
}
inline size_t operator()(const std::string& str) const
{
return hash_type{}(str);
}
};
struct WStringHash
{
using hash_type = std::hash<std::wstring_view>;
using is_transparent = void;
inline size_t operator()(const wchar_t* str) const
{
return hash_type{}(str);
}
inline size_t operator()(std::wstring_view str) const
{
return hash_type{}(str);
}
inline size_t operator()(const std::wstring& str) const
{
return hash_type{}(str);
}
};
}
#endif // _XLOG_H