summaryrefslogtreecommitdiffstats
path: root/lib/livestatus/livestatuslogutility.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/livestatus/livestatuslogutility.cpp')
-rw-r--r--lib/livestatus/livestatuslogutility.cpp321
1 files changed, 321 insertions, 0 deletions
diff --git a/lib/livestatus/livestatuslogutility.cpp b/lib/livestatus/livestatuslogutility.cpp
new file mode 100644
index 0000000..565c2ca
--- /dev/null
+++ b/lib/livestatus/livestatuslogutility.cpp
@@ -0,0 +1,321 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "livestatus/livestatuslogutility.hpp"
+#include "icinga/service.hpp"
+#include "icinga/host.hpp"
+#include "icinga/user.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/eventcommand.hpp"
+#include "icinga/notificationcommand.hpp"
+#include "base/utility.hpp"
+#include "base/convert.hpp"
+#include "base/logger.hpp"
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <fstream>
+
+using namespace icinga;
+
+void LivestatusLogUtility::CreateLogIndex(const String& path, std::map<time_t, String>& index)
+{
+ Utility::Glob(path + "/icinga.log", [&index](const String& newPath) { CreateLogIndexFileHandler(newPath, index); }, GlobFile);
+ Utility::Glob(path + "/archives/*.log", [&index](const String& newPath) { CreateLogIndexFileHandler(newPath, index); }, GlobFile);
+}
+
+void LivestatusLogUtility::CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index)
+{
+ std::ifstream stream;
+ stream.open(path.CStr(), std::ifstream::in);
+
+ if (!stream)
+ BOOST_THROW_EXCEPTION(std::runtime_error("Could not open log file: " + path));
+
+ /* read the first bytes to get the timestamp: [123456789] */
+ char buffer[12];
+
+ stream.read(buffer, 12);
+
+ if (buffer[0] != '[' || buffer[11] != ']') {
+ /* this can happen for directories too, silently ignore them */
+ return;
+ }
+
+ /* extract timestamp */
+ buffer[11] = 0;
+ time_t ts_start = atoi(buffer+1);
+
+ stream.close();
+
+ Log(LogDebug, "LivestatusLogUtility")
+ << "Indexing log file: '" << path << "' with timestamp start: '" << ts_start << "'.";
+
+ index[ts_start] = path;
+}
+
+void LivestatusLogUtility::CreateLogCache(std::map<time_t, String> index, HistoryTable *table,
+ time_t from, time_t until, const AddRowFunction& addRowFn)
+{
+ ASSERT(table);
+
+ /* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
+ unsigned long line_count = 0;
+ for (const auto& kv : index) {
+ unsigned int ts = kv.first;
+
+ /* skip log files not in range (performance optimization) */
+ if (ts < from || ts > until)
+ continue;
+
+ String log_file = index[ts];
+ int lineno = 0;
+
+ std::ifstream fp;
+ fp.exceptions(std::ifstream::badbit);
+ fp.open(log_file.CStr(), std::ifstream::in);
+
+ while (fp.good()) {
+ std::string line;
+ std::getline(fp, line);
+
+ if (line.empty())
+ continue; /* Ignore empty lines */
+
+ Dictionary::Ptr log_entry_attrs = LivestatusLogUtility::GetAttributes(line);
+
+ /* no attributes available - invalid log line */
+ if (!log_entry_attrs) {
+ Log(LogDebug, "LivestatusLogUtility")
+ << "Skipping invalid log line: '" << line << "'.";
+ continue;
+ }
+
+ table->UpdateLogEntries(log_entry_attrs, line_count, lineno, addRowFn);
+
+ line_count++;
+ lineno++;
+ }
+
+ fp.close();
+ }
+}
+
+Dictionary::Ptr LivestatusLogUtility::GetAttributes(const String& text)
+{
+ Dictionary::Ptr bag = new Dictionary();
+
+ /*
+ * [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
+ */
+ unsigned long time = atoi(text.SubStr(1, 11).CStr());
+
+ Log(LogDebug, "LivestatusLogUtility")
+ << "Processing log line: '" << text << "'.";
+ bag->Set("time", time);
+
+ size_t colon = text.FindFirstOf(':');
+ size_t colon_offset = colon - 13;
+
+ String type = String(text.SubStr(13, colon_offset)).Trim();
+ String options = String(text.SubStr(colon + 1)).Trim();
+
+ bag->Set("type", type);
+ bag->Set("options", options);
+
+ std::vector<String> tokens = options.Split(";");
+
+ /* set default values */
+ bag->Set("class", LogEntryClassInfo);
+ bag->Set("log_type", 0);
+ bag->Set("state", 0);
+ bag->Set("attempt", 0);
+ bag->Set("message", text); /* used as 'message' in log table, and 'log_output' in statehist table */
+
+ if (type.Contains("INITIAL HOST STATE") ||
+ type.Contains("CURRENT HOST STATE") ||
+ type.Contains("HOST ALERT")) {
+ if (tokens.size() < 5)
+ return bag;
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("state", Host::StateFromString(tokens[1]));
+ bag->Set("state_type", tokens[2]);
+ bag->Set("attempt", atoi(tokens[3].CStr()));
+ bag->Set("plugin_output", tokens[4]);
+
+ if (type.Contains("INITIAL HOST STATE")) {
+ bag->Set("class", LogEntryClassState);
+ bag->Set("log_type", LogEntryTypeHostInitialState);
+ }
+ else if (type.Contains("CURRENT HOST STATE")) {
+ bag->Set("class", LogEntryClassState);
+ bag->Set("log_type", LogEntryTypeHostCurrentState);
+ }
+ else {
+ bag->Set("class", LogEntryClassAlert);
+ bag->Set("log_type", LogEntryTypeHostAlert);
+ }
+
+ return bag;
+ } else if (type.Contains("HOST DOWNTIME ALERT") || type.Contains("HOST FLAPPING ALERT")) {
+ if (tokens.size() < 3)
+ return bag;
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("state_type", tokens[1]);
+ bag->Set("comment", tokens[2]);
+
+ if (type.Contains("HOST FLAPPING ALERT")) {
+ bag->Set("class", LogEntryClassAlert);
+ bag->Set("log_type", LogEntryTypeHostFlapping);
+ } else {
+ bag->Set("class", LogEntryClassAlert);
+ bag->Set("log_type", LogEntryTypeHostDowntimeAlert);
+ }
+
+ return bag;
+ } else if (type.Contains("INITIAL SERVICE STATE") ||
+ type.Contains("CURRENT SERVICE STATE") ||
+ type.Contains("SERVICE ALERT")) {
+ if (tokens.size() < 6)
+ return bag;
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("service_description", tokens[1]);
+ bag->Set("state", Service::StateFromString(tokens[2]));
+ bag->Set("state_type", tokens[3]);
+ bag->Set("attempt", atoi(tokens[4].CStr()));
+ bag->Set("plugin_output", tokens[5]);
+
+ if (type.Contains("INITIAL SERVICE STATE")) {
+ bag->Set("class", LogEntryClassState);
+ bag->Set("log_type", LogEntryTypeServiceInitialState);
+ }
+ else if (type.Contains("CURRENT SERVICE STATE")) {
+ bag->Set("class", LogEntryClassState);
+ bag->Set("log_type", LogEntryTypeServiceCurrentState);
+ }
+ else {
+ bag->Set("class", LogEntryClassAlert);
+ bag->Set("log_type", LogEntryTypeServiceAlert);
+ }
+
+ return bag;
+ } else if (type.Contains("SERVICE DOWNTIME ALERT") ||
+ type.Contains("SERVICE FLAPPING ALERT")) {
+ if (tokens.size() < 4)
+ return bag;
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("service_description", tokens[1]);
+ bag->Set("state_type", tokens[2]);
+ bag->Set("comment", tokens[3]);
+
+ if (type.Contains("SERVICE FLAPPING ALERT")) {
+ bag->Set("class", LogEntryClassAlert);
+ bag->Set("log_type", LogEntryTypeServiceFlapping);
+ } else {
+ bag->Set("class", LogEntryClassAlert);
+ bag->Set("log_type", LogEntryTypeServiceDowntimeAlert);
+ }
+
+ return bag;
+ } else if (type.Contains("TIMEPERIOD TRANSITION")) {
+ if (tokens.size() < 4)
+ return bag;
+
+ bag->Set("class", LogEntryClassState);
+ bag->Set("log_type", LogEntryTypeTimeperiodTransition);
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("service_description", tokens[1]);
+ bag->Set("state_type", tokens[2]);
+ bag->Set("comment", tokens[3]);
+ } else if (type.Contains("HOST NOTIFICATION")) {
+ if (tokens.size() < 6)
+ return bag;
+
+ bag->Set("contact_name", tokens[0]);
+ bag->Set("host_name", tokens[1]);
+ bag->Set("state_type", tokens[2].CStr());
+ bag->Set("state", Service::StateFromString(tokens[3]));
+ bag->Set("command_name", tokens[4]);
+ bag->Set("plugin_output", tokens[5]);
+
+ bag->Set("class", LogEntryClassNotification);
+ bag->Set("log_type", LogEntryTypeHostNotification);
+
+ return bag;
+ } else if (type.Contains("SERVICE NOTIFICATION")) {
+ if (tokens.size() < 7)
+ return bag;
+
+ bag->Set("contact_name", tokens[0]);
+ bag->Set("host_name", tokens[1]);
+ bag->Set("service_description", tokens[2]);
+ bag->Set("state_type", tokens[3].CStr());
+ bag->Set("state", Service::StateFromString(tokens[4]));
+ bag->Set("command_name", tokens[5]);
+ bag->Set("plugin_output", tokens[6]);
+
+ bag->Set("class", LogEntryClassNotification);
+ bag->Set("log_type", LogEntryTypeServiceNotification);
+
+ return bag;
+ } else if (type.Contains("PASSIVE HOST CHECK")) {
+ if (tokens.size() < 3)
+ return bag;
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("state", Host::StateFromString(tokens[1]));
+ bag->Set("plugin_output", tokens[2]);
+
+ bag->Set("class", LogEntryClassPassive);
+
+ return bag;
+ } else if (type.Contains("PASSIVE SERVICE CHECK")) {
+ if (tokens.size() < 4)
+ return bag;
+
+ bag->Set("host_name", tokens[0]);
+ bag->Set("service_description", tokens[1]);
+ bag->Set("state", Host::StateFromString(tokens[2]));
+ bag->Set("plugin_output", tokens[3]);
+
+ bag->Set("class", LogEntryClassPassive);
+
+ return bag;
+ } else if (type.Contains("EXTERNAL COMMAND")) {
+ bag->Set("class", LogEntryClassCommand);
+ /* string processing not implemented in 1.x */
+
+ return bag;
+ } else if (type.Contains("LOG VERSION")) {
+ bag->Set("class", LogEntryClassProgram);
+ bag->Set("log_type", LogEntryTypeVersion);
+
+ return bag;
+ } else if (type.Contains("logging initial states")) {
+ bag->Set("class", LogEntryClassProgram);
+ bag->Set("log_type", LogEntryTypeInitialStates);
+
+ return bag;
+ } else if (type.Contains("starting... (PID=")) {
+ bag->Set("class", LogEntryClassProgram);
+ bag->Set("log_type", LogEntryTypeProgramStarting);
+
+ return bag;
+ }
+ /* program */
+ else if (type.Contains("restarting...") ||
+ type.Contains("shutting down...") ||
+ type.Contains("Bailing out") ||
+ type.Contains("active mode...") ||
+ type.Contains("standby mode...")) {
+ bag->Set("class", LogEntryClassProgram);
+
+ return bag;
+ }
+
+ return bag;
+}