]> granicus.if.org Git - icinga2/blob - lib/livestatus/livestatuslogutility.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / livestatus / livestatuslogutility.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
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>
17 #include <fstream>
18
19 using namespace icinga;
20
21 void LivestatusLogUtility::CreateLogIndex(const String& path, std::map<time_t, String>& index)
22 {
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);
25 }
26
27 void LivestatusLogUtility::CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index)
28 {
29         std::ifstream stream;
30         stream.open(path.CStr(), std::ifstream::in);
31
32         if (!stream)
33                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not open log file: " + path));
34
35         /* read the first bytes to get the timestamp: [123456789] */
36         char buffer[12];
37
38         stream.read(buffer, 12);
39
40         if (buffer[0] != '[' || buffer[11] != ']') {
41                 /* this can happen for directories too, silently ignore them */
42                 return;
43         }
44
45         /* extract timestamp */
46         buffer[11] = 0;
47         time_t ts_start = atoi(buffer+1);
48
49         stream.close();
50
51         Log(LogDebug, "LivestatusLogUtility")
52                 << "Indexing log file: '" << path << "' with timestamp start: '" << ts_start << "'.";
53
54         index[ts_start] = path;
55 }
56
57 void LivestatusLogUtility::CreateLogCache(std::map<time_t, String> index, HistoryTable *table,
58         time_t from, time_t until, const AddRowFunction& addRowFn)
59 {
60         ASSERT(table);
61
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;
66
67                 /* skip log files not in range (performance optimization) */
68                 if (ts < from || ts > until)
69                         continue;
70
71                 String log_file = index[ts];
72                 int lineno = 0;
73
74                 std::ifstream fp;
75                 fp.exceptions(std::ifstream::badbit);
76                 fp.open(log_file.CStr(), std::ifstream::in);
77
78                 while (fp.good()) {
79                         std::string line;
80                         std::getline(fp, line);
81
82                         if (line.empty())
83                                 continue; /* Ignore empty lines */
84
85                         Dictionary::Ptr log_entry_attrs = LivestatusLogUtility::GetAttributes(line);
86
87                         /* no attributes available - invalid log line */
88                         if (!log_entry_attrs) {
89                                 Log(LogDebug, "LivestatusLogUtility")
90                                         << "Skipping invalid log line: '" << line << "'.";
91                                 continue;
92                         }
93
94                         table->UpdateLogEntries(log_entry_attrs, line_count, lineno, addRowFn);
95
96                         line_count++;
97                         lineno++;
98                 }
99
100                 fp.close();
101         }
102 }
103
104 Dictionary::Ptr LivestatusLogUtility::GetAttributes(const String& text)
105 {
106         Dictionary::Ptr bag = new Dictionary();
107
108         /*
109          * [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
110          */
111         unsigned long time = atoi(text.SubStr(1, 11).CStr());
112
113         Log(LogDebug, "LivestatusLogUtility")
114                 << "Processing log line: '" << text << "'.";
115         bag->Set("time", time);
116
117         size_t colon = text.FindFirstOf(':');
118         size_t colon_offset = colon - 13;
119
120         String type = String(text.SubStr(13, colon_offset)).Trim();
121         String options = String(text.SubStr(colon + 1)).Trim();
122
123         bag->Set("type", type);
124         bag->Set("options", options);
125
126         std::vector<String> tokens = options.Split(";");
127
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 */
134
135         if (type.Contains("INITIAL HOST STATE") ||
136                 type.Contains("CURRENT HOST STATE") ||
137                 type.Contains("HOST ALERT")) {
138                 if (tokens.size() < 5)
139                         return bag;
140
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]);
146
147                 if (type.Contains("INITIAL HOST STATE")) {
148                         bag->Set("class", LogEntryClassState);
149                         bag->Set("log_type", LogEntryTypeHostInitialState);
150                 }
151                 else if (type.Contains("CURRENT HOST STATE")) {
152                         bag->Set("class", LogEntryClassState);
153                         bag->Set("log_type", LogEntryTypeHostCurrentState);
154                 }
155                 else {
156                         bag->Set("class", LogEntryClassAlert);
157                         bag->Set("log_type", LogEntryTypeHostAlert);
158                 }
159
160                 return bag;
161         } else if (type.Contains("HOST DOWNTIME ALERT") ||  type.Contains("HOST FLAPPING ALERT")) {
162                 if (tokens.size() < 3)
163                         return bag;
164
165                 bag->Set("host_name", tokens[0]);
166                 bag->Set("state_type", tokens[1]);
167                 bag->Set("comment", tokens[2]);
168
169                 if (type.Contains("HOST FLAPPING ALERT")) {
170                         bag->Set("class", LogEntryClassAlert);
171                         bag->Set("log_type", LogEntryTypeHostFlapping);
172                 } else {
173                         bag->Set("class", LogEntryClassAlert);
174                         bag->Set("log_type", LogEntryTypeHostDowntimeAlert);
175                 }
176
177                 return bag;
178         } else if (type.Contains("INITIAL SERVICE STATE") ||
179                 type.Contains("CURRENT SERVICE STATE") ||
180                 type.Contains("SERVICE ALERT")) {
181                 if (tokens.size() < 6)
182                         return bag;
183
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]);
190
191                 if (type.Contains("INITIAL SERVICE STATE")) {
192                         bag->Set("class", LogEntryClassState);
193                         bag->Set("log_type", LogEntryTypeServiceInitialState);
194                 }
195                 else if (type.Contains("CURRENT SERVICE STATE")) {
196                         bag->Set("class", LogEntryClassState);
197                         bag->Set("log_type", LogEntryTypeServiceCurrentState);
198                 }
199                 else {
200                         bag->Set("class", LogEntryClassAlert);
201                         bag->Set("log_type", LogEntryTypeServiceAlert);
202                 }
203
204                 return bag;
205         } else if (type.Contains("SERVICE DOWNTIME ALERT") ||
206                 type.Contains("SERVICE FLAPPING ALERT")) {
207                 if (tokens.size() < 4)
208                         return bag;
209
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]);
214
215                 if (type.Contains("SERVICE FLAPPING ALERT")) {
216                         bag->Set("class", LogEntryClassAlert);
217                         bag->Set("log_type", LogEntryTypeServiceFlapping);
218                 } else {
219                         bag->Set("class", LogEntryClassAlert);
220                         bag->Set("log_type", LogEntryTypeServiceDowntimeAlert);
221                 }
222
223                 return bag;
224         } else if (type.Contains("TIMEPERIOD TRANSITION")) {
225                 if (tokens.size() < 4)
226                         return bag;
227
228                 bag->Set("class", LogEntryClassState);
229                 bag->Set("log_type", LogEntryTypeTimeperiodTransition);
230
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)
237                         return bag;
238
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]);
245
246                 bag->Set("class", LogEntryClassNotification);
247                 bag->Set("log_type", LogEntryTypeHostNotification);
248
249                 return bag;
250         } else if (type.Contains("SERVICE NOTIFICATION")) {
251                 if (tokens.size() < 7)
252                         return bag;
253
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]);
261
262                 bag->Set("class", LogEntryClassNotification);
263                 bag->Set("log_type", LogEntryTypeServiceNotification);
264
265                 return bag;
266         } else if (type.Contains("PASSIVE HOST CHECK")) {
267                 if (tokens.size() < 3)
268                         return bag;
269
270                 bag->Set("host_name", tokens[0]);
271                 bag->Set("state", Host::StateFromString(tokens[1]));
272                 bag->Set("plugin_output", tokens[2]);
273
274                 bag->Set("class", LogEntryClassPassive);
275
276                 return bag;
277         } else if (type.Contains("PASSIVE SERVICE CHECK")) {
278                 if (tokens.size() < 4)
279                         return bag;
280
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]);
285
286                 bag->Set("class", LogEntryClassPassive);
287
288                 return bag;
289         } else if (type.Contains("EXTERNAL COMMAND")) {
290                 bag->Set("class", LogEntryClassCommand);
291                 /* string processing not implemented in 1.x */
292
293                 return bag;
294         } else if (type.Contains("LOG VERSION")) {
295                 bag->Set("class", LogEntryClassProgram);
296                 bag->Set("log_type", LogEntryTypeVersion);
297
298                 return bag;
299         } else if (type.Contains("logging initial states")) {
300                 bag->Set("class", LogEntryClassProgram);
301                 bag->Set("log_type", LogEntryTypeInitialStates);
302
303                 return bag;
304         } else if (type.Contains("starting... (PID=")) {
305                 bag->Set("class", LogEntryClassProgram);
306                 bag->Set("log_type", LogEntryTypeProgramStarting);
307
308                 return bag;
309         }
310         /* program */
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);
317
318                 return bag;
319         }
320
321         return bag;
322 }