summaryrefslogtreecommitdiffstats
path: root/lib/base/logger.cpp
blob: 38a2c6721b44ed4b1d6ef6034c50d74758c0cce5 (plain)
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
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */

#include "base/logger.hpp"
#include "base/logger-ti.cpp"
#include "base/application.hpp"
#include "base/streamlogger.hpp"
#include "base/configtype.hpp"
#include "base/utility.hpp"
#include "base/objectlock.hpp"
#include "base/context.hpp"
#include "base/scriptglobal.hpp"
#ifdef _WIN32
#include "base/windowseventloglogger.hpp"
#endif /* _WIN32 */
#include <algorithm>
#include <iostream>
#include <utility>

using namespace icinga;

template Log& Log::operator<<(const Value&);
template Log& Log::operator<<(const String&);
template Log& Log::operator<<(const std::string&);
template Log& Log::operator<<(const bool&);
template Log& Log::operator<<(const unsigned int&);
template Log& Log::operator<<(const int&);
template Log& Log::operator<<(const unsigned long&);
template Log& Log::operator<<(const long&);
template Log& Log::operator<<(const double&);

REGISTER_TYPE(Logger);

std::set<Logger::Ptr> Logger::m_Loggers;
std::mutex Logger::m_Mutex;
bool Logger::m_ConsoleLogEnabled = true;
std::atomic<bool> Logger::m_EarlyLoggingEnabled (true);
bool Logger::m_TimestampEnabled = true;
LogSeverity Logger::m_ConsoleLogSeverity = LogInformation;
std::mutex Logger::m_UpdateMinLogSeverityMutex;
Atomic<LogSeverity> Logger::m_MinLogSeverity (LogDebug);

INITIALIZE_ONCE([]() {
	ScriptGlobal::Set("System.LogDebug", LogDebug);
	ScriptGlobal::Set("System.LogNotice", LogNotice);
	ScriptGlobal::Set("System.LogInformation", LogInformation);
	ScriptGlobal::Set("System.LogWarning", LogWarning);
	ScriptGlobal::Set("System.LogCritical", LogCritical);
});

/**
 * Constructor for the Logger class.
 */
void Logger::Start(bool runtimeCreated)
{
	ObjectImpl<Logger>::Start(runtimeCreated);

	{
		std::unique_lock<std::mutex> lock(m_Mutex);
		m_Loggers.insert(this);
	}

	UpdateMinLogSeverity();
}

void Logger::Stop(bool runtimeRemoved)
{
	{
		std::unique_lock<std::mutex> lock(m_Mutex);
		m_Loggers.erase(this);
	}

	UpdateMinLogSeverity();

	ObjectImpl<Logger>::Stop(runtimeRemoved);
}

std::set<Logger::Ptr> Logger::GetLoggers()
{
	std::unique_lock<std::mutex> lock(m_Mutex);
	return m_Loggers;
}

/**
 * Retrieves the minimum severity for this logger.
 *
 * @returns The minimum severity.
 */
LogSeverity Logger::GetMinSeverity() const
{
	String severity = GetSeverity();
	if (severity.IsEmpty())
		return LogInformation;
	else {
		LogSeverity ls = LogInformation;

		try {
			ls = Logger::StringToSeverity(severity);
		} catch (const std::exception&) { /* use the default level */ }

		return ls;
	}
}

/**
 * Converts a severity enum value to a string.
 *
 * @param severity The severity value.
 */
String Logger::SeverityToString(LogSeverity severity)
{
	switch (severity) {
		case LogDebug:
			return "debug";
		case LogNotice:
			return "notice";
		case LogInformation:
			return "information";
		case LogWarning:
			return "warning";
		case LogCritical:
			return "critical";
		default:
			BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid severity."));
	}
}

/**
 * Converts a string to a severity enum value.
 *
 * @param severity The severity.
 */
LogSeverity Logger::StringToSeverity(const String& severity)
{
	if (severity == "debug")
		return LogDebug;
	else if (severity == "notice")
		return LogNotice;
	else if (severity == "information")
		return LogInformation;
	else if (severity == "warning")
		return LogWarning;
	else if (severity == "critical")
		return LogCritical;
	else
		BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid severity: " + severity));
}

void Logger::DisableConsoleLog()
{
	m_ConsoleLogEnabled = false;

	UpdateMinLogSeverity();
}

void Logger::EnableConsoleLog()
{
	m_ConsoleLogEnabled = true;

	UpdateMinLogSeverity();
}

