}
ReMemoryAppender::~ReMemoryAppender(){
}
+
+/**
+ * Joins the stored messages to one string.
+ *
+ * @param buffer IN/OUT the result buffer
+ * @param append <code>true</code>the buffer will not truncated before storage
+ * @return <code>buffer</code> (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.
*
*/
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());
+}
/**
* 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:
~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
+ * <code>ReSlaveAppender</code> which write to the main logger.
+ *
+ * Reason: Because of the architecture of the argument handling
+ * (<code>say().arg().end()</code> 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_ */
* @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)
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;
m_appenderListSize = 0;
if (m_globalLogger == this)
m_globalLogger = NULL;
+ sem_destroy(&m_mutex);
}
/** @brief Issues a log message.
*
*/
bool ReLogger::say(ReClassCategoryGranularity mode, ReLogLocation location,
const char* message) {
+ sem_wait(&m_mutex);
m_mode = mode;
m_location = location;
m_standardPrefix[0] = '\0';
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.
* @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,
m_location = location;
m_standardPrefix[0] = '\0';
reset(format);
+ sem_post(&m_mutex);
return *this;
}
/** Adds an appender to the appender list.
*
* @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) {
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;
// 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;
ReAppender* m_stdConsoleAppender;
ReFileAppender* m_stdFileAppender;
int m_locationOfOpenSayF;
+ sem_t m_mutex;
+ char m_charPrefix;
+private:
+
};
inline ReLogger* globalLogger() {
#include <stdarg.h>
#include <limits.h>
#include <pthread.h>
+#include <semaphore.h>
+
#define __LITTLE_ENDIAN__
//#define __BIG_ENDIAN__
}
private:
void run() {
+ testSlaveAppender();
testMemoryAppender();
testBase();
}
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);
}
void testAll() {
try {
- testNet();
+ //testNet();
if (s_testAll) {
testString();
testMath();
testOs();
- testNet();
+ //testNet();
testBase();
}
} catch (ReException e) {
* @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);
}
/**
void handleConnection();
private:
ReTCPServer* m_server;
+ ReSlaveAppender m_slaveAppender;
};
class ReNetCommandHandler;
/**