Browse Source

- Implemented the new logging API(not used currently) - Added lock typedefs to Global.h - Some layout updates to Global.h

beegee1 12 years ago
parent
commit
0baa261dfc

+ 108 - 90
Global.h

@@ -1,14 +1,3 @@
-#pragma once
-
-// Standard include file
-// Contents:
-// Includes C/C++ libraries, STL libraries, IOStream and String libraries
-// Includes the most important boost headers
-// Defines the import + export, override and exception handling macros
-// Defines the vstd library
-// Includes the logger
-
-// This file shouldn't be changed, except if there is a important header file missing which is shared among several projects.
 
 /*
  * Global.h, part of VCMI engine
@@ -20,11 +9,59 @@
  *
  */
 
+#pragma once
+
+/* ---------------------------------------------------------------------------- */
+/* Compiler detection */
+/* ---------------------------------------------------------------------------- */
+// Fixed width bool data type is important for serialization
+static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
+
+#if defined _M_X64 && defined _WIN32 //Win64 -> cannot load 32-bit DLLs for video handling
+    #define DISABLE_VIDEO
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__)
+#endif
+
+#if defined(__GNUC__) && (GCC_VERSION == 470 || GCC_VERSION == 471)
+#error This GCC version has buggy std::array::at version and should not be used. Please update to 4.7.2 or use 4.6.x.
+#endif
+
+/* ---------------------------------------------------------------------------- */
+/* Guarantee compiler features */
+/* ---------------------------------------------------------------------------- */
+//defining available c++11 features
+
+//initialization lists - only gcc-4.4 or later
+#if defined(__clang__) || (defined(__GNUC__) && (GCC_VERSION >= 440))
+#define CPP11_USE_INITIALIZERS_LIST
+#endif
+
+//nullptr -  only msvc and gcc-4.6 or later, othervice define it  as NULL
+#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 460))
+#define nullptr NULL
+#endif
+
+//override keyword - only msvc and gcc-4.7 or later.
+#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 470))
+#define override
+#endif
+
+//workaround to support existing code
+#define OVERRIDE override
+
+/* ---------------------------------------------------------------------------- */
+/* Suppress some compiler warnings */
+/* ---------------------------------------------------------------------------- */
 #ifdef _MSC_VER
 #pragma warning (disable : 4800 ) /* disable conversion to bool warning -- I think it's intended in all places */
-#endif //_MSC_VER
-
+#endif
 
+/* ---------------------------------------------------------------------------- */
+/* Commonly used C++, Boost headers */
+/* ---------------------------------------------------------------------------- */
 #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
 #include <cstdio>
 #include <stdio.h>
@@ -50,7 +87,6 @@
 #include <set>
 #include <sstream>
 #include <string>
-//#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -70,6 +106,7 @@
 #include <boost/bind.hpp>
 #include <boost/cstdint.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/date_time/posix_time/posix_time_io.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/foreach.hpp>
 #include <boost/format.hpp>
@@ -90,6 +127,17 @@
 #include <android/log.h>
 #endif
 
+/* ---------------------------------------------------------------------------- */
+/* Usings */
+/* ---------------------------------------------------------------------------- */
+using std::shared_ptr;
+using std::unique_ptr;
+using std::make_shared;
+namespace range = boost::range;
+
+/* ---------------------------------------------------------------------------- */
+/* Typedefs */
+/* ---------------------------------------------------------------------------- */
 // Integral data types
 typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes)
 typedef boost::uint32_t ui32;  //unsigned int 32 bits (4 bytes)
@@ -100,21 +148,14 @@ typedef boost::int32_t si32; //signed int 32 bits (4 bytes)
 typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
 typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 
-// Fixed width bool data type is important for serialization
-static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
-
-#if defined _M_X64 && defined _WIN32 //Win64 -> cannot load 32-bit DLLs for video handling
-	#define DISABLE_VIDEO
-#endif
-
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__)
-#endif
-
-#if defined(__GNUC__) && (GCC_VERSION == 470 || GCC_VERSION == 471)
-#error This GCC version has buggy std::array::at version and should not be used. Please update to 4.7.2 or use 4.6.x.
-#endif
+// Lock typedefs
+typedef boost::unique_lock<boost::shared_mutex> TWriteLock;
+typedef boost::shared_lock<boost::shared_mutex> TReadLock;
+typedef boost::lock_guard<boost::mutex> TLockGuard;
 
+/* ---------------------------------------------------------------------------- */
+/* Macros */
+/* ---------------------------------------------------------------------------- */
 // Import + Export macro declarations
 #ifdef _WIN32
 #ifdef __GNUC__
@@ -150,26 +191,46 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #define DLL_LINKAGE DLL_IMPORT
 #endif
 
-//defining available c++11 features
+#define THROW_FORMAT(message, formatting_elems)  throw std::runtime_error(boost::str(boost::format(message) % formatting_elems))
 
-//initialization lists - only gcc-4.4 or later
-#if defined(__clang__) || (defined(__GNUC__) && (GCC_VERSION >= 440))
-#define CPP11_USE_INITIALIZERS_LIST
-#endif
+#define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {tlog1 << __FUNCTION__ << "\n"; assert(0);}
 
-//nullptr -  only msvc and gcc-4.6 or later, othervice define it  as NULL
-#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 460))
-#define nullptr NULL
-#endif
+//XXX pls dont - 'debug macros' are usually more trouble than it's worth
+#define HANDLE_EXCEPTION  \
+    catch (const std::exception& e) {	\
+    tlog1 << e.what() << std::endl;		\
+    throw;								\
+}									\
+    catch (const std::exception * e)	\
+{									\
+    tlog1 << e->what()<< std::endl;	\
+    throw;							\
+}									\
+    catch (const std::string& e) {		\
+    tlog1 << e << std::endl;		\
+    throw;							\
+}
 
-//override keyword - only msvc and gcc-4.7 or later.
-#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 470))
-#define override
-#endif
+#define HANDLE_EXCEPTIONC(COMMAND)  \
+    catch (const std::exception& e) {	\
+    COMMAND;						\
+    tlog1 << e.what() << std::endl;	\
+    throw;							\
+}									\
+    catch (const std::string &e)	\
+{									\
+    COMMAND;						\
+    tlog1 << e << std::endl;	\
+    throw;							\
+}
 
-//workaround to support existing code
-#define OVERRIDE override
+// can be used for counting arrays
+template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
+#define ARRAY_COUNT(arr)    (sizeof(_ArrayCountObj(arr)))
 
