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/algorithm/string.hpp>
14 #include <boost/algorithm/string/replace.hpp>
15 #include <boost/algorithm/string/predicate.hpp>
18 using namespace icinga;
20 void LivestatusLogUtility::CreateLogIndex(const String& path, std::map<time_t, String>& index)
22 Utility::Glob(path + "/icinga.log", std::bind(&LivestatusLogUtility::CreateLogIndexFileHandler, _1, std::ref(index)), GlobFile);
23 Utility::Glob(path + "/archives/*.log", std::bind(&LivestatusLogUtility::CreateLogIndexFileHandler, _1, std::ref(index)), GlobFile);
26 void LivestatusLogUtility::CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index)
29 stream.open(path.CStr(), std::ifstream::in);
32 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open log file: " + path));
34 /* read the first bytes to get the timestamp: [123456789] */
37 stream.read(buffer, 12);
39 if (buffer[0] != '[' || buffer[11] != ']') {
40 /* this can happen for directories too, silently ignore them */
44 /* extract timestamp */
46 time_t ts_start = atoi(buffer+1);
50 Log(LogDebug, "LivestatusLogUtility")
51 << "Indexing log file: '" << path << "' with timestamp start: '" << ts_start << "'.";
53 index[ts_start] = path;
56 void LivestatusLogUtility::CreateLogCache(std::map<time_t, String> index, HistoryTable *table,
57 time_t from, time_t until, const AddRowFunction& addRowFn)
61 /* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
62 unsigned long line_count = 0;
63 for (const auto& kv : index) {
64 unsigned int ts = kv.first;
66 /* skip log files not in range (performance optimization) */
67 if (ts < from || ts > until)
70 String log_file = index[ts];
74 fp.exceptions(std::ifstream::badbit);
75 fp.open(log_file.CStr(), std::ifstream::in);
79 std::getline(fp, line);
82 continue; /* Ignore empty lines */
84 Dictionary::Ptr log_entry_attrs = LivestatusLogUtility::GetAttributes(line);
86 /* no attributes available - invalid log line */
87 if (!log_entry_attrs) {
88 Log(LogDebug, "LivestatusLogUtility")
89 << "Skipping invalid log line: '" << line << "'.";
93 table->UpdateLogEntries(log_entry_attrs, line_count, lineno, addRowFn);
103 Dictionary::Ptr LivestatusLogUtility::GetAttributes(const String& text)
105 Dictionary::Ptr bag = new Dictionary();
108 * [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
110 unsigned long time = atoi(text.SubStr(1, 11).CStr());
112 Log(LogDebug, "LivestatusLogUtility")
113 << "Processing log line: '" << text << "'.";
114 bag->Set("time", time);
116 size_t colon = text.FindFirstOf(':');
117 size_t colon_offset = colon - 13;
119 String type = String(text.SubStr(13, colon_offset)).Trim();
120 String options = String(text.SubStr(colon + 1)).Trim();
122 bag->Set("type", type);
123 bag->Set("options", options);
125 std::vector<String> tokens = options.Split(";");
127 /* set default values */
128 bag->Set("class", LogEntryClassInfo);
129 bag->Set("log_type", 0);
130 bag->Set("state", 0);
131 bag->Set("attempt", 0);
132 bag->Set("message", text); /* used as 'message' in log table, and 'log_output' in statehist table */
134 if (type.Contains("INITIAL HOST STATE") ||
135 type.Contains("CURRENT HOST STATE") ||
136 type.Contains("HOST ALERT")) {
137 if (tokens.size() < 5)
140 bag->Set("host_name", tokens[0]);
141 bag->Set("state", Host::StateFromString(tokens[1]));
142 bag->Set("state_type", tokens[2]);
143 bag->Set("attempt", atoi(tokens[3].CStr()));
144 bag->Set("plugin_output", tokens[4]);
146 if (type.Contains("INITIAL HOST STATE")) {
147 bag->Set("class", LogEntryClassState);
148 bag->Set("log_type", LogEntryTypeHostInitialState);
150 else if (type.Contains("CURRENT HOST STATE")) {
151 bag->Set("class", LogEntryClassState);
152 bag->Set("log_type", LogEntryTypeHostCurrentState);
155 bag->Set("class", LogEntryClassAlert);
156 bag->Set("log_type", LogEntryTypeHostAlert);
160 } else if (type.Contains("HOST DOWNTIME ALERT") || type.Contains("HOST FLAPPING ALERT")) {
161 if (tokens.size() < 3)
164 bag->Set("host_name", tokens[0]);
165 bag->Set("state_type", tokens[1]);
166 bag->Set("comment", tokens[2]);
168 if (type.Contains("HOST FLAPPING ALERT")) {
169 bag->Set("class", LogEntryClassAlert);
170 bag->Set("log_type", LogEntryTypeHostFlapping);
172 bag->Set("class", LogEntryClassAlert);
173 bag->Set("log_type", LogEntryTypeHostDowntimeAlert);
177 } else if (type.Contains("INITIAL SERVICE STATE") ||
178 type.Contains("CURRENT SERVICE STATE") ||
179 type.Contains("SERVICE ALERT")) {
180 if (tokens.size() < 6)
183 bag->Set("host_name", tokens[0]);
184 bag->Set("service_description", tokens[1]);
185 bag->Set("state", Service::StateFromString(tokens[2]));
186 bag->Set("state_type", tokens[3]);
187 bag->Set("attempt", atoi(tokens[4].CStr()));
188 bag->Set("plugin_output", tokens[5]);
190 if (type.Contains("INITIAL SERVICE STATE")) {
191 bag->Set("class", LogEntryClassState);
192 bag->Set("log_type", LogEntryTypeServiceInitialState);
194 else if (type.Contains("CURRENT SERVICE STATE")) {
195 bag->Set("class", LogEntryClassState);
196 bag->Set("log_type", LogEntryTypeServiceCurrentState);
199 bag->Set("class", LogEntryClassAlert);
200 bag->Set("log_type", LogEntryTypeServiceAlert);
204 } else if (type.Contains("SERVICE DOWNTIME ALERT") ||
205 type.Contains("SERVICE FLAPPING ALERT")) {
206 if (tokens.size() < 4)
209 bag->Set("host_name", tokens[0]);
210 bag->Set("service_description", tokens[1]);
211 bag->Set("state_type", tokens[2]);
212 bag->Set("comment", tokens[3]);
214 if (type.Contains("SERVICE FLAPPING ALERT")) {
215 bag->Set("class", LogEntryClassAlert);
216 bag->Set("log_type", LogEntryTypeServiceFlapping);
218 bag->Set("class", LogEntryClassAlert);
219 bag->Set("log_type", LogEntryTypeServiceDowntimeAlert);
223 } else if (type.Contains("TIMEPERIOD TRANSITION")) {
224 if (tokens.size() < 4)
227 bag->Set("class", LogEntryClassState);
228 bag->Set("log_type", LogEntryTypeTimeperiodTransition);
230 bag->Set("host_name", tokens[0]);
231 bag->Set("service_description", tokens[1]);
232 bag->Set("state_type", tokens[2]);
233 bag->Set("comment", tokens[3]);
234 } else if (type.Contains("HOST NOTIFICATION")) {
235 if (tokens.size() < 6)
238 bag->Set("contact_name", tokens[0]);
239 bag->Set("host_name", tokens[1]);
240 bag->Set("state_type", tokens[2].CStr());
241 bag->Set("state", Service::StateFromString(tokens[3]));
242 bag->Set("command_name", tokens[4]);
243 bag->Set("plugin_output", tokens[5]);
245 bag->Set("class", LogEntryClassNotification);
246 bag->Set("log_type", LogEntryTypeHostNotification);
249 } else if (type.Contains("SERVICE NOTIFICATION")) {
250 if (tokens.size() < 7)
253 bag->Set("contact_name", tokens[0]);
254 bag->Set("host_name", tokens[1]);
255 bag->Set("service_description", tokens[2]);
256 bag->Set("state_type", tokens[3].CStr());
257 bag->Set("state", Service::StateFromString(tokens[4]));
258 bag->Set("command_name", tokens[5]);
259 bag->Set("plugin_output", tokens[6]);
261 bag->Set("class", LogEntryClassNotification);
262 bag->Set("log_type", LogEntryTypeServiceNotification);
265 } else if (type.Contains("PASSIVE HOST CHECK")) {
266 if (tokens.size() < 3)
269 bag->Set("host_name", tokens[0]);
270 bag->Set("state", Host::StateFromString(tokens[1]));
271 bag->Set("plugin_output", tokens[2]);
273 bag->Set("class", LogEntryClassPassive);
276 } else if (type.Contains("PASSIVE SERVICE CHECK")) {
277 if (tokens.size() < 4)
280 bag->Set("host_name", tokens[0]);
281 bag->Set("service_description", tokens[1]);
282 bag->Set("state", Host::StateFromString(tokens[2]));
283 bag->Set("plugin_output", tokens[3]);
285 bag->Set("class", LogEntryClassPassive);
288 } else if (type.Contains("EXTERNAL COMMAND")) {
289 bag->Set("class", LogEntryClassCommand);
290 /* string processing not implemented in 1.x */
293 } else if (type.Contains("LOG VERSION")) {
294 bag->Set("class", LogEntryClassProgram);
295 bag->Set("log_type", LogEntryTypeVersion);
298 } else if (type.Contains("logging initial states")) {
299 bag->Set("class", LogEntryClassProgram);
300 bag->Set("log_type", LogEntryTypeInitialStates);
303 } else if (type.Contains("starting... (PID=")) {
304 bag->Set("class", LogEntryClassProgram);
305 bag->Set("log_type", LogEntryTypeProgramStarting);
310 else if (type.Contains("restarting...") ||
311 type.Contains("shutting down...") ||
312 type.Contains("Bailing out") ||
313 type.Contains("active mode...") ||
314 type.Contains("standby mode...")) {
315 bag->Set("class", LogEntryClassProgram);