1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

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

This commit is contained in:
beegee1 2013-04-05 10:29:46 +00:00
parent f39ad093b9
commit 0baa261dfc
20 changed files with 1511 additions and 132 deletions

198
Global.h
View File

@ -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 * 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 #ifdef _MSC_VER
#pragma warning (disable : 4800 ) /* disable conversion to bool warning -- I think it's intended in all places */ #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 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <cstdio> #include <cstdio>
#include <stdio.h> #include <stdio.h>
@ -50,7 +87,6 @@
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <string> #include <string>
//#include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -70,6 +106,7 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/cstdint.hpp> #include <boost/cstdint.hpp>
#include <boost/date_time/posix_time/posix_time.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/filesystem.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -90,6 +127,17 @@
#include <android/log.h> #include <android/log.h>
#endif #endif
/* ---------------------------------------------------------------------------- */
/* Usings */
/* ---------------------------------------------------------------------------- */
using std::shared_ptr;
using std::unique_ptr;
using std::make_shared;
namespace range = boost::range;
/* ---------------------------------------------------------------------------- */
/* Typedefs */
/* ---------------------------------------------------------------------------- */
// Integral data types // Integral data types
typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes) typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes)
typedef boost::uint32_t ui32; //unsigned int 32 bits (4 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::int16_t si16; //signed int 16 bits (2 bytes)
typedef boost::int8_t si8; //signed int 8 bits (1 byte) typedef boost::int8_t si8; //signed int 8 bits (1 byte)
// Fixed width bool data type is important for serialization // Lock typedefs
static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); typedef boost::unique_lock<boost::shared_mutex> TWriteLock;
typedef boost::shared_lock<boost::shared_mutex> TReadLock;
#if defined _M_X64 && defined _WIN32 //Win64 -> cannot load 32-bit DLLs for video handling typedef boost::lock_guard<boost::mutex> TLockGuard;
#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
/* ---------------------------------------------------------------------------- */
/* Macros */
/* ---------------------------------------------------------------------------- */
// Import + Export macro declarations // Import + Export macro declarations
#ifdef _WIN32 #ifdef _WIN32
#ifdef __GNUC__ #ifdef __GNUC__
@ -150,26 +191,46 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
#define DLL_LINKAGE DLL_IMPORT #define DLL_LINKAGE DLL_IMPORT
#endif #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 #define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {tlog1 << __FUNCTION__ << "\n"; assert(0);}
#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 //XXX pls dont - 'debug macros' are usually more trouble than it's worth
#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 460)) #define HANDLE_EXCEPTION \
#define nullptr NULL catch (const std::exception& e) { \
#endif 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. #define HANDLE_EXCEPTIONC(COMMAND) \
#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 470)) catch (const std::exception& e) { \
#define override COMMAND; \
#endif tlog1 << e.what() << std::endl; \
throw; \
} \
catch (const std::string &e) \
{ \
COMMAND; \
tlog1 << e << std::endl; \
throw; \
}
//workaround to support existing code // can be used for counting arrays
#define OVERRIDE override 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 //a normal std::map with a const operator[] for sanity
template<typename KeyT, typename ValT> template<typename KeyT, typename ValT>
class bmap : public std::map<KeyT, ValT> class bmap : public std::map<KeyT, ValT>
@ -508,53 +569,10 @@ namespace vstd
obj = (T)(((int)obj) + change); obj = (T)(((int)obj) + change);
} }
} }
using vstd::operator-=;
using std::shared_ptr;
using std::unique_ptr;
using std::make_shared;
using vstd::make_unique; using vstd::make_unique;
using vstd::operator-=; /* ---------------------------------------------------------------------------- */
/* VCMI headers */
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; \
}
#include "lib/CLogger.h" #include "lib/CLogger.h"

View File

@ -3,7 +3,7 @@
{ {
"type" : "object", "type" : "object",
"$schema": "http://json-schema.org/draft-04/schema", "$schema": "http://json-schema.org/draft-04/schema",
"required" : [ "general", "video", "adventure", "battle", "server" ], "required" : [ "general", "video", "adventure", "battle", "server", "logging" ],
"properties": "properties":
{ {
"general" : { "general" : {
@ -133,6 +133,72 @@
"default" : "StupidAI" "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" ]
}
}
}
}
}
} }
} }
} }

View File

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

View File

@ -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 /// Class which wraps the native console. It can print text based on
/// the chosen color /// the chosen color
class DLL_LINKAGE CConsoleHandler class DLL_LINKAGE CConsoleHandler
{ {
public: public:
boost::function<void(const std::string &)> *cb; //function to be called when message is received CConsoleHandler(); //c-tor
int curLvl; //logging level ~CConsoleHandler(); //d-tor
boost::thread *thread; void start(); //starts listening thread
int run(); template<typename T> void print(const T &data, EConsoleTextColor::EConsoleTextColor color = EConsoleTextColor::DEFAULT, bool printToStdErr = false)
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)
{ {
TLockGuard _(mx);
#ifndef _WIN32 #ifndef _WIN32
// with love from ffmpeg - library is trying to print some warnings from separate thread // 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 // this results in broken console on Linux. Lock stdout to print all our data at once
flockfile(stdout); flockfile(stdout);
#endif #endif
setColor(lvl); if(color != EConsoleTextColor::DEFAULT) setColor(color);
std::cout << data << std::flush; if(printToStdErr)
setColor(-1); {
std::cerr << data << std::flush;
}
else
{
std::cout << data << std::flush;
}
if(color != EConsoleTextColor::DEFAULT) setColor(EConsoleTextColor::DEFAULT);
#ifndef _WIN32 #ifndef _WIN32
funlockfile(stdout); funlockfile(stdout);
#endif #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;
}; };

View File

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

View File

@ -13,6 +13,12 @@ set(lib_SRCS
Filesystem/CResourceLoader.cpp Filesystem/CResourceLoader.cpp
Filesystem/CFileInputStream.cpp Filesystem/CFileInputStream.cpp
Filesystem/CCompressedStream.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/CCampaignHandler.cpp
Mapping/CMap.cpp Mapping/CMap.cpp
Mapping/CMapEditManager.cpp Mapping/CMapEditManager.cpp
@ -59,6 +65,14 @@ set(lib_SRCS
set(lib_HEADERS set(lib_HEADERS
Filesystem/CInputStream.h Filesystem/CInputStream.h
Filesystem/ISimpleResourceLoader.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/CCampaignHandler.h
Mapping/CMap.h Mapping/CMap.h
Mapping/CMapEditManager.h Mapping/CMapEditManager.h

View File

@ -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.");
}
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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
lib/Logging/CLogManager.h Normal file
View File

@ -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
lib/Logging/CLogger.cpp Normal file
View File

@ -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
lib/Logging/CLogger.h Normal file
View File

@ -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
lib/Logging/ILogTarget.h Normal file
View File

@ -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
lib/Logging/LogRecord.h Normal file
View File

@ -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;
};