+/* ---------------------------------------------------------------------------- */
+/* VCMI standard library */
+/* ---------------------------------------------------------------------------- */
 //a normal std::map with a const operator[] for sanity
 template<typename KeyT, typename ValT>
 class bmap : public std::map<KeyT, ValT>
@@ -508,53 +569,10 @@ namespace vstd
 		obj = (T)(((int)obj) + change);
 	}
 }
-
-using std::shared_ptr;
-using std::unique_ptr;
-using std::make_shared;
-using vstd::make_unique;
-
 using vstd::operator-=;
+using vstd::make_unique;
 
-namespace range = boost::range;
-
-// can be used for counting arrays
-template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
-#define ARRAY_COUNT(arr)    (sizeof(_ArrayCountObj(arr)))
-
-
-#define THROW_FORMAT(message, formatting_elems)  throw std::runtime_error(boost::str(boost::format(message) % formatting_elems))
-
-#define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {tlog1 << __FUNCTION__ << "\n"; assert(0);}
-
-//XXX pls dont - 'debug macros' are usually more trouble than it's worth
-#define HANDLE_EXCEPTION  \
-	catch (const std::exception& e) {	\
-	tlog1 << e.what() << std::endl;		\
-	throw;								\
-}									\
-	catch (const std::exception * e)	\
-{									\
-	tlog1 << e->what()<< std::endl;	\
-	throw;							\
-}									\
-	catch (const std::string& e) {		\
-	tlog1 << e << std::endl;		\
-	throw;							\
-}
-
-#define HANDLE_EXCEPTIONC(COMMAND)  \
-	catch (const std::exception& e) {	\
-	COMMAND;						\
-	tlog1 << e.what() << std::endl;	\
-	throw;							\
-}									\
-	catch (const std::string &e)	\
-{									\
-	COMMAND;						\
-	tlog1 << e << std::endl;	\
-	throw;							\
-}
-
-
+/* ---------------------------------------------------------------------------- */
+/* VCMI headers */
+/* ---------------------------------------------------------------------------- */
 #include "lib/CLogger.h"

+ 67 - 1
config/schemas/settings.json

@@ -3,7 +3,7 @@
 {
 	"type" : "object",
 	"$schema": "http://json-schema.org/draft-04/schema",
-	"required" : [ "general", "video", "adventure", "battle", "server" ],
+	"required" : [ "general", "video", "adventure", "battle", "server", "logging" ],
 	"properties":
 	{
 		"general" : {
@@ -133,6 +133,72 @@
 					"default" : "StupidAI"
 				}
 			}
+		},
+		"logging" : {
+			"type" : "object",
+			"default" : {},
+			"properties" : {
+				"console" : {
+					"type" : "object",
+					"properties" : {
+						"format" : {
+							"type" : "string",
+							"default" : "%l %n [%t] - %m"
+						},
+						"threshold" : {
+							"type" : "string",
+							"default" : "info",
+							"enum" : [ "trace", "debug", "info", "warn", "error" ]
+						},
+						"coloredOutputEnabled" : {
+							"type" : "boolean",
+							"default" : true
+						},
+						"colorMapping" : {
+							"type" : "array",
+							"default" : [
+								{ "domain" : "global", "level" : "trace", "color" : "gray"},
+								{ "domain" : "global", "level" : "debug", "color" : "white"},
+								{ "domain" : "global", "level" : "info", "color" : "green"},
+								{ "domain" : "global", "level" : "warn", "color" : "yellow"},
+								{ "domain" : "global", "level" : "error", "color" : "red"}
+							],
+							"items" : {
+								"type" : "object",
+								"required" : [ "domain", "level", "color" ],
+								"properties" : {
+									"domain" : { "type" : "string" },
+									"level" : { "type" : "string", "enum" : [ "trace", "debug", "info", "warn", "error" ] },
+									"color" : { "type" : "string", "enum" : [ "default", "green", "red", "magenta", "yellow", "white", "gray", "teal" ] }
+								}
+							}
+						}
+					}
+				},
+				"file" : {
+					"type" : "object",
+					"properties" : {
+						"format" : {
+							"type" : "string",
+							"default" : "%d %l %n [%t] - %m"
+						}
+					}
+				},
+				"loggers" : {
+					"type" : "object",
+					"additionalProperties" : {
+						"type":"object",
+						"required" : [ "level" ],
+						"properties" : {
+							"level" : {
+								"type" : "string",
+								"enum" : [ "trace", "debug", "info", "warn", "error" ]
+							}
+						}
+
+					}
+				}
+			}
 		}
 	}
 }

+ 24 - 25
lib/CConsoleHandler.cpp

@@ -141,43 +141,43 @@ LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception)
 #endif
 
 
-void CConsoleHandler::setColor(int level)
+void CConsoleHandler::setColor(EConsoleTextColor::EConsoleTextColor color)
 {
-	TColor color;
-	switch(level)
+    TColor colorCode;
+    switch(color)
 	{
-	case -1:
-		color = defColor;
+    case EConsoleTextColor::DEFAULT:
+        colorCode = defColor;
 		break;
-	case 0:
-		color = CONSOLE_GREEN;
+    case EConsoleTextColor::GREEN:
+        colorCode = CONSOLE_GREEN;
 		break;
-	case 1:
-		color = CONSOLE_RED;
+    case EConsoleTextColor::RED:
+        colorCode = CONSOLE_RED;
 		break;
-	case 2:
-		color = CONSOLE_MAGENTA;
+    case EConsoleTextColor::MAGENTA:
+        colorCode = CONSOLE_MAGENTA;
 		break;
-	case 3:
-		color = CONSOLE_YELLOW;
+    case EConsoleTextColor::YELLOW:
+        colorCode = CONSOLE_YELLOW;
 		break;
-	case 4:
-		color = CONSOLE_WHITE;
+    case EConsoleTextColor::WHITE:
+        colorCode = CONSOLE_WHITE;
 		break;
-	case 5:
-		color = CONSOLE_GRAY;
+    case EConsoleTextColor::GRAY:
+        colorCode = CONSOLE_GRAY;
 		break;
-	case -2:
-		color = CONSOLE_TEAL;
+    case EConsoleTextColor::TEAL:
+        colorCode = CONSOLE_TEAL;
 		break;
 	default:
-		color = defColor;
+        colorCode = defColor;
 		break;
 	}
 #ifdef _WIN32
-	SetConsoleTextAttribute(handleOut,color);
+    SetConsoleTextAttribute(handleOut, colorCode);
 #else
-	std::cout << color;
+    std::cout << colorCode;
 #endif
 }
 
