diff options
Diffstat (limited to '')
-rw-r--r-- | lib/livestatus/livestatuslogutility.cpp | 321 |
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; +} |