/
Logging.cpp
398 lines (361 loc) · 10.5 KB
/
Logging.cpp
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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
// Copyright 2014 Stellar Development Foundation and contributors. Licensed
// under the Apache License, Version 2.0. See the COPYING file at the root
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
#include "util/Logging.h"
#include "util/types.h"
#if defined(USE_SPDLOG)
#include "util/Timer.h"
#include <chrono>
#include <fmt/chrono.h>
#include <fstream>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/spdlog.h>
#endif
namespace stellar
{
std::array<std::string const, 14> const Logging::kPartitionNames = {
#define LOG_PARTITION(name) #name,
#include "util/LogPartitions.def"
#undef LOG_PARTITION
};
LogLevel Logging::mGlobalLogLevel = LogLevel::LVL_INFO;
std::map<std::string, LogLevel> Logging::mPartitionLogLevels;
#if defined(USE_SPDLOG)
bool Logging::mInitialized = false;
bool Logging::mColor = false;
std::string Logging::mLastPattern;
std::string Logging::mLastFilenamePattern;
#endif
// Right now this is hard-coded to log messages at least as important as INFO
CoutLogger::CoutLogger(LogLevel l) : mShouldLog(l <= Logging::getLogLevel(""))
{
}
CoutLogger::~CoutLogger()
{
if (mShouldLog)
{
std::cout << std::endl;
}
}
#if defined(USE_SPDLOG)
static spdlog::level::level_enum
convert_loglevel(LogLevel level)
{
auto slev = spdlog::level::info;
switch (level)
{
case LogLevel::LVL_FATAL:
slev = spdlog::level::critical;
break;
case LogLevel::LVL_ERROR:
slev = spdlog::level::err;
break;
case LogLevel::LVL_WARNING:
slev = spdlog::level::warn;
break;
case LogLevel::LVL_INFO:
slev = spdlog::level::info;
break;
case LogLevel::LVL_DEBUG:
slev = spdlog::level::debug;
break;
case LogLevel::LVL_TRACE:
slev = spdlog::level::trace;
break;
default:
break;
}
return slev;
}
#endif
void
Logging::init(bool truncate)
{
#if defined(USE_SPDLOG)
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
if (!mInitialized)
{
using namespace spdlog::sinks;
using std::make_shared;
using std::shared_ptr;
auto console = (mColor ? static_cast<shared_ptr<sink>>(
make_shared<stdout_color_sink_mt>())
: static_cast<shared_ptr<sink>>(
make_shared<stdout_sink_mt>()));
std::vector<shared_ptr<sink>> sinks{console};
if (!mLastFilenamePattern.empty())
{
VirtualClock clock(VirtualClock::REAL_TIME);
std::time_t time = VirtualClock::to_time_t(clock.system_now());
auto filename =
fmt::format(mLastFilenamePattern,
fmt::arg("datetime", fmt::localtime(time)));
// NB: We do _not_ pass 'truncate' through to spdlog here -- spdlog
// interprets 'truncate=true' as a request to open the file in "wb"
// mode rather than "ab" mode. Opening in "wb" mode does truncate
// the file but also sets it to a fundamentally different _mode_
// than "ab" mode.
//
// In particular, in "ab" mode the underlying file descriptor is
// opened with O_APPEND and this makes the kernel atomically preface
// any write(fd, buf, n) with an lseek(fd, SEEK_END, 0), adjusting
// the fd's offset to the current end of file, _even if the file
// shrank_ since the last write.
//
// In contrast, in "wb" mode the underlying file descriptor just
// increments its offset after each write, and if the file shrinks
// between writes the space between the actual end of file and the
// offset at the time of writing will be zero-filled!
//
// Why would a file shrink? Because users might choose to use an
// _external_ log-rotation program such as logrotate(1) rather than
// our internal log rotation command. This command happens to call
// truncate(2) on the log file after it's made a copy for rotation
// purposes. This is fine if we're writing to an fd with O_APPEND
// but it's a recipe for giant zero-filled files if we're not.
//
// So instead we just truncate ourselves here if it was requested,
// and always pass 'truncate=false' to spdlog, so it always opens in
// "ab" == O_APPEND mode. The "portable" way to truncate is to open
// an ofstream in out|trunc mode, then immediately close it.
std::ofstream out;
if (truncate)
{
out.open(filename, std::ios_base::out | std::ios_base::trunc);
}
else
{
out.open(filename, std::ios_base::out | std::ios_base::app);
}
if (out.fail())
{
throw std::runtime_error(fmt::format(
"Could not open log file {}, check access rights",
filename));
}
else
{
out.close();
}
sinks.emplace_back(
make_shared<basic_file_sink_mt>(filename, /*truncate=*/false));
}
auto makeLogger =
[&](std::string const& name) -> shared_ptr<spdlog::logger> {
auto logger =
make_shared<spdlog::logger>(name, sinks.begin(), sinks.end());
spdlog::register_logger(logger);
return logger;
};
spdlog::set_default_logger(makeLogger("default"));
for (auto const& partition : stellar::Logging::kPartitionNames)
{
makeLogger(partition);
}
if (mLastPattern.empty())
{
mLastPattern = "%Y-%m-%dT%H:%M:%S.%e [%^%n %l%$] %v";
}
spdlog::set_pattern(mLastPattern);
spdlog::set_level(convert_loglevel(mGlobalLogLevel));
for (auto const& pair : mPartitionLogLevels)
{
spdlog::get(pair.first)->set_level(convert_loglevel(pair.second));
}
spdlog::flush_every(std::chrono::seconds(1));
spdlog::flush_on(spdlog::level::err);
mInitialized = true;
}
#endif
}
void
Logging::deinit()
{
#if defined(USE_SPDLOG)
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
if (mInitialized)
{
#define LOG_PARTITION(name) Logging::name##LogPtr = nullptr;
#include "util/LogPartitions.def"
#undef LOG_PARTITION
spdlog::drop_all();
mInitialized = false;
}
#endif
}
void
Logging::setFmt(std::string const& peerID, bool timestamps)
{
#if defined(USE_SPDLOG)
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
init();
mLastPattern = std::string("%Y-%m-%dT%H:%M:%S.%e ") + peerID +
std::string(" [%^%n %l%$] %v");
spdlog::set_pattern(mLastPattern);
#endif
}
void
Logging::setLoggingToFile(std::string const& filename)
{
#if defined(USE_SPDLOG)
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
mLastFilenamePattern = filename;
deinit();
try
{
init();
}
catch (std::runtime_error& e)
{
// Could not initialize logging to file, fallback on
// console-only logging and throw
mLastFilenamePattern.clear();
deinit();
init();
throw;
}
#endif
}
void
Logging::setLoggingColor(bool color)
{
#if defined(USE_SPDLOG)
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
mColor = true;
deinit();
init();
#endif
}
void
Logging::setLogLevel(LogLevel level, const char* partition)
{
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
if (partition)
{
mPartitionLogLevels[partition] = level;
}
else
{
mGlobalLogLevel = level;
mPartitionLogLevels.clear();
}
#if defined(USE_SPDLOG)
init();
auto slev = convert_loglevel(level);
if (partition)
{
spdlog::get(partition)->set_level(slev);
}
else
{
spdlog::set_level(slev);
}
#endif
}
LogLevel
Logging::getLLfromString(std::string const& levelName)
{
if (iequals(levelName, "fatal"))
{
return LogLevel::LVL_FATAL;
}
if (iequals(levelName, "error"))
{
return LogLevel::LVL_ERROR;
}
if (iequals(levelName, "warning"))
{
return LogLevel::LVL_WARNING;
}
if (iequals(levelName, "debug"))
{
return LogLevel::LVL_DEBUG;
}
if (iequals(levelName, "trace"))
{
return LogLevel::LVL_TRACE;
}
return LogLevel::LVL_INFO;
}
LogLevel
Logging::getLogLevel(std::string const& partition)
{
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
auto p = mPartitionLogLevels.find(partition);
if (p != mPartitionLogLevels.end())
{
return p->second;
}
return mGlobalLogLevel;
}
std::string
Logging::getStringFromLL(LogLevel level)
{
switch (level)
{
case LogLevel::LVL_FATAL:
return "Fatal";
case LogLevel::LVL_ERROR:
return "Error";
case LogLevel::LVL_WARNING:
return "Warning";
case LogLevel::LVL_INFO:
return "Info";
case LogLevel::LVL_DEBUG:
return "Debug";
case LogLevel::LVL_TRACE:
return "Trace";
}
return "????";
}
bool
Logging::logDebug(std::string const& partition)
{
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
return mGlobalLogLevel <= LogLevel::LVL_DEBUG;
}
bool
Logging::logTrace(std::string const& partition)
{
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
return mGlobalLogLevel <= LogLevel::LVL_TRACE;
}
void
Logging::rotate()
{
std::lock_guard<std::recursive_mutex> guard(mLogMutex);
deinit();
init(/*truncate=*/true);
}
// throws if partition name is not recognized
std::string
Logging::normalizePartition(std::string const& partition)
{
for (auto& p : kPartitionNames)
{
if (iequals(partition, p))
{
return p;
}
}
throw std::invalid_argument("not a valid partition");
}
std::recursive_mutex Logging::mLogMutex;
#if defined(USE_SPDLOG)
#define LOG_PARTITION(name) \
LogPtr Logging::name##LogPtr = nullptr; \
LogPtr Logging::get##name##LogPtr() \
{ \
std::lock_guard<std::recursive_mutex> guard(mLogMutex); \
if (!name##LogPtr) \
{ \
name##LogPtr = spdlog::get(#name); \
} \
return name##LogPtr; \
}
#include "util/LogPartitions.def"
#undef LOG_PARTITION
#endif
}