@@ -210,7 +210,7 @@ int CConsoleHandler::run()
 	}
 	return -1;
 }
-CConsoleHandler::CConsoleHandler()
+CConsoleHandler::CConsoleHandler() : thread(nullptr)
 {
 #ifdef _WIN32
 	handleIn = GetStdHandle(STD_INPUT_HANDLE);
@@ -224,8 +224,7 @@ CConsoleHandler::CConsoleHandler()
 #else
 	defColor = "\x1b[0m";
 #endif
-	cb = new boost::function<void(const std::string &)>;
-	thread = NULL;
+    cb = new boost::function<void(const std::string &)>;
 }
 CConsoleHandler::~CConsoleHandler()
 {

+ 43 - 15
lib/CConsoleHandler.h

@@ -10,36 +10,64 @@
  *
  */
 
+namespace EConsoleTextColor
+{
+/** The color enum is used for colored text console output. */
+enum EConsoleTextColor
+{
+    DEFAULT = -1,
+    GREEN,
+    RED,
+    MAGENTA,
+    YELLOW,
+    WHITE,
+    GRAY,
+    TEAL = -2
+};
+}
 
 /// Class which wraps the native console. It can print text based on
 /// the chosen color
 class DLL_LINKAGE CConsoleHandler
 {
 public:
-	boost::function<void(const std::string &)> *cb; //function to be called when message is received
-	int curLvl; //logging level
-	boost::thread *thread;
+    CConsoleHandler(); //c-tor
+    ~CConsoleHandler(); //d-tor
+    void start(); //starts listening thread
 
-	int run();
-	void setColor(int level); //sets color of text appropriate for given logging level
-
-	CConsoleHandler(); //c-tor
-	~CConsoleHandler(); //d-tor
-	void start(); //starts listening thread
-	void end(); //kills listening thread
-
-	template<typename T> void print(const T &data, int lvl)
+    template<typename T> void print(const T &data, EConsoleTextColor::EConsoleTextColor color = EConsoleTextColor::DEFAULT, bool printToStdErr = false)
 	{
+        TLockGuard _(mx);
 #ifndef _WIN32
 		// with love from ffmpeg - library is trying to print some warnings from separate thread
 		// this results in broken console on Linux. Lock stdout to print all our data at once
 		flockfile(stdout);
 #endif
-		setColor(lvl);
-		std::cout << data << std::flush;
-		setColor(-1);
+        if(color != EConsoleTextColor::DEFAULT) setColor(color);
+        if(printToStdErr)
+        {
+            std::cerr << data << std::flush;
+        }
+        else
+        {
+            std::cout << data << std::flush;
+        }
+        if(color != EConsoleTextColor::DEFAULT) setColor(EConsoleTextColor::DEFAULT);
 #ifndef _WIN32
 		funlockfile(stdout);
 #endif
 	}
+
+    boost::function<void(const std::string &)> *cb; //function to be called when message is received
+
+private:
+    int run();
+
+    void end(); //kills listening thread
+
+    void setColor(EConsoleTextColor::EConsoleTextColor color); //sets color of text appropriate for given logging level
+
+    mutable boost::mutex mx;
+
+    boost::thread * thread;
 };

+ 1 - 1
lib/CLogger.h

@@ -41,7 +41,7 @@ public:
 		if(lvl < CLogger::CONSOLE_LOGGING_LEVEL)
 		{
 			if(console)
-				console->print(data, lvl);
+                console->print(data, static_cast<EConsoleTextColor::EConsoleTextColor>(lvl));
 			else
 				std::cout << data << std::flush;
 		}

+ 14 - 0
lib/CMakeLists.txt

@@ -13,6 +13,12 @@ set(lib_SRCS
 		Filesystem/CResourceLoader.cpp
 		Filesystem/CFileInputStream.cpp
 		Filesystem/CCompressedStream.cpp
+		Logging/CBasicLogConfigurator.cpp
+		Logging/CLogConsoleTarget.cpp
+		Logging/CLogFileTarget.cpp
+		Logging/CLogFormatter.cpp
+		Logging/CLogger.cpp
+		Logging/CLogManager.cpp
 		Mapping/CCampaignHandler.cpp
 		Mapping/CMap.cpp
 		Mapping/CMapEditManager.cpp
@@ -59,6 +65,14 @@ set(lib_SRCS
 set(lib_HEADERS
 		Filesystem/CInputStream.h
 		Filesystem/ISimpleResourceLoader.h
+		Logging/CBasicLogConfigurator.h
+		Logging/CLogConsoleTarget.h
+		Logging/CLogFileTarget.h
+		Logging/CLogFormatter.h
+		Logging/CLogger.h
+		Logging/CLogManager.h
+		Logging/ILogTarget.h
+		Logging/LogRecord.h
 		Mapping/CCampaignHandler.h
 		Mapping/CMap.h
 		Mapping/CMapEditManager.h

+ 113 - 0
lib/Logging/CBasicLogConfigurator.cpp

@@ -0,0 +1,113 @@
+#include "StdInc.h"
+#include "CBasicLogConfigurator.h"
+
+#include "../CConfigHandler.h"
+#include "CLogConsoleTarget.h"
+#include "CLogFileTarget.h"
+
+CBasicLogConfigurator::CBasicLogConfigurator(const std::string & filePath, CConsoleHandler * console)
+{
+    const JsonNode & logging = settings["logging"];
+
+    // Configure loggers
+    const JsonNode & loggers = logging["loggers"];
+    if(!loggers.isNull())
+    {
+        BOOST_FOREACH(auto & loggerPair, loggers.Struct())
+        {
+            // Get logger
+            std::string name = loggerPair.first;
+            CGLogger * logger = CGLogger::getLogger(name);
+
+            // Set log level
+            const JsonNode & loggerNode = loggerPair.second;
+            const JsonNode & levelNode = loggerNode["level"];
+            if(!levelNode.isNull())
+            {
+                logger->setLevel(getLogLevel(levelNode.String()));
+            }
+        }
+    }
+
+    // Add console target
+    CLogConsoleTarget * consoleTarget = new CLogConsoleTarget(console);
+    const JsonNode & consoleNode = logging["console"];
+    if(!consoleNode.isNull())
+    {
+        const JsonNode & consoleFormatNode = consoleNode["format"];
+        if(!consoleFormatNode.isNull()) consoleTarget->setFormatter(CLogFormatter(consoleFormatNode.String()));
+        const JsonNode & consoleThresholdNode = consoleNode["threshold"];
+        if(!consoleThresholdNode.isNull()) consoleTarget->setThreshold(getLogLevel(consoleThresholdNode.String()));
+        const JsonNode & coloredConsoleEnabledNode = consoleNode["coloredOutputEnabled"];
+        consoleTarget->setColoredOutputEnabled(coloredConsoleEnabledNode.Bool());
+
+        CColorMapping colorMapping;
+        const JsonNode & colorMappingNode = consoleNode["colorMapping"];
+        if(!colorMappingNode.isNull())
+        {
+            BOOST_FOREACH(const JsonNode & mappingNode, colorMappingNode.Vector())
+            {
+                std::string domain = mappingNode["domain"].String();
+                std::string level = mappingNode["level"].String();
+                std::string color = mappingNode["color"].String();
+                colorMapping.setColorFor(domain, getLogLevel(level), getConsoleColor(color));
+            }
+        }
+        consoleTarget->setColorMapping(colorMapping);
+    }
+    CGLogger::getGlobalLogger()->addTarget(consoleTarget);
+
+    // Add file target
+    CLogFileTarget * fileTarget = new CLogFileTarget(filePath);
+    const JsonNode & fileNode = logging["file"];
+    if(!fileNode.isNull())
+    {
+        const JsonNode & fileFormatNode = fileNode["format"];
+        if(!fileFormatNode.isNull()) fileTarget->setFormatter(CLogFormatter(fileFormatNode.String()));
+    }
+
+    // Add targets to the root logger by default
+    CGLogger::getGlobalLogger()->addTarget(consoleTarget);
+    CGLogger::getGlobalLogger()->addTarget(fileTarget);
+}
+
+ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & level) const
+{
+    static const std::map<std::string, ELogLevel::ELogLevel> levelMap = boost::assign::map_list_of
+            ("trace", ELogLevel::TRACE)
+            ("debug", ELogLevel::DEBUG)
+            ("info", ELogLevel::INFO)
+            ("warn", ELogLevel::WARN)
+            ("error", ELogLevel::ERROR);
+    const auto & levelPair = levelMap.find(level);
+    if(levelPair != levelMap.end())
+    {
+        return levelPair->second;
+    }
+    else
+    {
+        throw std::runtime_error("Log level " + level + " unknown.");
+    }
+}
+
+EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName) const
+{
+    static const std::map<std::string, EConsoleTextColor::EConsoleTextColor> colorMap = boost::assign::map_list_of
+            ("default", EConsoleTextColor::DEFAULT)
+            ("green", EConsoleTextColor::GREEN)
+            ("red", EConsoleTextColor::RED)
+            ("magenta", EConsoleTextColor::MAGENTA)
+            ("yellow", EConsoleTextColor::YELLOW)
+            ("white", EConsoleTextColor::WHITE)
+            ("gray", EConsoleTextColor::GRAY)
+            ("teal", EConsoleTextColor::TEAL);
+    const auto & colorPair = colorMap.find(colorName);
+    if(colorPair != colorMap.end())
+    {
+        return colorPair->second;
+    }
+    else
+    {
+        throw std::runtime_error("Color " + colorName + " unknown.");
+    }
+}

+ 55 - 0
lib/Logging/CBasicLogConfigurator.h

@@ -0,0 +1,55 @@
+
+/*
+ * CBasicLogConfigurator.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "CLogger.h"
+
+class CConsoleHandler;
+
+/**
+ * The basic log configurator reads log properties from the settings.json and
+ * sets up the logging system. The file path of the log file can be specified
+ * via the constructor.
+ */
+class DLL_LINKAGE CBasicLogConfigurator
+{
+public:
+    /**
+     * Constructor.
+     *
+     * @param fileName The file path of the log file.
+     *
+     * @throws std::runtime_error if the configuration has errors
+     */
+    CBasicLogConfigurator(const std::string & filePath, CConsoleHandler * console);
+
+private:
+    /**
+     * Gets the log level enum from a string.
+     *
+     * @param level The log level string to parse.
+     * @return the corresponding log level enum value
+     *
+     * @throws std::runtime_error if the log level is unknown
+     */
+    ELogLevel::ELogLevel getLogLevel(const std::string & level) const;
+
+    /**
+     * Gets the console text color enum from a string.
+     *
+     * @param colorName The color string to parse.
+     * @return the corresponding console text color enum value
+     *
+     * @throws std::runtime_error if the log level is unknown
+     */
+    EConsoleTextColor::EConsoleTextColor getConsoleColor(const std::string & colorName) const;
+};

+ 126 - 0
lib/Logging/CLogConsoleTarget.cpp

@@ -0,0 +1,126 @@
+#include "StdInc.h"
+#include "CLogConsoleTarget.h"
+
+#include "LogRecord.h"
+
+CColorMapping::CColorMapping()
+{
+    // Set default mappings
+    auto & levelMap = map[""];
+    levelMap[ELogLevel::TRACE] = EConsoleTextColor::GRAY;
+    levelMap[ELogLevel::DEBUG] = EConsoleTextColor::WHITE;
+    levelMap[ELogLevel::INFO] = EConsoleTextColor::GREEN;
+    levelMap[ELogLevel::WARN] = EConsoleTextColor::YELLOW;
+    levelMap[ELogLevel::ERROR] = EConsoleTextColor::RED;
+}
+
+void CColorMapping::setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor::EConsoleTextColor color)
+{
+    if(level == ELogLevel::NOT_SET) throw std::runtime_error("Log level NOT_SET not allowed for configuring the color mapping.");
+    map[domain.getName()][level] = color;
+}
+
+EConsoleTextColor::EConsoleTextColor CColorMapping::getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const
+{
+    std::string name = domain.getName();
+    while(true)
+    {
+        const auto & loggerPair = map.find(name);
+        if(loggerPair != map.end())
+        {
+            const auto & levelMap = loggerPair->second;
+            const auto & levelPair = levelMap.find(level);
+            if(levelPair != levelMap.end())
+            {
+                return levelPair->second;
+            }
+        }
+
+        CLoggerDomain currentDomain(name);
+        if(!currentDomain.isGlobalDomain())
+        {
+            name = currentDomain.getParent().getName();
+        }
+        else
+        {
+            break;
+        }
+    }
+    throw std::runtime_error("No color mapping found. Should not happen.");
+}
+
+CLogConsoleTarget::CLogConsoleTarget(CConsoleHandler * console) : console(console), threshold(ELogLevel::INFO), coloredOutputEnabled(true)
+{
+
+}
+
+void CLogConsoleTarget::write(const LogRecord & record)
+{
+    if(threshold > record.level) return;
+
+    std::string message = formatter.format(record);
+    bool printToStdErr = record.level >= ELogLevel::WARN;
+    if(console)
+    {
+        if(coloredOutputEnabled)
+        {
+            console->print(message, colorMapping.getColorFor(record.domain, record.level));
+        }
+        else
+        {
+            console->print(message, EConsoleTextColor::DEFAULT, printToStdErr);
+        }
+    }
+    else
+    {
+        TLockGuard _(mx);
+        if(printToStdErr)
+        {
+            std::cerr << message << std::flush;
+        }
+        else
+        {
+            std::cout << message << std::flush;
+        }
+    }
+}
+
+bool CLogConsoleTarget::isColoredOutputEnabled() const
+{
+    return coloredOutputEnabled;
+}
+
+void CLogConsoleTarget::setColoredOutputEnabled(bool coloredOutputEnabled)
+{
+    this->coloredOutputEnabled = coloredOutputEnabled;
+}
+
+ELogLevel::ELogLevel CLogConsoleTarget::getThreshold() const
+{
+    return threshold;
+}
+
+void CLogConsoleTarget::setThreshold(ELogLevel::ELogLevel threshold)
+{
+    this->threshold = threshold;
+}
+
+const CLogFormatter & CLogConsoleTarget::getFormatter() const
+{
+    return formatter;
+}
+
+void CLogConsoleTarget::setFormatter(const CLogFormatter & formatter)
+{
+    this->formatter = formatter;
+}
+
+const CColorMapping & CLogConsoleTarget::getColorMapping() const
+{
+    return colorMapping;
+}
+
+void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping)
+{
+    this->colorMapping = colorMapping;
+}

