From: Gunnar Beutner Date: Fri, 17 Oct 2014 07:45:46 +0000 (+0200) Subject: Implement generic color support for terminals X-Git-Tag: v2.2.0~346 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8cc6368954843b787bb7ef1924b8912c7ef36512;p=icinga2 Implement generic color support for terminals fixes #7396 --- diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 9b37a2e61..acae5d544 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -29,6 +29,7 @@ #include "base/scriptvariable.hpp" #include "base/context.hpp" #include "base/clicommand.hpp" +#include "base/console.hpp" #include "config.h" #include #include @@ -166,6 +167,9 @@ int Main(void) visibleDesc.add_options() ("help", "show this help message") ("version,V", "show version information") +#ifndef _WIN32 + ("color", "use VT100 color codes even when stdout is not a terminal") +#endif /* _WIN32 */ ("define,D", po::value >(), "define a constant") ("library,l", po::value >(), "load a library") ("include,I", po::value >(), "add include search directory") @@ -201,6 +205,13 @@ int Main(void) ConfigCompiler::CompileFile(initconfig); } +#ifndef _WIN32 + if (vm.count("color")) { + Console::SetType(std::cout, Console_VT100); + Console::SetType(std::cerr, Console_VT100); + } +#endif /* _WIN32 */ + if (vm.count("define")) { BOOST_FOREACH(const String& define, vm["define"].as >()) { String key, value; diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 2374c6716..1ce98e09f 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -23,7 +23,7 @@ mkclass_target(streamlogger.ti streamlogger.thpp) mkclass_target(sysloglogger.ti sysloglogger.thpp) set(base_SOURCES - application.cpp application.thpp array.cpp clicommand.cpp configerror.cpp context.cpp + application.cpp application.thpp array.cpp clicommand.cpp configerror.cpp console.cpp context.cpp convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp exception.cpp fifo.cpp filelogger.cpp filelogger.thpp logger.cpp logger.thpp netstring.cpp networkstream.cpp object.cpp objectlock.cpp process.cpp diff --git a/lib/base/console.cpp b/lib/base/console.cpp new file mode 100644 index 000000000..75b1d9f1f --- /dev/null +++ b/lib/base/console.cpp @@ -0,0 +1,221 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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 "base/console.hpp" +#include "base/initialize.hpp" + +using namespace icinga; + +INITIALIZE_ONCE(&Console::DetectType); + +static ConsoleType l_ConsoleType = Console_Dumb; + +ConsoleColorTag::ConsoleColorTag(int color) + : m_Color(color) +{ } + +std::ostream& icinga::operator<<(std::ostream& fp, const ConsoleColorTag& cct) +{ + fp.flush(); + +#ifndef _WIN32 + if (l_ConsoleType == Console_VT100) + Console::PrintVT100ColorCode(fp, cct.m_Color); +#else /* _WIN32 */ + if (l_ConsoleType == Console_Windows) { + fp.flush(); + Console::SetWindowsConsoleColor(fp, cct.m_Color); + } +#endif /* _WIN32 */ + + return fp; +} + +void Console::DetectType(void) +{ + l_ConsoleType = Console_Dumb; + +#ifndef _WIN32 + if (isatty(1)) + l_ConsoleType = Console_VT100; +#else /* _WIN32 */ + l_ConsoleType = Console_Windows; +#endif /* _WIN32 */ +} + +void Console::SetType(std::ostream& fp, ConsoleType type) +{ + if (&fp == &std::cout || &fp == &std::cerr) + l_ConsoleType = type; +} + +ConsoleType Console::GetType(std::ostream& fp) +{ + if (&fp == &std::cout || &fp == &std::cerr) + return l_ConsoleType; + else + return Console_Dumb; +} + +#ifndef _WIN32 +void Console::PrintVT100ColorCode(std::ostream& fp, int color) +{ + if (color == Console_Normal) { + fp << "\33[0m"; + return; + } + + switch (color & 0xff) { + case Console_ForegroundBlack: + fp << "\33[30m"; + break; + case Console_ForegroundRed: + fp << "\33[31m"; + break; + case Console_ForegroundGreen: + fp << "\33[32m"; + break; + case Console_ForegroundYellow: + fp << "\33[33m"; + break; + case Console_ForegroundBlue: + fp << "\33[34m"; + break; + case Console_ForegroundMagenta: + fp << "\33[35m"; + break; + case Console_ForegroundCyan: + fp << "\33[36m"; + break; + case Console_ForegroundWhite: + fp << "\33[37m"; + break; + } + + switch (color & 0xff00) { + case Console_BackgroundBlack: + fp << "\33[40m"; + break; + case Console_BackgroundRed: + fp << "\33[41m"; + break; + case Console_BackgroundGreen: + fp << "\33[42m"; + break; + case Console_BackgroundYellow: + fp << "\33[43m"; + break; + case Console_BackgroundBlue: + fp << "\33[44m"; + break; + case Console_BackgroundMagenta: + fp << "\33[45m"; + break; + case Console_BackgroundCyan: + fp << "\33[46m"; + break; + case Console_BackgroundWhite: + fp << "\33[47m"; + break; + } + + if (color & Console_Bold) + fp << "\33[1m"; +} +#else /* _WIN32 */ +void Console::SetWindowsConsoleColor(std::ostream& fp, int color) +{ + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + HANDLE hConsole; + + if (&fp == &std::cout) + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + else if (&fp == &std::cerr) + hConsole = GetStdHandle(STD_ERROR_HANDLE); + else + return; + + if (!GetConsoleScreenBufferInfo(hConsole, &consoleInfo)) + return; + + WORD attrs = 0; + + if (color == Console_Normal) + attrs = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + + switch (color & 0xff) { + case Console_ForegroundBlack: + attrs |= 0; + break; + case Console_ForegroundRed: + attrs |= FOREGROUND_RED; + break; + case Console_ForegroundGreen: + attrs |= FOREGROUND_GREEN; + break; + case Console_ForegroundYellow: + attrs |= FOREGROUND_RED | FOREGROUND_GREEN; + break; + case Console_ForegroundBlue: + attrs |= FOREGROUND_BLUE; + break; + case Console_ForegroundMagenta: + attrs |= FOREGROUND_RED | FOREGROUND_BLUE; + break; + case Console_ForegroundCyan: + attrs |= FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + case Console_ForegroundWhite: + attrs |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + } + + switch (color & 0xff00) { + case Console_BackgroundBlack: + attrs |= 0; + break; + case Console_BackgroundRed: + attrs |= BACKGROUND_RED; + break; + case Console_BackgroundGreen: + attrs |= BACKGROUND_GREEN; + break; + case Console_BackgroundYellow: + attrs |= BACKGROUND_RED | BACKGROUND_GREEN; + break; + case Console_BackgroundBlue: + attrs |= BACKGROUND_BLUE; + break; + case Console_BackgroundMagenta: + attrs |= BACKGROUND_RED | BACKGROUND_BLUE; + break; + case Console_BackgroundCyan: + attrs |= BACKGROUND_GREEN | BACKGROUND_BLUE; + break; + case Console_BackgroundWhite: + attrs |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; + break; + } + + if (color & Console_Bold) + attrs |= FOREGROUND_INTENSITY; + + SetConsoleTextAttribute(hConsole, attrs); +} +#endif /* _WIN32 */ \ No newline at end of file diff --git a/lib/base/console.hpp b/lib/base/console.hpp new file mode 100644 index 000000000..cf9d7fd0a --- /dev/null +++ b/lib/base/console.hpp @@ -0,0 +1,103 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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 CONSOLE_H +#define CONSOLE_H + +#include "base/i2-base.hpp" +#include + +namespace icinga +{ + +enum ConsoleColor +{ + Console_Normal, + + // bit 0-7: foreground + Console_ForegroundBlack = 1, + Console_ForegroundRed = 2, + Console_ForegroundGreen = 3, + Console_ForegroundYellow = 4, + Console_ForegroundBlue = 5, + Console_ForegroundMagenta = 6, + Console_ForegroundCyan = 7, + Console_ForegroundWhite = 8, + + // bit 8-15: background + Console_BackgroundBlack = 256, + Console_BackgroundRed = 266, + Console_BackgroundGreen = 267, + Console_BackgroundYellow = 268, + Console_BackgroundBlue = 269, + Console_BackgroundMagenta = 270, + Console_BackgroundCyan = 271, + Console_BackgroundWhite = 272, + + // bit 16-23: flags + Console_Bold = 65536 +}; + +enum ConsoleType +{ + Console_Dumb, +#ifndef _WIN32 + Console_VT100, +#else /* _WIN32 */ + Console_Windows, +#endif /* _WIN32 */ +}; + +class I2_BASE_API ConsoleColorTag +{ +public: + ConsoleColorTag(int color); + + friend I2_BASE_API std::ostream& operator<<(std::ostream& fp, const ConsoleColorTag& cct); + +private: + int m_Color; +}; + +/** + * Console utilities. + * + * @ingroup base + */ +class I2_BASE_API Console +{ +public: + static void DetectType(void); + + static void SetType(std::ostream& fp, ConsoleType type); + static ConsoleType GetType(std::ostream& fp); + +#ifndef _WIN32 + static void PrintVT100ColorCode(std::ostream& fp, int color); +#else /* _WIN32 */ + static void SetWindowsConsoleColor(std::ostream& fp, int color); +#endif /* _WIN32 */ + +private: + Console(void); +}; + +} + +#endif /* CONSOLE_H */ diff --git a/lib/base/logger.cpp b/lib/base/logger.cpp index 10ed3f842..205103199 100644 --- a/lib/base/logger.cpp +++ b/lib/base/logger.cpp @@ -106,11 +106,8 @@ void icinga::Log(LogSeverity severity, const String& facility, logger->ProcessLogEntry(entry); } - if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity()) { - static bool tty = StreamLogger::IsTty(std::cout); - - StreamLogger::ProcessLogEntry(std::cout, tty, entry); - } + if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity()) + StreamLogger::ProcessLogEntry(std::cout, entry); } /** diff --git a/lib/base/streamlogger.cpp b/lib/base/streamlogger.cpp index a10682b71..34c478a0c 100644 --- a/lib/base/streamlogger.cpp +++ b/lib/base/streamlogger.cpp @@ -20,6 +20,7 @@ #include "base/streamlogger.hpp" #include "base/utility.hpp" #include "base/objectlock.hpp" +#include "base/console.hpp" #include using namespace icinga; @@ -78,7 +79,6 @@ void StreamLogger::BindStream(std::ostream *stream, bool ownsStream) m_Stream = stream; m_OwnsStream = ownsStream; - m_Tty = IsTty(*stream); m_FlushLogTimer = make_shared(); m_FlushLogTimer->SetInterval(1); @@ -90,10 +90,9 @@ void StreamLogger::BindStream(std::ostream *stream, bool ownsStream) * Processes a log entry and outputs it to a stream. * * @param stream The output stream. - * @param tty Whether the output stream is a TTY. * @param entry The log entry. */ -void StreamLogger::ProcessLogEntry(std::ostream& stream, bool tty, const LogEntry& entry) +void StreamLogger::ProcessLogEntry(std::ostream& stream, const LogEntry& entry) { String timestamp = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", entry.Timestamp); @@ -101,35 +100,28 @@ void StreamLogger::ProcessLogEntry(std::ostream& stream, bool tty, const LogEntr stream << "[" << timestamp << "] "; - if (tty) { - switch (entry.Severity) { - case LogNotice: - stream << "\x1b[1;34m"; // blue - break; - case LogInformation: - stream << "\x1b[1;32m"; // green - break; - case LogWarning: - stream << "\x1b[1;33m"; // yellow; - break; - case LogCritical: - stream << "\x1b[1;31m"; // red - break; - default: - break; - } + ConsoleColor color; + + switch (entry.Severity) { + case LogNotice: + color = Console_ForegroundBlue; + break; + case LogInformation: + color = Console_ForegroundGreen; + break; + case LogWarning: + color = Console_ForegroundYellow; + break; + case LogCritical: + color = Console_ForegroundRed; + break; + default: + return; } - try { - stream << Logger::SeverityToString(entry.Severity); - } catch (const std::exception&) { - /* bail early */ - return; - } - - if (tty) - stream << "\x1b[0m"; // clear colors - + stream << ConsoleColorTag(color); + stream << Logger::SeverityToString(entry.Severity); + stream << ConsoleColorTag(Console_Normal); stream << "/" << entry.Facility << ": " << entry.Message << "\n"; } @@ -140,25 +132,6 @@ void StreamLogger::ProcessLogEntry(std::ostream& stream, bool tty, const LogEntr */ void StreamLogger::ProcessLogEntry(const LogEntry& entry) { - ProcessLogEntry(*m_Stream, m_Tty, entry); + ProcessLogEntry(*m_Stream, entry); } -/** - * Checks whether the specified stream is a terminal. - * - * @param stream The stream. - * @returns true if the stream is a terminal, false otherwise. - */ -bool StreamLogger::IsTty(std::ostream& stream) -{ -#ifndef _WIN32 - /* Eww... */ - if (&stream == &std::cout) - return isatty(fileno(stdout)); - - if (&stream == &std::cerr) - return isatty(fileno(stderr)); -#endif /*_ WIN32 */ - - return false; -} diff --git a/lib/base/streamlogger.hpp b/lib/base/streamlogger.hpp index 8c898ab25..bc340f6a9 100644 --- a/lib/base/streamlogger.hpp +++ b/lib/base/streamlogger.hpp @@ -44,8 +44,7 @@ public: void BindStream(std::ostream *stream, bool ownsStream); - static void ProcessLogEntry(std::ostream& stream, bool tty, const LogEntry& entry); - static bool IsTty(std::ostream& stream); + static void ProcessLogEntry(std::ostream& stream, const LogEntry& entry); protected: virtual void ProcessLogEntry(const LogEntry& entry); diff --git a/lib/cli/objectlistcommand.cpp b/lib/cli/objectlistcommand.cpp index 76ec05a8b..9c5a40351 100644 --- a/lib/cli/objectlistcommand.cpp +++ b/lib/cli/objectlistcommand.cpp @@ -29,6 +29,7 @@ #include "base/stdiostream.hpp" #include "base/debug.hpp" #include "base/objectlock.hpp" +#include "base/console.hpp" #include #include #include @@ -144,8 +145,8 @@ void ObjectListCommand::PrintObject(std::ostream& fp, bool& first, const String& else fp << "Object '"; - fp << "\x1b[1;34m" << internal_name << "\x1b[0m" << "'"; //blue - fp << " of type '" << "\x1b[1;34m" << type << "\x1b[0m" << "':\n"; //blue + fp << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << internal_name << ConsoleColorTag(Console_Normal) << "'"; + fp << " of type '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << type << ConsoleColorTag(Console_Normal) << "':\n"; PrintProperties(fp, properties, debug_hints, 2); @@ -166,7 +167,7 @@ void ObjectListCommand::PrintProperties(std::ostream& fp, const Dictionary::Ptr& Value val = kv.second; /* key & value */ - fp << std::setw(indent) << " " << "* " << "\x1b[1;32m" << key << "\x1b[0m"; //green + fp << std::setw(indent) << " " << "* " << ConsoleColorTag(Console_ForegroundGreen) << key << ConsoleColorTag(Console_Normal); /* extract debug hints for key */ Dictionary::Ptr debug_hints_fwd; @@ -201,8 +202,8 @@ void ObjectListCommand::PrintHints(std::ostream& fp, const Dictionary::Ptr& debu void ObjectListCommand::PrintHint(std::ostream& fp, const Array::Ptr& msg, int indent) { - fp << std::setw(indent) << " " << "\x1b[1;36m" "% " << msg->Get(0) << " modified in '" << msg->Get(1) << "', lines " - << msg->Get(2) << ":" << msg->Get(3) << "-" << msg->Get(4) << ":" << msg->Get(5) << "\x1b[0m" "\n"; //cyan + fp << std::setw(indent) << " " << ConsoleColorTag(Console_ForegroundCyan) << "% " << msg->Get(0) << " modified in '" << msg->Get(1) << "', lines " + << msg->Get(2) << ":" << msg->Get(3) << "-" << msg->Get(4) << ":" << msg->Get(5) << ConsoleColorTag(Console_Normal) << "\n"; } void ObjectListCommand::PrintTypeCounts(std::ostream& fp, const std::map& type_count) diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 7122452fa..44f941cdc 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -302,7 +302,10 @@ void ConfigItem::WriteObjectsFile(const String& filename) persistentItem->Set("type", item->GetType()); persistentItem->Set("name", item->GetName()); persistentItem->Set("abstract", item->IsAbstract()); - persistentItem->Set("properties", item->GetProperties()); + { + ObjectLock olock(item); + persistentItem->Set("properties", item->GetProperties()); + } persistentItem->Set("debug_hints", item->GetDebugHints()); String json = JsonSerialize(persistentItem);