forked from ad-freiburg/qlever
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLog.h
More file actions
180 lines (152 loc) · 6.25 KB
/
Copy pathLog.h
File metadata and controls
180 lines (152 loc) · 6.25 KB
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
// Copyright 2011 - 2024, University of Freiburg
// Chair of Algorithms and Data Structures
// Authors: Björn Buchhold <buchhold@cs.uni-freiburg.de> [2011 - 2014]
// Johannes Kalmbach <bast@cs.uni-freiburg.de>
// Hannah Bast <bast@cs.uni-freiburg.de>
#ifndef QLEVER_SRC_UTIL_LOG_H
#define QLEVER_SRC_UTIL_LOG_H
#include <absl/strings/str_cat.h>
#include <absl/strings/str_format.h>
#include <absl/time/clock.h>
#include <absl/time/time.h>
#include <algorithm>
#include <atomic>
#include <iostream>
#include <locale>
#include <mutex>
#include <sstream>
#include <string>
#include "backports/keywords.h"
#include "util/EnumWithStrings.h"
#include "util/TypeTraits.h"
#ifndef LOGLEVEL
#define LOGLEVEL INFO
#endif
namespace ad_utility {
namespace detail {
enum class LogLevelEnum {
FATAL = 0,
ERROR = 1,
WARN = 2,
INFO = 3,
DEBUG = 4,
TIMING = 5,
TRACE = 6
};
}
// Log level wrapper using the `EnumWithStrings` CRTP base to provide string
// conversion, JSON serialization, and boost::program_options integration.
class LogLevel : public EnumWithStrings<LogLevel, detail::LogLevelEnum> {
public:
using Enum = detail::LogLevelEnum;
static constexpr std::array<std::pair<Enum, std::string_view>, 7>
descriptions_{{{Enum::FATAL, "FATAL"},
{Enum::ERROR, "ERROR"},
{Enum::WARN, "WARN"},
{Enum::INFO, "INFO"},
{Enum::DEBUG, "DEBUG"},
{Enum::TIMING, "TIMING"},
{Enum::TRACE, "TRACE"}}};
static constexpr std::string_view typeName() { return "log level"; }
using EnumWithStrings::EnumWithStrings;
};
} // namespace ad_utility
// Global type alias and using-enum so that `LogLevel::FATAL` etc. and the
// compile-time `LOGLEVEL` macro keep working outside `namespace ad_utility`.
using LogLevel = ad_utility::LogLevel;
using enum LogLevel::Enum;
// Both the compile-time level (LOGLEVEL) and the runtime level must pass for a
// message to be logged. The LogLock temporary is held for the entire <<
// chain and released at the semicolon that ends the statement.
#define AD_LOG(x) \
if (x > LOGLEVEL || x > ::ad_utility::detail::runtimeLogLevel.load( \
std::memory_order_relaxed)) \
; \
else \
(::ad_utility::detail::LogLock{::ad_utility::detail::logMutex}, \
::ad_utility::Log::getLog<x>()) // NOLINT
// Macros for the different log levels.
#define AD_LOG_FATAL AD_LOG(LogLevel::Enum::FATAL)
#define AD_LOG_ERROR AD_LOG(LogLevel::Enum::ERROR)
#define AD_LOG_WARN AD_LOG(LogLevel::Enum::WARN)
#define AD_LOG_INFO AD_LOG(LogLevel::Enum::INFO)
#define AD_LOG_DEBUG AD_LOG(LogLevel::Enum::DEBUG)
#define AD_LOG_TIMING AD_LOG(LogLevel::Enum::TIMING)
#define AD_LOG_TRACE AD_LOG(LogLevel::Enum::TRACE)
namespace ad_utility {
namespace detail {
// Global mutex to ensure log messages from different threads are not
// interleaved (acquired via the comma-operator trick in the AD_LOG macro).
inline std::mutex logMutex;
static constexpr LogLevel::Enum defaultLogLevel =
std::min(LOGLEVEL, LogLevel::Enum::INFO);
// Runtime log level; messages with a higher level than this are suppressed.
// Defaults to the less verbose of INFO and the compile-time LOGLEVEL so that
// the runtime level is never set to something the binary cannot log.
inline std::atomic<LogLevel::Enum> runtimeLogLevel = defaultLogLevel;
// Non-[[nodiscard]] wrapper so the comma-operator pattern doesn't trigger
// -Wunused-value warnings (std::lock_guard itself is [[nodiscard]] in libc++).
struct LogLock {
std::lock_guard<std::mutex> lock_;
explicit LogLock(std::mutex& m) : lock_{m} {}
};
} // namespace detail
// Set the runtime log level. Throws if `level` is more verbose than the
// compile-time LOGLEVEL, because such messages are compiled out and can never
// appear regardless of the runtime setting.
inline void setRuntimeLogLevel(LogLevel level) {
if (level.value() > LOGLEVEL) {
throw std::runtime_error{absl::StrCat(
"Cannot set runtime log level to `", level.toString(),
"` because the compile-time log level is `",
LogLevel{LOGLEVEL}.toString(), "`. Recompile with -DLOGLEVEL=",
level.toString(), " or higher to enable this log level.")};
}
detail::runtimeLogLevel.store(level.value(), std::memory_order_relaxed);
}
// A singleton that holds a pointer to a single `std::ostream`. This enables us
// to globally redirect the `AD_LOG_...` macros to another output stream.
struct LogstreamChoice {
std::ostream& getStream() { return *_stream; }
void setStream(std::ostream* stream) { _stream = stream; }
static LogstreamChoice& get() {
static LogstreamChoice s;
return s;
} // instance
LogstreamChoice(const LogstreamChoice&) = delete;
LogstreamChoice& operator=(const LogstreamChoice&) = delete;
private:
LogstreamChoice() {}
~LogstreamChoice() {}
// default to cout since it was the default before
std::ostream* _stream = &std::cout;
};
// After this call, every use of `AD_LOG_...` will use the specified stream.
// Used in various tests to redirect or suppress logging output.
inline void setGlobalLoggingStream(std::ostream* stream) {
LogstreamChoice::get().setStream(stream);
}
// Helper class to get thousandth separators in a locale
class CommaNumPunct : public std::numpunct<char> {
protected:
virtual char do_thousands_sep() const { return ','; }
virtual std::string do_grouping() const { return "\03"; }
};
const static std::locale commaLocale(std::locale(), new CommaNumPunct());
// The class that actually does the logging.
class Log {
public:
template <LogLevel::Enum LEVEL>
static std::ostream& getLog() {
// use the singleton logging stream as target.
return LogstreamChoice::get().getStream()
<< getTimeStamp() << " - " << LogLevel{LEVEL}.toString() << ": ";
}
static void imbue(const std::locale& locale) { std::cout.imbue(locale); }
static std::string getTimeStamp() {
return absl::FormatTime("%Y-%m-%d %H:%M:%E3S", absl::Now(),
absl::LocalTimeZone());
}
};
} // namespace ad_utility
#endif // QLEVER_SRC_UTIL_LOG_H