+ 147 - 0
lib/Logging/CLogConsoleTarget.h

@@ -0,0 +1,147 @@
+
+/*
+ * CLogConsoleTarget.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "ILogTarget.h"
+#include "CLogger.h"
+#include "CLogFormatter.h"
+#include "CConsoleHandler.h"
+
+class LogRecord;
+
+/**
+ * The color mapping maps a logger name and a level to a specific color.
+ */
+class DLL_LINKAGE CColorMapping
+{
+public:
+    /**
+     * Constructor. There are default color mappings for the root logger, which child loggers inherit if not overriden.
+     */
+    CColorMapping();
+
+    /**
+     * Sets a console text color for a logger name and a level.
+     *
+     * @param domain The domain of the logger.
+     * @param level The logger level.
+     * @param color The console text color to use as the mapping.
+     */
+    void setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor::EConsoleTextColor color);
+
+    /**
+     * Gets a console text color for a logger name and a level.
+     *
+     * @param domain The domain of the logger.
+     * @param level The logger level.
+     * @return the console text color which has been applied for the mapping
+     */
+    EConsoleTextColor::EConsoleTextColor getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const;
+
+private:
+    /** The color mapping, 1. Key: Logger domain, 2. Key: Level, Value: Color. */
+    std::map<std::string, std::map<ELogLevel::ELogLevel, EConsoleTextColor::EConsoleTextColor> > map;
+};
+
+/**
+ * The console target is a logging target which writes message to the console.
+ */
+class DLL_LINKAGE CLogConsoleTarget : public ILogTarget
+{
+public:
+    /**
+     * Constructor.
+     *
+     * @param console Optional. The console handler which is used to output messages to the console.
+     */
+    explicit CLogConsoleTarget(CConsoleHandler * console);
+
+    /**
+     * Writes a log record.
+     *
+     * @param record The log record to write.
+     */
+    void write(const LogRecord & record);
+
+    /**
+     * True if colored output is enabled.
+     *
+     * @return true if colored output is enabled, false if not
+     */
+    bool isColoredOutputEnabled() const;
+
+    /**
+     * Sets the colored output flag.
+     *
+     * @param coloredOutput True if the log target should write colored messages to the console, false if not.
+     */
+    void setColoredOutputEnabled(bool coloredOutputEnabled);
+
+    /**
+     * Gets the threshold log level.
+     *
+     * @return the threshold log level
+     */
+    ELogLevel::ELogLevel getThreshold() const;
+
+    /**
+     * Sets the threshold log level.
+     *
+     * @param threshold The threshold log level.
+     */
+    void setThreshold(ELogLevel::ELogLevel threshold);
+
+    /**
+     * Gets the log formatter.
+     *
+     * @return The log formatter.
+     */
+    const CLogFormatter & getFormatter() const;
+
+    /**
+     * Sets the log formatter.
+     *
+     * @param formatter The log formatter.
+     */
+    void setFormatter(const CLogFormatter & formatter);
+
+    /**
+     * Gets the color mapping.
+     */
+    const CColorMapping & getColorMapping() const;
+
+    /**
+     * Sets the color mapping.
+     *
+     * @param colorMapping The color mapping.
+     */
+    void setColorMapping(const CColorMapping & colorMapping);
+
+private:
+    /** The console handler which is used to output messages to the console. */
+    CConsoleHandler * console;
+
+    /** The threshold log level. */
+    ELogLevel::ELogLevel threshold;
+
+    /** Flag whether colored console output is enabled. */
+    bool coloredOutputEnabled;
+
+    /** The log formatter to log messages. */
+    CLogFormatter formatter;
+
+    /** The color mapping which maps a logger and a log level to a color. */
+    CColorMapping colorMapping;
+
+    /** The shared mutex for providing synchronous thread-safe access to the log console target. */
+    mutable boost::mutex mx;
+};

