Restructure troubleshootcollect, more after the jump

Changed `--output-file` to `--output`
Default output file now has a timestamp in the name, no more overwriting
Added Section headers and file markers
Taken time is now measured and output
And some format changes were made

refs #3446
This commit is contained in:
Jean Flach 2015-02-19 17:12:32 +01:00
parent a76f816623
commit 1a9c4ceef6
4 changed files with 341 additions and 227 deletions

View File

@ -189,6 +189,11 @@ void Logger::DisableConsoleLog(void)
m_ConsoleLogEnabled = false; m_ConsoleLogEnabled = false;
} }
void Logger::EnableConsoleLog(void)
{
m_ConsoleLogEnabled = false;
}
bool Logger::IsConsoleLogEnabled(void) bool Logger::IsConsoleLogEnabled(void)
{ {
return m_ConsoleLogEnabled; return m_ConsoleLogEnabled;

View File

@ -82,6 +82,7 @@ public:
static std::set<Logger::Ptr> GetLoggers(void); static std::set<Logger::Ptr> GetLoggers(void);
static void DisableConsoleLog(void); static void DisableConsoleLog(void);
static void EnableConsoleLog(void);
static bool IsConsoleLogEnabled(void); static bool IsConsoleLogEnabled(void);
static void DisableTimestamp(bool); static void DisableTimestamp(bool);
static bool IsTimestampEnabled(void); static bool IsTimestampEnabled(void);

View File

@ -18,6 +18,7 @@
******************************************************************************/ ******************************************************************************/
#include "cli/troubleshootcollectcommand.hpp" #include "cli/troubleshootcollectcommand.hpp"
#include "cli/objectlistutility.hpp"
#include "cli/featureutility.hpp" #include "cli/featureutility.hpp"
#include "cli/daemonutility.hpp" #include "cli/daemonutility.hpp"
#include "base/netstring.hpp" #include "base/netstring.hpp"
@ -25,6 +26,7 @@
#include "base/stdiostream.hpp" #include "base/stdiostream.hpp"
#include "base/json.hpp" #include "base/json.hpp"
#include "base/objectlock.hpp" #include "base/objectlock.hpp"
#include "base/convert.hpp"
#include "config/configitembuilder.hpp" #include "config/configitembuilder.hpp"
@ -49,68 +51,143 @@ String TroubleshootCollectCommand::GetShortDescription(void) const
return "Collect information for troubleshooting"; return "Collect information for troubleshooting";
} }
static void GetLatestReport(const String& filename, time_t& bestTimestamp, String& bestFilename) class TroubleshootCollectCommand::InfoLog
{ {
#ifdef _WIN32 bool console;
struct _stat buf; std::ofstream os;
if (_stat(filename.CStr(), &buf)) public:
return; InfoLog(const String& path, const bool cons)
#else
struct stat buf;
if (stat(filename.CStr(), &buf))
return;
#endif /*_WIN32*/
if (buf.st_mtime > bestTimestamp) {
bestTimestamp = buf.st_mtime;
bestFilename = filename;
}
}
/*Print the latest crash report to *os* */
static void PrintCrashReports(std::ostream& os)
{ {
String spath = Application::GetLocalStateDir() + "/log/icinga2/crash/report.*"; console = cons;
time_t bestTimestamp = 0; if (console) {
String bestFilename; os.copyfmt(std::cout);
os.clear(std::cout.rdstate());
try { os.basic_ios<char>::rdbuf(std::cout.rdbuf());
Utility::Glob(spath,
boost::bind(&GetLatestReport, _1, boost::ref(bestTimestamp), boost::ref(bestFilename)), GlobFile);
}
#ifdef _WIN32
catch (win32_error &ex) {
if (int const * err = boost::get_error_info<errinfo_win32_error>(ex)) {
if (*err != 3) //Error code for path does not exist
throw ex;
os << Application::GetLocalStateDir() + "/log/icinga2/crash/ does not exist\n";
} else { } else {
throw ex; os.open(path.CStr(), std::ios::out | std::ios::trunc);
} }
};
void logLine(const LogSeverity sev, const String& str)
{
if (!console)
Log(sev, "troubleshoot", str);
if (sev == LogCritical || sev == LogWarning) {
os << std::string(24, '#') << '\n'
<< "# " << str << '\n'
<< std::string(24, '#') << '\n';
} else
os << str << '\n';
} }
#else
catch (...) { bool GetStreamHealth()
throw; {
return console || os.is_open();
} }
#endif /*_WIN32*/ };
class TroubleshootCollectCommand::InfoLogLine
{
public:
InfoLogLine(InfoLog& log, LogSeverity sev = LogInformation)
: log(log), sev(sev) {}
~InfoLogLine()
{
log.logLine(sev, os.str());
}
template <typename T>
InfoLogLine& operator<<(const T& info)
{
os << info;
return *this;
}
private:
std::ostringstream os;
InfoLog& log;
LogSeverity sev;
};
if (!bestTimestamp) bool TroubleshootCollectCommand::GeneralInfo(InfoLog& log, boost::program_options::variables_map vm)
os << "\nNo crash logs found in " << Application::GetLocalStateDir().CStr() << "/log/icinga2/crash/\n"; {
else { InfoLogLine(log) << '\n' << std::string(14, '=') << " GENERAL INFORMATION " << std::string(14, '=') << '\n';
const std::tm tm = Utility::LocalTime(bestTimestamp);
char *tmBuf = new char[200]; //Should always be enough //Application::DisplayInfoMessage() but formatted
const char *fmt = "%Y-%m-%d %H:%M:%S" ; InfoLogLine(log)
if (!strftime(tmBuf, 199, fmt, &tm)) << "\tApplication version: " << Application::GetVersion() << '\n'
return; << "\tInstallation root: " << Application::GetPrefixDir() << '\n'
os << "\nLatest crash report is from " << tmBuf << "\tSysconf directory: " << Application::GetSysconfDir() << '\n'
<< "\nFile: " << bestFilename << '\n'; << "\tRun directory: " << Application::GetRunDir() << '\n'
TroubleshootCollectCommand::tail(bestFilename, 20, os); << "\tLocal state directory: " << Application::GetLocalStateDir() << '\n'
<< "\tPackage data directory: " << Application::GetPkgDataDir() << '\n'
<< "\tState path: " << Application::GetStatePath() << '\n'
<< "\tObjects path: " << Application::GetObjectsPath() << '\n'
<< "\tVars path: " << Application::GetVarsPath() << '\n'
<< "\tPID path: " << Application::GetPidPath() << '\n'
<< "\tApplication type: " << Application::GetApplicationType() << '\n';
return true;
} }
bool TroubleshootCollectCommand::FeatureInfo(InfoLog& log, boost::program_options::variables_map vm)
{
TroubleshootCollectCommand::CheckFeatures(log);
//TODO Check whether active faetures are operational.
return true;
} }
bool TroubleshootCollectCommand::ObjectInfo(InfoLog& log, boost::program_options::variables_map vm, Dictionary::Ptr& logs)
{
InfoLogLine(log) << '\n' << std::string(14, '=') << " OBJECT INFORMATION " << std::string(14, '=') << '\n';
String objectfile = Application::GetObjectsPath();
std::set<String> configs;
if (!Utility::PathExists(objectfile)) {
InfoLogLine(log, LogCritical) << "Cannot open object file '" << objectfile << "'.\n"
<< "FAILED: This probably means you have a fault configuration.";
return false;
} else
CheckObjectFile(objectfile, log, vm.count("include-objects"), logs, configs);
return true;
}
bool TroubleshootCollectCommand::ReportInfo(InfoLog& log, boost::program_options::variables_map vm, Dictionary::Ptr& logs)
{
InfoLogLine(log) << '\n' << std::string(14, '=') << " LOGS AND CRASH REPORTS " << std::string(14, '=') << '\n';
PrintLoggers(log, logs);
PrintCrashReports(log);
return true;
}
bool TroubleshootCollectCommand::ConfigInfo(InfoLog& log, boost::program_options::variables_map vm)
{
InfoLogLine(log) << '\n' << std::string(14, '=') << " CONFIGURATION FILES " << std::string(14, '=') << '\n';
InfoLogLine(log) << "A collection of important configuration files follows, please make sure to remove any sensitive data such as credentials, internal company names, etc";
if (!PrintConf(log, Application::GetSysconfDir() + "/icinga2/icinga2.conf")) {
InfoLogLine(log, LogWarning) << "icinga2.conf not found, therefore skipping validation.\n"
<< "If you are using an icinga2.conf somewhere but the default path please validate it via 'icinga2 daemon -C -c \"path\to/icinga2.conf\"'\n"
<< "and provide it with your support request.";
}
if (!PrintConf(log, Application::GetSysconfDir() + "/icinga2/zones.conf")) {
InfoLogLine(log, LogWarning) << "zones.conf not found.\n"
<< "If you are using a zones.conf somewhere but the default path please provide it with your support request";
}
return true;
}
/*Print the last *numLines* of *file* to *os* */ /*Print the last *numLines* of *file* to *os* */
int TroubleshootCollectCommand::tail(const String& file, int numLines, std::ostream& os) int TroubleshootCollectCommand::tail(const String& file, int numLines, InfoLog& log)
{ {
boost::circular_buffer<std::string> ringBuf(numLines); boost::circular_buffer<std::string> ringBuf(numLines);
std::ifstream text; std::ifstream text;
@ -129,73 +206,15 @@ int TroubleshootCollectCommand::tail(const String& file, int numLines, std::ostr
if (lines < numLines) if (lines < numLines)
numLines = lines; numLines = lines;
for (int k = 0; k < numLines; k++)
os << '\t' << ringBuf[k] << '\n';;
for (int k = 0; k < numLines; k++)
InfoLogLine(log) << '\t' << ringBuf[k];
text.close(); text.close();
InfoLogLine(log) << "[end: '" << file << "' line: " << lines << ']';
return numLines; return numLines;
} }
static bool PrintIcingaConf(std::ostream& os) bool TroubleshootCollectCommand::CheckFeatures(InfoLog& log)
{
String path = Application::GetSysconfDir() + "/icinga2/icinga2.conf";
std::ifstream text;
text.open(path.CStr(), std::ifstream::in);
if (!text.is_open()) {
Log(LogCritical, "troubleshooting", "Could not find icinga2.conf at its default location (" + path + ")");
os << "! Could not open " << path
<< "\n!\tIf you use a custom icinga2.conf provide it after validating it via `icinga2 daemon -C`"
<< "\n!\tIf you do not have a icinga2.conf you just found your problem.\n";
return false;
}
std::string line;
os << "\nFound main Icinga2 configuration file at " << path << '\n';
while (std::getline(text, line)) {
os << '\t' << line << '\n';
}
return true;
}
static bool PrintZonesConf(std::ostream& os)
{
String path = Application::GetSysconfDir() + "/icinga2/zones.conf";
std::ifstream text;
text.open(path.CStr(), std::ifstream::in);
if (!text.is_open()) {
Log(LogWarning, "troubleshooting", "Could not find zones.conf at its default location (" + path + ")");
os << "!Could not open " << path
<< "\n!\tThis could be the root of your problems, if you trying to use multiple Icinga2 instances.\n";
return false;
}
std::string line;
os << "\nFound zones configuration file at " << path << '\n';
while (std::getline(text, line)) {
os << '\t' << line << '\n';
}
return true;
}
static void ValidateConfig(std::ostream& os)
{
/* Not loading the icinga library would make config validation fail.
(Depending on the configuration and core count of your machine.) */
Logger::DisableConsoleLog();
Utility::LoadExtensionLibrary("icinga");
std::vector<std::string> configs;
configs.push_back(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
if (DaemonUtility::ValidateConfigFiles(configs, Application::GetObjectsPath()))
os << "Config validation successful\n";
else
os << "! Config validation failed\n"
<< "Run `icinga2 daemon --validate` to recieve additional information\n";
}
static void CheckFeatures(std::ostream& os)
{ {
Dictionary::Ptr features = new Dictionary; Dictionary::Ptr features = new Dictionary;
std::vector<String> disabled_features; std::vector<String> disabled_features;
@ -203,11 +222,10 @@ static void CheckFeatures(std::ostream& os)
if (!FeatureUtility::GetFeatures(disabled_features, true) if (!FeatureUtility::GetFeatures(disabled_features, true)
|| !FeatureUtility::GetFeatures(enabled_features, false)) { || !FeatureUtility::GetFeatures(enabled_features, false)) {
Log(LogWarning, "troubleshoot", "Could not collect features"); InfoLogLine(log, LogCritical) << "Failed to collect enabled and/or disabled features. Check\n"
os << "! Failed to collect enabled and/or disabled features. Check\n"
<< FeatureUtility::GetFeaturesAvailablePath() << '\n' << FeatureUtility::GetFeaturesAvailablePath() << '\n'
<< FeatureUtility::GetFeaturesEnabledPath() << '\n'; << FeatureUtility::GetFeaturesEnabledPath();
return; return false;
} }
BOOST_FOREACH(const String feature, disabled_features) BOOST_FOREACH(const String feature, disabled_features)
@ -215,47 +233,134 @@ static void CheckFeatures(std::ostream& os)
BOOST_FOREACH(const String feature, enabled_features) BOOST_FOREACH(const String feature, enabled_features)
features->Set(feature, true); features->Set(feature, true);
os << "Icinga2 feature list\n" InfoLogLine(log) << "Enabled features:\n\t" << boost::algorithm::join(enabled_features, " ") << '\n'
<< "Enabled features:\n\t" << boost::algorithm::join(enabled_features, " ") << '\n'
<< "Disabled features:\n\t" << boost::algorithm::join(disabled_features, " ") << '\n'; << "Disabled features:\n\t" << boost::algorithm::join(disabled_features, " ") << '\n';
if (!features->Get("checker").ToBool())
InfoLogLine(log, LogWarning) << "checker is disabled, no checks can be run from this instance";
if (!features->Get("mainlog").ToBool()) if (!features->Get("mainlog").ToBool())
os << "! mainlog is disabled, please activate it and rerun icinga2\n"; InfoLogLine(log, LogWarning) << "mainlog is disabled, please activate it and rerun icinga2";
if (!features->Get("debuglog").ToBool()) if (!features->Get("debuglog").ToBool())
os << "! debuglog is disabled, please activate it and rerun icinga2\n"; InfoLogLine(log, LogWarning) << "debuglog is disabled, please activate it and rerun icinga2";
return true;
} }
static void CheckObjectFile(const String& objectfile, std::ostream& os) void TroubleshootCollectCommand::GetLatestReport(const String& filename, time_t& bestTimestamp, String& bestFilename)
{ {
os << "Checking object file from " << objectfile << '\n'; #ifdef _WIN32
struct _stat buf;
if (_stat(filename.CStr(), &buf))
return;
#else
struct stat buf;
if (stat(filename.CStr(), &buf))
return;
#endif /*_WIN32*/
if (buf.st_mtime > bestTimestamp) {
bestTimestamp = buf.st_mtime;
bestFilename = filename;
}
}
bool TroubleshootCollectCommand::PrintCrashReports(InfoLog& log)
{
String spath = Application::GetLocalStateDir() + "/log/icinga2/crash/report.*";
time_t bestTimestamp = 0;
String bestFilename;
try {
Utility::Glob(spath,
boost::bind(&GetLatestReport, _1, boost::ref(bestTimestamp), boost::ref(bestFilename)),
GlobFile);
}
#ifdef _WIN32
catch (win32_error &ex) {
if (int const * err = boost::get_error_info<errinfo_win32_error>(ex)) {
if (*err != 3) {//Error code for path does not exist
InfoLogLine(log, LogWarning) << Application::GetLocalStateDir() << "/log/icinga2/crash/ does not exist";
return false;
}
}
InfoLogLine(log, LogWarning) << "Error printing crash reports";
return false;
}
#else
catch (...) {
InfoLogLine(log, LogWarning) << "Error printing crash reports.\nDoes "
<< Application::GetLocalStateDir() << "/log/icinga2/crash/ exist?";
return false;
}
#endif /*_WIN32*/
if (!bestTimestamp)
InfoLogLine(log) << "No crash logs found in " << Application::GetLocalStateDir().CStr() << "/log/icinga2/crash/";
else {
InfoLogLine(log) << "Latest crash report is from " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", Utility::GetTime())
<< "\nFile: " << bestFilename;
tail(bestFilename, 20, log);
}
return true;
}
bool TroubleshootCollectCommand::PrintConf(InfoLog& log, const String& path)
{
std::ifstream text;
text.open(path.CStr(), std::ifstream::in);
if (!text.is_open())
return false;
std::string line;
InfoLogLine(log) << "\n[begin: '" << path << "']";
while (std::getline(text, line)) {
InfoLogLine(log) << '\t' << line;
}
InfoLogLine(log) << "\n[end: '" << path << "']";
return true;
}
bool TroubleshootCollectCommand::CheckConfig(void)
{
/* Not loading the icinga library would make config validation fail.
* (Depending on the configuration and the speed of your machine.)
*/
Utility::LoadExtensionLibrary("icinga");
std::vector<std::string> configs;
configs.push_back(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
return DaemonUtility::ValidateConfigFiles(configs, Application::GetObjectsPath());
}
void TroubleshootCollectCommand::CheckObjectFile(const String& objectfile, InfoLog& log, const bool print,
Dictionary::Ptr& logs, std::set<String>& configs)
{
InfoLogLine(log) << "Checking object file from " << objectfile;
std::fstream fp; std::fstream fp;
std::set<String> configSet;
Dictionary::Ptr typeCount = new Dictionary();
Dictionary::Ptr logPath = new Dictionary();
fp.open(objectfile.CStr(), std::ios_base::in); fp.open(objectfile.CStr(), std::ios_base::in);
if (!fp.is_open()) { if (!fp.is_open()) {
Log(LogWarning, "troubleshoot", "Could not open objectfile"); InfoLogLine(log, LogWarning) << "Could not open object file.";
os << "! Could not open object file.\n";
return; return;
} }
StdioStream::Ptr sfp = new StdioStream(&fp, false); StdioStream::Ptr sfp = new StdioStream(&fp, false);
String::SizeType typeL = 0, countTotal = 0;
int typeL = 0, countTotal = 0;
String message; String message;
StreamReadContext src; StreamReadContext src;
for (;;) { StreamReadStatus srs;
StreamReadStatus srs = NetString::ReadStringFromStream(sfp, &message, src); std::map<String, int> type_count;
bool first = true;
if (srs == StatusEof) while ((srs = NetString::ReadStringFromStream(sfp, &message, src)) != StatusEof) {
break;
if (srs != StatusNewItem) if (srs != StatusNewItem)
continue; continue;
bool first = true;
if (print)
ObjectListUtility::PrintObject(std::ostream(nullptr), first, message, type_count, "", "");
else
ObjectListUtility::PrintObject(std::ostream(nullptr), first, message, type_count, "", "");
Dictionary::Ptr object = JsonDecode(message); Dictionary::Ptr object = JsonDecode(message);
Dictionary::Ptr properties = object->Get("properties"); Dictionary::Ptr properties = object->Get("properties");
@ -266,14 +371,9 @@ static void CheckObjectFile(const String& objectfile, std::ostream& os)
typeL = type.GetLength() > typeL ? type.GetLength() : typeL; typeL = type.GetLength() > typeL ? type.GetLength() : typeL;
countTotal++; countTotal++;
if (!typeCount->Contains(type))
typeCount->Set(type, 1);
else
typeCount->Set(type, typeCount->Get(type)+1);
Array::Ptr debug_info = object->Get("debug_info"); Array::Ptr debug_info = object->Get("debug_info");
if (debug_info) { if (debug_info) {
configSet.insert(debug_info->Get(0)); configs.insert(debug_info->Get(0));
} }
if (Utility::Match(type, "FileLogger")) { if (Utility::Match(type, "FileLogger")) {
@ -283,44 +383,48 @@ static void CheckObjectFile(const String& objectfile, std::ostream& os)
ObjectLock olock(properties); ObjectLock olock(properties);
BOOST_FOREACH(const Dictionary::Pair& kv, properties) { BOOST_FOREACH(const Dictionary::Pair& kv, properties) {
if (Utility::Match(kv.first, "path")) if (Utility::Match(kv.first, "path"))
logPath->Set(name, kv.second); logs->Set(name, kv.second);
} }
} }
} }
if (!countTotal) { if (!countTotal) {
os << "! No objects found in objectfile.\n"; InfoLogLine(log, LogCritical) << "No objects found in objectfile.";
return; return;
} }
//Print objects with count //Print objects with count
os << "Found the following objects:\n" InfoLogLine(log) << "Found the " << countTotal << " objects:"
<< "\tType" << std::string(typeL-4, ' ') << " : Count\n"; << "\tType" << std::string(typeL-4, ' ') << " : Count";
ObjectLock olock(typeCount);
BOOST_FOREACH(const Dictionary::Pair& kv, typeCount) { BOOST_FOREACH(const Dictionary::Pair& kv, type_count) {
os << '\t' << kv.first << std::string(typeL - kv.first.GetLength(), ' ') InfoLogLine(log) << '\t' << kv.first << std::string(typeL - kv.first.GetLength(), ' ')
<< " : " << kv.second << '\n'; << " : " << kv.second;
}
} }
//Print location of .config files void TroubleshootCollectCommand::PrintLoggers(InfoLog& log, Dictionary::Ptr& logs)
os << '\n' << countTotal << " objects in total, originating from these files:\n"; {
if (!logs->GetLength()) {
InfoLogLine(log, LogWarning) << "No loggers found, check whether you enabled any logging features";
} else {
InfoLogLine(log) << "Getting the last 20 lines of " << logs->GetLength() << " FileLogger objects.";
ObjectLock ulock(logs);
BOOST_FOREACH(const Dictionary::Pair& kv, logs)
{
InfoLogLine(log) << "\nLogger " << kv.first << " at path: " << kv.second;
if (!tail(kv.second, 20, log))
InfoLogLine(log, LogWarning) << kv.second << " either does not exist or is empty";
}
}
}
void TroubleshootCollectCommand::PrintConfig(InfoLog& log, const std::set<String>& configSet, const String::SizeType& countTotal)
{
InfoLogLine(log) << countTotal << " objects in total, originating from these files:";
for (std::set<String>::iterator it = configSet.begin(); for (std::set<String>::iterator it = configSet.begin();
it != configSet.end(); it++) it != configSet.end(); it++)
os << '\t' << *it << '\n'; InfoLogLine(log) << '\t' << *it;
//Print tail of file loggers
if (!logPath->GetLength()) {
os << "! No loggers found, check whether you enabled any logging features\n";
} else {
os << "\nGetting the last 20 lines of the " << logPath->GetLength() << " found FileLogger objects.\n";
ObjectLock ulock(logPath);
BOOST_FOREACH(const Dictionary::Pair& kv, logPath)
{
os << "\nLogger " << kv.first << " at path: " << kv.second << "\n";
if (!TroubleshootCollectCommand::tail(kv.second, 20, os))
os << "\t" << kv.second << " either does not exist or is empty\n";
}
}
} }
void TroubleshootCollectCommand::InitParameters(boost::program_options::options_description& visibleDesc, void TroubleshootCollectCommand::InitParameters(boost::program_options::options_description& visibleDesc,
@ -328,83 +432,66 @@ void TroubleshootCollectCommand::InitParameters(boost::program_options::options_
{ {
visibleDesc.add_options() visibleDesc.add_options()
("console,c", "print to console instead of file") ("console,c", "print to console instead of file")
("output-file", boost::program_options::value<std::string>(), "path to output file") ("output,o", boost::program_options::value<std::string>(), "path to output file")
("include-objects", "Print the whole objectfile (like `object list`)")
; ;
} }
int TroubleshootCollectCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const int TroubleshootCollectCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{ {
std::ofstream os;
String path; String path;
InfoLog *log;
Logger::SetConsoleLogSeverity(LogWarning);
if (vm.count("console")) { if (vm.count("console")) {
Logger::DisableConsoleLog(); log = new InfoLog("", true);
os.copyfmt(std::cout);
os.clear(std::cout.rdstate());
os.basic_ios<char>::rdbuf(std::cout.rdbuf());
} else { } else {
if (vm.count("output-file")) if (vm.count("output"))
path = vm["output-file"].as<std::string>(); path = vm["output"].as<std::string>();
else else {
path = Application::GetLocalStateDir() +"/log/icinga2/troubleshooting.log"; #ifdef _WIN32 //Dislikes ':' in filenames
os.open(path.CStr(), std::ios::out | std::ios::trunc); path = Application::GetLocalStateDir() + "/log/icinga2/troubleshooting-"
if (!os.is_open()) { + Utility::FormatDateTime("%Y-%m-%d_%H-%M-%S", Utility::GetTime()) + ".log";
#else
path = Application::GetLocalStateDir() + "/log/icinga2/troubleshooting-"
+ Utility::FormatDateTime("%Y-%m-%d_%H:%M:%S", Utility::GetTime()) + ".log";
#endif /*_WIN32*/
}
log = new InfoLog(path, false);
if (!log->GetStreamHealth()) {
Log(LogCritical, "troubleshoot", "Failed to open file to write: " + path); Log(LogCritical, "troubleshoot", "Failed to open file to write: " + path);
return 3; return 3;
} }
} }
String appName = Utility::BaseName(Application::GetArgV()[0]); String appName = Utility::BaseName(Application::GetArgV()[0]);
double goTime = Utility::GetTime();
os << appName << " -- Troubleshooting help:" << std::endl InfoLogLine(*log) << appName << " -- Troubleshooting help:\n"
<< "Should you run into problems with Icinga please add this file to your help request\n\n"; << "Should you run into problems with Icinga please add this file to your help request\n"
<< "Began procedure at timestamp " << Convert::ToString(goTime) << '\n';
if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-") if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
appName = appName.SubStr(3, appName.GetLength() - 3); appName = appName.SubStr(3, appName.GetLength() - 3);
//Application::DisplayInfoMessage() but formatted Dictionary::Ptr logs = new Dictionary;
os << "\tApplication version: " << Application::GetVersion() << "\n"
<< "\tInstallation root: " << Application::GetPrefixDir() << "\n"
<< "\tSysconf directory: " << Application::GetSysconfDir() << "\n"
<< "\tRun directory: " << Application::GetRunDir() << "\n"
<< "\tLocal state directory: " << Application::GetLocalStateDir() << "\n"
<< "\tPackage data directory: " << Application::GetPkgDataDir() << "\n"
<< "\tState path: " << Application::GetStatePath() << "\n"
<< "\tObjects path: " << Application::GetObjectsPath() << "\n"
<< "\tVars path: " << Application::GetVarsPath() << "\n"
<< "\tPID path: " << Application::GetPidPath() << "\n"
<< "\tApplication type: " << Application::GetApplicationType() << "\n";
os << '\n'; if (!GeneralInfo(*log, vm)
CheckFeatures(os); || !FeatureInfo(*log, vm)
os << '\n'; || !ObjectInfo(*log, vm, logs)
|| !ReportInfo(*log, vm, logs)
String objectfile = Application::GetObjectsPath(); || !ConfigInfo(*log, vm)) {
InfoLogLine(*log, LogCritical) << "Could not recover from critical failure, exiting.";
if (!Utility::PathExists(objectfile)) { delete log;
Log(LogWarning, "troubleshoot", "Failed to open objectfile"); return 3;
os << "! Cannot open object file '" << objectfile << "'."
<< "! Run 'icinga2 daemon -C' to validate config and generate the cache file.\n";
} else
CheckObjectFile(objectfile, os);
os << "\nA collection of important configuration files follows, please make sure to censor your sensible data\n";
if (PrintIcingaConf(os)) {
ValidateConfig(os);
} else {
Log(LogWarning, "troubleshoot", "Failed to open icinga2.conf");
os << "! icinga2.conf not found, therefore skipping validation.\n";
} }
os << '\n';
PrintZonesConf(os);
os << '\n';
std::cout << "Finished collection"; double endTime = Utility::GetTime();
InfoLogLine(*log) << "\nFinished collection at timestamp " << Convert::ToString(endTime)
<< "\nTook " << Convert::ToString(endTime - goTime) << " seconds\n";
if (!vm.count("console")) { if (!vm.count("console")) {
os.close(); std::cout << "\nFinished collection. See '" << path << "'\n";
std::cout << ", see " << path;
} }
std::cout << std::endl;
delete log;
return 0; return 0;
} }

View File

@ -21,6 +21,8 @@
#define TROUBLESHOOTCOLLECTCOMMAND_H #define TROUBLESHOOTCOLLECTCOMMAND_H
#include "cli/clicommand.hpp" #include "cli/clicommand.hpp"
#include "base/i2-base.hpp"
#include "base/dictionary.hpp"
namespace icinga namespace icinga
{ {
@ -39,7 +41,26 @@ namespace icinga
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const; virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc, virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const; boost::program_options::options_description& hiddenDesc) const;
static int tail(const String& file, int numLines, std::ostream& os);
private:
class InfoLog;
class InfoLogLine;
static bool GeneralInfo(InfoLog& log, boost::program_options::variables_map vm);
static bool FeatureInfo(InfoLog& log, boost::program_options::variables_map vm);
static bool ObjectInfo(InfoLog& log, boost::program_options::variables_map vm, Dictionary::Ptr& logs);
static bool ReportInfo(InfoLog& log, boost::program_options::variables_map vm, Dictionary::Ptr& logs);
static bool ConfigInfo(InfoLog& log, boost::program_options::variables_map vm);
static int tail(const String& file, int numLines, InfoLog& log);
static bool CheckFeatures(InfoLog& log);
static void GetLatestReport(const String& filename, time_t& bestTimestamp, String& bestFilename);
static bool PrintCrashReports(InfoLog& log);
static bool PrintConf(InfoLog& log, const String& path);
static bool CheckConfig(void);
static void CheckObjectFile(const String& objectfile, InfoLog& log, const bool print,
Dictionary::Ptr& logs, std::set<String>& configs);
static void PrintLoggers(InfoLog& log, Dictionary::Ptr& logs);
static void PrintConfig(InfoLog& log, const std::set<String>& configSet, const String::SizeType& countTotal);
}; };
} }
#endif /* TROUBLESHOOTCOLLECTCOMMAND_H */ #endif /* TROUBLESHOOTCOLLECTCOMMAND_H */