1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "livestatus/livestatuslogutility.hpp"
4 #include "icinga/service.hpp"
5 #include "icinga/host.hpp"
6 #include "icinga/user.hpp"
7 #include "icinga/checkcommand.hpp"
8 #include "icinga/eventcommand.hpp"
9 #include "icinga/notificationcommand.hpp"
10 #include "base/utility.hpp"
11 #include "base/convert.hpp"
12 #include "base/logger.hpp"
13 #include <boost/tuple/tuple.hpp>
14 #include <boost/algorithm/string.hpp>
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/algorithm/string/predicate.hpp>
19 using namespace icinga;
21 void LivestatusLogUtility::CreateLogIndex(const String& path, std::map<time_t, String>& index)
23 Utility::Glob(path + "/icinga.log", std::bind(&LivestatusLogUtility::CreateLogIndexFileHandler, _1, std::ref(index)), GlobFile);
24 Utility::Glob(path + "/archives/*.log", std::bind(&LivestatusLogUtility::CreateLogIndexFileHandler, _1, std::ref(index)), GlobFile);
27 void LivestatusLogUtility::CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index)
30 stream.open(path.CStr(), std::ifstream::in);
33 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open log file: " + path));
35 /* read the first bytes to get the timestamp: [123456789] */
38 stream.read(buffer, 12);
40 if (buffer[0] != '[' || buffer[11] != ']') {
41 /* this can happen for directories too, silently ignore them */
45 /* extract timestamp */
47 time_t ts_start = atoi(buffer+1);
51 Log(LogDebug, "LivestatusLogUtility")
52 << "Indexing log file: '" << path << "' with timestamp start: '" << ts_start << "'.";
54 index[ts_start] = path;
57 void LivestatusLogUtility::CreateLogCache(std::map<time_t, String> index, HistoryTable *table,
58 time_t from, time_t until, const AddRowFunction& addRowFn)
62 /* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
63 unsigned long line_count = 0;
64 for (const auto& kv : index) {
65 unsigned int ts = kv.first;
67 /* skip log files not in range (performance optimization) */
68 if (ts < from || ts > until)
71 String log_file = index[ts];
75 fp.exceptions(std::ifstream::badbit);
76 fp.open(log_file.CStr(), std::ifstream::in);
80 std::getline(fp, line);
83 continue; /* Ignore empty lines */
85 Dictionary::Ptr log_entry_attrs = LivestatusLogUtility::GetAttributes(line);
87 /* no attributes available - invalid log line */
88 if (!log_entry_attrs) {
89 Log(LogDebug, "LivestatusLogUtility")
90 << "Skipping invalid log line: '" << line << "'.";
94 table->UpdateLogEntries(log_entry_attrs, line_count, lineno, addRowFn);
104 Dictionary::Ptr LivestatusLogUtility::GetAttributes(const String& text)
106 Dictionary::Ptr bag = new Dictionary();
109 * [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
111 unsigned long time = atoi(text.SubStr(1, 11).CStr());
113 Log(LogDebug, "LivestatusLogUtility")
114 << "Processing log line: '" << text << "'.";
115 bag->Set("time", time);
117 size_t colon = text.FindFirstOf(':');
118 size_t colon_offset = colon - 13;
120 String type = String(text.SubStr(13, colon_offset)).Trim();
121 String options = String(text.SubStr(colon + 1)).Trim();
123 bag->Set("type", type);
124 bag->Set("options", options);
126 std::vector<String> tokens = options.Split(";");
128 /* set default values */
129 bag->Set("class", LogEntryClassInfo);
130 bag->Set("log_type", 0);
131 bag->Set("state", 0);
132 bag->Set("attempt", 0);
133 bag->Set("message", text); /* used as 'message' in log table, and 'log_output' in statehist table */
135 if (type.Contains("INITIAL HOST STATE") ||
136 type.Contains("CURRENT HOST STATE") ||
137 type.Contains("HOST ALERT")) {
138 if (tokens.size() < 5)
141 bag->Set("host_name", tokens[0]);
142 bag->Set("state", Host::StateFromString(tokens[1]));
143 bag->Set("state_type", tokens[2]);
144 bag->Set("attempt", atoi(tokens[3].CStr()));
145 bag->Set("plugin_output", tokens[4]);
147 if (type.Contains("INITIAL HOST STATE")) {
148 bag->Set("class", LogEntryClassState);
149 bag->Set("log_type", LogEntryTypeHostInitialState);
151 else if (type.Contains("CURRENT HOST STATE")) {
152 bag->Set("class", LogEntryClassState);
153 bag->Set("log_type", LogEntryTypeHostCurrentState);
156 bag->Set("class", LogEntryClassAlert);
157 bag->Set("log_type", LogEntryTypeHostAlert);
161 } else if (type.Contains("HOST DOWNTIME ALERT") || type.Contains("HOST FLAPPING ALERT")) {
162 if (tokens.size() < 3)
165 bag->Set("host_name", tokens[0]);
166 bag->Set("state_type", tokens[1]);
167 bag->Set("comment", tokens[2]);
169 if (type.Contains("HOST FLAPPING ALERT")) {
170 bag->Set("class", LogEntryClassAlert);
171 bag->Set("log_type", LogEntryTypeHostFlapping);
173 bag->Set("class", LogEntryClassAlert);
174 bag->Set("log_type", LogEntryTypeHostDowntimeAlert);
178 } else if (type.Contains("INITIAL SERVICE STATE") ||
179 type.Contains("CURRENT SERVICE STATE") ||
180 type.Contains("SERVICE ALERT")) {
181 if (tokens.size() < 6)
184 bag->Set("host_name", tokens[0]);
185 bag->Set("service_description", tokens[1]);
186 bag->Set("state", Service::StateFromString(tokens[2]));
187 bag->Set("state_type", tokens[3]);
188 bag->Set("attempt", atoi(tokens[4].CStr()));
189 bag->Set("plugin_output", tokens[5]);
191 if (type.Contains("INITIAL SERVICE STATE")) {
192 bag->Set("class", LogEntryClassState);
193 bag->Set("log_type", LogEntryTypeServiceInitialState);
195 else if (type.Contains("CURRENT SERVICE STATE")) {
196 bag->Set("class", LogEntryClassState);
197 bag->Set("log_type", LogEntryTypeServiceCurrentState);
200 bag->Set("class", LogEntryClassAlert);
201 bag->Set("log_type", LogEntryTypeServiceAlert);
205 } else if (type.Contains("SERVICE DOWNTIME ALERT") ||
206 type.Contains("SERVICE FLAPPING ALERT")) {
207 if (tokens.size() < 4)
210 bag->Set("host_name", tokens[0]);
211 bag->Set("service_description", tokens[1]);
212 bag->Set("state_type", tokens[2]);
213 bag->Set("comment", tokens[3]);
215 if (type.Contains("SERVICE FLAPPING ALERT")) {
216 bag->Set("class", LogEntryClassAlert);
217 bag->Set("log_type", LogEntryTypeServiceFlapping);
219 bag->Set("class", LogEntryClassAlert);
220 bag->Set("log_type", LogEntryTypeServiceDowntimeAlert);
224 } else if (type.Contains("TIMEPERIOD TRANSITION")) {
225 if (tokens.size() < 4)
228 bag->Set("class", LogEntryClassState);
229 bag->Set("log_type", LogEntryTypeTimeperiodTransition);
231 bag->Set("host_name", tokens[0]);
232 bag->Set("service_description", tokens[1]);
233 bag->Set("state_type", tokens[2]);
234 bag->Set("comment", tokens[3]);
235 } else if (type.Contains("HOST NOTIFICATION")) {
236 if (tokens.size() < 6)
239 bag->Set("contact_name", tokens[0]);
240 bag->Set("host_name", tokens[1]);
241 bag->Set("state_type", tokens[2].CStr());
242 bag->Set("state", Service::StateFromString(tokens[3]));
243 bag->Set("command_name", tokens[4]);
244 bag->Set("plugin_output", tokens[5]);
246 bag->Set("class", LogEntryClassNotification);
247 bag->Set("log_type", LogEntryTypeHostNotification);
250 } else if (type.Contains("SERVICE NOTIFICATION")) {
251 if (tokens.size() < 7)
254 bag->Set("contact_name", tokens[0]);
255 bag->Set("host_name", tokens[1]);
256 bag->Set("service_description", tokens[2]);
257 bag->Set("state_type", tokens[3].CStr());
258 bag->Set("state", Service::StateFromString(tokens[4]));
259 bag->Set("command_name", tokens[5]);
260 bag->Set("plugin_output", tokens[6]);
262 bag->Set("class", LogEntryClassNotification);
263 bag->Set("log_type", LogEntryTypeServiceNotification);
266 } else if (type.Contains("PASSIVE HOST CHECK")) {
267 if (tokens.size() < 3)
270 bag->Set("host_name", tokens[0]);
271 bag->Set("state", Host::StateFromString(tokens[1]));
272 bag->Set("plugin_output", tokens[2]);
274 bag->Set("class", LogEntryClassPassive);
277 } else if (type.Contains("PASSIVE SERVICE CHECK")) {
278 if (tokens.size() < 4)
281 bag->Set("host_name", tokens[0]);
282 bag->Set("service_description", tokens[1]);
283 bag->Set("state", Host::StateFromString(tokens[2]));
284 bag->Set("plugin_output", tokens[3]);
286 bag->Set("class", LogEntryClassPassive);
289 } else if (type.Contains("EXTERNAL COMMAND")) {
290 bag->Set("class", LogEntryClassCommand);
291 /* string processing not implemented in 1.x */
294 } else if (type.Contains("LOG VERSION")) {
295 bag->Set("class", LogEntryClassProgram);
296 bag->Set("log_type", LogEntryTypeVersion);
299 } else if (type.Contains("logging initial states")) {
300 bag->Set("class", LogEntryClassProgram);
301 bag->Set("log_type", LogEntryTypeInitialStates);
304 } else if (type.Contains("starting... (PID=")) {
305 bag->Set("class", LogEntryClassProgram);
306 bag->Set("log_type", LogEntryTypeProgramStarting);
311 else if (type.Contains("restarting...") ||
312 type.Contains("shutting down...") ||
313 type.Contains("Bailing out") ||
314 type.Contains("active mode...") ||
315 type.Contains("standby mode...")) {
316 bag->Set("class", LogEntryClassProgram);