mirror of https://github.com/Icinga/icinga2.git
parent
7fa9188df8
commit
88fa67c108
|
@ -19,7 +19,7 @@ mkclass_target(listener.ti listener.th)
|
|||
|
||||
mkembedconfig_target(livestatus-type.conf livestatus-type.cpp)
|
||||
|
||||
add_library(livestatus SHARED aggregator.cpp andfilter.cpp attributefilter.cpp avgaggregator.cpp column.cpp combinerfilter.cpp commandstable.cpp commentstable.cpp contactgroupstable.cpp contactstable.cpp countaggregator.cpp downtimestable.cpp filter.cpp hostgroupstable.cpp hoststable.cpp invavgaggregator.cpp invsumaggregator.cpp listener.cpp listener.th logtable.cpp maxaggregator.cpp minaggregator.cpp negatefilter.cpp orfilter.cpp query.cpp servicegroupstable.cpp servicestable.cpp statehisttable.cpp statustable.cpp stdaggregator.cpp sumaggregator.cpp table.cpp timeperiodstable.cpp livestatus-type.cpp)
|
||||
add_library(livestatus SHARED aggregator.cpp andfilter.cpp attributefilter.cpp avgaggregator.cpp column.cpp combinerfilter.cpp commandstable.cpp commentstable.cpp contactgroupstable.cpp contactstable.cpp countaggregator.cpp downtimestable.cpp filter.cpp hostgroupstable.cpp hoststable.cpp invavgaggregator.cpp invsumaggregator.cpp listener.cpp listener.th logutility.cpp logtable.cpp maxaggregator.cpp minaggregator.cpp negatefilter.cpp orfilter.cpp query.cpp servicegroupstable.cpp servicestable.cpp statehisttable.cpp statustable.cpp stdaggregator.cpp sumaggregator.cpp table.cpp timeperiodstable.cpp livestatus-type.cpp)
|
||||
|
||||
target_link_libraries(livestatus ${Boost_LIBRARIES} base config icinga)
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include "livestatus/logtable.h"
|
||||
#include "livestatus/logutility.h"
|
||||
#include "livestatus/hoststable.h"
|
||||
#include "livestatus/servicestable.h"
|
||||
#include "livestatus/contactstable.h"
|
||||
|
@ -55,70 +56,25 @@ LogTable::LogTable(const String& compat_log_path, const unsigned long& from, con
|
|||
m_TimeUntil = until;
|
||||
|
||||
/* create log file index */
|
||||
CreateLogIndex(compat_log_path);
|
||||
LogUtility::CreateLogIndex(compat_log_path, m_LogFileIndex);
|
||||
|
||||
/* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
|
||||
unsigned long ts;
|
||||
unsigned long line_count = 0;
|
||||
BOOST_FOREACH(boost::tie(ts, boost::tuples::ignore), m_LogFileIndex) {
|
||||
/* skip log files not in range (performance optimization) */
|
||||
if (ts < m_TimeFrom || ts > m_TimeUntil)
|
||||
continue;
|
||||
|
||||
String log_file = m_LogFileIndex[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 */
|
||||
/*
|
||||
* [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
|
||||
*/
|
||||
unsigned long time = atoi(line.substr(1, 11).c_str());
|
||||
|
||||
size_t colon = line.find_first_of(':');
|
||||
size_t colon_offset = colon - 13;
|
||||
|
||||
std::string type_str = line.substr(13, colon_offset);
|
||||
std::string options_str = line.substr(colon + 1);
|
||||
String type = String(type_str);
|
||||
String options = String(options_str);
|
||||
type.Trim();
|
||||
options.Trim();
|
||||
|
||||
Dictionary::Ptr bag = GetLogEntryAttributes(type, options);
|
||||
|
||||
if (!bag)
|
||||
continue;
|
||||
|
||||
bag->Set("time", time);
|
||||
bag->Set("lineno", lineno);
|
||||
bag->Set("message", String(line)); /* complete line */
|
||||
bag->Set("type", type);
|
||||
bag->Set("options", options);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_Mutex);
|
||||
m_RowsCache[line_count] = bag;
|
||||
}
|
||||
|
||||
line_count++;
|
||||
lineno++;
|
||||
}
|
||||
|
||||
fp.close();
|
||||
}
|
||||
/* generate log cache */
|
||||
LogUtility::CreateLogCache(m_LogFileIndex, this, from, until);
|
||||
|
||||
AddColumns(this);
|
||||
}
|
||||
|
||||
void LogTable::UpdateLogCache(const Dictionary::Ptr& bag, int line_count, int lineno)
|
||||
{
|
||||
/* additional attributes only for log table */
|
||||
bag->Set("lineno", lineno);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_Mutex);
|
||||
m_RowsCache[line_count] = bag;
|
||||
}
|
||||
}
|
||||
|
||||
void LogTable::AddColumns(Table *table, const String& prefix,
|
||||
const Column::ObjectAccessor& objectAccessor)
|
||||
{
|
||||
|
@ -289,282 +245,5 @@ Value LogTable::CommandNameAccessor(const Value& row)
|
|||
return static_cast<Dictionary::Ptr>(row)->Get("command_name");
|
||||
}
|
||||
|
||||
void LogTable::CreateLogIndex(const String& path)
|
||||
{
|
||||
Utility::Glob(path + "/icinga.log", boost::bind(&LogTable::CreateLogIndexFileHandler, _1, boost::ref(m_LogFileIndex)), GlobFile);
|
||||
Utility::Glob(path + "/archives/*.log", boost::bind(&LogTable::CreateLogIndexFileHandler, _1, boost::ref(m_LogFileIndex)), GlobFile);
|
||||
}
|
||||
|
||||
void LogTable::CreateLogIndexFileHandler(const String& path, std::map<unsigned long, 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;
|
||||
unsigned int ts_start = atoi(buffer+1);
|
||||
|
||||
stream.close();
|
||||
|
||||
Log(LogDebug, "livestatus", "Indexing log file: '" + path + "' with timestamp start: '" + Convert::ToString(ts_start) + "'.");
|
||||
|
||||
index[ts_start] = path;
|
||||
}
|
||||
|
||||
Dictionary::Ptr LogTable::GetLogEntryAttributes(const String& type, const String& options)
|
||||
{
|
||||
int log_class, log_type = 0;
|
||||
unsigned long state, attempt;
|
||||
String host_name, service_description, contact_name, command_name, comment, plugin_output, state_type;
|
||||
|
||||
std::vector<String> tokens;
|
||||
boost::algorithm::split(tokens, options, boost::is_any_of(";"));
|
||||
|
||||
/* States - TODO refactor */
|
||||
if (boost::algorithm::contains(type, "INITIAL HOST STATE")) {
|
||||
if (tokens.size() < 5)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassState;
|
||||
log_type = LogTypeHostInitialState;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
state_type = tokens[2];
|
||||
attempt = atoi(tokens[3].CStr());
|
||||
plugin_output = tokens[4];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "CURRENT HOST STATE")) {
|
||||
if (tokens.size() < 5)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassState;
|
||||
log_type = LogTypeHostCurrentState;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
state_type = tokens[2];
|
||||
attempt = atoi(tokens[3].CStr());
|
||||
plugin_output = tokens[4];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "HOST ALERT")) {
|
||||
if (tokens.size() < 5)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassAlert;
|
||||
log_type = LogTypeHostAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
state_type = tokens[2];
|
||||
attempt = atoi(tokens[3].CStr());
|
||||
plugin_output = tokens[4];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "HOST DOWNTIME ALERT")) {
|
||||
if (tokens.size() < 3)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassAlert;
|
||||
log_type = LogTypeHostDowntimeAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
state_type = tokens[1];
|
||||
comment = tokens[2];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "HOST FLAPPING ALERT")) {
|
||||
if (tokens.size() < 3)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassAlert;
|
||||
log_type = LogTypeHostFlapping;
|
||||
|
||||
host_name = tokens[0];
|
||||
state_type = tokens[1];
|
||||
comment = tokens[2];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "INITIAL SERVICE STATE")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassState;
|
||||
log_type = LogTypeServiceInitialState;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
state_type = tokens[3];
|
||||
attempt = atoi(tokens[4].CStr());
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "CURRENT SERVICE STATE")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassState;
|
||||
log_type = LogTypeServiceCurrentState;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
state_type = tokens[3];
|
||||
attempt = atoi(tokens[4].CStr());
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE ALERT")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassAlert;
|
||||
log_type = LogTypeServiceAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
state_type = tokens[3];
|
||||
attempt = atoi(tokens[4].CStr());
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE DOWNTIME ALERT")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassAlert;
|
||||
log_type = LogTypeServiceDowntimeAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state_type = tokens[2];
|
||||
comment = tokens[3];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE FLAPPING ALERT")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassAlert;
|
||||
log_type = LogTypeServiceFlapping;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state_type = tokens[2];
|
||||
comment = tokens[3];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "TIMEPERIOD TRANSITION")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassState;
|
||||
log_type = LogTypeTimeperiodTransition;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state_type = tokens[2];
|
||||
comment = tokens[3];
|
||||
}
|
||||
/* Notifications - TODO refactor */
|
||||
else if (boost::algorithm::contains(type, "HOST NOTIFICATION")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassNotification;
|
||||
log_type = LogTypeHostNotification;
|
||||
|
||||
contact_name = tokens[0];
|
||||
host_name = tokens[1];
|
||||
state_type = tokens[2];
|
||||
state = Host::StateFromString(tokens[3]);
|
||||
command_name = tokens[4];
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE NOTIFICATION")) {
|
||||
if (tokens.size() < 7)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassNotification;
|
||||
log_type = LogTypeHostNotification;
|
||||
|
||||
contact_name = tokens[0];
|
||||
host_name = tokens[1];
|
||||
service_description = tokens[2];
|
||||
state_type = tokens[3];
|
||||
state = Service::StateFromString(tokens[4]);
|
||||
command_name = tokens[5];
|
||||
plugin_output = tokens[6];
|
||||
}
|
||||
/* Passive Checks - TODO refactor */
|
||||
else if (boost::algorithm::contains(type, "PASSIVE HOST CHECK")) {
|
||||
if (tokens.size() < 3)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassPassive;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
plugin_output = tokens[2];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "PASSIVE SERVICE CHECK")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogClassPassive;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
plugin_output = tokens[3];
|
||||
}
|
||||
/* External Command - TODO refactor */
|
||||
else if (boost::algorithm::contains(type, "EXTERNAL COMMAND")) {
|
||||
log_class = LogClassCommand;
|
||||
/* string processing not implemented in 1.x */
|
||||
}
|
||||
/* normal text entries */
|
||||
else if (boost::algorithm::contains(type, "LOG VERSION")) {
|
||||
log_class = LogClassProgram;
|
||||
log_type = LogTypeVersion;
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "logging initial states")) {
|
||||
log_class = LogClassProgram;
|
||||
log_type = LogTypeInitialStates;
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "starting... (PID=")) {
|
||||
log_class = LogClassProgram;
|
||||
log_type = LogTypeProgramStarting;
|
||||
}
|
||||
/* program */
|
||||
else if (boost::algorithm::contains(type, "restarting...") ||
|
||||
boost::algorithm::contains(type, "shutting down...") ||
|
||||
boost::algorithm::contains(type, "Bailing out") ||
|
||||
boost::algorithm::contains(type, "active mode...") ||
|
||||
boost::algorithm::contains(type, "standby mode...")) {
|
||||
log_class = LogClassProgram;
|
||||
} else
|
||||
return Dictionary::Ptr();
|
||||
|
||||
Dictionary::Ptr bag = make_shared<Dictionary>();
|
||||
|
||||
bag->Set("class", log_class); /* 0 is the default if not populated */
|
||||
bag->Set("comment", comment);
|
||||
bag->Set("plugin_output", plugin_output);
|
||||
bag->Set("state", state);
|
||||
bag->Set("state_type", state_type);
|
||||
bag->Set("attempt", attempt);
|
||||
bag->Set("host_name", host_name);
|
||||
bag->Set("service_description", service_description);
|
||||
bag->Set("contact_name", contact_name);
|
||||
bag->Set("command_name", command_name);
|
||||
|
||||
return bag;
|
||||
}
|
||||
|
|
|
@ -28,36 +28,6 @@ using namespace icinga;
|
|||
namespace icinga
|
||||
{
|
||||
|
||||
enum LogType {
|
||||
LogTypeHostAlert,
|
||||
LogTypeHostDowntimeAlert,
|
||||
LogTypeHostFlapping,
|
||||
LogTypeHostNotification,
|
||||
LogTypeHostInitialState,
|
||||
LogTypeHostCurrentState,
|
||||
LogTypeServiceAlert,
|
||||
LogTypeServiceDowntimeAlert,
|
||||
LogTypeServiceFlapping,
|
||||
LogTypeServiceNotification,
|
||||
LogTypeServiceInitialState,
|
||||
LogTypeServiceCurrentState,
|
||||
LogTypeTimeperiodTransition,
|
||||
LogTypeVersion,
|
||||
LogTypeInitialStates,
|
||||
LogTypeProgramStarting
|
||||
};
|
||||
|
||||
enum LogClass {
|
||||
LogClassInfo = 0,
|
||||
LogClassAlert = 1,
|
||||
LogClassProgram = 2,
|
||||
LogClassNotification = 3,
|
||||
LogClassPassive = 4,
|
||||
LogClassCommand = 5,
|
||||
LogClassState = 6,
|
||||
LogClassText = 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup livestatus
|
||||
*/
|
||||
|
@ -73,6 +43,8 @@ public:
|
|||
|
||||
virtual String GetName(void) const;
|
||||
|
||||
void UpdateLogCache(const Dictionary::Ptr& bag, int line_count, int lineno);
|
||||
|
||||
protected:
|
||||
virtual void FetchRows(const AddRowFunction& addRowFn);
|
||||
|
||||
|
@ -96,18 +68,13 @@ protected:
|
|||
static Value HostNameAccessor(const Value& row);
|
||||
static Value ContactNameAccessor(const Value& row);
|
||||
static Value CommandNameAccessor(const Value& row);
|
||||
|
||||
|
||||
private:
|
||||
std::map<unsigned long, String> m_LogFileIndex;
|
||||
std::map<unsigned long, Dictionary::Ptr> m_RowsCache;
|
||||
unsigned long m_TimeFrom;
|
||||
unsigned long m_TimeUntil;
|
||||
std::map<unsigned int, String> m_LogFileIndex;
|
||||
std::map<unsigned int, Dictionary::Ptr> m_RowsCache;
|
||||
unsigned int m_TimeFrom;
|
||||
unsigned int m_TimeUntil;
|
||||
boost::mutex m_Mutex;
|
||||
|
||||
void CreateLogIndex(const String& path);
|
||||
static void CreateLogIndexFileHandler(const String& path, std::map<unsigned long, String>& index);
|
||||
void GetLogClassType(const String& text, int& log_class, int& log_type);
|
||||
Dictionary::Ptr GetLogEntryAttributes(const String& type, const String& options);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,365 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#include "livestatus/logutility.h"
|
||||
#include "icinga/service.h"
|
||||
#include "icinga/host.h"
|
||||
#include "icinga/user.h"
|
||||
#include "icinga/checkcommand.h"
|
||||
#include "icinga/eventcommand.h"
|
||||
#include "icinga/notificationcommand.h"
|
||||
#include "base/utility.h"
|
||||
#include "base/convert.h"
|
||||
#include "base/logger_fwd.h"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <fstream>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
void LogUtility::CreateLogIndex(const String& path, std::map<unsigned int, String>& index)
|
||||
{
|
||||
Utility::Glob(path + "/icinga.log", boost::bind(&LogUtility::CreateLogIndexFileHandler, _1, boost::ref(index)), GlobFile);
|
||||
Utility::Glob(path + "/archives/*.log", boost::bind(&LogUtility::CreateLogIndexFileHandler, _1, boost::ref(index)), GlobFile);
|
||||
}
|
||||
|
||||
void LogUtility::CreateLogIndexFileHandler(const String& path, std::map<unsigned int, 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;
|
||||
unsigned int ts_start = atoi(buffer+1);
|
||||
|
||||
stream.close();
|
||||
|
||||
Log(LogDebug, "livestatus", "Indexing log file: '" + path + "' with timestamp start: '" + Convert::ToString(ts_start) + "'.");
|
||||
|
||||
index[ts_start] = path;
|
||||
}
|
||||
|
||||
void LogUtility::CreateLogCache(std::map<unsigned int, String> index, Table *table,
|
||||
const unsigned int& from, const unsigned int& until)
|
||||
{
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
/* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
|
||||
unsigned int ts;
|
||||
unsigned long line_count = 0;
|
||||
BOOST_FOREACH(boost::tie(ts, boost::tuples::ignore), index) {
|
||||
/* 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 bag = LogUtility::GetAttributes(line);
|
||||
|
||||
/* no attributes available - invalid log line */
|
||||
if (!bag) {
|
||||
Log(LogDebug, "livestatus", "Skipping invalid log line: '" + line + "'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
table->UpdateLogCache(bag, line_count, lineno);
|
||||
|
||||
line_count++;
|
||||
lineno++;
|
||||
}
|
||||
|
||||
fp.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Dictionary::Ptr LogUtility::GetAttributes(const String& text)
|
||||
{
|
||||
Dictionary::Ptr bag = make_shared<Dictionary>();
|
||||
|
||||
/*
|
||||
* [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
|
||||
*/
|
||||
unsigned long time = atoi(text.SubStr(1, 11).CStr());
|
||||
|
||||
Log(LogDebug, "livestatus", "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));
|
||||
String options = String(text.SubStr(colon + 1));
|
||||
|
||||
type.Trim();
|
||||
options.Trim();
|
||||
|
||||
bag->Set("type", type);
|
||||
bag->Set("options", options);
|
||||
|
||||
std::vector<String> tokens;
|
||||
boost::algorithm::split(tokens, options, boost::is_any_of(";"));
|
||||
|
||||
/* set default values */
|
||||
bag->Set("log_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 */
|
||||
|
||||
/* Host States */
|
||||
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("log_class", LogEntryClassState);
|
||||
bag->Set("log_type", LogEntryTypeHostInitialState);
|
||||
}
|
||||
else if (type.Contains("CURRENT HOST STATE")) {
|
||||
bag->Set("log_class", LogEntryClassState);
|
||||
bag->Set("log_type", LogEntryTypeHostCurrentState);
|
||||
}
|
||||
else {
|
||||
bag->Set("log_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("log_class", LogEntryClassAlert);
|
||||
bag->Set("log_type", LogEntryTypeHostFlapping);
|
||||
} else {
|
||||
bag->Set("log_class", LogEntryClassAlert);
|
||||
bag->Set("log_type", LogEntryTypeHostDowntimeAlert);
|
||||
}
|
||||
|
||||
return bag;
|
||||
}
|
||||
/* Service States */
|
||||
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("log_class", LogEntryClassState);
|
||||
bag->Set("log_type", LogEntryTypeServiceInitialState);
|
||||
}
|
||||
else if (type.Contains("CURRENT SERVICE STATE")) {
|
||||
bag->Set("log_class", LogEntryClassState);
|
||||
bag->Set("log_type", LogEntryTypeServiceCurrentState);
|
||||
}
|
||||
else {
|
||||
bag->Set("log_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("log_class", LogEntryClassAlert);
|
||||
bag->Set("log_type", LogEntryTypeServiceFlapping);
|
||||
} else {
|
||||
bag->Set("log_class", LogEntryClassAlert);
|
||||
bag->Set("log_type", LogEntryTypeServiceDowntimeAlert);
|
||||
}
|
||||
|
||||
return bag;
|
||||
}
|
||||
/* Timeperiods */
|
||||
else if (type.Contains("TIMEPERIOD TRANSITION")) {
|
||||
if (tokens.size() < 4)
|
||||
return bag;
|
||||
|
||||
bag->Set("log_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]);
|
||||
}
|
||||
/* Notifications */
|
||||
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]);
|
||||
bag->Set("state", Service::StateFromString(tokens[3]));
|
||||
bag->Set("command_name", atoi(tokens[4].CStr()));
|
||||
bag->Set("plugin_output", tokens[5]);
|
||||
|
||||
bag->Set("log_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]);
|
||||
bag->Set("state", Service::StateFromString(tokens[4]));
|
||||
bag->Set("command_name", atoi(tokens[5].CStr()));
|
||||
bag->Set("plugin_output", tokens[6]);
|
||||
|
||||
bag->Set("log_class", LogEntryClassNotification);
|
||||
bag->Set("log_type", LogEntryTypeServiceNotification);
|
||||
|
||||
return bag;
|
||||
}
|
||||
/* Passive Checks */
|
||||
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("log_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("log_class", LogEntryClassPassive);
|
||||
|
||||
return bag;
|
||||
}
|
||||
/* External Command */
|
||||
else if (type.Contains("EXTERNAL COMMAND")) {
|
||||
bag->Set("log_class", LogEntryClassCommand);
|
||||
/* string processing not implemented in 1.x */
|
||||
|
||||
return bag;
|
||||
}
|
||||
/* normal text entries */
|
||||
else if (type.Contains("LOG VERSION")) {
|
||||
bag->Set("log_class", LogEntryClassProgram);
|
||||
bag->Set("log_type", LogEntryTypeVersion);
|
||||
|
||||
return bag;
|
||||
}
|
||||
else if (type.Contains("logging initial states")) {
|
||||
bag->Set("log_class", LogEntryClassProgram);
|
||||
bag->Set("log_type", LogEntryTypeInitialStates);
|
||||
|
||||
return bag;
|
||||
}
|
||||
else if (type.Contains("starting... (PID=")) {
|
||||
bag->Set("log_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("log_class", LogEntryClassProgram);
|
||||
|
||||
return bag;
|
||||
}
|
||||
|
||||
return bag;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LOGUTILITY_H
|
||||
#define LOGUTILITY_H
|
||||
|
||||
#include "livestatus/table.h"
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
enum LogEntryType {
|
||||
LogEntryTypeHostAlert,
|
||||
LogEntryTypeHostDowntimeAlert,
|
||||
LogEntryTypeHostFlapping,
|
||||
LogEntryTypeHostNotification,
|
||||
LogEntryTypeHostInitialState,
|
||||
LogEntryTypeHostCurrentState,
|
||||
LogEntryTypeServiceAlert,
|
||||
LogEntryTypeServiceDowntimeAlert,
|
||||
LogEntryTypeServiceFlapping,
|
||||
LogEntryTypeServiceNotification,
|
||||
LogEntryTypeServiceInitialState,
|
||||
LogEntryTypeServiceCurrentState,
|
||||
LogEntryTypeTimeperiodTransition,
|
||||
LogEntryTypeVersion,
|
||||
LogEntryTypeInitialStates,
|
||||
LogEntryTypeProgramStarting
|
||||
};
|
||||
|
||||
enum LogEntryClass {
|
||||
LogEntryClassInfo = 0,
|
||||
LogEntryClassAlert = 1,
|
||||
LogEntryClassProgram = 2,
|
||||
LogEntryClassNotification = 3,
|
||||
LogEntryClassPassive = 4,
|
||||
LogEntryClassCommand = 5,
|
||||
LogEntryClassState = 6,
|
||||
LogEntryClassText = 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup livestatus
|
||||
*/
|
||||
class LogUtility
|
||||
{
|
||||
|
||||
public:
|
||||
static void CreateLogIndex(const String& path, std::map<unsigned int, String>& index);
|
||||
static void CreateLogIndexFileHandler(const String& path, std::map<unsigned int, String>& index);
|
||||
static void CreateLogCache(std::map<unsigned int, String> index, Table *table, const unsigned int& from, const unsigned int& until);
|
||||
static Dictionary::Ptr GetAttributes(const String& text);
|
||||
|
||||
private:
|
||||
LogUtility(void);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LOGUTILITY_H */
|
|
@ -18,6 +18,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include "livestatus/statehisttable.h"
|
||||
#include "livestatus/logutility.h"
|
||||
#include "livestatus/hoststable.h"
|
||||
#include "livestatus/servicestable.h"
|
||||
#include "livestatus/contactstable.h"
|
||||
|
@ -56,209 +57,169 @@ StateHistTable::StateHistTable(const String& compat_log_path, const unsigned lon
|
|||
m_TimeUntil = until;
|
||||
|
||||
/* create log file index */
|
||||
CreateLogIndex(compat_log_path);
|
||||
LogUtility::CreateLogIndex(compat_log_path, m_LogFileIndex);
|
||||
|
||||
/* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */
|
||||
unsigned long ts;
|
||||
BOOST_FOREACH(boost::tie(ts, boost::tuples::ignore), m_LogFileIndex) {
|
||||
/* skip log files not in range (performance optimization) */
|
||||
if (ts < m_TimeFrom || ts > m_TimeUntil)
|
||||
continue;
|
||||
|
||||
String log_file = m_LogFileIndex[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 */
|
||||
/*
|
||||
* [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output
|
||||
*/
|
||||
unsigned long time = atoi(line.substr(1, 11).c_str());
|
||||
|
||||
size_t colon = line.find_first_of(':');
|
||||
size_t colon_offset = colon - 13;
|
||||
|
||||
std::string type_str = line.substr(13, colon_offset);
|
||||
std::string options_str = line.substr(colon + 1);
|
||||
String type = String(type_str);
|
||||
String options = String(options_str);
|
||||
String log_line = String(line);
|
||||
type.Trim();
|
||||
options.Trim();
|
||||
log_line.Trim();
|
||||
|
||||
Dictionary::Ptr bag = GetStateHistAttributes(type, options);
|
||||
|
||||
/* no attributes available - invalid log line */
|
||||
if (!bag) {
|
||||
//Log(LogDebug, "livestatus", "Skipping invalid statehist line: '" + log_line + "'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
String host_name = bag->Get("host_name");
|
||||
String service_description = bag->Get("service_description");
|
||||
unsigned long state = bag->Get("state");
|
||||
int log_class = bag->Get("log_class");
|
||||
int log_type = bag->Get("log_type");
|
||||
String state_type = bag->Get("state_type"); //SOFT, HARD, STARTED, STOPPED, ...
|
||||
|
||||
Service::Ptr state_hist_service;
|
||||
/* host alert == get service check */
|
||||
if (service_description.IsEmpty()) {
|
||||
Host::Ptr state_host = Host::GetByName(host_name);
|
||||
state_hist_service = state_host->GetCheckService();
|
||||
} else {
|
||||
/* assign service ptr as key */
|
||||
state_hist_service = Service::GetByNamePair(host_name, service_description);
|
||||
}
|
||||
|
||||
/* invalid log line for state history */
|
||||
if (!state_hist_service) {
|
||||
//Log(LogDebug, "livestatus", "Skipping invalid statehist line: '" + log_line + "'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Array::Ptr state_hist_service_states;
|
||||
Dictionary::Ptr state_hist_bag;
|
||||
unsigned long query_part = until - from;
|
||||
|
||||
/* insert new service states array with values if not existing */
|
||||
if (m_ServicesCache.find(state_hist_service) == m_ServicesCache.end()) {
|
||||
|
||||
/* create new values */
|
||||
state_hist_service_states = make_shared<Array>();
|
||||
state_hist_bag = make_shared<Dictionary>();
|
||||
|
||||
state_hist_bag->Set("host_name", state_hist_service->GetHost()->GetName());
|
||||
state_hist_bag->Set("service_description", state_hist_service->GetShortName());
|
||||
state_hist_bag->Set("state", state);
|
||||
state_hist_bag->Set("in_downtime", 0);
|
||||
state_hist_bag->Set("in_host_downtime", 0);
|
||||
state_hist_bag->Set("in_notification_period", 1); // assume "always"
|
||||
state_hist_bag->Set("is_flapping", 0);
|
||||
state_hist_bag->Set("time", time);
|
||||
state_hist_bag->Set("lineno", lineno);
|
||||
state_hist_bag->Set("log_output", log_line); /* complete line */
|
||||
state_hist_bag->Set("from", time); /* starting at current timestamp */
|
||||
state_hist_bag->Set("until", time); /* will be updated later on state change */
|
||||
state_hist_bag->Set("query_part", query_part); /* required for _part calculations */
|
||||
|
||||
state_hist_service_states->Add(state_hist_bag);
|
||||
|
||||
Log(LogDebug, "livestatus", "statehist: Adding new service '" + state_hist_service->GetName() + "' to services cache.");
|
||||
} else {
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_Mutex);
|
||||
state_hist_service_states = m_ServicesCache[state_hist_service];
|
||||
state_hist_bag = state_hist_service_states->Get(state_hist_service_states->GetLength()-1); /* fetch latest state from history */
|
||||
}
|
||||
|
||||
/* state duration */
|
||||
bool gone = state_hist_bag->Get("gone"); /* this requires initial state logging and setting it if not found TODO */
|
||||
|
||||
/* determine service notifications notification_period and compare against current timestamp */
|
||||
bool in_notification_period = true;
|
||||
String notification_period_name;
|
||||
BOOST_FOREACH(const Notification::Ptr& notification, state_hist_service->GetNotifications()) {
|
||||
TimePeriod::Ptr notification_period = notification->GetNotificationPeriod();
|
||||
|
||||
if (notification_period) {
|
||||
if (notification_period->IsInside(static_cast<double>(time)))
|
||||
in_notification_period = true;
|
||||
else
|
||||
in_notification_period = false;
|
||||
|
||||
notification_period_name = notification_period->GetName(); // last one wins
|
||||
} else
|
||||
in_notification_period = true; // assume "always"
|
||||
}
|
||||
|
||||
/* check for state changes, flapping & downtime start/end */
|
||||
switch (log_type) {
|
||||
case LogStateHistTypeHostAlert:
|
||||
case LogStateHistTypeHostInitialState:
|
||||
case LogStateHistTypeHostCurrentState:
|
||||
case LogStateHistTypeServiceAlert:
|
||||
case LogStateHistTypeServiceInitialState:
|
||||
case LogStateHistTypeServiceCurrentState:
|
||||
if (state != state_hist_bag->Get("state")) {
|
||||
/* 1. seal old state_hist_bag */
|
||||
state_hist_bag->Set("until", time); /* add until record for duration calculation */
|
||||
|
||||
/* 2. add new state_hist_bag */
|
||||
Dictionary::Ptr state_hist_bag_new = make_shared<Dictionary>();
|
||||
|
||||
state_hist_bag_new->Set("host_name", state_hist_bag->Get("host_name"));
|
||||
state_hist_bag_new->Set("service_description", state_hist_bag->Get("service_description"));
|
||||
state_hist_bag_new->Set("state", state);
|
||||
state_hist_bag_new->Set("in_downtime", state_hist_bag->Get("in_downtime")); // keep value from previous state!
|
||||
state_hist_bag_new->Set("in_host_downtime", state_hist_bag->Get("in_host_downtime")); // keep value from previous state!
|
||||
state_hist_bag_new->Set("in_notification_period", (in_notification_period ? 1 : 0));
|
||||
state_hist_bag_new->Set("notification_period", notification_period_name);
|
||||
state_hist_bag_new->Set("is_flapping", state_hist_bag->Get("is_flapping")); // keep value from previous state!
|
||||
state_hist_bag_new->Set("time", time);
|
||||
state_hist_bag_new->Set("lineno", lineno);
|
||||
state_hist_bag_new->Set("log_output", log_line); /* complete line */
|
||||
state_hist_bag_new->Set("from", time); /* starting at current timestamp */
|
||||
state_hist_bag_new->Set("until", time + 1); /* will be updated later */
|
||||
state_hist_bag_new->Set("query_part", query_part);
|
||||
|
||||
state_hist_service_states->Add(state_hist_bag_new);
|
||||
|
||||
Log(LogDebug, "livestatus", "statehist: State change detected for service '" +
|
||||
state_hist_service->GetName() + "' in '" + log_line + "'.");
|
||||
}
|
||||
break;
|
||||
case LogStateHistTypeHostFlapping:
|
||||
case LogStateHistTypeServiceFlapping:
|
||||
if (state_type == "STARTED")
|
||||
state_hist_bag->Set("is_flapping", 1);
|
||||
else if (state_type == "STOPPED" || state_type == "DISABLED")
|
||||
state_hist_bag->Set("is_flapping", 0);
|
||||
break;
|
||||
break;
|
||||
case LogStateHistTypeHostDowntimeAlert:
|
||||
case LogStateHistTypeServiceDowntimeAlert:
|
||||
if (state_type == "STARTED") {
|
||||
state_hist_bag->Set("in_downtime", 1);
|
||||
if (log_type == LogStateHistTypeHostDowntimeAlert)
|
||||
state_hist_bag->Set("in_host_downtime", 1);
|
||||
}
|
||||
else if (state_type == "STOPPED" || state_type == "CANCELLED") {
|
||||
state_hist_bag->Set("in_downtime", 0);
|
||||
if (log_type == LogStateHistTypeHostDowntimeAlert)
|
||||
state_hist_bag->Set("in_host_downtime", 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//nothing to update
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_Mutex);
|
||||
m_ServicesCache[state_hist_service] = state_hist_service_states;
|
||||
}
|
||||
|
||||
lineno++;
|
||||
}
|
||||
|
||||
fp.close();
|
||||
}
|
||||
/* generate log cache */
|
||||
LogUtility::CreateLogCache(m_LogFileIndex, this, from, until);
|
||||
|
||||
AddColumns(this);
|
||||
}
|
||||
|
||||
void StateHistTable::UpdateLogCache(const Dictionary::Ptr& bag, int line_count, int lineno)
|
||||
{
|
||||
unsigned int time = bag->Get("time");
|
||||
String host_name = bag->Get("host_name");
|
||||
String service_description = bag->Get("service_description");
|
||||
unsigned long state = bag->Get("state");
|
||||
int log_class = bag->Get("log_class");
|
||||
int log_type = bag->Get("log_type");
|
||||
String state_type = bag->Get("state_type"); //SOFT, HARD, STARTED, STOPPED, ...
|
||||
String log_line = bag->Get("message"); /* use message from log table */
|
||||
|
||||
Service::Ptr state_hist_service;
|
||||
|
||||
/* host alert == get service check */
|
||||
if (service_description.IsEmpty()) {
|
||||
Host::Ptr state_host = Host::GetByName(host_name);
|
||||
|
||||
if (!state_host)
|
||||
return;
|
||||
|
||||
state_hist_service = state_host->GetCheckService();
|
||||
} else {
|
||||
/* assign service ptr as key */
|
||||
state_hist_service = Service::GetByNamePair(host_name, service_description);
|
||||
}
|
||||
|
||||
/* invalid log line for state history */
|
||||
if (!state_hist_service)
|
||||
return;
|
||||
|
||||
Array::Ptr state_hist_service_states;
|
||||
Dictionary::Ptr state_hist_bag;
|
||||
unsigned long query_part = m_TimeUntil - m_TimeFrom;
|
||||
|
||||
/* insert new service states array with values if not existing */
|
||||
if (m_ServicesCache.find(state_hist_service) == m_ServicesCache.end()) {
|
||||
|
||||
/* create new values */
|
||||
state_hist_service_states = make_shared<Array>();
|
||||
state_hist_bag = make_shared<Dictionary>();
|
||||
|
||||
state_hist_bag->Set("host_name", state_hist_service->GetHost()->GetName());
|
||||
state_hist_bag->Set("service_description", state_hist_service->GetShortName());
|
||||
state_hist_bag->Set("state", state);
|
||||
state_hist_bag->Set("in_downtime", 0);
|
||||
state_hist_bag->Set("in_host_downtime", 0);
|
||||
state_hist_bag->Set("in_notification_period", 1); // assume "always"
|
||||
state_hist_bag->Set("is_flapping", 0);
|
||||
state_hist_bag->Set("time", time);
|
||||
state_hist_bag->Set("lineno", lineno);
|
||||
state_hist_bag->Set("log_output", log_line); /* complete line */
|
||||
state_hist_bag->Set("from", time); /* starting at current timestamp */
|
||||
state_hist_bag->Set("until", time); /* will be updated later on state change */
|
||||
state_hist_bag->Set("query_part", query_part); /* required for _part calculations */
|
||||
|
||||
state_hist_service_states->Add(state_hist_bag);
|
||||
|
||||
Log(LogDebug, "livestatus", "statehist: Adding new service '" + state_hist_service->GetName() + "' to services cache.");
|
||||
} else {
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_Mutex);
|
||||
state_hist_service_states = m_ServicesCache[state_hist_service];
|
||||
state_hist_bag = state_hist_service_states->Get(state_hist_service_states->GetLength()-1); /* fetch latest state from history */
|
||||
}
|
||||
|
||||
/* state duration */
|
||||
bool gone = state_hist_bag->Get("gone"); /* this requires initial state logging and setting it if not found TODO */
|
||||
|
||||
/* determine service notifications notification_period and compare against current timestamp */
|
||||
bool in_notification_period = true;
|
||||
String notification_period_name;
|
||||
BOOST_FOREACH(const Notification::Ptr& notification, state_hist_service->GetNotifications()) {
|
||||
TimePeriod::Ptr notification_period = notification->GetNotificationPeriod();
|
||||
|
||||
if (notification_period) {
|
||||
if (notification_period->IsInside(static_cast<double>(time)))
|
||||
in_notification_period = true;
|
||||
else
|
||||
in_notification_period = false;
|
||||
|
||||
notification_period_name = notification_period->GetName(); // last one wins
|
||||
} else
|
||||
in_notification_period = true; // assume "always"
|
||||
}
|
||||
|
||||
/* check for state changes, flapping & downtime start/end */
|
||||
switch (log_type) {
|
||||
case LogEntryTypeHostAlert:
|
||||
case LogEntryTypeHostInitialState:
|
||||
case LogEntryTypeHostCurrentState:
|
||||
case LogEntryTypeServiceAlert:
|
||||
case LogEntryTypeServiceInitialState:
|
||||
case LogEntryTypeServiceCurrentState:
|
||||
if (state != state_hist_bag->Get("state")) {
|
||||
/* 1. seal old state_hist_bag */
|
||||
state_hist_bag->Set("until", time); /* add until record for duration calculation */
|
||||
|
||||
/* 2. add new state_hist_bag */
|
||||
Dictionary::Ptr state_hist_bag_new = make_shared<Dictionary>();
|
||||
|
||||
state_hist_bag_new->Set("host_name", state_hist_bag->Get("host_name"));
|
||||
state_hist_bag_new->Set("service_description", state_hist_bag->Get("service_description"));
|
||||
state_hist_bag_new->Set("state", state);
|
||||
state_hist_bag_new->Set("in_downtime", state_hist_bag->Get("in_downtime")); // keep value from previous state!
|
||||
state_hist_bag_new->Set("in_host_downtime", state_hist_bag->Get("in_host_downtime")); // keep value from previous state!
|
||||
state_hist_bag_new->Set("in_notification_period", (in_notification_period ? 1 : 0));
|
||||
state_hist_bag_new->Set("notification_period", notification_period_name);
|
||||
state_hist_bag_new->Set("is_flapping", state_hist_bag->Get("is_flapping")); // keep value from previous state!
|
||||
state_hist_bag_new->Set("time", time);
|
||||
state_hist_bag_new->Set("lineno", lineno);
|
||||
state_hist_bag_new->Set("log_output", log_line); /* complete line */
|
||||
state_hist_bag_new->Set("from", time); /* starting at current timestamp */
|
||||
state_hist_bag_new->Set("until", time + 1); /* will be updated later */
|
||||
state_hist_bag_new->Set("query_part", query_part);
|
||||
|
||||
state_hist_service_states->Add(state_hist_bag_new);
|
||||
|
||||
Log(LogDebug, "livestatus", "statehist: State change detected for service '" +
|
||||
state_hist_service->GetName() + "' in '" + log_line + "'.");
|
||||
}
|
||||
break;
|
||||
case LogEntryTypeHostFlapping:
|
||||
case LogEntryTypeServiceFlapping:
|
||||
if (state_type == "STARTED")
|
||||
state_hist_bag->Set("is_flapping", 1);
|
||||
else if (state_type == "STOPPED" || state_type == "DISABLED")
|
||||
state_hist_bag->Set("is_flapping", 0);
|
||||
break;
|
||||
break;
|
||||
case LogEntryTypeHostDowntimeAlert:
|
||||
case LogEntryTypeServiceDowntimeAlert:
|
||||
if (state_type == "STARTED") {
|
||||
state_hist_bag->Set("in_downtime", 1);
|
||||
if (log_type == LogEntryTypeHostDowntimeAlert)
|
||||
state_hist_bag->Set("in_host_downtime", 1);
|
||||
}
|
||||
else if (state_type == "STOPPED" || state_type == "CANCELLED") {
|
||||
state_hist_bag->Set("in_downtime", 0);
|
||||
if (log_type == LogEntryTypeHostDowntimeAlert)
|
||||
state_hist_bag->Set("in_host_downtime", 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//nothing to update
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_Mutex);
|
||||
m_ServicesCache[state_hist_service] = state_hist_service_states;
|
||||
}
|
||||
}
|
||||
|
||||
void StateHistTable::AddColumns(Table *table, const String& prefix,
|
||||
const Column::ObjectAccessor& objectAccessor)
|
||||
{
|
||||
|
@ -514,208 +475,4 @@ Value StateHistTable::DurationPartUnmonitoredAccessor(const Value& row)
|
|||
return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StateHistTable::CreateLogIndex(const String& path)
|
||||
{
|
||||
Utility::Glob(path + "/icinga.log", boost::bind(&StateHistTable::CreateLogIndexFileHandler, _1, boost::ref(m_LogFileIndex)), GlobFile);
|
||||
Utility::Glob(path + "/archives/*.log", boost::bind(&StateHistTable::CreateLogIndexFileHandler, _1, boost::ref(m_LogFileIndex)), GlobFile);
|
||||
}
|
||||
|
||||
void StateHistTable::CreateLogIndexFileHandler(const String& path, std::map<unsigned long, 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;
|
||||
unsigned int ts_start = atoi(buffer+1);
|
||||
|
||||
stream.close();
|
||||
|
||||
Log(LogDebug, "livestatus", "Indexing log file: '" + path + "' with timestamp start: '" + Convert::ToString(ts_start) + "'.");
|
||||
|
||||
index[ts_start] = path;
|
||||
}
|
||||
|
||||
Dictionary::Ptr StateHistTable::GetStateHistAttributes(const String& type, const String& options)
|
||||
{
|
||||
int log_class, log_type = 0;
|
||||
unsigned long state, attempt;
|
||||
String host_name, service_description, contact_name, command_name, comment, plugin_output, state_type;
|
||||
|
||||
std::vector<String> tokens;
|
||||
boost::algorithm::split(tokens, options, boost::is_any_of(";"));
|
||||
|
||||
/* States - TODO refactor */
|
||||
if (boost::algorithm::contains(type, "INITIAL HOST STATE")) {
|
||||
if (tokens.size() < 5)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassState;
|
||||
log_type = LogStateHistTypeHostInitialState;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
state_type = tokens[2];
|
||||
attempt = atoi(tokens[3].CStr());
|
||||
plugin_output = tokens[4];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "CURRENT HOST STATE")) {
|
||||
if (tokens.size() < 5)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassState;
|
||||
log_type = LogStateHistTypeHostCurrentState;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
state_type = tokens[2];
|
||||
attempt = atoi(tokens[3].CStr());
|
||||
plugin_output = tokens[4];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "HOST ALERT")) {
|
||||
if (tokens.size() < 5)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassAlert;
|
||||
log_type = LogStateHistTypeHostAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
state = Host::StateFromString(tokens[1]);
|
||||
state_type = tokens[2];
|
||||
attempt = atoi(tokens[3].CStr());
|
||||
plugin_output = tokens[4];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "HOST DOWNTIME ALERT")) {
|
||||
if (tokens.size() < 3)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassAlert;
|
||||
log_type = LogStateHistTypeHostDowntimeAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
state_type = tokens[1];
|
||||
comment = tokens[2];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "HOST FLAPPING ALERT")) {
|
||||
if (tokens.size() < 3)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassAlert;
|
||||
log_type = LogStateHistTypeHostFlapping;
|
||||
|
||||
host_name = tokens[0];
|
||||
state_type = tokens[1];
|
||||
comment = tokens[2];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "INITIAL SERVICE STATE")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassState;
|
||||
log_type = LogStateHistTypeServiceInitialState;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
state_type = tokens[3];
|
||||
attempt = atoi(tokens[4].CStr());
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "CURRENT SERVICE STATE")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassState;
|
||||
log_type = LogStateHistTypeServiceCurrentState;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
state_type = tokens[3];
|
||||
attempt = atoi(tokens[4].CStr());
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE ALERT")) {
|
||||
if (tokens.size() < 6)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassAlert;
|
||||
log_type = LogStateHistTypeServiceAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state = Service::StateFromString(tokens[2]);
|
||||
state_type = tokens[3];
|
||||
attempt = atoi(tokens[4].CStr());
|
||||
plugin_output = tokens[5];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE DOWNTIME ALERT")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassAlert;
|
||||
log_type = LogStateHistTypeServiceDowntimeAlert;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state_type = tokens[2];
|
||||
comment = tokens[3];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "SERVICE FLAPPING ALERT")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassAlert;
|
||||
log_type = LogStateHistTypeServiceFlapping;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state_type = tokens[2];
|
||||
comment = tokens[3];
|
||||
}
|
||||
else if (boost::algorithm::contains(type, "TIMEPERIOD TRANSITION")) {
|
||||
if (tokens.size() < 4)
|
||||
return Dictionary::Ptr();
|
||||
|
||||
log_class = LogStateHistClassState;
|
||||
log_type = LogStateHistTypeTimeperiodTransition;
|
||||
|
||||
host_name = tokens[0];
|
||||
service_description = tokens[1];
|
||||
state_type = tokens[2];
|
||||
comment = tokens[3];
|
||||
}
|
||||
else
|
||||
return Dictionary::Ptr();
|
||||
|
||||
Dictionary::Ptr bag = make_shared<Dictionary>();
|
||||
|
||||
bag->Set("log_class", log_class); /* 0 is the default if not populated */
|
||||
bag->Set("log_type", log_type);
|
||||
bag->Set("comment", comment);
|
||||
bag->Set("plugin_output", plugin_output);
|
||||
bag->Set("state", state);
|
||||
bag->Set("state_type", state_type);
|
||||
bag->Set("attempt", attempt);
|
||||
bag->Set("host_name", host_name);
|
||||
bag->Set("service_description", service_description);
|
||||
bag->Set("contact_name", contact_name);
|
||||
bag->Set("command_name", command_name);
|
||||
|
||||
return bag;
|
||||
}
|
||||
}
|
|
@ -29,36 +29,6 @@ using namespace icinga;
|
|||
namespace icinga
|
||||
{
|
||||
|
||||
enum LogStateHistType {
|
||||
LogStateHistTypeHostAlert,
|
||||
LogStateHistTypeHostDowntimeAlert,
|
||||
LogStateHistTypeHostFlapping,
|
||||
LogStateHistTypeHostNotification,
|
||||
LogStateHistTypeHostInitialState,
|
||||
LogStateHistTypeHostCurrentState,
|
||||
LogStateHistTypeServiceAlert,
|
||||
LogStateHistTypeServiceDowntimeAlert,
|
||||
LogStateHistTypeServiceFlapping,
|
||||
LogStateHistTypeServiceNotification,
|
||||
LogStateHistTypeServiceInitialState,
|
||||
LogStateHistTypeServiceCurrentState,
|
||||
LogStateHistTypeTimeperiodTransition,
|
||||
LogStateHistTypeVersion,
|
||||
LogStateHistTypeInitialStates,
|
||||
LogStateHistTypeProgramStarting
|
||||
};
|
||||
|
||||
enum LogStateHistClass {
|
||||
LogStateHistClassInfo = 0,
|
||||
LogStateHistClassAlert = 1,
|
||||
LogStateHistClassProgram = 2,
|
||||
LogStateHistClassNotification = 3,
|
||||
LogStateHistClassPassive = 4,
|
||||
LogStateHistClassCommand = 5,
|
||||
LogStateHistClassState = 6,
|
||||
LogStateHistClassText = 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup livestatus
|
||||
*/
|
||||
|
@ -74,6 +44,8 @@ public:
|
|||
|
||||
virtual String GetName(void) const;
|
||||
|
||||
void UpdateLogCache(const Dictionary::Ptr& bag, int line_count, int lineno);
|
||||
|
||||
protected:
|
||||
virtual void FetchRows(const AddRowFunction& addRowFn);
|
||||
|
||||
|
@ -108,16 +80,11 @@ protected:
|
|||
static Value DurationPartUnmonitoredAccessor(const Value& row);
|
||||
|
||||
private:
|
||||
std::map<unsigned long, String> m_LogFileIndex;
|
||||
std::map<unsigned int, String> m_LogFileIndex;
|
||||
std::map<Service::Ptr, Array::Ptr> m_ServicesCache;
|
||||
unsigned long m_TimeFrom;
|
||||
unsigned long m_TimeUntil;
|
||||
unsigned int m_TimeFrom;
|
||||
unsigned int m_TimeUntil;
|
||||
boost::mutex m_Mutex;
|
||||
|
||||
void CreateLogIndex(const String& path);
|
||||
static void CreateLogIndexFileHandler(const String& path, std::map<unsigned long, String>& index);
|
||||
void GetLogStateHistClassType(const String& text, int& log_class, int& log_type);
|
||||
Dictionary::Ptr GetStateHistAttributes(const String& type, const String& options);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -146,3 +146,8 @@ Value Table::EmptyDictionaryAccessor(const Value&)
|
|||
{
|
||||
return make_shared<Dictionary>();
|
||||
}
|
||||
|
||||
void Table::UpdateLogCache(const Dictionary::Ptr&, int, int)
|
||||
{
|
||||
/* does nothing by default */
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ public:
|
|||
Column GetColumn(const String& name) const;
|
||||
std::vector<String> GetColumnNames(void) const;
|
||||
|
||||
virtual void UpdateLogCache(const Dictionary::Ptr& bag, int line_count, int lineno);
|
||||
|
||||
protected:
|
||||
Table(void);
|
||||
|
||||
|
|
Loading…
Reference in New Issue