Vault  4.1
vlogger.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved.
00003 This file is part of the Code Vault version 4.1
00004 http://www.bombaydigital.com/
00005 License: MIT. See LICENSE.md in the Vault top level directory.
00006 */
00007 
00010 #include "vlogger.h"
00011 
00012 #include "vthread.h"
00013 #include "vmutexlocker.h"
00014 #include "vsettings.h"
00015 #include "vbento.h"
00016 #include "vchar.h"
00017 
00018 static const VNamedLoggerPtr NULL_NAMED_LOGGER_PTR;
00019 static const VLogAppenderPtr NULL_LOG_APPENDER_PTR;
00020 
00021 volatile int VLogger::gMaxActiveLevel = 0;
00022 VNamedLoggerPtr VLogger::gDefaultLogger = NULL_NAMED_LOGGER_PTR;
00023 VLogAppenderPtr VLogger::gDefaultAppender = NULL_LOG_APPENDER_PTR;
00024 VFSNode VLogger::gBaseLogDirectory(".");
00025 
00026 // VNamedLogger ---------------------------------------------------------------
00027 
00028 VNamedLogger::VNamedLogger(const VString& name, int level, const VStringVector& appenderNames, VLogAppenderPtr specificAppender)
00029     : VEnableSharedFromThis<VNamedLogger>()
00030     , mName(name)
00031     , mLevel(level)
00032     , mAppendersMutex(VSTRING_FORMAT("VNamedLogger[%s]", name.chars()), true/*suppress logging*/)
00033     , mAppenderNames(appenderNames)
00034     , mSpecificAppender(specificAppender)
00035     , mRepetitionFilter()
00036     , mPrintStackConfig() {
00037     if (appenderNames.empty() && (specificAppender == NULL_LOG_APPENDER_PTR)) {
00038         mAppenderNames.push_back(VString::EMPTY());
00039     }
00040 }
00041 
00042 VNamedLogger::~VNamedLogger() {
00043 }
00044 
00045 void VNamedLogger::clearAppenders() {
00046     mAppenderNames.clear();
00047 }
00048 
00049 void VNamedLogger::setAppender(const VString& appenderName) {
00050     mAppenderNames.clear();
00051     mAppenderNames.push_back(appenderName);
00052 }
00053 
00054 void VNamedLogger::addAppender(const VString& appenderName) {
00055     mAppenderNames.push_back(appenderName);
00056 }
00057 
00058 void VNamedLogger::log(int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName) {
00059     if (level > mLevel) {
00060         return;
00061     }
00062 
00063     VNamedLogger::_breakpointLocationForLog();
00064 
00065     if (mRepetitionFilter.isEnabled()) { // avoid mutex if no need to check filter
00066         VMutexLocker timeoutLocker(&mAppendersMutex, "VNamedLogger::log() checkTimeout");
00067         mRepetitionFilter.checkTimeout(*this);
00068     }
00069 
00070     VMutexLocker locker(&mAppendersMutex, "VNamedLogger::log");
00071     if (mRepetitionFilter.checkMessage(*this, level, file, line, message, specifiedLoggerName, mName)) {
00072         this->_emitToAppenders(level, file, line, true, message, specifiedLoggerName, false, VString::EMPTY());
00073         if (mPrintStackConfig.shouldPrintStack(level, *this)) {
00074             locker.unlock(); // avoid recursive deadlock, we're done with our data until we recur
00075             VThread::logStackCrawl(message, VNamedLoggerPtr(shared_from_this()), false);
00076         }
00077     }
00078 }
00079 
00080 void VNamedLogger::addInfo(VBentoNode& infoNode) const {
00081     infoNode.addString("name", mName);
00082     infoNode.addInt("level", mLevel);
00083 
00084     if (this->isDefaultLogger()) {
00085         infoNode.addBool("is-default-logger", true);
00086     }
00087 
00088     if (mAppenderNames.size() == 1) {
00089         infoNode.addString("appender", mAppenderNames[0]);
00090     } else if (mAppenderNames.size() > 1) {
00091         (void) infoNode.addStringArray("appenders", mAppenderNames);
00092     }
00093 
00094     infoNode.addBool("repetition-filter-enabled", mRepetitionFilter.isEnabled());
00095     infoNode.addInt("print-stack-level", mPrintStackConfig.getLevel());
00096 }
00097 
00098 void VNamedLogger::log(int level, const VString& message) {
00099     this->log(level, NULL, 0, message);
00100 }
00101 
00102 void VNamedLogger::logHexDump(int level, const VString& message, const VString& specifiedLoggerName, const Vu8* buffer, Vs64 length) {
00103     if (level > mLevel) {
00104         return;
00105     }
00106 
00107     VNamedLogger::_breakpointLocationForLog();
00108 
00109     // Try to be efficient here:
00110     // Form the hex dump only if the length is > 0.
00111     // But do it once ahead of time, then interate over the appenders, writing to each one.
00112 
00113     VString hexString;
00114     if (length > 0) {
00115         VMemoryStream   tempBuffer;
00116         VTextIOStream   stream(tempBuffer);
00117         VHex            hex(&stream);
00118         hex.printHex(buffer, length);
00119         hexString.copyFromBuffer((const char*)tempBuffer.getBuffer(), 0, (int) tempBuffer.getEOFOffset());
00120     }
00121 
00122     VMutexLocker locker(&mAppendersMutex, "VNamedLogger::logHexDump");
00123     this->_emitToAppenders(level, NULL, 0, true, message, specifiedLoggerName, true, hexString);
00124 }
00125 
00126 void VNamedLogger::emitStackCrawlLine(const VString& message) {
00127     VMutexLocker locker(&mAppendersMutex, "VNamedLogger::emitStackCrawlLine");
00128     this->_emitToAppenders(VLoggerLevel::TRACE /* not used for raw line emit */, NULL, 0, false, VString::EMPTY(), VString::EMPTY(), true, message);
00129 }
00130 
00131 void VNamedLogger::setLevel(int level) {
00132     int oldLevel = mLevel;
00133     mLevel = level;
00134     VLogger::checkMaxActiveLogLevelForChangedLogger(oldLevel, level);
00135 }
00136 
00137 bool VNamedLogger::isDefaultLogger() const {
00138     return VLogger::gDefaultLogger.get() == this;
00139 }
00140 
00141 void VNamedLogger::_emitToAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, bool emitRawLine, const VString& rawLine) {
00142     if (mSpecificAppender != NULL_LOG_APPENDER_PTR) {
00143         mSpecificAppender->emit(level, file, line, emitMessage, message, specifiedLoggerName, mName, emitRawLine, rawLine);
00144     }
00145 
00146     for (VStringVector::const_iterator i = mAppenderNames.begin(); i != mAppenderNames.end(); ++i) {
00147         VLogAppenderPtr appender = (*i).isEmpty() ? VLogger::getDefaultAppender() : VLogger::getAppender(*i);
00148         appender->emit(level, file, line, emitMessage, message, specifiedLoggerName, mName, emitRawLine, rawLine);
00149     }
00150 
00151     VLogger::emitToGlobalAppenders(level, file, line, emitMessage, message, specifiedLoggerName, mName, emitRawLine, rawLine);
00152 }
00153 
00154 VString VNamedLogger::_toString() const {
00155     VString s(VSTRING_ARGS("VNamedLogger '%s' (%d) ->", mName.chars(), mLevel));
00156 
00157     VMutexLocker locker(&mAppendersMutex, "VNamedLogger::_toString");
00158     for (VStringVector::const_iterator i = mAppenderNames.begin(); i != mAppenderNames.end(); ++i) {
00159         s += VSTRING_FORMAT(" '%s'", (*i).chars());
00160     }
00161 
00162     return s;
00163 }
00164 
00165 // static
00166 void VNamedLogger::_breakpointLocationForLog() {
00167     // Put a breakpoint here if you want to break on every message that is logged,
00168     // once basic level filtering has been passed.
00169 }
00170 
00171 // Provided factories ---------------------------------------------------------
00172 
00173 class VCoutLogAppenderFactory : public VLogAppenderFactory {
00174     public:
00175         VCoutLogAppenderFactory() : VLogAppenderFactory() {}
00176         virtual ~VCoutLogAppenderFactory() {}
00177 
00178         virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const
00179             { return VLogAppenderPtr(new VCoutLogAppender(settings, defaults)); }
00180         virtual void addInfo(VBentoNode& infoNode) const
00181             { infoNode.addString("type", "VCoutLogAppenderFactory"); }
00182 };
00183 
00184 class VFileLogAppenderFactory : public VLogAppenderFactory {
00185     public:
00186         VFileLogAppenderFactory() : VLogAppenderFactory() {}
00187         virtual ~VFileLogAppenderFactory() {}
00188 
00189         virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const
00190             { return VLogAppenderPtr(new VFileLogAppender(settings, defaults)); }
00191         virtual void addInfo(VBentoNode& infoNode) const
00192             { infoNode.addString("type", "VFileLogAppenderFactory"); }
00193 };
00194 
00195 class VRollingFileLogAppenderFactory : public VLogAppenderFactory {
00196     public:
00197         VRollingFileLogAppenderFactory() : VLogAppenderFactory() {}
00198         virtual ~VRollingFileLogAppenderFactory() {}
00199 
00200         virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const
00201             { return VLogAppenderPtr(new VRollingFileLogAppender(settings, defaults)); }
00202         virtual void addInfo(VBentoNode& infoNode) const
00203             { infoNode.addString("type", "VRollingFileLogAppenderFactory"); }
00204 };
00205 
00206 class VSilentLogAppenderFactory : public VLogAppenderFactory {
00207     public:
00208         VSilentLogAppenderFactory() : VLogAppenderFactory() {}
00209         virtual ~VSilentLogAppenderFactory() {}
00210 
00211         virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const
00212             { return VLogAppenderPtr(new VSilentLogAppender(settings, defaults)); }
00213         virtual void addInfo(VBentoNode& infoNode) const
00214             { infoNode.addString("type", "VSilentLogAppenderFactory"); }
00215 };
00216 
00217 class VStringLogAppenderFactory : public VLogAppenderFactory {
00218     public:
00219         VStringLogAppenderFactory() : VLogAppenderFactory() {}
00220         virtual ~VStringLogAppenderFactory() {}
00221 
00222         virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const
00223             { return VLogAppenderPtr(new VStringLogAppender(settings, defaults)); }
00224         virtual void addInfo(VBentoNode& infoNode) const
00225             { infoNode.addString("type", "VStringLogAppenderFactory"); }
00226 };
00227 
00228 class VStringVectorLogAppenderFactory : public VLogAppenderFactory {
00229     public:
00230         VStringVectorLogAppenderFactory() : VLogAppenderFactory() {}
00231         virtual ~VStringVectorLogAppenderFactory() {}
00232 
00233         virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const
00234             { return VLogAppenderPtr(new VStringVectorLogAppender(settings, defaults)); }
00235         virtual void addInfo(VBentoNode& infoNode) const
00236             { infoNode.addString("type", "VStringVectorLogAppenderFactory"); }
00237 };
00238 
00239 // VLogger -------------------------------------------------------------------
00240 
00241 // This style of static mutex declaration and access ensures correct
00242 // initialization if accessed during the static initialization phase.
00243 static VMutex* _mutexInstance() {
00244     static VMutex* gVLoggerMutex = new VMutex("gVLoggerMutex", true/*suppress logging of the logger mutex*/);
00245     return gVLoggerMutex;
00246 }
00247 
00248 // _mutexInstance() must be used internally whenever referencing these static accessors:
00249 
00250 typedef std::map<VString, VNamedLoggerPtr> VNamedLoggerMap;
00251 static VNamedLoggerMap& _getLoggerMap() {
00252     static VNamedLoggerMap* gLoggerMap = new VNamedLoggerMap();
00253     return *gLoggerMap;
00254 };
00255 
00256 typedef std::map<VString, VLogAppenderPtr> VLogAppendersMap;
00257 static VLogAppendersMap& _getAppendersMap() {
00258     static VLogAppendersMap* gAppendersMap = new VLogAppendersMap();
00259     return *gAppendersMap;
00260 }
00261 static VLogAppendersMap& _getGlobalAppendersMap() {
00262     static VLogAppendersMap* gGlobalAppendersMap = new VLogAppendersMap();
00263     return *gGlobalAppendersMap;
00264 }
00265 
00266 typedef std::map<VString, VLogAppenderFactoryPtr> VLogAppenderFactoriesMap;
00267 static VLogAppenderFactoriesMap& _getAppenderFactoriesMap() {
00268     static VLogAppenderFactoriesMap* gAppenderFactoriesMap = new VLogAppenderFactoriesMap();
00269     return *gAppenderFactoriesMap;
00270 }
00271 
00272 // static
00273 VString VLoggerLevel::getName(int level) {
00274     // We make all strings 5 characters long for clean layout in the log output.
00275 
00276     if (level == OFF)
00277         return VSTRING_COPY("OFF  "); // Of course, OFF will never appear in output because there will be no such output!
00278     else if (level == FATAL)
00279         return VSTRING_COPY("FATAL");
00280     else if (level == ERROR)
00281         return VSTRING_COPY("ERROR");
00282     else if (level == WARN)
00283         return VSTRING_COPY("WARN ");
00284     else if (level == INFO)
00285         return VSTRING_COPY("INFO ");
00286     else if (level == DEBUG)
00287         return VSTRING_COPY("DEBUG");
00288     else if (level == TRACE)
00289         return VSTRING_COPY("TRACE");
00290     else if (level > DEBUG)
00291         return VSTRING_FORMAT("DBG%02d", level);
00292     else if (level > INFO)
00293         return VSTRING_FORMAT("INF%02d", level);
00294     else if (level > WARN)
00295         return VSTRING_FORMAT("WRN%02d", level);
00296     else if (level > ERROR)
00297         return VSTRING_FORMAT("ERR%02d", level);
00298     else
00299         return VSTRING_FORMAT("%05d", level);
00300 }
00301 
00302 // static
00303 int VLoggerLevel::fromString(const VString& value) {
00304     if (value.equalsIgnoreCase("OFF")) {
00305         return VLoggerLevel::OFF;
00306     } else if (value.equalsIgnoreCase("FATAL")) {
00307         return VLoggerLevel::FATAL;
00308     } else if (value.equalsIgnoreCase("ERROR")) {
00309         return VLoggerLevel::ERROR;
00310     } else if (value.equalsIgnoreCase("WARN")) {
00311         return VLoggerLevel::WARN;
00312     } else if (value.equalsIgnoreCase("INFO")) {
00313         return VLoggerLevel::INFO;
00314     } else if (value.equalsIgnoreCase("DEBUG")) {
00315         return VLoggerLevel::DEBUG;
00316     } else if (value.equalsIgnoreCase("TRACE")) {
00317         return VLoggerLevel::TRACE;
00318     } else if (value.equalsIgnoreCase("ALL")) {
00319         return VLoggerLevel::ALL;
00320     }
00321     
00322     return value.parseInt(); // Will throw VRangeException if value is not an integer.
00323 }
00324 
00325 // static
00326 void VLogger::installNewLogAppender(const VSettingsNode& appenderSettings, const VSettingsNode& appenderDefaults) {
00327     VMutexLocker locker(_mutexInstance(), "VLogger::installNewLogAppender");
00328     VLogAppenderFactoriesMap::const_iterator pos = _getAppenderFactoriesMap().find(appenderSettings.getString("kind"));
00329     if (pos != _getAppenderFactoriesMap().end()) {
00330         VLogAppenderPtr appender = pos->second->instantiateLogAppender(appenderSettings, appenderDefaults);
00331 
00332         locker.unlock();
00333         VLogger::registerLogAppender(appender);
00334     }
00335 }
00336 
00337 // static
00338 void VLogger::installNewNamedLogger(const VSettingsNode& loggerSettings) {
00339     VString name = loggerSettings.getString("name");
00340     VString levelText = loggerSettings.getString("level", "INFO");
00341     int level = VLoggerLevel::fromString(levelText);
00342     VStringVector appenderNames;
00343     VString appenderName = loggerSettings.getString("appender", VString::EMPTY());
00344     if (appenderName.isNotEmpty()) {
00345         appenderNames.push_back(appenderName);
00346     }
00347 
00348     const int numAppenders = loggerSettings.countNamedChildren("appender");
00349     for (int i = 0; i < numAppenders; ++i) {
00350         const VSettingsNode* appenderNode = loggerSettings.getNamedChild("appender", i);
00351         appenderName = appenderNode->getString("name", VString::EMPTY());
00352         if (appenderName.isNotEmpty()) {
00353             appenderNames.push_back(appenderName);
00354         }
00355     }
00356 
00357     VNamedLoggerPtr logger(new VNamedLogger(name, level, appenderNames));
00358     int printStackLevel = loggerSettings.getInt("print-stack-level", VLoggerLevel::OFF);
00359     if (printStackLevel != VLoggerLevel::OFF) {
00360         int maxNumOccurrences = loggerSettings.getInt("print-stack-count", -1);
00361         VDuration timeLimit = loggerSettings.getDuration("print-stack-duration", VDuration::POSITIVE_INFINITY());
00362         logger->setPrintStackInfo(printStackLevel, maxNumOccurrences, timeLimit);
00363     }
00364 
00365     VMutexLocker locker(_mutexInstance(), "VLogger::installNewNamedLogger");
00366     VLogger::_registerLogger(logger, false);
00367 }
00368 
00369 // static
00370 void VLogger::installNewNamedLogger(const VString& name, int level, const VStringVector& appenderNames) {
00371     VNamedLoggerPtr logger(new VNamedLogger(name, level, appenderNames));
00372 
00373     VMutexLocker locker(_mutexInstance(), "VLogger::installNewNamedLogger");
00374     VLogger::_registerLogger(logger, false);
00375 }
00376 
00377 // static
00378 void VLogger::installNewNamedLogger(const VString& name, int level, const VString& appenderName) {
00379     VStringVector appenderNames;
00380     appenderNames.push_back(appenderName);
00381     VLogger::installNewNamedLogger(name, level, appenderNames);
00382 }
00383 
00384 // static
00385 void VLogger::registerLogAppenderFactory(const VString& appenderKind, VLogAppenderFactoryPtr factory) {
00386     VMutexLocker locker(_mutexInstance(), "VLogger::registerLogAppenderFactory");
00387     _getAppenderFactoriesMap()[appenderKind] = factory;
00388 }
00389 
00390 // static
00391 void VLogger::configure(const VFSNode& baseLogDirectory, const VSettingsNode& loggingSettings) {
00392     if (baseLogDirectory.getPath().isNotEmpty())
00393         gBaseLogDirectory = baseLogDirectory;
00394 
00395     // The caller is welcome to register other appender factory types before calling configure where we register the built-in types.
00396     VLogger::registerLogAppenderFactory("cout", VLogAppenderFactoryPtr(new VCoutLogAppenderFactory()));
00397     VLogger::registerLogAppenderFactory("file", VLogAppenderFactoryPtr(new VFileLogAppenderFactory()));
00398     VLogger::registerLogAppenderFactory("rolling-file", VLogAppenderFactoryPtr(new VRollingFileLogAppenderFactory()));
00399     VLogger::registerLogAppenderFactory("silent", VLogAppenderFactoryPtr(new VSilentLogAppenderFactory()));
00400     VLogger::registerLogAppenderFactory("string", VLogAppenderFactoryPtr(new VStringLogAppenderFactory()));
00401     VLogger::registerLogAppenderFactory("string-vector", VLogAppenderFactoryPtr(new VStringVectorLogAppenderFactory()));
00402 
00403     // Stash any per-appender defaults in a map while we configure, so we can pass them to the factories we call.
00404     std::map<VString, const VSettingsNode*> defaultsForAppenders;
00405     VSettings emptyDefaults;
00406     const int numAppenderDefaults = loggingSettings.countNamedChildren("appender-defaults");
00407     for (int i = 0; i < numAppenderDefaults; ++i) {
00408         const VSettingsNode* appenderDefaultsNode = loggingSettings.getNamedChild("appender-defaults", i);
00409         defaultsForAppenders[appenderDefaultsNode->getString("kind", VString::EMPTY())] = appenderDefaultsNode;
00410     }
00411 
00412     const int numAppenders = loggingSettings.countNamedChildren("appender");
00413     for (int i = 0; i < numAppenders; ++i) {
00414         const VSettingsNode* appenderNode = loggingSettings.getNamedChild("appender", i);
00415         const VSettingsNode* appenderDefaults = defaultsForAppenders[appenderNode->getString("kind")];
00416         if (appenderDefaults == NULL) {
00417             appenderDefaults = &emptyDefaults;
00418         }
00419 
00420         VLogger::installNewLogAppender(*appenderNode, *appenderDefaults);
00421     }
00422 
00423     const int numLoggers = loggingSettings.countNamedChildren("logger");
00424     for (int i = 0; i < numLoggers; ++i) {
00425         const VSettingsNode* loggerNode = loggingSettings.getNamedChild("logger", i);
00426         VLogger::installNewNamedLogger(*loggerNode);
00427     }
00428 }
00429 
00430 // static
00431 void VLogger::shutdown() {
00432     VMutexLocker locker(_mutexInstance(), "VLogger::shutdown");
00433 
00434     // Clear all shared_ptr references. This will allow all referenced objects to be deleted (unless someone outside retains a reference).
00435     gDefaultLogger.reset();
00436     gDefaultAppender.reset();
00437     _getLoggerMap().clear();
00438     _getAppendersMap().clear();
00439     _getAppenderFactoriesMap().clear();
00440 
00441     gMaxActiveLevel = 0;
00442 }
00443 
00444 // static
00445 void VLogger::registerLogAppender(VLogAppenderPtr appender, bool asDefaultAppender) {
00446     VMutexLocker locker(_mutexInstance(), "VLogger::registerLogAppender");
00447     VLogger::_registerAppender(appender, asDefaultAppender, false);
00448 }
00449 
00450 // static
00451 void VLogger::registerGlobalAppender(VLogAppenderPtr appender, bool asDefaultAppender) {
00452     VMutexLocker locker(_mutexInstance(), "VLogger::registerLogAppender");
00453     VLogger::_registerAppender(appender, asDefaultAppender, true);
00454 }
00455 
00456 // static
00457 void VLogger::registerLogger(VNamedLoggerPtr namedLogger, bool asDefaultLogger) {
00458     VMutexLocker locker(_mutexInstance(), "VLogger::registerLogger");
00459     VLogger::_registerLogger(namedLogger, asDefaultLogger);
00460 }
00461 
00462 // static
00463 void VLogger::deregisterLogAppender(VLogAppenderPtr appender) {
00464     VMutexLocker locker(_mutexInstance(), "VLogger::deregisterLogAppender");
00465 
00466     if (gDefaultAppender == appender) {
00467         gDefaultAppender.reset();
00468     }
00469 
00470     VLogAppendersMap::iterator pos = _getAppendersMap().find(appender->getName());
00471     if (pos != _getAppendersMap().end()) {
00472         _getAppendersMap().erase(pos);
00473     }
00474 
00475     pos = _getGlobalAppendersMap().find(appender->getName());
00476     if (pos != _getGlobalAppendersMap().end()) {
00477         _getGlobalAppendersMap().erase(pos);
00478     }
00479 
00480 }
00481 
00482 // static
00483 void VLogger::deregisterLogAppender(const VString& name) {
00484     VLogAppenderPtr appender = VLogger::findAppender(name);
00485     if (appender != nullptr) {
00486         VLogger::deregisterLogAppender(appender);
00487     }
00488 }
00489 
00490 // static
00491 void VLogger::deregisterLogger(VNamedLoggerPtr namedLogger) {
00492     VMutexLocker locker(_mutexInstance(), "VLogger::deregisterLogger");
00493 
00494     if (gDefaultLogger == namedLogger) {
00495         gDefaultLogger.reset();
00496     }
00497 
00498     VNamedLoggerMap::iterator pos = _getLoggerMap().find(namedLogger->getName());
00499     if (pos != _getLoggerMap().end()) {
00500         _getLoggerMap().erase(pos);
00501     }
00502 
00503     VLogger::_checkMaxActiveLogLevelForRemovedLogger(namedLogger->getLevel());
00504 }
00505 
00506 // static
00507 void VLogger::deregisterLogger(const VString& name) {
00508     VNamedLoggerPtr logger = VLogger::findNamedLogger(name);
00509     if (logger != nullptr) {
00510         VLogger::deregisterLogger(logger);
00511     }
00512 }
00513 
00514 // static
00515 bool VLogger::isDefaultLogLevelActive(int level) {
00516     return VLogger::isLogLevelActive(level) && VLogger::getDefaultLogger()->isEnabledFor(level);
00517 }
00518 
00519 // static
00520 bool VLogger::isLogLevelActive(int level) {
00521     // No locking necessary to simply read this volatile value.
00522     return (level <= gMaxActiveLevel);
00523 }
00524 
00525 // static
00526 VNamedLoggerPtr VLogger::getDefaultLogger() {
00527     VMutexLocker locker(_mutexInstance(), "VLogger::getDefaultLogger");
00528 
00529     if (gDefaultLogger == nullptr) {
00530         VLogger::_registerLogger(VNamedLoggerPtr(new VNamedLogger("auto-default-logger", VLoggerLevel::INFO, VStringVector())), true);
00531     }
00532 
00533     return gDefaultLogger;
00534 }
00535 
00536 #ifdef VLOGGER_INTERNAL_DEBUGGING
00537 // static
00538 void VLogger::_reportLoggerChange(bool before, const VString& label, const VNamedLoggerPtr& was, const VNamedLoggerPtr& is) {
00539     VString wasName = (was == nullptr) ? "null" : was->getName();
00540     VString isName = is->getName();
00541     VString msg(VSTRING_ARGS("_reportLoggerChange %s %s: was '%s', is '%s'", (before ? "before" : "after"), label.chars(), wasName.chars(), isName.chars()));
00542     std::cout << msg << std::endl;
00543 }
00544 #endif /* VLOGGER_INTERNAL_DEBUGGING */
00545 
00546 // static
00547 void VLogger::setDefaultLogger(VNamedLoggerPtr namedLogger) {
00548     VMutexLocker locker(_mutexInstance(), "VLogger::getDefaultLogger");
00549     VLogger::_reportLoggerChange(true, "setDefaultLogger", gDefaultLogger, namedLogger);
00550     gDefaultLogger = namedLogger;
00551     VLogger::_reportLoggerChange(false, "setDefaultLogger", gDefaultLogger, namedLogger);
00552 }
00553 
00554 // static
00555 VNamedLoggerPtr VLogger::getLogger(const VString& name) {
00556     VNamedLoggerPtr logger = findNamedLogger(name);
00557 
00558     if (logger == nullptr) {
00559         logger = VLogger::getDefaultLogger();
00560     }
00561 
00562     return logger;
00563 }
00564 
00565 // static
00566 VNamedLoggerPtr VLogger::findDefaultLogger() {
00567     VMutexLocker locker(_mutexInstance(), "VLogger::findDefaultLogger");
00568     return gDefaultLogger;
00569 }
00570 
00571 // static
00572 VNamedLoggerPtr VLogger::findDefaultLoggerForLevel(int level) {
00573     VMutexLocker locker(_mutexInstance(), "VLogger::findDefaultLoggerForLevel");
00574 
00575     if (gDefaultLogger == nullptr) {
00576         VLogger::_registerLogger(VNamedLoggerPtr(new VNamedLogger("default", VLoggerLevel::INFO, VStringVector())), true);
00577     }
00578 
00579     return gDefaultLogger->isEnabledFor(level) ? gDefaultLogger : NULL_NAMED_LOGGER_PTR;
00580 }
00581 
00582 // static
00583 VNamedLoggerPtr VLogger::findNamedLogger(const VString& name) {
00584     VMutexLocker locker(_mutexInstance(), "VLogger::findNamedLogger");
00585     return VLogger::_findNamedLoggerFromPathName(name);
00586 }
00587 
00588 // static
00589 VNamedLoggerPtr VLogger::findNamedLoggerForLevel(const VString& name, int level) {
00590     // Fast as possible short-circuit: If no logger is enabled at the level (global int test), further searching is not necessary.
00591     if (! VLogger::isLogLevelActive(level)) {
00592         return NULL_NAMED_LOGGER_PTR;
00593     }
00594 
00595     VNamedLoggerPtr logger = VLogger::findNamedLogger(name);
00596 
00597     // If found but level is too high, return null so it won't log.
00598     if ((logger != nullptr) && (logger->getLevel() < level)) {
00599         return NULL_NAMED_LOGGER_PTR;
00600     }
00601 
00602     // If not found, get the default logger with a level check (returns null if level is too high).
00603     if (logger == nullptr) {
00604         logger = VLogger::findDefaultLoggerForLevel(level);
00605     }
00606 
00607     return logger;
00608 }
00609 
00610 #ifdef VLOGGER_INTERNAL_DEBUGGING
00611 // static
00612 void VLogger::_reportAppenderChange(bool before, const VString& label, const VLogAppenderPtr& was, const VLogAppenderPtr& is) {
00613     VString wasName = (was == nullptr) ? "null" : was->getName();
00614     VString isName = is->getName();
00615     VString msg(VSTRING_ARGS("_reportAppenderChange %s %s: was '%s', is '%s'", (before ? "before" : "after"), label.chars(), wasName.chars(), isName.chars()));
00616     std::cout << msg << std::endl;
00617 }
00618 #endif /* VLOGGER_INTERNAL_DEBUGGING */
00619 
00620 // static
00621 VLogAppenderPtr VLogger::getDefaultAppender() {
00622     VMutexLocker locker(_mutexInstance(), "VLogger::getDefaultAppender");
00623 
00624     if (gDefaultAppender == nullptr) {
00625         VLogger::_registerAppender(VLogAppenderPtr(new VCoutLogAppender("auto-default-cout-appender", VLogAppender::DO_FORMAT_OUTPUT, VString::EMPTY(), VString::EMPTY())), true);
00626     }
00627 
00628     return gDefaultAppender;
00629 }
00630 
00631 // static
00632 VLogAppenderPtr VLogger::getAppender(const VString& appenderName) {
00633     /* locker scope */ {
00634         VMutexLocker locker(_mutexInstance(), "VLogger::getAppender");
00635         VLogAppendersMap::const_iterator pos = _getAppendersMap().find(appenderName);
00636         if (pos != _getAppendersMap().end()) {
00637             return pos->second;
00638         }
00639     }
00640 
00641     // Doesn't exist. Return default appender.
00642     return VLogger::getDefaultAppender();
00643 }
00644 
00645 // static
00646 VLogAppenderPtrList VLogger::getAllAppenders() {
00647     VLogAppenderPtrList result;
00648 
00649     /* locker scope */ {
00650         VMutexLocker locker(_mutexInstance(), "VLogger::getAllAppenders");
00651 
00652         for (VLogAppendersMap::const_iterator i = _getAppendersMap().begin(); i != _getAppendersMap().end(); ++i) {
00653             result.push_back((*i).second);
00654         }
00655 
00656         for (VLogAppendersMap::const_iterator i = _getGlobalAppendersMap().begin(); i != _getGlobalAppendersMap().end(); ++i) {
00657             result.push_back((*i).second);
00658         }
00659     }
00660 
00661     return result;
00662 }
00663 
00664 // static
00665 VLogAppenderPtr VLogger::findDefaultAppender() {
00666     VMutexLocker locker(_mutexInstance(), "VLogger::findDefaultAppender");
00667     return gDefaultAppender;
00668 }
00669 
00670 // static
00671 VLogAppenderPtr VLogger::findAppender(const VString& name) {
00672     VLogAppendersMap::iterator pos = _getAppendersMap().find(name);
00673     if (pos != _getAppendersMap().end()) {
00674         return pos->second;
00675     }
00676 
00677     return NULL_LOG_APPENDER_PTR;
00678 }
00679 
00680 // static
00681 const VFSNode& VLogger::getBaseLogDirectory() {
00682     return gBaseLogDirectory;
00683 }
00684 
00685 // static
00686 VBentoNode* VLogger::commandGetInfo() {
00687     VMutexLocker locker(_mutexInstance(), "VLogger::commandGetInfo");
00688     return VLogger::_commandGetInfo();
00689 }
00690 
00691 // static
00692 VBentoNode* VLogger::_commandGetInfo() {
00693     VBentoNode* rootNode = new VBentoNode("logger-info");
00694     VBentoNode* factoriesNode = rootNode->addNewChildNode("factories");
00695     VBentoNode* appendersNode = rootNode->addNewChildNode("appenders");
00696     VBentoNode* loggersNode = rootNode->addNewChildNode("loggers");
00697 
00698     rootNode->addInt("max-active-log-level", gMaxActiveLevel);
00699 
00700     const VLogAppenderFactoriesMap& factories = _getAppenderFactoriesMap();
00701     for (VLogAppenderFactoriesMap::const_iterator i = factories.begin(); i != factories.end(); ++i) {
00702         VBentoNode* factoryNode = factoriesNode->addNewChildNode("factory");
00703         (*i).second->addInfo(*factoryNode);
00704     }
00705 
00706     const VLogAppendersMap& appenders = _getAppendersMap();
00707     for (VLogAppendersMap::const_iterator i = appenders.begin(); i != appenders.end(); ++i) {
00708         VBentoNode* appenderNode = appendersNode->addNewChildNode("appender");
00709         (*i).second->addInfo(*appenderNode);
00710     }
00711 
00712     const VNamedLoggerMap& loggers = _getLoggerMap();
00713     for (VNamedLoggerMap::const_iterator i = loggers.begin(); i != loggers.end(); ++i) {
00714         VBentoNode* loggerNode = loggersNode->addNewChildNode("logger");
00715         (*i).second->addInfo(*loggerNode);
00716     }
00717 
00718     return rootNode;
00719 }
00720 
00721 // static
00722 VString VLogger::commandGetInfoString() {
00723     VMutexLocker locker(_mutexInstance(), "VLogger::commandGetInfoString");
00724     return VLogger::_commandGetInfoString();
00725 }
00726 
00727 // static
00728 VString VLogger::_commandGetInfoString() {
00729     VUniquePtr<VBentoNode> bento(VLogger::_commandGetInfo());
00730     VString s;
00731     bento->writeToBentoTextString(s, true);
00732     return s;
00733 }
00734 
00735 // static
00736 void VLogger::commandSetLogLevel(const VString& loggerName, int level) {
00737 
00738     std::vector<VNamedLoggerPtr> targetLoggers;
00739 
00740     // First, get all the desired loggers, with required locking in place.
00741     /* locker scope */ {
00742         VMutexLocker locker(_mutexInstance(), "VLogger::commandSetLogLevel()");
00743         const VNamedLoggerMap& loggers = _getLoggerMap();
00744         for (VNamedLoggerMap::const_iterator i = loggers.begin(); i != loggers.end(); ++i) {
00745             VNamedLoggerPtr logger = (*i).second;
00746             if (loggerName.isEmpty() || (logger->getName() == loggerName)) {
00747                 targetLoggers.push_back(logger);
00748             }
00749         }
00750     }
00751 
00752     // Now, set each logger's level with the public API. It locks each time.
00753     for (std::vector<VNamedLoggerPtr>::const_iterator i = targetLoggers.begin(); i != targetLoggers.end(); ++i) {
00754         (*i)->setLevel(level);
00755     }
00756 }
00757 
00758 // static
00759 void VLogger::commandSetPrintStackLevel(const VString& loggerName, int printStackLevel, int count, const VDuration& timeLimit) {
00760     VMutexLocker locker(_mutexInstance(), "VLogger::commandSetLogLevel()");
00761     const VNamedLoggerMap& loggers = _getLoggerMap();
00762     for (VNamedLoggerMap::const_iterator i = loggers.begin(); i != loggers.end(); ++i) {
00763         VNamedLoggerPtr logger = (*i).second;
00764         if (loggerName.isEmpty() || (logger->getName() == loggerName)) {
00765             logger->setPrintStackInfo(printStackLevel, count, timeLimit);
00766         }
00767     }
00768 }
00769 
00770 // static
00771 void VLogger::emitToGlobalAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName, bool emitRawLine, const VString& rawLine) {
00772     VMutexLocker locker(_mutexInstance(), "VLogger::emitToGlobalAppenders");
00773     for (VLogAppendersMap::const_iterator i = _getGlobalAppendersMap().begin(); i != _getGlobalAppendersMap().end(); ++i) {
00774         VLogAppenderPtr appender = (*i).second;
00775         appender->emit(level, file, line, emitMessage, message, specifiedLoggerName, actualLoggerName, emitRawLine, rawLine);
00776     }
00777 }
00778 
00779 // static
00780 VString VLogger::getCleansedLoggerName(const VString& s) {
00781     VString cleansed(s);
00782     cleansed.replace(VCodePoint('.'), VCodePoint('-'));
00783     return cleansed;
00784 }
00785 
00786 // static
00787 void VLogger::_registerAppender(VLogAppenderPtr appender, bool asDefaultAppender, bool asGlobalAppender) {
00788     // ASSUMES CALLER HOLDS _mutexInstance().
00789 
00790     VLogger::_reportAppenderChange(true, "_registerAppender", gDefaultAppender, appender);
00791 
00792     if (asDefaultAppender || (gDefaultAppender == nullptr)) {
00793         gDefaultAppender = appender;
00794     }
00795 
00796     _getAppendersMap()[appender->getName()] = appender;
00797 
00798     if (asGlobalAppender) {
00799         _getGlobalAppendersMap()[appender->getName()] = appender;
00800     }
00801 
00802     VLogger::_reportAppenderChange(false, "_registerAppender", gDefaultAppender, appender);
00803 }
00804 
00805 
00806 // static
00807 void VLogger::_registerLogger(VNamedLoggerPtr namedLogger, bool asDefaultLogger) {
00808     // ASSUMES CALLER HOLDS _mutexInstance().
00809 
00810     VLogger::_reportLoggerChange(true, "_registerLogger", gDefaultLogger, namedLogger);
00811 
00812     if (asDefaultLogger || (gDefaultLogger == nullptr)) {
00813         gDefaultLogger = namedLogger;
00814     }
00815 
00816     _getLoggerMap()[namedLogger->getName()] = namedLogger;
00817 
00818     VLogger::_checkMaxActiveLogLevelForNewLogger(namedLogger->getLevel());
00819 
00820     VLogger::_reportLoggerChange(false, "_registerLogger", gDefaultLogger, namedLogger);
00821 }
00822 
00823 // static
00824 void VLogger::_checkMaxActiveLogLevelForNewLogger(int newActiveLevel) {
00825     // ASSUMES CALLER HOLDS _mutexInstance().
00826 
00827     // If the logger has a higher level, then its level is the new max.
00828     if (newActiveLevel > gMaxActiveLevel) {
00829         gMaxActiveLevel = newActiveLevel;
00830     }
00831 }
00832 
00833 // static
00834 void VLogger::checkMaxActiveLogLevelForRemovedLogger(int removedActiveLevel) {
00835     VMutexLocker locker(_mutexInstance(), "checkMaxActiveLogLevelForRemovedLogger");
00836     _checkMaxActiveLogLevelForRemovedLogger(removedActiveLevel);
00837 }
00838 
00839 // static
00840 void VLogger::_checkMaxActiveLogLevelForRemovedLogger(int removedActiveLevel) {
00841     // ASSUMES CALLER HOLDS _mutexInstance().
00842 
00843     // If the logger had the highest level, we need to search to find the new max.
00844     if (removedActiveLevel >= gMaxActiveLevel) {
00845         VLogger::_recalculateMaxActiveLogLevel();
00846     }
00847 }
00848 
00849 // static
00850 void VLogger::checkMaxActiveLogLevelForChangedLogger(int oldActiveLevel, int newActiveLevel) {
00851     VMutexLocker locker(_mutexInstance(), "checkMaxActiveLogLevelForChangedLogger");
00852     _checkMaxActiveLogLevelForChangedLogger(oldActiveLevel, newActiveLevel);
00853 }
00854 
00855 // static
00856 void VLogger::_checkMaxActiveLogLevelForChangedLogger(int oldActiveLevel, int newActiveLevel) {
00857     // ASSUMES CALLER HOLDS _mutexInstance().
00858 
00859     // If the logger's new level is higher than current max, then its level is the new max.
00860     // Otherwise, if the old level was the max, and the new level is lower than it, we need to search to find the new max.
00861     if (newActiveLevel > gMaxActiveLevel) {
00862         gMaxActiveLevel = newActiveLevel;
00863     } else if ((oldActiveLevel >= gMaxActiveLevel) && (newActiveLevel < gMaxActiveLevel)) {
00864         VLogger::_recalculateMaxActiveLogLevel();
00865     }
00866 }
00867 
00868 // static
00869 void VLogger::_recalculateMaxActiveLogLevel() {
00870     // ASSUMES CALLER HOLDS _mutexInstance().
00871 
00872     // This value is less than previous max. Scan all loggers to see what the new max is.
00873     int newMax = 0;
00874     for (VNamedLoggerMap::const_iterator i = _getLoggerMap().begin(); i != _getLoggerMap().end(); ++i) {
00875         newMax = V_MAX(newMax, (*i).second->getLevel());
00876     }
00877 
00878     gMaxActiveLevel = newMax;
00879 }
00880 
00881 // static
00882 VNamedLoggerPtr VLogger::_findNamedLoggerFromExactName(const VString& name) {
00883     VNamedLoggerMap::const_iterator pos = _getLoggerMap().find(name);
00884     if (pos == _getLoggerMap().end()) {
00885         return NULL_NAMED_LOGGER_PTR;
00886     }
00887 
00888     return pos->second;
00889 }
00890 
00891 // static
00892 VNamedLoggerPtr VLogger::_findNamedLoggerFromPathName(const VString& pathName) {
00893     VString nextNameToSearch(pathName);
00894 
00895     while (nextNameToSearch.contains('.')) {
00896         VNamedLoggerPtr foundLogger = VLogger::_findNamedLoggerFromExactName(nextNameToSearch);
00897         if (foundLogger != nullptr) {
00898             return foundLogger;
00899         }
00900 
00901         nextNameToSearch.substringInPlace(0, nextNameToSearch.lastIndexOf('.'));
00902     }
00903 
00904     return VLogger::_findNamedLoggerFromExactName(nextNameToSearch);
00905 }
00906 
00907 // VLogAppender ------------------------------------------------------
00908 
00909 //static const VString DEFAULT_APPENDER_FORMAT_SPEC("$localtime $level | $thread | $specifiedlogger=>$actuallogger | $location$message"); // <- useful for debugging the named logger routing
00910 static const VString DEFAULT_APPENDER_FORMAT_SPEC("$localtime $level | $thread | $location$message");
00911 static const VString DEFAULT_TIME_FORMAT("y-MM-dd HH:mm:ss.SSS");
00912 
00913 VLogAppender::VLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat)
00914     : mMutex(VSTRING_FORMAT("VLogAppender(%s)", name.chars()), true/*this mutex itself must not log*/)
00915     , mName(name)
00916     , mFormatOutput(formatOutput)
00917     , mFormatSpec(formatSpec.isEmpty() ? DEFAULT_APPENDER_FORMAT_SPEC : formatSpec)
00918     , mTimeFormatter(timeFormat.isEmpty() ? DEFAULT_TIME_FORMAT : timeFormat)
00919     , mFormatUsesLocalTime(mFormatSpec.contains("$localtime"))
00920     , mFormatUsesUTCTime(mFormatSpec.contains("$utctime"))
00921     , mFormatUsesLevel(mFormatSpec.contains("$level"))
00922     , mFormatUsesThread(mFormatSpec.contains("$thread"))
00923     , mFormatUsesLocation(mFormatSpec.contains("$location"))
00924     , mFormatUsesSpecifiedLoggerName(mFormatSpec.contains("$specifiedlogger"))
00925     , mFormatUsesActualLoggerName(mFormatSpec.contains("$actuallogger"))
00926     {
00927 }
00928 
00929 VLogAppender::VLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults)
00930     : mMutex(VSTRING_FORMAT("VLogAppender(%s)", settings.getString("name").chars()), true/*this mutex itself must not log*/)
00931     , mName(settings.getString("name"))
00932     , mFormatOutput(VLogAppender::_getBooleanInitSetting("format-output", settings, defaults, DO_FORMAT_OUTPUT))
00933     , mFormatSpec(VLogAppender::_getStringInitSetting("format-spec", settings, defaults, DEFAULT_APPENDER_FORMAT_SPEC))
00934     , mTimeFormatter(VLogAppender::_getStringInitSetting("time-format", settings, defaults, DEFAULT_TIME_FORMAT))
00935     , mFormatUsesLocalTime(mFormatSpec.contains("$localtime"))
00936     , mFormatUsesUTCTime(mFormatSpec.contains("$utctime"))
00937     , mFormatUsesLevel(mFormatSpec.contains("$level"))
00938     , mFormatUsesThread(mFormatSpec.contains("$thread"))
00939     , mFormatUsesLocation(mFormatSpec.contains("$location"))
00940     , mFormatUsesSpecifiedLoggerName(mFormatSpec.contains("$specifiedlogger"))
00941     , mFormatUsesActualLoggerName(mFormatSpec.contains("$actuallogger"))
00942     {
00943 }
00944 
00945 VLogAppender::~VLogAppender() {
00946 }
00947 
00948 void VLogAppender::addInfo(VBentoNode& infoNode) const {
00949     infoNode.addString("name", mName);
00950 
00951     if (this->isDefaultAppender()) {
00952         infoNode.addBool("is-default-appender", true);
00953     }
00954 
00955     if (!mFormatOutput) {
00956         infoNode.addBool("format-output", DONT_FORMAT_OUTPUT);
00957     }
00958     
00959     infoNode.addString("format-spec", mFormatSpec);
00960     infoNode.addString("time-format", mTimeFormatter.getFormatSpecifier());
00961 }
00962 
00963 void VLogAppender::emit(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName, bool emitRawLine, const VString& rawLine) {
00964     VLogAppender::_breakpointLocationForEmit();
00965 
00966     VMutexLocker locker(&mMutex, "emit"); // ensure multiple threads don't intertwine lines of a single emission
00967 
00968     if (emitMessage) {
00969         this->_emitMessage(level, file, line, message, specifiedLoggerName, actualLoggerName);
00970     }
00971 
00972     if (emitRawLine) {
00973         this->_emitRawLine(rawLine);
00974     }
00975 }
00976 
00977 void VLogAppender::emitRaw(const VString& message) {
00978     this->emit(VLoggerLevel::TRACE, NULL, 0, false, VString::EMPTY(), VString::EMPTY(), VString::EMPTY(), true, message);
00979 }
00980 
00981 bool VLogAppender::isDefaultAppender() const {
00982     return VLogger::gDefaultAppender.get() == this;
00983 }
00984 
00985 // static
00986 bool VLogAppender::_getBooleanInitSetting(const VString& attributePath, const VSettingsNode& settings, const VSettingsNode& defaults, bool defaultValue) {
00987     return settings.getBoolean(attributePath, defaults.getBoolean(attributePath, defaultValue));
00988 }
00989 
00990 // static
00991 int VLogAppender::_getIntInitSetting(const VString& attributePath, const VSettingsNode& settings, const VSettingsNode& defaults, int defaultValue) {
00992     return settings.getInt(attributePath, defaults.getInt(attributePath, defaultValue));
00993 }
00994 
00995 // static
00996 VString VLogAppender::_getStringInitSetting(const VString& attributePath, const VSettingsNode& settings, const VSettingsNode& defaults, const VString& defaultValue) {
00997     return settings.getString(attributePath, defaults.getString(attributePath, defaultValue));
00998 }
00999 
01000 void VLogAppender::_emitMessage(int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName) {
01001     if (mFormatOutput) {
01002         VString formattedMessage = this->_formatMessage(level, file, line, message, specifiedLoggerName, actualLoggerName);
01003         this->_emitRawLine(formattedMessage);
01004     } else {
01005         this->_emitRawLine(message); // directly, without applying formatting
01006     }
01007 }
01008 
01009 VString VLogAppender::_formatMessage(int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName) {
01010     VInstant now;
01011     VInstant trueNow(now); // copy constructor avoids another call to read the clock
01012 
01013     // If we are running in simulated time, display both the current and simulated time.
01014     bool prependTrueTime = (mFormatUsesLocalTime || mFormatUsesUTCTime) && ((VInstant::getSimulatedClockOffset() != VDuration::ZERO()) || VInstant::isTimeFrozen());
01015     if (prependTrueTime) {
01016         trueNow.setTrueNow();
01017     }
01018 
01019     VString formattedMessage = mFormatSpec;
01020     
01021     // I am do replacement in this order (mainly with $message last) to be sure that
01022     // none of the replacements put a $ specifier into the string.
01023     if (mFormatUsesLocalTime) {
01024         VString localTimeStampString;
01025         if (prependTrueTime) {
01026             localTimeStampString = trueNow.getLocalString(mTimeFormatter) + " ";
01027         }
01028 
01029         localTimeStampString += now.getLocalString(mTimeFormatter);
01030         formattedMessage.replace("$localtime", localTimeStampString);
01031     }
01032 
01033     if (mFormatUsesUTCTime) {
01034         VString utcTimeStampString;
01035         if (prependTrueTime) {
01036             utcTimeStampString = trueNow.getUTCString(mTimeFormatter) + " ";
01037         }
01038         utcTimeStampString += now.getUTCString(mTimeFormatter);
01039         formattedMessage.replace("$utctime", utcTimeStampString);
01040     }
01041 
01042     if (mFormatUsesLevel) {
01043         formattedMessage.replace("$level", VLoggerLevel::getName(level));
01044     }
01045 
01046     if (mFormatUsesLocation) {
01047         if (file == NULL) {
01048             formattedMessage.replace("$location", VString::EMPTY());
01049         } else {
01050             formattedMessage.replace("$location", VSTRING_FORMAT("@ %s:%d: ", file, line));
01051         }
01052     }
01053 
01054     if (mFormatUsesThread) {
01055         formattedMessage.replace("$thread", VThread::getCurrentThreadName());
01056     }
01057 
01058     if (mFormatUsesSpecifiedLoggerName) {
01059         formattedMessage.replace("$specifiedlogger", specifiedLoggerName);
01060     }
01061 
01062     if (mFormatUsesActualLoggerName) {
01063         formattedMessage.replace("$actuallogger", actualLoggerName);
01064     }
01065 
01066     formattedMessage.replace("$message", message);
01067 
01068     return formattedMessage;
01069 }
01070 
01071 VString VLogAppender::_toString() const {
01072     return VSTRING_FORMAT("VLogAppender '%s'", mName.chars());
01073 }
01074 
01075 // static
01076 void VLogAppender::_breakpointLocationForEmit() {
01077     // Put a breakpoint here if you want to break on every message that is actually
01078     // emitted to an appender after the log level filtering and routing has passed.
01079 }
01080 
01081 // VCoutLogAppender ----------------------------------------------------------
01082 
01083 VCoutLogAppender::VCoutLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat)
01084     : VLogAppender(name, formatOutput, formatSpec, timeFormat)
01085     {
01086     std::cout << std::endl;
01087 }
01088 
01089 VCoutLogAppender::VCoutLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults)
01090     : VLogAppender(settings, defaults)
01091     {
01092     std::cout << std::endl;
01093 }
01094 
01095 void VCoutLogAppender::addInfo(VBentoNode& infoNode) const {
01096     VLogAppender::addInfo(infoNode);
01097     infoNode.addString("type", "VCoutLogAppender");
01098 }
01099 
01100 void VCoutLogAppender::_emitRawLine(const VString& line) {
01101     std::cout << line.chars() << std::endl;
01102     (void) ::fflush(stdout);
01103 }
01104 
01105 // VFileLogAppender ----------------------------------------------------------
01106 
01107 VFileLogAppender::VFileLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat, const VString& filePath)
01108     : VLogAppender(name, formatOutput, formatSpec, timeFormat)
01109     , mFileStream(VFSNode(filePath))
01110     , mOutputStream(mFileStream)
01111     {
01112     this->_openFile();
01113 }
01114 
01115 VFileLogAppender::VFileLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults)
01116     : VLogAppender(settings, defaults)
01117     , mFileStream()
01118     , mOutputStream(mFileStream)
01119     {
01120     // If no path is specified, we'll use "<appendername>.log" in the base log directory, simply
01121     // using the appender's "name" property as our base file name.
01122     VString defaultPath;
01123     VLogger::getBaseLogDirectory().getChildPath(settings.getString("name") + ".log", defaultPath);
01124     mFileStream.setNode(VFSNode(_getStringInitSetting("path", settings, defaults, defaultPath)));
01125 
01126     this->_openFile();
01127 }
01128 
01129 void VFileLogAppender::_openFile() {
01130     VFSNode newLogFileDir;
01131     mFileStream.getNode().getParentNode(newLogFileDir);
01132     newLogFileDir.mkdirs();
01133 
01134     mFileStream.openReadWrite();
01135     mFileStream.seek(CONST_S64(0), SEEK_END);
01136 
01137     mOutputStream.writeLineEnd();
01138 }
01139 
01140 void VFileLogAppender::addInfo(VBentoNode& infoNode) const {
01141     VLogAppender::addInfo(infoNode);
01142     infoNode.addString("type", "VFileLogAppender");
01143     infoNode.addString("file", mFileStream.getNode().getPath());
01144 }
01145 
01146 void VFileLogAppender::_emitRawLine(const VString& line) {
01147     mOutputStream.writeLine(line);
01148     mOutputStream.flush();
01149 }
01150 
01151 // VRollingFileLogAppender ---------------------------------------------------
01152 
01153 VRollingFileLogAppender::VRollingFileLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat, const VString& /*dirPath*/, const VString& /*fileNamePrefix*/, int /*maxNumLines*/)
01154     : VLogAppender(name, formatOutput, formatSpec, timeFormat)
01155     {
01156 }
01157 
01158 VRollingFileLogAppender::VRollingFileLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults)
01159     : VLogAppender(settings, defaults)
01160     {
01161     /* todo: use these settings properties to construct, something like:
01162         mDirPath = VLogAppender::_getStringInitSetting("dir", settings, defaults, VLogger::getBaseLogDirectory().getPath());
01163         mPrefix = VLogAppender::_getStringInitSetting("prefix", settings, defaults, settings.getString("name"));
01164         mMaxLines = VLogAppender::_getIntInitSetting("max-lines", settings, defaults, 10000);
01165     */
01166 }
01167 
01168 void VRollingFileLogAppender::addInfo(VBentoNode& infoNode) const {
01169     VLogAppender::addInfo(infoNode);
01170     infoNode.addString("type", "VRollingFileLogAppender");
01171     // TODO: current file etc.
01172 }
01173 
01174 void VRollingFileLogAppender::_emitRawLine(const VString& /*line*/) {
01175     // TODO!
01176 }
01177 
01178 // VSilentLogAppender ----------------------------------------------------------
01179 
01180 void VSilentLogAppender::addInfo(VBentoNode& infoNode) const {
01181     VLogAppender::addInfo(infoNode);
01182     infoNode.addString("type", "VSilentLogAppender");
01183 }
01184 
01185 // VStringLogAppender ----------------------------------------------------------
01186 
01187 VStringLogAppender::VStringLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat)
01188     : VLogAppender(name, formatOutput, formatSpec, timeFormat)
01189     , mLines()
01190     {
01191 }
01192 
01193 VStringLogAppender::VStringLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults)
01194     : VLogAppender(settings, defaults)
01195     , mLines()
01196     {
01197 }
01198 
01199 void VStringLogAppender::addInfo(VBentoNode& infoNode) const {
01200     VLogAppender::addInfo(infoNode);
01201     infoNode.addString("type", "VStringLogAppender");
01202 }
01203 
01204 void VStringLogAppender::_emitRawLine(const VString& line) {
01205     mLines += line;
01206     mLines += VString::NATIVE_LINE_ENDING();
01207 }
01208 
01209 // VStringVectorLogAppender ---------------------------------------------------
01210 
01211 VStringVectorLogAppender::VStringVectorLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat, /*@Nullable*/VStringVector* storage)
01212     : VLogAppender(name, formatOutput, formatSpec, timeFormat)
01213     , mStorage(storage)
01214     , mLines()
01215     {
01216     if (mStorage == NULL) { // If not supplied, use our internal lines vector storage.
01217         mStorage = &mLines;
01218     }
01219 }
01220 
01221 VStringVectorLogAppender::VStringVectorLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults)
01222     : VLogAppender(settings, defaults)
01223     , mStorage(NULL)
01224     , mLines()
01225     {
01226     mStorage = &mLines;
01227 }
01228 
01229 VStringVectorLogAppender::~VStringVectorLogAppender() {
01230     mStorage = NULL; // We don't own the pointer.
01231 }
01232 
01233 void VStringVectorLogAppender::addInfo(VBentoNode& infoNode) const {
01234     VLogAppender::addInfo(infoNode);
01235     infoNode.addString("type", "VStringVectorLogAppender");
01236     infoNode.addBool("external-storage", mStorage != NULL);
01237 }
01238 
01239 void VStringVectorLogAppender::_emitRawLine(const VString& line) {
01240     mStorage->push_back(line);
01241 }
01242 
01243 // VStringLogger -------------------------------------------------------------
01244 
01245 VStringLogger::VStringLogger(const VString& name, int level, bool formatOutput, const VString& formatSpec, const VString& timeFormat)
01246     : VNamedLogger(name, level, VStringVector())
01247     , mAppender(name + ".appender", formatOutput, formatSpec, timeFormat)
01248     {
01249 }
01250 
01251 void VStringLogger::addInfo(VBentoNode& infoNode) const {
01252     VNamedLogger::addInfo(infoNode);
01253     infoNode.addString("type", "VStringLogger");
01254 }
01255 
01256 void VStringLogger::_emitToAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, bool emitRawLine, const VString& rawLine) {
01257     mAppender.emit(level, file, line, emitMessage, message, specifiedLoggerName, this->getName(), emitRawLine, rawLine);
01258 }
01259 
01260 // VStringVectorLogger -------------------------------------------------------------
01261 
01262 VStringVectorLogger::VStringVectorLogger(const VString& name, int level, /*@Nullable*/VStringVector* storage, bool formatOutput, const VString& formatSpec, const VString& timeFormat)
01263     : VNamedLogger(name, level, VStringVector())
01264     , mAppender(name + ".appender", formatOutput, formatSpec, timeFormat, storage)
01265     {
01266 }
01267 
01268 void VStringVectorLogger::addInfo(VBentoNode& infoNode) const {
01269     VNamedLogger::addInfo(infoNode);
01270     infoNode.addString("type", "VStringVectorLogger");
01271 }
01272 
01273 void VStringVectorLogger::_emitToAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, bool emitRawLine, const VString& rawLine) {
01274     mAppender.emit(level, file, line, emitMessage, message, specifiedLoggerName, this->getName(), emitRawLine, rawLine);
01275 }
01276 
01277 // VLoggerRepetitionFilter ---------------------------------------------------
01278 
01279 VLoggerRepetitionFilter::VLoggerRepetitionFilter()
01280     : mEnabled(true)
01281     , mHasSavedMessage(false)
01282     , mNumSuppressedOccurrences(0)
01283     , mTimeOfLastOccurrence(VInstant::NEVER_OCCURRED())
01284     , mLevel(0)
01285     , mFile(NULL)
01286     , mLine(0)
01287     , mMessage()
01288     , mSpecifiedLoggerName()
01289     , mActualLoggerName()
01290     {
01291 }
01292 
01293 void VLoggerRepetitionFilter::reset() {
01294     mHasSavedMessage = false;
01295     mNumSuppressedOccurrences = 0;
01296     mTimeOfLastOccurrence = VInstant::NEVER_OCCURRED();
01297     mLevel = 0;
01298     mFile = NULL;
01299     mLine = 0;
01300     mMessage = VString::EMPTY();
01301     mSpecifiedLoggerName = VString::EMPTY();
01302     mActualLoggerName = VString::EMPTY();
01303 }
01304 
01305 bool VLoggerRepetitionFilter::checkMessage(VNamedLogger& logger, int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName) {
01306     if (!mEnabled) {
01307         return true;
01308     }
01309 
01310     bool isRepeatMessage = mHasSavedMessage &&
01311                            (level == mLevel) &&
01312                            (file == mFile) &&
01313                            (line == mLine) &&
01314                            (message == mMessage);
01315 
01316     if (isRepeatMessage) {
01317         // This is a repeat message. Update our info and return false to indicate that
01318         // this message should not yet be emitted.
01319         ++mNumSuppressedOccurrences;
01320         mTimeOfLastOccurrence.setNow();
01321     } else {
01322         // This is not a repeat message. Emit any pending saved recurring message,
01323         // then reset to store this message, and return true to indicate that
01324         // this message should be emitted (the first occurrence of a message is
01325         // always emitted).
01326 
01327         // Emit pending saved message.
01328         if (mHasSavedMessage && (mNumSuppressedOccurrences > 0)) {
01329             this->_emitSuppressedMessages(logger);
01330         }
01331 
01332         // Reset and store this new message.
01333         mHasSavedMessage = true;
01334         mNumSuppressedOccurrences = 0;
01335         mTimeOfLastOccurrence.setNow();
01336         mLevel = level;
01337         mFile = file;
01338         mLine = line;
01339         mMessage = message;
01340         mSpecifiedLoggerName = specifiedLoggerName;
01341         mActualLoggerName = actualLoggerName;
01342     }
01343 
01344     return ! isRepeatMessage;
01345 }
01346 
01347 void VLoggerRepetitionFilter::checkTimeout(VNamedLogger& logger) {
01348     if (!mEnabled) {
01349         return;
01350     }
01351 
01352     if (mHasSavedMessage && (mNumSuppressedOccurrences > 0)) {
01353         VInstant now;
01354         if ((now - mTimeOfLastOccurrence) > VDuration::MINUTE()) {
01355             this->_emitSuppressedMessages(logger);
01356         }
01357     }
01358 }
01359 
01360 void VLoggerRepetitionFilter::_emitSuppressedMessages(VNamedLogger& logger) {
01361     // If there was only 1 suppressed message, no need to mark it.
01362     if (mNumSuppressedOccurrences > 1) {
01363         VString tweakedMessage(VSTRING_ARGS("[%dx] %s", mNumSuppressedOccurrences, mMessage.chars()));
01364         logger._emitToAppenders(mLevel, mFile, mLine, true, tweakedMessage, mSpecifiedLoggerName, false, VString::EMPTY());
01365     } else {
01366         logger._emitToAppenders(mLevel, mFile, mLine, true, mMessage, mSpecifiedLoggerName, false, VString::EMPTY());
01367     }
01368 
01369     mHasSavedMessage = false;
01370     mNumSuppressedOccurrences = 0;
01371 }
01372 
01373 // VLoggerPrintStackConfig ----------------------------------------------------
01374 
01375 VLoggerPrintStackConfig::VLoggerPrintStackConfig()
01376     : mLevel(VLoggerLevel::OFF)
01377     , mMaxCount(-1)
01378     , mDuration(VDuration::POSITIVE_INFINITY())
01379     , mCountdown(-1)
01380     , mExpiration(VInstant::INFINITE_FUTURE())
01381     {
01382 }
01383 
01384 void VLoggerPrintStackConfig::configure(int level, int maxNumOccurrences, const VDuration& timeLimit) {
01385     mLevel = level;
01386     mMaxCount = (maxNumOccurrences > 0) ? maxNumOccurrences : -1; // 0 really means off (-1)
01387     mDuration = timeLimit;
01388     mCountdown = mMaxCount;
01389 
01390     if (timeLimit.isSpecific()) {
01391         mExpiration = VInstant(/*now*/) + timeLimit;
01392     } else {
01393         mExpiration = VInstant::INFINITE_FUTURE();
01394     }
01395 }
01396 
01397 bool VLoggerPrintStackConfig::shouldPrintStack(int level, VNamedLogger& logger) {
01398     if (level > mLevel) {
01399         return false;
01400     }
01401 
01402     // mCountdown of -1 means no count limit, just a timeout limit, then turn off
01403     // mExpiration of infinite means no time limit, just a count limit, then turn off
01404     // if both are set, it means count down to zero, then suppress until timeout is reached
01405     // if neither is set, we always print the stack crawl for this level
01406 
01407     bool resetCountdown = false;
01408     bool turnOff = false;
01409     bool printStack = false;
01410     VInstant now;
01411 
01412     if (mCountdown == -1) {
01413         if (mDuration == VDuration::POSITIVE_INFINITY()) {
01414             // no count limit, no time limit: always print
01415             printStack = true;
01416         } else {
01417             // no count limit, but time limit defined: print if not expired, turn off if expired
01418             printStack = (now < mExpiration);
01419             turnOff = !printStack;
01420         }
01421     } else if (mCountdown == 0) {
01422         if (mDuration == VDuration::POSITIVE_INFINITY()) {
01423             // count limit reached, no time limit: turn off printing completely
01424             turnOff = true;
01425         } else {
01426             // count limit reached, time limit defined: check time limit and reset if expired
01427             resetCountdown = (now >= mExpiration);
01428             printStack = resetCountdown;
01429         }
01430     } else { // mCountdown > 0
01431         // count limit exists but not yet reached, so print
01432         printStack = true;
01433 
01434         if (mDuration == VDuration::POSITIVE_INFINITY()) {
01435             // no time limit: nothing to do
01436         } else {
01437             // time limit defined: reset if expired
01438             resetCountdown = (now >= mExpiration);
01439         }
01440     }
01441 
01442     if (resetCountdown) {
01443         mExpiration = (now + mDuration); // easier than looping increment from previous to future; good enough
01444 
01445         if (mMaxCount > 0) {
01446             mCountdown = mMaxCount;
01447         }
01448     }
01449 
01450     if (printStack) {
01451         if (mCountdown > 0) {
01452             --mCountdown;
01453         }
01454     }
01455 
01456     if (turnOff) {
01457         mLevel = VLoggerLevel::OFF;
01458         mMaxCount = -1;
01459         mDuration = VDuration::POSITIVE_INFINITY();
01460         mCountdown = -1;
01461         mExpiration = VInstant::INFINITE_FUTURE();
01462         logger._emitToAppenders(VLoggerLevel::INFO, NULL, 0, true, "Print stack crawl for this logger is auto-disabling now.", VString::EMPTY(), false, VString::EMPTY());
01463     }
01464 
01465     return printStack;
01466 }
01467 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.