+ 28 - 0
lib/Logging/CLogFileTarget.cpp

@@ -0,0 +1,28 @@
+#include "StdInc.h"
+#include "CLogFileTarget.h"
+
+CLogFileTarget::CLogFileTarget(const std::string & filePath) : file(filePath)
+{
+
+}
+
+CLogFileTarget::~CLogFileTarget()
+{
+    file.close();
+}
+
+void CLogFileTarget::write(const LogRecord & record)
+{
+    TLockGuard _(mx);
+    file << formatter.format(record) << std::endl;
+}
+
+const CLogFormatter & CLogFileTarget::getFormatter() const
+{
+    return formatter;
+}
+
+void CLogFileTarget::setFormatter(const CLogFormatter & formatter)
+{
+    this->formatter = formatter;
+}

+ 66 - 0
lib/Logging/CLogFileTarget.h

@@ -0,0 +1,66 @@
+
+/*
+ * CLogFileTarget.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "ILogTarget.h"
+#include "CLogFormatter.h"
+
+/**
+ * The log file target is a logging target which writes messages to a log file.
+ */
+class DLL_LINKAGE CLogFileTarget : public ILogTarget
+{
+public:
+    /**
+     * Constructor.
+     *
+     * @param filePath The file path of the log file.
+     */
+    explicit CLogFileTarget(const std::string & filePath);
+
+    /**
+     * Destructor.
+     */
+    ~CLogFileTarget();
+
+    /**
+     * Writes a log record.
+     *
+     * @param record The log record to write.
+     */
+    void write(const LogRecord & record);
+
+    /**
+     * Gets the log formatter.
+     *
+     * @return The log formatter.
+     */
+    const CLogFormatter & getFormatter() const;
+
+    /**
+     * Sets the log formatter.
+     *
+     * @param formatter The log formatter.
+     */
+    void setFormatter(const CLogFormatter & formatter);
+
+private:
+    /** The output file stream. */
+    std::ofstream file;
+
+    /** The log formatter to log messages. */
+    CLogFormatter formatter;
+
+    /** The shared mutex for providing synchronous thread-safe access to the log file target. */
+    mutable boost::mutex mx;
+};
+

