Add colours to troubleshoot cli

refs #3446
This commit is contained in:
Jean-Marcel Flach 2015-02-27 14:09:26 +01:00
parent d397933eff
commit 07fd4e6031
1 changed files with 110 additions and 82 deletions

View File

@ -18,6 +18,7 @@
******************************************************************************/ ******************************************************************************/
#include "base/application.hpp" #include "base/application.hpp"
#include "base/console.hpp"
#include "base/convert.hpp" #include "base/convert.hpp"
#include "base/json.hpp" #include "base/json.hpp"
#include "base/netstring.hpp" #include "base/netstring.hpp"
@ -59,8 +60,15 @@ public:
InfoLog(const String& path, const bool cons) InfoLog(const String& path, const bool cons)
{ {
m_Console = cons; m_Console = cons;
if (m_Console) m_ConsoleType = Console_Dumb;
if (m_Console) {
m_Stream = new std::ostream(std::cout.rdbuf()); m_Stream = new std::ostream(std::cout.rdbuf());
#ifndef _WIN32
m_ConsoleType = Console_VT100;
#else /*_WIN32*/
m_ConsoleType = Console_Windows;
#endif /*_WIN32*/
}
else { else {
std::ofstream *ofs = new std::ofstream(); std::ofstream *ofs = new std::ofstream();
ofs->open(path.CStr(), std::ios::out | std::ios::trunc); ofs->open(path.CStr(), std::ios::out | std::ios::trunc);
@ -73,19 +81,27 @@ public:
delete m_Stream; delete m_Stream;
} }
void WriteLine(const LogSeverity sev, const String& str) void WriteLine(const LogSeverity sev, const int colour, const String& str)
{ {
if (!m_Console) if (!m_Console)
Log(sev, "troubleshoot", str); Log(sev, "troubleshoot", str);
if (sev == LogCritical || sev == LogWarning) { if (sev == LogWarning) {
*m_Stream *m_Stream
<< std::string(24, '#') << '\n' << '\n' << ConsoleColorTag(Console_ForegroundYellow, m_ConsoleType) << std::string(24, '#') << '\n'
<< "# " << str << '\n' << ConsoleColorTag(Console_Normal, m_ConsoleType) << str
<< std::string(24, '#') << '\n'; << ConsoleColorTag(Console_ForegroundYellow, m_ConsoleType) << std::string(24, '#') << "\n\n"
<< ConsoleColorTag(Console_Normal, m_ConsoleType);
} else if (sev == LogCritical) {
*m_Stream
<< '\n' << ConsoleColorTag(Console_ForegroundRed, m_ConsoleType) << std::string(24, '#') << '\n'
<< ConsoleColorTag(Console_Normal, m_ConsoleType) << str
<< ConsoleColorTag(Console_ForegroundRed, m_ConsoleType) << std::string(24, '#') << "\n\n"
<< ConsoleColorTag(Console_Normal, m_ConsoleType);
} else } else
*m_Stream *m_Stream
<< str << '\n'; << ConsoleColorTag(colour, m_ConsoleType) << str
<< ConsoleColorTag(Console_Normal, m_ConsoleType);
} }
bool GetStreamHealth(void) const bool GetStreamHealth(void) const
@ -95,18 +111,19 @@ public:
private: private:
bool m_Console; bool m_Console;
ConsoleType m_ConsoleType;
std::ostream *m_Stream; std::ostream *m_Stream;
}; };
class TroubleshootCommand::InfoLogLine class TroubleshootCommand::InfoLogLine
{ {
public: public:
InfoLogLine(InfoLog& log, LogSeverity sev = LogInformation) InfoLogLine(InfoLog& log, int col = Console_Normal, LogSeverity sev = LogInformation)
: m_Log(log), m_Sev(sev) {} : m_Log(log), m_colour(col), m_Sev(sev) {}
~InfoLogLine(void) ~InfoLogLine(void)
{ {
m_Log.WriteLine(m_Sev, m_String.str()); m_Log.WriteLine(m_Sev, m_colour, m_String.str());
} }
template <typename T> template <typename T>
@ -120,13 +137,14 @@ private:
std::ostringstream m_String; std::ostringstream m_String;
InfoLog& m_Log; InfoLog& m_Log;
LogSeverity m_Sev; LogSeverity m_Sev;
int m_colour;
}; };
bool TroubleshootCommand::GeneralInfo(InfoLog& log, const boost::program_options::variables_map& vm) bool TroubleshootCommand::GeneralInfo(InfoLog& log, const boost::program_options::variables_map& vm)
{ {
InfoLogLine(log) InfoLogLine(log, Console_ForegroundBlue)
<< '\n' << std::string(14, '=') << " GENERAL INFORMATION " << std::string(14, '=') << '\n'; << '\n' << std::string(14, '=') << " GENERAL INFORMATION " << std::string(14, '=') << "\n\n";
//Application::DisplayInfoMessage() but formatted //Application::DisplayInfoMessage() but formatted
InfoLogLine(log) InfoLogLine(log)
@ -140,7 +158,7 @@ bool TroubleshootCommand::GeneralInfo(InfoLog& log, const boost::program_options
<< "\tObjects path: " << Application::GetObjectsPath() << '\n' << "\tObjects path: " << Application::GetObjectsPath() << '\n'
<< "\tVars path: " << Application::GetVarsPath() << '\n' << "\tVars path: " << Application::GetVarsPath() << '\n'
<< "\tPID path: " << Application::GetPidPath() << '\n' << "\tPID path: " << Application::GetPidPath() << '\n'
<< "\tApplication type: " << Application::GetApplicationType() << '\n'; << "\tApplication type: " << Application::GetApplicationType() << "\n\n";
return true; return true;
} }
@ -154,28 +172,28 @@ bool TroubleshootCommand::FeatureInfo(InfoLog& log, const boost::program_options
bool TroubleshootCommand::ObjectInfo(InfoLog& log, const boost::program_options::variables_map& vm, Dictionary::Ptr& logs, const String& path) bool TroubleshootCommand::ObjectInfo(InfoLog& log, const boost::program_options::variables_map& vm, Dictionary::Ptr& logs, const String& path)
{ {
InfoLogLine(log) InfoLogLine(log, Console_ForegroundBlue)
<< '\n' << std::string(14, '=') << " OBJECT INFORMATION " << std::string(14, '=') << '\n'; << '\n' << std::string(14, '=') << " OBJECT INFORMATION " << std::string(14, '=') << "\n\n";
String objectfile = Application::GetObjectsPath(); String objectfile = Application::GetObjectsPath();
std::set<String> configs; std::set<String> configs;
if (!Utility::PathExists(objectfile)) { if (!Utility::PathExists(objectfile)) {
InfoLogLine(log, LogCritical) InfoLogLine(log, 0, LogCritical)
<< "Cannot open object file '" << objectfile << "'.\n" << "Cannot open object file '" << objectfile << "'.\n"
<< "FAILED: This probably means you have a fault configuration."; << "FAILED: This probably means you have a fault configuration.\n";
return false; return false;
} else { } else {
InfoLog *OFile = NULL; InfoLog *OFile = NULL;
if (vm.count("include-objects")) { if (vm.count("include-objects")) {
OFile = new InfoLog(path+"-objects", false); OFile = new InfoLog(path+"-objects", false);
if (!OFile->GetStreamHealth()) { if (!OFile->GetStreamHealth()) {
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "Failed to open Object-write-stream, not printing objects\n"; << "Failed to open Object-write-stream, not printing objects\n\n";
OFile = NULL; OFile = NULL;
} else } else
InfoLogLine(log) InfoLogLine(log)
<< "Printing all objects to " << path+"-objects"; << "Printing all objects to " << path+"-objects\n";
} }
CheckObjectFile(objectfile, log, OFile, logs, configs); CheckObjectFile(objectfile, log, OFile, logs, configs);
if (OFile != NULL) if (OFile != NULL)
@ -185,10 +203,10 @@ bool TroubleshootCommand::ObjectInfo(InfoLog& log, const boost::program_options:
if (vm.count("include-vars")) { if (vm.count("include-vars")) {
if (PrintVarsFile(path)) if (PrintVarsFile(path))
InfoLogLine(log) InfoLogLine(log)
<< "Successfully printed all variables to " << path+"-vars"; << "Successfully printed all variables to " << path+"-vars\n";
else else
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "Failed to prin vars to " << path+"-vars"; << "Failed to prin vars to " << path+"-vars\n";
} }
return true; return true;
@ -196,8 +214,8 @@ bool TroubleshootCommand::ObjectInfo(InfoLog& log, const boost::program_options:
bool TroubleshootCommand::ReportInfo(InfoLog& log, const boost::program_options::variables_map& vm, Dictionary::Ptr& logs) bool TroubleshootCommand::ReportInfo(InfoLog& log, const boost::program_options::variables_map& vm, Dictionary::Ptr& logs)
{ {
InfoLogLine(log) InfoLogLine(log, Console_ForegroundBlue)
<< '\n' << std::string(14, '=') << " LOGS AND CRASH REPORTS " << std::string(14, '=') << '\n'; << '\n' << std::string(14, '=') << " LOGS AND CRASH REPORTS " << std::string(14, '=') << "\n\n";
PrintLoggers(log, logs); PrintLoggers(log, logs);
PrintCrashReports(log); PrintCrashReports(log);
@ -206,23 +224,23 @@ bool TroubleshootCommand::ReportInfo(InfoLog& log, const boost::program_options:
bool TroubleshootCommand::ConfigInfo(InfoLog& log, const boost::program_options::variables_map& vm) bool TroubleshootCommand::ConfigInfo(InfoLog& log, const boost::program_options::variables_map& vm)
{ {
InfoLogLine(log) InfoLogLine(log, Console_ForegroundBlue)
<< '\n' << std::string(14, '=') << " CONFIGURATION FILES " << std::string(14, '=') << '\n'; << '\n' << std::string(14, '=') << " CONFIGURATION FILES " << std::string(14, '=') << "\n\n";
InfoLogLine(log) InfoLogLine(log)
<< "A collection of important configuration files follows, please make sure to remove any sensitive data such as credentials, internal company names, etc"; << "A collection of important configuration files follows, please make sure to remove any sensitive data such as credentials, internal company names, etc\n";
if (!PrintConf(log, Application::GetSysconfDir() + "/icinga2/icinga2.conf")) { if (!PrintConf(log, Application::GetSysconfDir() + "/icinga2/icinga2.conf")) {
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "icinga2.conf not found, therefore skipping validation.\n" << "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" << "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."; << "and provide it with your support request.\n";
} }
if (!PrintConf(log, Application::GetSysconfDir() + "/icinga2/zones.conf")) { if (!PrintConf(log, Application::GetSysconfDir() + "/icinga2/zones.conf")) {
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "zones.conf not found.\n" << "zones.conf not found.\n"
<< "If you are using a zones.conf somewhere but the default path please provide it with your support request"; << "If you are using a zones.conf somewhere but the default path please provide it with your support request\n";
} }
return true; return true;
@ -248,18 +266,20 @@ int TroubleshootCommand::Tail(const String& file, int numLines, InfoLog& log)
if (lines < numLines) if (lines < numLines)
numLines = lines; numLines = lines;
InfoLogLine(log) InfoLogLine(log, Console_ForegroundCyan)
<< "\n[begin: '" << file << "' line: " << numLines-lines << ']'; << "[begin: '" << file << "' line: " << lines-numLines << "]\n";
for (int k = 0; k < numLines; k++) { for (int k = 0; k < numLines; k++) {
InfoLogLine(log) InfoLogLine(log, Console_ForegroundCyan)
<< '\t' << ringBuf[k]; << "# ";
InfoLogLine(log)
<< ringBuf[k] << '\n';
} }
text.close(); text.close();
InfoLogLine(log) InfoLogLine(log, Console_ForegroundCyan)
<< "[end: '" << file << "' line: " << lines << "]\n"; << "[end: '" << file << "' line: " << lines << "]\n\n";
return numLines; return numLines;
} }
@ -272,10 +292,10 @@ bool TroubleshootCommand::CheckFeatures(InfoLog& log)
if (!FeatureUtility::GetFeatures(disabled_features, true) || if (!FeatureUtility::GetFeatures(disabled_features, true) ||
!FeatureUtility::GetFeatures(enabled_features, false)) { !FeatureUtility::GetFeatures(enabled_features, false)) {
InfoLogLine(log, LogCritical) InfoLogLine(log, 0, LogCritical)
<< "Failed to collect enabled and/or disabled features. Check\n" << "Failed to collect enabled and/or disabled features. Check\n"
<< FeatureUtility::GetFeaturesAvailablePath() << '\n' << FeatureUtility::GetFeaturesAvailablePath() << '\n'
<< FeatureUtility::GetFeaturesEnabledPath(); << FeatureUtility::GetFeaturesEnabledPath() << '\n';
return false; return false;
} }
@ -285,18 +305,23 @@ bool TroubleshootCommand::CheckFeatures(InfoLog& log)
features->Set(feature, true); features->Set(feature, true);
InfoLogLine(log) InfoLogLine(log)
<< "Enabled features:\n\t" << boost::algorithm::join(enabled_features, " ") << '\n' << "Enabled features:\n";
<< "Disabled features:\n\t" << boost::algorithm::join(disabled_features, " ") << '\n'; InfoLogLine(log, Console_ForegroundGreen)
<< '\t' << boost::algorithm::join(enabled_features, " ") << '\n';
InfoLogLine(log)
<< "Disabled features:\n";
InfoLogLine(log, Console_ForegroundRed)
<< '\t' << boost::algorithm::join(disabled_features, " ") << '\n';
if (!features->Get("checker").ToBool()) if (!features->Get("checker").ToBool())
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "checker is disabled, no checks can be run from this instance"; << "checker is disabled, no checks can be run from this instance\n";
if (!features->Get("mainlog").ToBool()) if (!features->Get("mainlog").ToBool())
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "mainlog is disabled, please activate it and rerun icinga2"; << "mainlog is disabled, please activate it and rerun icinga2\n";
if (!features->Get("debuglog").ToBool()) if (!features->Get("debuglog").ToBool())
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "debuglog is disabled, please activate it and rerun icinga2"; << "debuglog is disabled, please activate it and rerun icinga2\n";
return true; return true;
} }
@ -332,20 +357,20 @@ bool TroubleshootCommand::PrintCrashReports(InfoLog& log)
catch (win32_error &ex) { catch (win32_error &ex) {
if (int const * err = boost::get_error_info<errinfo_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 if (*err != 3) {//Error code for path does not exist
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< Application::GetLocalStateDir() << "/log/icinga2/crash/ does not exist\n"; << Application::GetLocalStateDir() << "/log/icinga2/crash/ does not exist\n";
return false; return false;
} }
} }
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "Error printing crash reports"; << "Error printing crash reports\n";
return false; return false;
} }
#else #else
catch (...) { catch (...) {
InfoLogLine(log, LogWarning) << "Error printing crash reports.\n" InfoLogLine(log, 0, LogWarning) << "Error printing crash reports.\n"
<< "Does " << Application::GetLocalStateDir() << "/log/icinga2/crash/ exist?\n"; << "Does " << Application::GetLocalStateDir() << "/log/icinga2/crash/ exist?\n";
return false; return false;
@ -353,15 +378,15 @@ bool TroubleshootCommand::PrintCrashReports(InfoLog& log)
#endif /*_WIN32*/ #endif /*_WIN32*/
if (!bestTimestamp) if (!bestTimestamp)
InfoLogLine(log) InfoLogLine(log, Console_ForegroundYellow)
<< "No crash logs found in " << Application::GetLocalStateDir().CStr() << "/log/icinga2/crash/\n"; << "No crash logs found in " << Application::GetLocalStateDir().CStr() << "/log/icinga2/crash/\n\n";
else { else {
InfoLogLine(log) InfoLogLine(log)
<< "Latest crash report is from " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", Utility::GetTime()) << "Latest crash report is from " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", Utility::GetTime()) << '\n'
<< "\nFile: " << bestFilename; << "File: " << bestFilename << '\n';
Tail(bestFilename, 20, log); Tail(bestFilename, 20, log);
InfoLogLine(log) InfoLogLine(log)
<< ""; << '\n';
} }
return true; return true;
@ -376,16 +401,18 @@ bool TroubleshootCommand::PrintConf(InfoLog& log, const String& path)
std::string line; std::string line;
InfoLogLine(log) InfoLogLine(log, Console_ForegroundCyan)
<< "\n[begin: '" << path << "']"; << "[begin: '" << path << "']\n";
while (std::getline(text, line)) { while (std::getline(text, line)) {
InfoLogLine(log) InfoLogLine(log, Console_ForegroundCyan)
<< '\t' << line; << "# ";
InfoLogLine(log)
<< line << '\n';
} }
InfoLogLine(log) InfoLogLine(log, Console_ForegroundCyan)
<< "[end: '" << path << "']"; << "[end: '" << path << "']\n";
return true; return true;
} }
@ -407,14 +434,14 @@ void TroubleshootCommand::CheckObjectFile(const String& objectfile, InfoLog& log
Dictionary::Ptr& logs, std::set<String>& configs) Dictionary::Ptr& logs, std::set<String>& configs)
{ {
InfoLogLine(log) InfoLogLine(log)
<< "Checking object file from " << objectfile; << "Checking object file from " << objectfile << '\n';
std::fstream fp; std::fstream fp;
fp.open(objectfile.CStr(), std::ios_base::in); fp.open(objectfile.CStr(), std::ios_base::in);
if (!fp.is_open()) { if (!fp.is_open()) {
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "Could not open object file."; << "Could not open object file.\n";
return; return;
} }
@ -468,24 +495,24 @@ void TroubleshootCommand::CheckObjectFile(const String& objectfile, InfoLog& log
} }
if (!countTotal) { if (!countTotal) {
InfoLogLine(log, LogCritical) InfoLogLine(log, 0, LogCritical)
<< "No objects found in objectfile."; << "No objects found in objectfile.\n";
return; return;
} }
//Print objects with count //Print objects with count
InfoLogLine(log) InfoLogLine(log)
<< "Found the " << countTotal << " objects:\n" << "Found the " << countTotal << " objects:\n"
<< "\tType" << std::string(typeL-4, ' ') << " : Count"; << " Type" << std::string(typeL-4, ' ') << " : Count\n";
BOOST_FOREACH(const Dictionary::Pair& kv, type_count) { BOOST_FOREACH(const Dictionary::Pair& kv, type_count) {
InfoLogLine(log) InfoLogLine(log)
<< '\t' << kv.first << std::string(typeL - kv.first.GetLength(), ' ') << " " << kv.first << std::string(typeL - kv.first.GetLength(), ' ')
<< " : " << kv.second; << " : " << kv.second << '\n';
} }
InfoLogLine(log) InfoLogLine(log)
<< ""; << '\n';
TroubleshootCommand::PrintObjectOrigin(log, configs); TroubleshootCommand::PrintObjectOrigin(log, configs);
} }
@ -504,19 +531,19 @@ bool TroubleshootCommand::PrintVarsFile(const String& path) {
void TroubleshootCommand::PrintLoggers(InfoLog& log, Dictionary::Ptr& logs) void TroubleshootCommand::PrintLoggers(InfoLog& log, Dictionary::Ptr& logs)
{ {
if (!logs->GetLength()) { if (!logs->GetLength()) {
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< "No loggers found, check whether you enabled any logging features\n"; << "No loggers found, check whether you enabled any logging features\n";
} else { } else {
InfoLogLine(log) InfoLogLine(log)
<< "Getting the last 20 lines of " << logs->GetLength() << " FileLogger objects."; << "Getting the last 20 lines of " << logs->GetLength() << " FileLogger objects.\n";
ObjectLock ulock(logs); ObjectLock ulock(logs);
BOOST_FOREACH(const Dictionary::Pair& kv, logs) { BOOST_FOREACH(const Dictionary::Pair& kv, logs) {
InfoLogLine(log) InfoLogLine(log)
<< "Logger " << kv.first << " at path: " << kv.second; << "Logger " << kv.first << " at path: " << kv.second << '\n';
if (!Tail(kv.second, 20, log)) { if (!Tail(kv.second, 20, log)) {
InfoLogLine(log, LogWarning) InfoLogLine(log, 0, LogWarning)
<< kv.second << " either does not exist or is empty\n"; << kv.second << " either does not exist or is empty\n";
} }
} }
@ -526,11 +553,11 @@ void TroubleshootCommand::PrintLoggers(InfoLog& log, Dictionary::Ptr& logs)
void TroubleshootCommand::PrintObjectOrigin(InfoLog& log, const std::set<String>& configSet) void TroubleshootCommand::PrintObjectOrigin(InfoLog& log, const std::set<String>& configSet)
{ {
InfoLogLine(log) InfoLogLine(log)
<< "The objects origins are:"; << "The objects origins are:\n";
for (std::set<String>::iterator it = configSet.begin(); it != configSet.end(); it++) { for (std::set<String>::iterator it = configSet.begin(); it != configSet.end(); it++) {
InfoLogLine(log) InfoLogLine(log)
<< '\t' << *it; << " " << *it << '\n';
} }
} }
@ -577,7 +604,7 @@ int TroubleshootCommand::Run(const boost::program_options::variables_map& vm, co
InfoLogLine(*log) InfoLogLine(*log)
<< appName << " -- Troubleshooting help:\n" << appName << " -- Troubleshooting help:\n"
<< "Should you run into problems with Icinga please add this file to your help request\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'; << "Began procedure at timestamp " << Convert::ToString(goTime) << "\n\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);
@ -589,8 +616,8 @@ int TroubleshootCommand::Run(const boost::program_options::variables_map& vm, co
!ObjectInfo(*log, vm, logs, path) || !ObjectInfo(*log, vm, logs, path) ||
!ReportInfo(*log, vm, logs) || !ReportInfo(*log, vm, logs) ||
!ConfigInfo(*log, vm)) { !ConfigInfo(*log, vm)) {
InfoLogLine(*log, LogCritical) InfoLogLine(*log, 0, LogCritical)
<< "Could not recover from critical failure, exiting."; << "Could not recover from critical failure, exiting.\n";
delete log; delete log;
return 3; return 3;
@ -604,7 +631,8 @@ int TroubleshootCommand::Run(const boost::program_options::variables_map& vm, co
if (!vm.count("console")) { if (!vm.count("console")) {
std::cout std::cout
<< "\nFinished collection. See '" << path << "'\n"; << "\nFinished collection. See '" << path << "'\n"
<< "Please compress these files with tar or zip before uploading them\n";
} }
delete log; delete log;