]> granicus.if.org Git - icinga2/commitdiff
Implement generic color support for terminals
authorGunnar Beutner <gunnar@beutner.name>
Fri, 17 Oct 2014 07:45:46 +0000 (09:45 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Fri, 17 Oct 2014 18:44:17 +0000 (20:44 +0200)
fixes #7396

icinga-app/icinga.cpp
lib/base/CMakeLists.txt
lib/base/console.cpp [new file with mode: 0644]
lib/base/console.hpp [new file with mode: 0644]
lib/base/logger.cpp
lib/base/streamlogger.cpp
lib/base/streamlogger.hpp
lib/cli/objectlistcommand.cpp
lib/config/configitem.cpp

index 9b37a2e61d735b47797992aef7cb97921002a3c2..acae5d54410f3dbafc63a68a7c7b2e35a01fbbbf 100644 (file)
@@ -29,6 +29,7 @@
 #include "base/scriptvariable.hpp"
 #include "base/context.hpp"
 #include "base/clicommand.hpp"
+#include "base/console.hpp"
 #include "config.h"
 #include <boost/program_options.hpp>
 #include <boost/tuple/tuple.hpp>
@@ -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<std::vector<std::string> >(), "define a constant")
                ("library,l", po::value<std::vector<std::string> >(), "load a library")
                ("include,I", po::value<std::vector<std::string> >(), "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<std::vector<std::string> >()) {
                        String key, value;
index 2374c6716b403fea928c73c947bb576ffceef98c..1ce98e09f5cd7fb082731f0c552a4dfb918102e5 100644 (file)
@@ -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 (file)
index 0000000..75b1d9f
--- /dev/null
@@ -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 (file)
index 0000000..cf9d7fd
--- /dev/null
@@ -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 <ostream>
+
+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 */
index 10ed3f842eccbe47ad1654dedfbb2dd7db96e847..205103199557a6e10f69356ce7ede366a7f0ea16 100644 (file)
@@ -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);
 }
 
 /**
index a10682b71570b319b7f7f9eb182a075bd85b8171..34c478a0c19af6a387da769b938d3c0547b6f1d1 100644 (file)
@@ -20,6 +20,7 @@
 #include "base/streamlogger.hpp"
 #include "base/utility.hpp"
 #include "base/objectlock.hpp"
+#include "base/console.hpp"
 #include <iostream>
 
 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<Timer>();
        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;
-}
index 8c898ab25f3b1900fe3111e4d316c4da700c5c5c..bc340f6a9e2e48e938982e754b38e89a00e2e73e 100644 (file)
@@ -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);
index 76ec05a8be7e11fb1c63e8cab01bf9cbbb4ec701..9c5a40351555889e1d089019f91c0dd92da3c336 100644 (file)
@@ -29,6 +29,7 @@
 #include "base/stdiostream.hpp"
 #include "base/debug.hpp"
 #include "base/objectlock.hpp"
+#include "base/console.hpp"
 #include <boost/foreach.hpp>
 #include <boost/algorithm/string/join.hpp>
 #include <boost/algorithm/string/replace.hpp>
@@ -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<String, int>& type_count)
index 7122452fa4c900d0b416028a90c7bc2704ca612e..44f941cdc3a34afe5f5ea87e3f2b2e219f3322d1 100644 (file)
@@ -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);