bool Logger::IsConsoleLogEnabled()
{
	return m_ConsoleLogEnabled;
}

void Logger::SetConsoleLogSeverity(LogSeverity logSeverity)
{
	m_ConsoleLogSeverity = logSeverity;
}

LogSeverity Logger::GetConsoleLogSeverity()
{
	return m_ConsoleLogSeverity;
}

void Logger::DisableEarlyLogging() {
	m_EarlyLoggingEnabled = false;

	UpdateMinLogSeverity();
}

bool Logger::IsEarlyLoggingEnabled() {
	return m_EarlyLoggingEnabled;
}

void Logger::DisableTimestamp()
{
	m_TimestampEnabled = false;
}

void Logger::EnableTimestamp()
{
	m_TimestampEnabled = true;
}

bool Logger::IsTimestampEnabled()
{
	return m_TimestampEnabled;
}

void Logger::SetSeverity(const String& value, bool suppress_events, const Value& cookie)
{
	ObjectImpl<Logger>::SetSeverity(value, suppress_events, cookie);

	UpdateMinLogSeverity();
}

void Logger::ValidateSeverity(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	ObjectImpl<Logger>::ValidateSeverity(lvalue, utils);

	try {
		StringToSeverity(lvalue());
	} catch (...) {
		BOOST_THROW_EXCEPTION(ValidationError(this, { "severity" }, "Invalid severity specified: " + lvalue()));
	}
}

void Logger::UpdateMinLogSeverity()
{
	std::unique_lock<std::mutex> lock (m_UpdateMinLogSeverityMutex);
	auto result (LogNothing);

	for (auto& logger : Logger::GetLoggers()) {
		ObjectLock llock (logger);

		if (logger->IsActive()) {
			result = std::min(result, logger->GetMinSeverity());
		}
	}

	if (Logger::IsConsoleLogEnabled()) {
		result = std::min(result, Logger::GetConsoleLogSeverity());
	}

#ifdef _WIN32
	if (Logger::IsEarlyLoggingEnabled()) {
		result = std::min(result, LogCritical);
	}
#endif /* _WIN32 */

	m_MinLogSeverity.store(result);
}

Log::Log(LogSeverity severity, String facility, const String& message)
	: Log(severity, std::move(facility))
{
	if (!m_IsNoOp) {
		m_Buffer << message;
	}
}

Log::Log(LogSeverity severity, String facility)
	: m_Severity(severity), m_Facility(std::move(facility)), m_IsNoOp(severity < Logger::GetMinLogSeverity())
{ }

/**
 * Writes the message to the application's log.
 */
Log::~Log()
{
	if (m_IsNoOp) {
		return;
	}

	LogEntry entry;
	entry.Timestamp = Utility::GetTime();
	entry.Severity = m_Severity;
	entry.Facility = m_Facility;

	{
		auto msg (m_Buffer.str());
		msg.erase(msg.find_last_not_of("\n") + 1u);

		entry.Message = std::move(msg);
	}

	if (m_Severity >= LogWarning) {
		ContextTrace context;

		if (context.GetLength() > 0) {
			std::ostringstream trace;
			trace << context;
			entry.Message += "\nContext:" + trace.str();
		}
	}

	for (const Logger::Ptr& logger : Logger::GetLoggers()) {
		ObjectLock llock(logger);

		if (!logger->IsActive())
			continue;

		if (entry.Severity >= logger->GetMinSeverity())
			logger->ProcessLogEntry(entry);

#ifdef I2_DEBUG /* I2_DEBUG */
		/* Always flush, don't depend on the timer. Enable this for development sprints on Linux/macOS only. Windows crashes. */
		//logger->Flush();
#endif /* I2_DEBUG */
	}

	if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity()) {
		StreamLogger::ProcessLogEntry(std::cout, entry);

		/* "Console" might be a pipe/socket (systemd, daemontools, docker, ...),
		 * then cout will not flush lines automatically. */
		std::cout << std::flush;
	}

#ifdef _WIN32
	if (Logger::IsEarlyLoggingEnabled() && entry.Severity >= LogCritical) {
		WindowsEventLogLogger::WriteToWindowsEventLog(entry);
	}
#endif /* _WIN32 */
}

Log& Log::operator<<(const char *val)
{
	if (!m_IsNoOp) {
		m_Buffer << val;
	}

	return *this;
}