]> granicus.if.org Git - icinga2/blob - lib/livestatus/livestatuslogutility.cpp
Update copyright year
[icinga2] / lib / livestatus / livestatuslogutility.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "livestatus/livestatuslogutility.hpp"
21 #include "icinga/service.hpp"
22 #include "icinga/host.hpp"
23 #include "icinga/user.hpp"
24 #include "icinga/checkcommand.hpp"
25 #include "icinga/eventcommand.hpp"
26 #include "icinga/notificationcommand.hpp"
27 #include "base/utility.hpp"
28 #include "base/convert.hpp"
29 #include "base/logger.hpp"
30 #include <boost/foreach.hpp>
31 #include <boost/tuple/tuple.hpp>
32 #include <boost/algorithm/string.hpp>
33 #include <boost/algorithm/string/split.hpp>
34 #include <boost/algorithm/string/classification.hpp>
35 #include <boost/algorithm/string/replace.hpp>
36 #include <boost/algorithm/string/predicate.hpp>
37 #include <fstream>
38
39 using namespace icinga;
40
41 void LivestatusLogUtility::CreateLogIndex(const String& path, std::map<time_t, String>& index)
42 {
43         Utility::Glob(path + "/icinga.log", boost::bind(&LivestatusLogUtility::CreateLogIndexFileHandler, _1, boost::ref(index)), GlobFile);
44         Utility::Glob(path + "/archives/*.log", boost::bind(&LivestatusLogUtility::CreateLogIndexFileHandler, _1, boost::ref(index)), GlobFile);
45 }
46
47 void LivestatusLogUtility::CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index)
48 {
49         std::ifstream stream;
50         stream.open(path.CStr(), std::ifstream::in);
51
52         if (!stream)
53                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open log file: " + path));
54
55         /* read the first bytes to get the timestamp: [123456789] */
56         char buffer[12];
57
58         stream.read(buffer, 12);
59
60         if (buffer[0] != '[' || buffer[11] != ']') {
61                 /* this can happen for directories too, silently ignore them */
62                 return;
63         }
64
65         /* extract timestamp */
66         buffer[11] = 0;
67         time_t ts_start = atoi(buffer+1);
68
69         stream.close();
70
71         Log(LogDebug, "LivestatusLogUtility")
72             << "Indexing log file: '" << path << "' with timestamp start: '" << ts_start << "'.";
73
74         index[ts_start] = path;
75 }
76
77 void LivestatusLogUtility::CreateLogCache(std::map<time_t, String> index, HistoryTable *table,
78     time_t from, time_t until, const AddRowFunction& addRowFn)
79 {
80         ASSERT(table);
81
82         /* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
83         unsigned int ts;
84         unsigned long line_count = 0;
85         BOOST_FOREACH(boost::tie(ts, boost::tuples::ignore), index) {
86                 /* skip log files not in range (performance optimization) */
87                 if (ts < from || ts > until)
88                         continue;
89
90                 String log_file = index[ts];
91                 int lineno = 0;
92
93                 std::ifstream fp;
94                 fp.exceptions(std::ifstream::badbit);
95                 fp.open(log_file.CStr(), std::ifstream::in);
96
97                 while (fp.good()) {
98                         std::string line;
99                         std::getline(fp, line);
100
101                         if (line.empty())
102                                 continue; /* Ignore empty lines */
103
104                         Dictionary::Ptr log_entry_attrs = LivestatusLogUtility::GetAttributes(line);
105
106                         /* no attributes available - invalid log line */
107                         if (!log_entry_attrs) {
108                                 Log(LogDebug, "LivestatusLogUtility")
109                                     << "Skipping invalid log line: '" << line << "'.";
110                                 continue;
111                         }
112
113                         table->UpdateLogEntries(log_entry_attrs, line_count, lineno, addRowFn);
114
115                         line_count++;
116                         lineno++;
117                 }
118
119                 fp.close();
120         }
121 }
122
123 Dictionary::Ptr LivestatusLogUtility::GetAttributes(const String& text)
124 {
125         Dictionary::Ptr bag = new Dictionary();
126
127         /*
128          * [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
129          */
130         unsigned long time = atoi(text.SubStr(1, 11).CStr());
131
132         Log(LogDebug, "LivestatusLogUtility")
133             << "Processing log line: '" << text << "'.";
134         bag->Set("time", time);
135
136         size_t colon = text.FindFirstOf(':');
137         size_t colon_offset = colon - 13;
138
139         String type = String(text.SubStr(13, colon_offset));
140         String options = String(text.SubStr(colon + 1));
141
142         type.Trim();
143         options.Trim();
144
145         bag->Set("type", type);
146         bag->Set("options", options);
147
148         std::vector<String> tokens;
149         boost::algorithm::split(tokens, options, boost::is_any_of(";"));
150
151         /* set default values */
152         bag->Set("log_class", LogEntryClassInfo);
153         bag->Set("log_type", 0);
154         bag->Set("state", 0);
155         bag->Set("attempt", 0);
156         bag->Set("message", text); /* used as 'message' in log table, and 'log_output' in statehist table */
157
158         if (type.Contains("INITIAL HOST STATE") ||
159             type.Contains("CURRENT HOST STATE") ||
160             type.Contains("HOST ALERT")) {
161                 if (tokens.size() < 5)
162                         return bag;
163
164                 bag->Set("host_name", tokens[0]);
165                 bag->Set("state", Host::StateFromString(tokens[1]));
166                 bag->Set("state_type", tokens[2]);
167                 bag->Set("attempt", atoi(tokens[3].CStr()));
168                 bag->Set("plugin_output", tokens[4]);
169
170                 if (type.Contains("INITIAL HOST STATE")) {
171                         bag->Set("log_class", LogEntryClassState);
172                         bag->Set("log_type", LogEntryTypeHostInitialState);
173                 }
174                 else if (type.Contains("CURRENT HOST STATE")) {
175                         bag->Set("log_class", LogEntryClassState);
176                         bag->Set("log_type", LogEntryTypeHostCurrentState);
177                 }
178                 else {
179                         bag->Set("log_class", LogEntryClassAlert);
180                         bag->Set("log_type", LogEntryTypeHostAlert);
181                 }
182
183                 return bag;
184         } else if (type.Contains("HOST DOWNTIME ALERT") ||
185                  type.Contains("HOST FLAPPING ALERT")) {
186                 if (tokens.size() < 3)
187                         return bag;
188
189                 bag->Set("host_name", tokens[0]);
190                 bag->Set("state_type", tokens[1]);
191                 bag->Set("comment", tokens[2]);
192
193                 if (type.Contains("HOST FLAPPING ALERT")) {
194                         bag->Set("log_class", LogEntryClassAlert);
195                         bag->Set("log_type", LogEntryTypeHostFlapping);
196                 } else {
197                         bag->Set("log_class", LogEntryClassAlert);
198                         bag->Set("log_type", LogEntryTypeHostDowntimeAlert);
199                 }
200
201                 return bag;
202         } else if (type.Contains("INITIAL SERVICE STATE") ||
203                  type.Contains("CURRENT SERVICE STATE") ||
204                  type.Contains("SERVICE ALERT")) {
205                 if (tokens.size() < 6)
206                         return bag;
207
208                 bag->Set("host_name", tokens[0]);
209                 bag->Set("service_description", tokens[1]);
210                 bag->Set("state", Service::StateFromString(tokens[2]));
211                 bag->Set("state_type", tokens[3]);
212                 bag->Set("attempt", atoi(tokens[4].CStr()));
213                 bag->Set("plugin_output", tokens[5]);
214
215                 if (type.Contains("INITIAL SERVICE STATE")) {
216                         bag->Set("log_class", LogEntryClassState);
217                         bag->Set("log_type", LogEntryTypeServiceInitialState);
218                 }
219                 else if (type.Contains("CURRENT SERVICE STATE")) {
220                         bag->Set("log_class", LogEntryClassState);
221                         bag->Set("log_type", LogEntryTypeServiceCurrentState);
222                 }
223                 else {
224                         bag->Set("log_class", LogEntryClassAlert);
225                         bag->Set("log_type", LogEntryTypeServiceAlert);
226                 }
227
228                 return bag;
229         } else if (type.Contains("SERVICE DOWNTIME ALERT") ||
230                  type.Contains("SERVICE FLAPPING ALERT")) {
231                 if (tokens.size() < 4)
232                         return bag;
233
234                 bag->Set("host_name", tokens[0]);
235                 bag->Set("service_description", tokens[1]);
236                 bag->Set("state_type", tokens[2]);
237                 bag->Set("comment", tokens[3]);
238
239                 if (type.Contains("SERVICE FLAPPING ALERT")) {
240                         bag->Set("log_class", LogEntryClassAlert);
241                         bag->Set("log_type", LogEntryTypeServiceFlapping);
242                 } else {
243                         bag->Set("log_class", LogEntryClassAlert);
244                         bag->Set("log_type", LogEntryTypeServiceDowntimeAlert);
245                 }
246
247                 return bag;
248         } else if (type.Contains("TIMEPERIOD TRANSITION")) {
249                 if (tokens.size() < 4)
250                         return bag;
251
252                 bag->Set("log_class", LogEntryClassState);
253                 bag->Set("log_type", LogEntryTypeTimeperiodTransition);
254
255                 bag->Set("host_name", tokens[0]);
256                 bag->Set("service_description", tokens[1]);
257                 bag->Set("state_type", tokens[2]);
258                 bag->Set("comment", tokens[3]);
259         } else if (type.Contains("HOST NOTIFICATION")) {
260                 if (tokens.size() < 6)
261                         return bag;
262
263                 bag->Set("contact_name", tokens[0]);
264                 bag->Set("host_name", tokens[1]);
265                 bag->Set("state_type", tokens[2].CStr());
266                 bag->Set("state", Service::StateFromString(tokens[3]));
267                 bag->Set("command_name", tokens[4]);
268                 bag->Set("plugin_output", tokens[5]);
269
270                 bag->Set("log_class", LogEntryClassNotification);
271                 bag->Set("log_type", LogEntryTypeHostNotification);
272
273                 return bag;
274         } else if (type.Contains("SERVICE NOTIFICATION")) {
275                 if (tokens.size() < 7)
276                         return bag;
277
278                 bag->Set("contact_name", tokens[0]);
279                 bag->Set("host_name", tokens[1]);
280                 bag->Set("service_description", tokens[2]);
281                 bag->Set("state_type", tokens[3].CStr());
282                 bag->Set("state", Service::StateFromString(tokens[4]));
283                 bag->Set("command_name", tokens[5]);
284                 bag->Set("plugin_output", tokens[6]);
285
286                 bag->Set("log_class", LogEntryClassNotification);
287                 bag->Set("log_type", LogEntryTypeServiceNotification);
288
289                 return bag;
290         } else if (type.Contains("PASSIVE HOST CHECK")) {
291                 if (tokens.size() < 3)
292                         return bag;
293
294                 bag->Set("host_name", tokens[0]);
295                 bag->Set("state", Host::StateFromString(tokens[1]));
296                 bag->Set("plugin_output", tokens[2]);
297
298                 bag->Set("log_class", LogEntryClassPassive);
299
300                 return bag;
301         } else if (type.Contains("PASSIVE SERVICE CHECK")) {
302                 if (tokens.size() < 4)
303                         return bag;
304
305                 bag->Set("host_name", tokens[0]);
306                 bag->Set("service_description", tokens[1]);
307                 bag->Set("state", Host::StateFromString(tokens[2]));
308                 bag->Set("plugin_output", tokens[3]);
309
310                 bag->Set("log_class", LogEntryClassPassive);
311
312                 return bag;
313         } else if (type.Contains("EXTERNAL COMMAND")) {
314                 bag->Set("log_class", LogEntryClassCommand);
315                 /* string processing not implemented in 1.x */
316
317                 return bag;
318         } else if (type.Contains("LOG VERSION")) {
319                 bag->Set("log_class", LogEntryClassProgram);
320                 bag->Set("log_type", LogEntryTypeVersion);
321
322                 return bag;
323         } else if (type.Contains("logging initial states")) {
324                 bag->Set("log_class", LogEntryClassProgram);
325                 bag->Set("log_type", LogEntryTypeInitialStates);
326
327                 return bag;
328         } else if (type.Contains("starting... (PID=")) {
329                 bag->Set("log_class", LogEntryClassProgram);
330                 bag->Set("log_type", LogEntryTypeProgramStarting);
331
332                 return bag;
333         }
334         /* program */
335         else if (type.Contains("restarting...") ||
336                  type.Contains("shutting down...") ||
337                  type.Contains("Bailing out") ||
338                  type.Contains("active mode...") ||
339                  type.Contains("standby mode...")) {
340                 bag->Set("log_class", LogEntryClassProgram);
341
342                 return bag;
343         }
344
345         return bag;
346 }