From: hama Date: Sun, 8 Mar 2015 12:13:30 +0000 (+0100) Subject: ReMemoryAppender, ReSlaveAppender, prefixChar in logging X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=88efcea56726f1455b3b4eb086e422e15d596cf1;p=crepublib ReMemoryAppender, ReSlaveAppender, prefixChar in logging --- diff --git a/base/ReAppenders.cpp b/base/ReAppenders.cpp index b0862ba..a7c5ba8 100644 --- a/base/ReAppenders.cpp +++ b/base/ReAppenders.cpp @@ -24,6 +24,24 @@ ReMemoryAppender::ReMemoryAppender(int maxLines) : } ReMemoryAppender::~ReMemoryAppender(){ } + +/** + * Joins the stored messages to one string. + * + * @param buffer IN/OUT the result buffer + * @param append truethe buffer will not truncated before storage + * @return buffer (for chaining) + */ +ReByteBuffer& ReMemoryAppender::join(ReByteBuffer& buffer, bool append){ + ReByteBuffer current; + if (! append) + buffer.setLength(0); + for (int ix = count() - 1; ix >= 0; ix--){ + get(ix, current); + buffer.append(current).appendChar('\n'); + } + return buffer; +} /** * Stores a logging message. * @@ -32,10 +50,38 @@ ReMemoryAppender::~ReMemoryAppender(){ */ void ReMemoryAppender::say(ReLogger* logger, const char* message){ int theCount = count(); - if (theCount > m_maxLines) + if (theCount >= m_maxLines) remove(theCount - 1); // we store in reverse order: add(0, message != NULL ? message : logger->asCString()); } - +/** + * Constructor. + * + * @param masterLogger the central logger which really does the work + * @param charPrefix '\0' or a character which marks each line of the log + * written with this appender. It makes it possible + * to distinct the threads in the log + */ +ReSlaveAppender::ReSlaveAppender(ReLogger* masterLogger, char charPrefix) : + m_masterLogger(masterLogger), + m_charPrefix(charPrefix) { +} +/** + * Destructor. + */ +ReSlaveAppender::~ReSlaveAppender(){ +} +/** + * Writes the message to the master logger. + * + * @param logger the caller + * @param message the logging message to store + */ +void ReSlaveAppender::say(ReLogger* logger, const char* message){ + ReByteBuffer buffer(logger->standardPrefix(m_charPrefix)); + buffer.append(message); + m_masterLogger->say(logger->currentMode(), + logger->currentLocation(), buffer.str()); +} diff --git a/base/ReAppenders.hpp b/base/ReAppenders.hpp index 212a3e6..7c65df2 100644 --- a/base/ReAppenders.hpp +++ b/base/ReAppenders.hpp @@ -16,6 +16,9 @@ /** * Implements an appender which stores the logging messages. + * + * The lines are stored in reverse order (the line with index 0 is the last written line): + * This makes removing of the last message more efficient: no packing is necessary. */ class ReMemoryAppender: public ReAppender, public ReSeqArray { public: @@ -23,8 +26,31 @@ public: ~ReMemoryAppender(); public: virtual void say(ReLogger* logger, const char* message); + ReByteBuffer& join(ReByteBuffer& buffer, bool append = false); protected: int m_maxLines; }; +/** + * This appender writes the messages to another logger. + * + * This is useful in multithreaded applications: There is one "normal logger" + * in the main thread and each other thread have its own logger with a + * ReSlaveAppender which write to the main logger. + * + * Reason: Because of the architecture of the argument handling + * (say().arg().end() it is not possible to protect the argument + * preparation in a thread safe manner. + */ +class ReSlaveAppender : public ReAppender { +public: + ReSlaveAppender(ReLogger* masterLogger, char charPrefix = '\0'); + virtual ~ReSlaveAppender(); +public: +public: + virtual void say(ReLogger* logger, const char* message); +private: + ReLogger* m_masterLogger; + char m_charPrefix; +}; #endif /* BASE_REAPPENDERS_HPP_ */ diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp index eaf997f..99a1503 100644 --- a/base/ReLogger.cpp +++ b/base/ReLogger.cpp @@ -120,7 +120,7 @@ ReStreamAppender::~ReStreamAppender() { * @param message The message to write. */ void ReStreamAppender::say(ReLogger* logger, const char* message) { - const char* prefix = logger->getStandardPrefix(); + const char* prefix = logger->standardPrefix(); if (m_stream != NULL) { fputs(prefix, m_stream); if (message != NULL) @@ -262,7 +262,10 @@ ReLogger::ReLogger(bool isGlobal) : m_location(0), m_stdConsoleAppender(NULL), m_stdFileAppender(NULL), - m_locationOfOpenSayF(0) { + m_locationOfOpenSayF(0), + m_mutex(), + m_charPrefix('\0') { + sem_init(&m_mutex, 0, 1); if (isGlobal) { delete m_globalLogger; m_globalLogger = this; @@ -282,6 +285,7 @@ ReLogger::~ReLogger() { m_appenderListSize = 0; if (m_globalLogger == this) m_globalLogger = NULL; + sem_destroy(&m_mutex); } /** @brief Issues a log message. * @@ -296,6 +300,7 @@ ReLogger::~ReLogger() { */ bool ReLogger::say(ReClassCategoryGranularity mode, ReLogLocation location, const char* message) { + sem_wait(&m_mutex); m_mode = mode; m_location = location; m_standardPrefix[0] = '\0'; @@ -305,8 +310,39 @@ bool ReLogger::say(ReClassCategoryGranularity mode, ReLogLocation location, if (app->accept(mode)) app->say(this, message); } + sem_post(&m_mutex); return true; } +/** @brief Issues a log message. + * + * The message will be put to all appenders which accept the log message. + * + * @param mode The mode controlling the issuing of the logging. + * This mode will be compared to the settings of the appenders. + * @param position The identification of the call. + * @param message The message to log. + * + * @return true + */ +bool ReLogger::say(char prefix, ReClassCategoryGranularity mode, + ReLogLocation location, const char* message) { + sem_wait(&m_mutex); + char safePrefix = m_charPrefix; + m_charPrefix = prefix; + m_mode = mode; + m_location = location; + m_standardPrefix[0] = '\0'; + + for (size_t ii = 0; ii < m_appenderListLength; ii++) { + ReAppender* app = m_appenderList[ii]; + if (app->accept(mode)) + app->say(this, message); + } + m_charPrefix = safePrefix; + sem_post(&m_mutex); + return true; +} + /** @brief Issues a formatted log message. * * The message will be put to all appenders which accept the log message. @@ -321,7 +357,8 @@ bool ReLogger::say(ReClassCategoryGranularity mode, ReLogLocation location, * @return true */ ReVarArgs& ReLogger::sayF(ReClassCategoryGranularity mode, - ReLogLocation location, const char* format) { + ReLogLocation location, const char* format) { + sem_wait(&m_mutex); if (m_locationOfOpenSayF != 0) { char message[128]; _snprintf(message, sizeof message, @@ -333,6 +370,7 @@ ReVarArgs& ReLogger::sayF(ReClassCategoryGranularity mode, m_location = location; m_standardPrefix[0] = '\0'; reset(format); + sem_post(&m_mutex); return *this; } /** Adds an appender to the appender list. @@ -377,7 +415,7 @@ void ReLogger::addStandardAppenders(bool console, const char* file, * * @return The standard prefix. */ -const char* ReLogger::getStandardPrefix(void) { +const char* ReLogger::standardPrefix(char charPrefix) { if (m_standardPrefix[0] == 0) { char cc; switch (m_mode & LOG_CLASS_ALL) { @@ -395,10 +433,14 @@ const char* ReLogger::getStandardPrefix(void) { break; } m_standardPrefix[0] = cc; - + int start = 1; + if (charPrefix == '\0') + charPrefix = m_charPrefix; + if (charPrefix != '\0') + m_standardPrefix[start++] = charPrefix; time_t now = time(NULL); struct tm* now1 = localtime(&now); - strftime(m_standardPrefix + 1, sizeof m_standardPrefix - 1, + strftime(m_standardPrefix + start, sizeof m_standardPrefix - start, "%Y.%m.%d %H:%M:%S", now1); size_t len = strlen(m_standardPrefix); char* ptr = m_standardPrefix + len; diff --git a/base/ReLogger.hpp b/base/ReLogger.hpp index e09086d..6ec0321 100644 --- a/base/ReLogger.hpp +++ b/base/ReLogger.hpp @@ -147,19 +147,29 @@ private: // Not accessible, not implemented! ReLogger& operator =(const ReLogger& source); public: + void addAppender(ReAppender* appender); + void addStandardAppenders(bool console, const char* file, int fileCount = 5, + int fileSize = 1000100); + virtual void end(void); bool say(ReClassCategoryGranularity mode, ReLogLocation location, const char* message); + bool say(char prefix, ReClassCategoryGranularity mode, ReLogLocation location, + const char* message); ReVarArgs& sayF(ReClassCategoryGranularity mode, ReLogLocation location, const char* format); - virtual void end(void); - - void addAppender(ReAppender* appender); - - const char* getStandardPrefix(void); - ReClassCategoryGranularity getCurrentMode(void) const; - int getCurrentPosition(void) const; - void addStandardAppenders(bool console, const char* file, int fileCount = 5, - int fileSize = 1000100); + /** Returns the current mode of the logging call. + * @return the current mode (class, category and granularity) + */ + inline ReClassCategoryGranularity currentMode(void) const{ + return m_mode; + } + /** Returns the current location of the logging call. + * @return the current location + */ + inline ReLogLocation currentLocation(void) const{ + return m_location; + } + const char* standardPrefix(char prefix = '\0'); protected: ReAppender** m_appenderList; size_t m_appenderListSize; @@ -170,6 +180,10 @@ protected: ReAppender* m_stdConsoleAppender; ReFileAppender* m_stdFileAppender; int m_locationOfOpenSayF; + sem_t m_mutex; + char m_charPrefix; +private: + }; inline ReLogger* globalLogger() { diff --git a/base/rebase.hpp b/base/rebase.hpp index 4e3dc9c..9f0e26e 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -21,6 +21,8 @@ #include #include #include +#include + #define __LITTLE_ENDIAN__ //#define __BIG_ENDIAN__ diff --git a/cunit/cuReLogger.cpp b/cunit/cuReLogger.cpp index 22d16df..e024cc3 100644 --- a/cunit/cuReLogger.cpp +++ b/cunit/cuReLogger.cpp @@ -16,6 +16,7 @@ public: } private: void run() { + testSlaveAppender(); testMemoryAppender(); testBase(); } @@ -36,20 +37,53 @@ private: globalLogger()->say(CAT_LIB, __LINE__, "globalLogger()"); } void testMemoryAppender(){ + ReLogger logger(false); ReMemoryAppender appender(3); - appender.say(NULL, "1"); - appender.say(NULL, "2"); - appender.say(NULL, "3"); + logger.addAppender(&appender); + appender.say(NULL, "x1"); + appender.say(NULL, "x2"); + appender.say(NULL, "x3"); ReByteBuffer line; checkEqu(3U, appender.count()); - checkEqu("1", appender.get(0, line)); - checkEqu("2", appender.get(1, line)); - checkEqu("3", appender.get(2, line)); - appender.say(NULL, "4"); + checkT(appender.get(2, line)); + checkT(line.indexOf("x1", 2) >= 0); + checkT(appender.get(1, line)); + checkT(line.indexOf("x2", 2) >= 0); + checkT(appender.get(0, line)); + checkT(line.indexOf("x3", 2) >= 0); + appender.say(NULL, "x4"); checkEqu(3U, appender.count()); - checkEqu("2", appender.get(0, line)); - checkEqu("3", appender.get(1, line)); - checkEqu("4", appender.get(2, line)); + checkT(appender.get(2, line)); + checkT(line.indexOf("x2", 2) >= 0); + checkT(appender.get(1, line)); + checkT(line.indexOf("x3", 2) >= 0); + checkT(appender.get(0, line)); + checkT(line.indexOf("x4", 2) >= 0); + ReByteBuffer all; + checkEqu(3, appender.join(all).count("\n")); + } + void testSlaveAppender(){ + ReLogger masterLogger(false); + ReMemoryAppender memoryAppender(10); + masterLogger.addAppender(&memoryAppender); + + ReLogger slave1(false); + ReSlaveAppender slaveAppender1(&masterLogger, '$'); + slave1.addAppender(&slaveAppender1); + + ReLogger slave2(false); + ReSlaveAppender slaveAppender2(&masterLogger, '%'); + slave2.addAppender(&slaveAppender2); + + slave1.say(LOG_INFO | CAT_TEST, 9999, "slave1"); + slave2.say(LOG_INFO | CAT_TEST, 9999, "slave2"); + ReByteBuffer line; + memoryAppender.get(0, line); + checkT(line.indexOf('%') >= 0); + checkT(line.indexOf("slave2", -1) >= 0); + memoryAppender.get(1, line); + checkT(line.indexOf('$') >= 0); + checkT(line.indexOf("slave1", -1) >= 0); } }; extern void testReLogger(void); diff --git a/cunit/testall.cpp b/cunit/testall.cpp index 532ef92..0d79d1b 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -85,12 +85,12 @@ void testMath() { } void testAll() { try { - testNet(); + //testNet(); if (s_testAll) { testString(); testMath(); testOs(); - testNet(); + //testNet(); testBase(); } } catch (ReException e) { diff --git a/net/ReTCP.cpp b/net/ReTCP.cpp index 8792a73..364a0c8 100644 --- a/net/ReTCP.cpp +++ b/net/ReTCP.cpp @@ -211,10 +211,12 @@ void ReTCPConnection::send(const char* command, const char* message, * @param id an identifier for logging * @param logger the logger for error handling */ -ReTCPServerConnection::ReTCPServerConnection(int id, ReLogger* logger, +ReTCPServerConnection::ReTCPServerConnection(int id, ReLogger* masterLogger, ReTCPServer* server) : - ReTCPConnection(id, logger), - m_server(server){ + ReTCPConnection(id, new ReLogger(false)), + m_server(server), + m_slaveAppender(masterLogger, '0' + id % ('z' - '0' + 1)){ + m_logger->addAppender(&m_slaveAppender); } /** diff --git a/net/ReTCP.hpp b/net/ReTCP.hpp index c0d7195..0149a6b 100644 --- a/net/ReTCP.hpp +++ b/net/ReTCP.hpp @@ -127,6 +127,7 @@ public: void handleConnection(); private: ReTCPServer* m_server; + ReSlaveAppender m_slaveAppender; }; class ReNetCommandHandler; /**