+ 65 - 0
lib/Logging/CLogFormatter.cpp

@@ -0,0 +1,65 @@
+#include "StdInc.h"
+#include "CLogFormatter.h"
+
+#include "LogRecord.h"
+
+CLogFormatter::CLogFormatter() : pattern("%m")
+{
+
+}
+
+CLogFormatter::CLogFormatter(const std::string & pattern) : pattern(pattern)
+{
+
+}
+
+std::string CLogFormatter::format(const LogRecord & record) const
+{
+    std::string message = pattern;
+
+    // Format date
+    std::stringstream dateStream;
+    boost::posix_time::time_facet * facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S");
+    dateStream.imbue(std::locale(dateStream.getloc(), facet));
+    dateStream << record.timeStamp;
+    boost::algorithm::replace_all(message, "%d", dateStream.str());
+
+    // Format log level
+    std::string level;
+    switch(record.level)
+    {
+    case ELogLevel::TRACE:
+        level = "TRACE";
+        break;
+    case ELogLevel::DEBUG:
+        level = "DEBUG";
+        break;
+    case ELogLevel::INFO:
+        level = "INFO";
+        break;
+    case ELogLevel::WARN:
+        level = "WARN";
+        break;
+    case ELogLevel::ERROR:
+        level = "ERROR";
+        break;
+    }
+    boost::algorithm::replace_all(message, "%l", level);
+
+    // Format name, thread id and message
+    boost::algorithm::replace_all(message, "%n", record.domain.getName());
+    boost::algorithm::replace_all(message, "%t", boost::lexical_cast<std::string>(record.threadId));
+    boost::algorithm::replace_all(message, "%m", record.message);
+
+    return message;
+}
+
+void CLogFormatter::setPattern(const std::string & pattern)
+{
+    this->pattern = pattern;
+}
+
+const std::string & CLogFormatter::getPattern() const
+{
+    return pattern;
+}

+ 66 - 0
lib/Logging/CLogFormatter.h

@@ -0,0 +1,66 @@
+
+/*
+ * CLogFormatter.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+class LogRecord;
+
+/**
+ * The log formatter formats log records.
+ *
+ * There are several pattern characters which can be used to format a log record:
+ * %d = Date/Time
+ * %l = Log level
+ * %n = Logger name
+ * %t = Thread ID
+ * %m = Message
+ */
+class DLL_LINKAGE CLogFormatter
+{
+public:
+    /**
+     * Default constructor.
+     */
+    CLogFormatter();
+
+    /**
+     * Constructor.
+     *
+     * @param pattern The pattern to format the log record with.
+     */
+    CLogFormatter(const std::string & pattern);
+
+    /**
+     * Formats a log record.
+     *
+     * @param record The log record to format.
+     * @return the formatted log record as a string
+     */
+    std::string format(const LogRecord & record) const;
+
+    /**
+     * Sets the pattern.
+     *
+     * @param pattern The pattern string which describes how to format the log record.
+     */
+    void setPattern(const std::string & pattern);
+
+    /**
+     * Gets the pattern.
+     *
+     * @return the pattern string which describes how to format the log record
+     */
+    const std::string & getPattern() const;
+
+private:
+    /** The pattern string which describes how to format the log record. */
+    std::string pattern;
+};

+ 51 - 0
lib/Logging/CLogManager.cpp

@@ -0,0 +1,51 @@
+#include "StdInc.h"
+#include "CLogManager.h"
+
+#include "CLogger.h"
+
+CLogManager * CLogManager::instance = nullptr;
+
+boost::mutex CLogManager::smx;
+
+CLogManager * CLogManager::get()
+{
+    TLockGuard _(smx);
+    if(!instance)
+    {
+        instance = new CLogManager();
+    }
+    return instance;
+}
+
+CLogManager::CLogManager()
+{
+
+}
+
+CLogManager::~CLogManager()
+{
+    BOOST_FOREACH(auto & i, loggers)
+    {
+        delete i.second;
+    }
+}
+
+void CLogManager::addLogger(CGLogger * logger)
+{
+    TWriteLock _(mx);
+    loggers[logger->getDomain().getName()] = logger;
+}
+
+CGLogger * CLogManager::getLogger(const CLoggerDomain & domain)
+{
+    TReadLock _(mx);
+    auto it = loggers.find(domain.getName());
+    if(it != loggers.end())
+    {
+        return it->second;
+    }
+    else
+    {
+        return nullptr;
+    }
+}

+ 67 - 0
lib/Logging/CLogManager.h

@@ -0,0 +1,67 @@
+
+/*
+ * CLogManager.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+class CGLogger;
+class CLoggerDomain;
+
+/**
+ * The log manager is a global storage of all logger objects.
+ */
+class DLL_LINKAGE CLogManager : public boost::noncopyable
+{
+public:
+    /**
+     * Gets an instance of the log manager.
+     *
+     * @return an instance of the log manager
+     */
+    static CLogManager * get();
+
+    /**
+     * Adds a logger. The log manager holds strong ownership of the logger object.
+     *
+     * @param logger The logger to add.
+     */
+    void addLogger(CGLogger * logger);
+
+    /**
+     * Gets a logger by domain.
+     *
+     * @param domain The domain of the logger.
+     * @return a logger by domain or nullptr if the logger was not found
+     */
+    CGLogger * getLogger(const CLoggerDomain & domain);
+
+    /**
+     * Destructor.
+     */
+    ~CLogManager();
+
+private:
+    /**
+     * Constructor.
+     */
+    CLogManager();
+
+    /** The instance of the log manager. */
+    static CLogManager * instance;
+
+    /** The loggers map where the key is the logger name and the value the corresponding logger. */
+    std::map<std::string, CGLogger *> loggers;
+
+    /** The shared mutex for providing synchronous thread-safe access to the log manager. */
+    mutable boost::shared_mutex mx;
+
+    /** The unique mutex for providing thread-safe singleton access. */
+    static boost::mutex smx;
+};

+ 165 - 0
lib/Logging/CLogger.cpp

@@ -0,0 +1,165 @@
+#include "StdInc.h"
+#include "CLogger.h"
+#include "LogRecord.h"
+#include "ILogTarget.h"
+#include "CLogManager.h"
+
+boost::mutex CGLogger::smx;
+
+const std::string CLoggerDomain::DOMAIN_GLOBAL = "global";
+
+CLoggerDomain::CLoggerDomain(const std::string & name) : name(name)
+{
+    if(name.empty()) throw std::runtime_error("Logger domain cannot be empty.");
+}
+
+CLoggerDomain CLoggerDomain::getParent() const
+{
+    if(isGlobalDomain()) return *this;
+
+    size_t pos = name.find_last_of(".");
+    if(pos != std::string::npos)
+    {
+        return CLoggerDomain(name.substr(0, pos));
+    }
+    else
+    {
+        return CLoggerDomain(DOMAIN_GLOBAL);
+    }
+}
+
+bool CLoggerDomain::isGlobalDomain() const
+{
+    return name == DOMAIN_GLOBAL;
+}
+
+std::string CLoggerDomain::getName() const
+{
+    return name;
+}
+
+CGLogger * CGLogger::getLogger(const CLoggerDomain & domain)
+{
+    TLockGuard _(smx);
+    CGLogger * logger = CLogManager::get()->getLogger(domain);
+    if(logger)
+    {
+        return logger;
+    }
+    else
+    {
+        logger = new CGLogger(domain);
+        if(domain.isGlobalDomain())
+        {
+            logger->setLevel(ELogLevel::INFO);
+        }
+        CLogManager::get()->addLogger(logger);
+        return logger;
+    }
+}
+
+CGLogger * CGLogger::getGlobalLogger()
+{
+    return getLogger(CLoggerDomain::DOMAIN_GLOBAL);
+}
+
+CGLogger::CGLogger(const CLoggerDomain & domain) : domain(domain), level(ELogLevel::INFO)
+{
+    if(!domain.isGlobalDomain())
+    {
+        parent = getLogger(domain.getParent());
+    }
+}
+
+CGLogger::~CGLogger()
+{
+    BOOST_FOREACH(ILogTarget * target, targets)
+    {
+        delete target;
+    }
+}
+
+void CGLogger::trace(const std::string & message) const
+{
+    log(ELogLevel::TRACE, message);
+}
+
+void CGLogger::debug(const std::string & message) const
+{
+    log(ELogLevel::DEBUG, message);
+}
+
+void CGLogger::info(const std::string & message) const
+{
+    log(ELogLevel::INFO, message);
+}
+
+void CGLogger::warn(const std::string & message) const
+{
+    log(ELogLevel::WARN, message);
+}
+
+void CGLogger::error(const std::string & message) const
+{
+    log(ELogLevel::ERROR, message);
+}
+
+void CGLogger::log(ELogLevel::ELogLevel level, const std::string & message) const
+{
+    if(getEffectiveLevel() <= level)
+    {
+        callTargets(LogRecord(domain, level, message));
+    }
+}
+
+ELogLevel::ELogLevel CGLogger::getLevel() const
+{
+    TReadLock _(mx);
+    return level;
+}
+
+void CGLogger::setLevel(ELogLevel::ELogLevel level)
+{
+    TWriteLock _(mx);
+    if(domain.isGlobalDomain() && level == ELogLevel::NOT_SET) return;
+    this->level = level;
+}
+
+const CLoggerDomain & CGLogger::getDomain() const
+{
+    return domain;
+}
+
+void CGLogger::addTarget(ILogTarget * target)
+{
+    TWriteLock _(mx);
+    targets.push_back(target);
+}
+
+std::list<ILogTarget *> CGLogger::getTargets() const
+{
+    TReadLock _(mx);
+    return targets;
+}
+
+ELogLevel::ELogLevel CGLogger::getEffectiveLevel() const
+{
+    for(const CGLogger * logger = this; logger != nullptr; logger = logger->parent)
+    {
+        if(logger->getLevel() != ELogLevel::NOT_SET) return logger->getLevel();
+    }
+
+    // This shouldn't be reached, as the root logger must have set a log level
+    return ELogLevel::INFO;
+}
+
+void CGLogger::callTargets(const LogRecord & record) const
+{
+    for(const CGLogger * logger = this; logger != nullptr; logger = logger->parent)
+    {
+        BOOST_FOREACH(ILogTarget * target, logger->getTargets())
+        {
+            target->write(record);
+        }
+    }
+}

+ 230 - 0
lib/Logging/CLogger.h

@@ -0,0 +1,230 @@
+
+/*
+ * CLogger.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+class CGLogger;
+class LogRecord;
+class ILogTarget;
+
+namespace ELogLevel
+{
+    /**
+     * The log level enum holds various log level definitions.
+     */
+    enum ELogLevel
+    {
+        NOT_SET = 0,
+        TRACE,
+        DEBUG,
+        INFO,
+        WARN,
+        ERROR
+    };
+}
+
+/**
+ * The logger domain provides convenient access to super domains from a sub domain.
+ */
+class DLL_LINKAGE CLoggerDomain
+{
+public:
+    /**
+     * Constructor.
+     *
+     * @param domain The domain name. Sub-domains can be specified by separating domains by a dot, e.g. "ai.battle". The global domain is named "global".
+     */
+    CLoggerDomain(const std::string & name);
+
+    /**
+     * Gets the parent logger domain.
+     *
+     * @return the parent logger domain or the same domain logger if this method has been called on the global domain
+     */
+    CLoggerDomain getParent() const;
+
+    /**
+     * Returns true if this domain is the global domain.
+     *
+     * @return true if this is the global domain or false if not
+     */
+    bool isGlobalDomain() const;
+
+    /**
+     * Gets the name of the domain.
+     *
+     * @return the name of the domain
+     */
+    std::string getName() const;
+
+    /** Constant to the global domain name. */
+    static const std::string DOMAIN_GLOBAL;
+
+private:
+    /** The domain name. */
+    std::string name;
+};
+
+/**
+ * The logger is used to log messages to certain targets of a specific domain/name. Temporary name is
+ * CGLogger, should be renamed to CLogger after refactoring.
+ */
+class DLL_LINKAGE CGLogger : public boost::noncopyable
+{
+public:
+    /**
+     * Gets a logger by domain.
+     *
+     * @param domain The logger domain.
+     * @return the logger object
+     */
+    static CGLogger * getLogger(const CLoggerDomain & domain);
+
+    /**
+     * Gets the global logger which is the parent of all domain-specific loggers.
+     *
+     * @return the global logger object
+     */
+    static CGLogger * getGlobalLogger();
+
+    /**
+     * Logs a message with the trace level.
+     *
+     * @param message The message to log.
+     */
+    void trace(const std::string & message) const;
+
+    /**
+     * Logs a message with the debug level.
+     *
+     * @param message The message to log.
+     */
+    void debug(const std::string & message) const;
+
+    /**
+     * Logs a message with the info level.
+     *
+     * @param message The message to log.
+     */
+    void info(const std::string & message) const;
+
+    /**
+     * Logs a message with the warn level.
+     *
+     * @param message The message to log.
+     */
+    void warn(const std::string & message) const;
+
+    /**
+     * Logs a message with the error level.
+     *
+     * @param message The message to log.
+     */
+    void error(const std::string & message) const;
+
+    /**
+     * Gets the log level applied for this logger. The default level for the root logger is INFO.
+     *
+     * @return the log level
+     */
+    inline ELogLevel::ELogLevel getLevel() const;
+
+    /**
+     * Sets the log level.
+     *
+     * @param level The log level.
+     */
+    void setLevel(ELogLevel::ELogLevel level);
+
+    /**
+     * Gets the logger domain.
+     *
+     * @return the domain of the logger
+     */
+    const CLoggerDomain & getDomain() const;
+
+    /**
+     * Adds a target to this logger and indirectly to all loggers which derive from this logger.
+     * The logger holds strong-ownership of the target object.
+     *
+     * @param target The log target to add.
+     */
+    void addTarget(ILogTarget * target);
+
+    /**
+     * Destructor.
+     */
+    ~CGLogger();
+
+private:
+    /**
+     * Constructor.
+     *
+     * @param domain The domain of the logger.
+     */
+    explicit CGLogger(const CLoggerDomain & domain);
+
+    /**
+     * Gets the parent logger.
+     *
+     * @return the parent logger or nullptr if this is the root logger
+     */
+    CGLogger * getParent() const;
+
+    /**
+     * Logs a message of the given log level.
+     *
+     * @param level The log level of the message to log.
+     * @param message The message to log.
+     */
+    inline void log(ELogLevel::ELogLevel level, const std::string & message) const;
+
+    /**
+     * Gets the effective log level.
+     *
+     * @return the effective log level with respect to parent log levels
+     */
+    inline ELogLevel::ELogLevel getEffectiveLevel() const;
+
+    /**
+     * Calls all targets in the hierarchy to write the message.
+     *
+     * @param record The log record to write.
+     */
+    inline void callTargets(const LogRecord & record) const;
+
+    /**
+     * Gets all log targets attached to this logger.
+     *
+     * @return all log targets as a list
+     */
+    inline std::list<ILogTarget *> getTargets() const;
+
+    /** The domain of the logger. */
+    CLoggerDomain domain;
+
+    /** A reference to the parent logger. */
+    CGLogger * parent;
+
+    /** The log level of the logger. */
+    ELogLevel::ELogLevel level;
+
+    /** A list of log targets. */
+    std::list<ILogTarget *> targets;
+
+    /** The shared mutex for providing synchronous thread-safe access to the logger. */
+    mutable boost::shared_mutex mx;
+
+    /** The unique mutex for providing thread-safe get logger access. */
+    static boost::mutex smx;
+};
+
+//extern CLogger * logGlobal;

+ 31 - 0
lib/Logging/ILogTarget.h

@@ -0,0 +1,31 @@
+
+/*
+ * LogRecord.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+class LogRecord;
+
+/**
+ * The interface log target is used by all log target implementations. It holds
+ * the abstract method write which sub-classes should implement.
+ */
+class DLL_LINKAGE ILogTarget : public boost::noncopyable
+{
+public:
+    /**
+     * Writes a log record.
+     *
+     * @param record The log record to write.
+     */
+    virtual void write(const LogRecord & record) = 0;
+
+    virtual ~ILogTarget() { };
+};

+ 44 - 0
lib/Logging/LogRecord.h

@@ -0,0 +1,44 @@
+
+/*
+ * LogRecord.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "CLogger.h"
+
+/**
+ * The log records holds the log message and additional logging information.
+ */
+struct DLL_LINKAGE LogRecord
+{
+    /**
+     * Constructor.
+     */
+    LogRecord(const CLoggerDomain & domain, ELogLevel::ELogLevel level, const std::string & message)
+        : domain(domain), level(level), message(message), timeStamp(boost::posix_time::second_clock::local_time()), threadId(boost::this_thread::get_id())
+    {
+
+    }
+
+    /** The logger domain. */
+    CLoggerDomain domain;
+
+    /** The log level. */
+    ELogLevel::ELogLevel level;
+
+    /** The message. */
+    std::string message;
+
+    /** The time when the message was created. */
+    boost::posix_time::ptime timeStamp;
+
+    /** The thread id. */
+    boost::thread::id threadId;
+};