llerror.cpp

Go to the documentation of this file.
00001 
00033 #include "linden_common.h"
00034 
00035 #include "llerror.h"
00036 #include "llerrorcontrol.h"
00037 
00038 #include <cctype>
00039 #ifdef __GNUC__
00040 # include <cxxabi.h>
00041 #endif // __GNUC__
00042 #include <sstream>
00043 #if !LL_WINDOWS
00044 # include <syslog.h>
00045 # include <unistd.h>
00046 #endif // !LL_WINDOWS
00047 #if LL_WINDOWS
00048 # include <windows.h>
00049 #endif // LL_WINDOWS
00050 #include <vector>
00051 
00052 #include "llapp.h"
00053 #include "llapr.h"
00054 #include "llfile.h"
00055 #include "llfixedbuffer.h"
00056 #include "lllivefile.h"
00057 #include "llsd.h"
00058 #include "llsdserialize.h"
00059 #include "llstl.h"
00060 
00061 
00062 namespace {
00063 #if !LL_WINDOWS
00064         class RecordToSyslog : public LLError::Recorder
00065         {
00066         public:
00067                 RecordToSyslog(const std::string& identity)
00068                         : mIdentity(identity)
00069                 {
00070                         openlog(mIdentity.c_str(), LOG_CONS|LOG_PID, LOG_LOCAL0);
00071                                 // we need to set the string from a local copy of the string
00072                                 // since apparanetly openlog expects the const char* to remain
00073                                 // valid even after it returns (presumably until closelog)
00074                 }
00075                 
00076                 ~RecordToSyslog()
00077                 {
00078                         closelog();
00079                 }
00080                 
00081                 virtual void recordMessage(LLError::ELevel level,
00082                                                                         const std::string& message)
00083                 {
00084                         int syslogPriority = LOG_CRIT;
00085                         switch (level) {
00086                                 case LLError::LEVEL_DEBUG:      syslogPriority = LOG_DEBUG;     break;
00087                                 case LLError::LEVEL_INFO:       syslogPriority = LOG_INFO;      break;
00088                                 case LLError::LEVEL_WARN:       syslogPriority = LOG_WARNING; break;
00089                                 case LLError::LEVEL_ERROR:      syslogPriority = LOG_CRIT;      break;
00090                                 default:                                        syslogPriority = LOG_CRIT;
00091                         }
00092                         
00093                         syslog(syslogPriority, "%s", message.c_str());
00094                 }
00095         private:
00096                 std::string mIdentity;
00097         };
00098 #endif
00099 
00100         class RecordToFile : public LLError::Recorder
00101         {
00102         public:
00103                 RecordToFile(const std::string& filename)
00104                 {
00105                         mFile.open(filename.c_str(), llofstream::out | llofstream::app);
00106                         if (!mFile)
00107                         {
00108                                 llinfos << "Error setting log file to " << filename << llendl;
00109                         }
00110                 }
00111                 
00112                 ~RecordToFile()
00113                 {
00114                         mFile.close();
00115                 }
00116                 
00117                 bool okay() { return mFile; }
00118                 
00119                 virtual bool wantsTime() { return true; }
00120                 
00121                 virtual void recordMessage(LLError::ELevel level,
00122                                                                         const std::string& message)
00123                 {
00124                         mFile << message << std::endl;
00125                         // mFile.flush();
00126                                 // *FIX: should we do this? 
00127                 }
00128         
00129         private:
00130                 llofstream mFile;
00131         };
00132         
00133         
00134         class RecordToStderr : public LLError::Recorder
00135         {
00136         public:
00137                 RecordToStderr(bool timestamp) : mTimestamp(timestamp), mUseANSI(ANSI_PROBE) { }
00138 
00139                 virtual bool wantsTime() { return mTimestamp; }
00140                 
00141                 virtual void recordMessage(LLError::ELevel level,
00142                                            const std::string& message)
00143                 {
00144                         if (ANSI_PROBE == mUseANSI)
00145                                 mUseANSI = (checkANSI() ? ANSI_YES : ANSI_NO);
00146 
00147                         if (ANSI_YES == mUseANSI)
00148                         {
00149                                 // Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries.
00150                                 colorANSI("1"); // bold
00151                                 switch (level) {
00152                                 case LLError::LEVEL_ERROR:
00153                                         colorANSI("31"); // red
00154                                         break;
00155                                 case LLError::LEVEL_WARN:
00156                                         colorANSI("34"); // blue
00157                                         break;
00158                                 case LLError::LEVEL_DEBUG:
00159                                         colorANSI("35"); // magenta
00160                                         break;
00161                                 default:
00162                                         break;
00163                                 }
00164                         }
00165                         fprintf(stderr, "%s\n", message.c_str());
00166                         if (ANSI_YES == mUseANSI) colorANSI("0"); // reset
00167                 }
00168         
00169         private:
00170                 bool mTimestamp;
00171                 typedef enum ANSIState {ANSI_PROBE, ANSI_YES, ANSI_NO};
00172                 ANSIState mUseANSI;
00173                 void colorANSI(const std::string color)
00174                 {
00175                         // ANSI color code escape sequence
00176                         fprintf(stderr, "\033[%sm", color.c_str() );
00177                 };
00178                 bool checkANSI(void)
00179                 {
00180 #if LL_LINUX || LL_DARWIN
00181                         // Check whether it's okay to use ANSI; if stderr is
00182                         // a tty then we assume yes.  Can be turned off with
00183                         // the LL_NO_ANSI_COLOR env var.
00184                         return (0 != isatty(2)) &&
00185                                 (NULL == getenv("LL_NO_ANSI_COLOR"));
00186 #endif // LL_LINUX
00187                         return false;
00188                 };
00189         };
00190 
00191         class RecordToFixedBuffer : public LLError::Recorder
00192         {
00193         public:
00194                 RecordToFixedBuffer(LLFixedBuffer& buffer) : mBuffer(buffer) { }
00195                 
00196                 virtual void recordMessage(LLError::ELevel level,
00197                                                                         const std::string& message)
00198                 {
00199                         mBuffer.addLine(message.c_str());
00200                 }
00201         
00202         private:
00203                 LLFixedBuffer& mBuffer;
00204         };
00205 
00206 #if LL_WINDOWS
00207         class RecordToWinDebug: public LLError::Recorder
00208         {
00209         public:
00210                 virtual void recordMessage(LLError::ELevel level,
00211                                                                         const std::string& message)
00212                 {
00213                         llutf16string utf16str =
00214                                 wstring_to_utf16str(utf8str_to_wstring(message));
00215                         utf16str += '\n';
00216                         OutputDebugString(utf16str.c_str());
00217                 }
00218         };
00219 #endif
00220 }
00221 
00222 
00223 namespace
00224 {
00225         std::string className(const std::type_info& type)
00226         {
00227 #ifdef __GNUC__
00228                 // GCC: type_info::name() returns a mangled class name, must demangle
00229 
00230                 static size_t abi_name_len = 100;
00231                 static char* abi_name_buf = (char*)malloc(abi_name_len);
00232                         // warning: above is voodoo inferred from the GCC manual,
00233                         // do NOT change
00234 
00235                 int status;
00236                         // We don't use status, and shouldn't have to pass apointer to it
00237                         // but gcc 3.3 libstc++'s implementation of demangling is broken
00238                         // and fails without.
00239                         
00240                 char* name = abi::__cxa_demangle(type.name(),
00241                                                                                 abi_name_buf, &abi_name_len, &status);
00242                         // this call can realloc the abi_name_buf pointer (!)
00243 
00244                 return name ? name : type.name();
00245 
00246 #elif LL_WINDOWS
00247                 // DevStudio: type_info::name() includes the text "class " at the start
00248 
00249                 static const std::string class_prefix = "class ";
00250 
00251                 std::string name = type.name();
00252                 std::string::size_type p = name.find(class_prefix);
00253                 if (p == std::string::npos)
00254                 {
00255                         return name;
00256                 }
00257 
00258                 return name.substr(p + class_prefix.size());
00259 
00260 #else           
00261                 return type.name();
00262 #endif
00263         }
00264 
00265         std::string functionName(const std::string& preprocessor_name)
00266         {
00267 #if LL_WINDOWS
00268                 // DevStudio: the __FUNCTION__ macro string includes
00269                 // the type and/or namespace prefixes
00270 
00271                 std::string::size_type p = preprocessor_name.rfind(':');
00272                 if (p == std::string::npos)
00273                 {
00274                         return preprocessor_name;
00275                 }
00276                 return preprocessor_name.substr(p + 1);
00277 
00278 #else
00279                 return preprocessor_name;
00280 #endif
00281         }
00282 
00283 
00284         class LogControlFile : public LLLiveFile
00285         {
00286                 LOG_CLASS(LogControlFile);
00287         
00288         public:
00289                 static LogControlFile& fromDirectory(const std::string& dir);
00290                 
00291                 virtual void loadFile();
00292                 
00293         private:
00294                 LogControlFile(const std::string &filename)
00295                         : LLLiveFile(filename)
00296                         { }
00297         };
00298 
00299         LogControlFile& LogControlFile::fromDirectory(const std::string& dir)
00300         {
00301                 std::string dirBase = dir + "/";
00302                         // NB: We have no abstraction in llcommon  for the "proper"
00303                         // delimiter but it turns out that "/" works on all three platforms
00304                         
00305                 std::string file = dirBase + "logcontrol-dev.xml";
00306                 
00307                 llstat stat_info;
00308                 if (LLFile::stat(file.c_str(), &stat_info)) {
00309                         // NB: stat returns non-zero if it can't read the file, for example
00310                         // if it doesn't exist.  LLFile has no better abstraction for 
00311                         // testing for file existence.
00312                         
00313                         file = dirBase + "logcontrol.xml";
00314                 }
00315                 return * new LogControlFile(file);
00316                         // NB: This instance is never freed
00317         }
00318         
00319         void LogControlFile::loadFile()
00320         {
00321                 LLSD configuration;
00322 
00323                 {
00324                         llifstream file(filename().c_str());
00325                         if (file.is_open())
00326                         {
00327                                 LLSDSerialize::fromXML(configuration, file);
00328                         }
00329 
00330                         if (configuration.isUndefined())
00331                         {
00332                                 llwarns << filename() << " missing, ill-formed,"
00333                                                         " or simply undefined; not changing configuration"
00334                                                 << llendl;
00335                                 return;
00336                         }
00337                 }
00338                 
00339                 LLError::configure(configuration);
00340                 llinfos << "logging reconfigured from " << filename() << llendl;
00341         }
00342 
00343 
00344         typedef std::map<std::string, LLError::ELevel> LevelMap;
00345         typedef std::vector<LLError::Recorder*> Recorders;
00346         typedef std::vector<LLError::CallSite*> CallSiteVector;
00347 
00348         class Globals
00349         {
00350         public:
00351                 std::ostringstream messageStream;
00352                 bool messageStreamInUse;
00353 
00354                 void addCallSite(LLError::CallSite&);
00355                 void invalidateCallSites();
00356                 
00357                 static Globals& get();
00358                         // return the one instance of the globals
00359 
00360         private:
00361                 CallSiteVector callSites;
00362 
00363                 Globals()
00364                         :       messageStreamInUse(false)
00365                         { }
00366                 
00367         };
00368 
00369         void Globals::addCallSite(LLError::CallSite& site)
00370         {
00371                 callSites.push_back(&site);
00372         }
00373         
00374         void Globals::invalidateCallSites()
00375         {
00376                 for (CallSiteVector::const_iterator i = callSites.begin();
00377                          i != callSites.end();
00378                          ++i)
00379                 {
00380                         (*i)->invalidate();
00381                 }
00382                 
00383                 callSites.clear();
00384         }
00385 
00386         Globals& Globals::get()
00387         {
00388                 /* This pattern, of returning a reference to a static function
00389                    variable, is to ensure that this global is constructed before
00390                    it is used, no matter what the global initializeation sequence
00391                    is.
00392                    See C++ FAQ Lite, sections 10.12 through 10.14
00393                 */
00394                 static Globals* globals = new Globals;          
00395                 return *globals;
00396         }
00397 }
00398 
00399 namespace LLError
00400 {
00401         class Settings
00402         {
00403         public:
00404                 bool printLocation;
00405 
00406                 LLError::ELevel defaultLevel;
00407                 
00408                 LevelMap functionLevelMap;
00409                 LevelMap classLevelMap;
00410                 LevelMap fileLevelMap;
00411                 LevelMap tagLevelMap;
00412                 std::map<std::string, unsigned int> uniqueLogMessages;
00413                 
00414                 LLError::FatalFunction crashFunction;
00415                 LLError::TimeFunction timeFunction;
00416                 
00417                 Recorders recorders;
00418                 Recorder* fileRecorder;
00419                 Recorder* fixedBufferRecorder;
00420                 std::string fileRecorderFileName;
00421                 
00422                 int shouldLogCallCounter;
00423                 
00424                 static Settings& get();
00425         
00426                 static void reset();
00427                 static Settings* saveAndReset();
00428                 static void restore(Settings*);
00429                 
00430         private:
00431                 Settings()
00432                         :       printLocation(false),
00433                                 defaultLevel(LLError::LEVEL_DEBUG),
00434                                 crashFunction(NULL),
00435                                 timeFunction(NULL),
00436                                 fileRecorder(NULL),
00437                                 fixedBufferRecorder(NULL),
00438                                 shouldLogCallCounter(0)
00439                         { }
00440                 
00441                 ~Settings()
00442                 {
00443                         for_each(recorders.begin(), recorders.end(),
00444                                          DeletePointer());
00445                 }
00446                 
00447                 static Settings*& getPtr();
00448         };
00449         
00450         Settings& Settings::get()
00451         {
00452                 Settings* p = getPtr();
00453                 if (!p)
00454                 {
00455                         reset();
00456                         p = getPtr();
00457                 }
00458                 return *p;
00459         }
00460         
00461         void Settings::reset()
00462         {
00463                 Globals::get().invalidateCallSites();
00464                 
00465                 Settings*& p = getPtr();
00466                 delete p;
00467                 p = new Settings();
00468         }
00469         
00470         Settings* Settings::saveAndReset()
00471         {
00472                 Globals::get().invalidateCallSites();
00473                 
00474                 Settings*& p = getPtr();
00475                 Settings* originalSettings = p;
00476                 p = new Settings();
00477                 return originalSettings;
00478         }
00479         
00480         void Settings::restore(Settings* originalSettings)
00481         {
00482                 Globals::get().invalidateCallSites();
00483                 
00484                 Settings*& p = getPtr();
00485                 delete p;
00486                 p = originalSettings;
00487         }
00488         
00489         Settings*& Settings::getPtr()
00490         {
00491                 static Settings* currentSettings = NULL;
00492                 return currentSettings;
00493         }
00494 }
00495 
00496 namespace LLError
00497 {
00498         CallSite::CallSite(ELevel level,
00499                                         const char* file,
00500                                         int line,
00501                                         const std::type_info& class_info, 
00502                                         const char* function, 
00503                                         const char* broadTag, 
00504                                         const char* narrowTag,
00505                                         bool printOnce)
00506                 : mLevel(level), mFile(file), mLine(line),
00507                   mClassInfo(class_info), mFunction(function),
00508                   mCached(false), mShouldLog(false), 
00509                   mBroadTag(broadTag), mNarrowTag(narrowTag), mPrintOnce(printOnce)
00510                 { }
00511 
00512 
00513         void CallSite::invalidate()
00514                 { mCached = false; }
00515 }
00516 
00517 namespace
00518 {
00519         bool shouldLogToStderr()
00520         {
00521 #if LL_DARWIN
00522                 // On Mac OS X, stderr from apps launched from the Finder goes to the
00523                 // console log.  It's generally considered bad form to spam too much
00524                 // there.
00525                 
00526                 // If stdin is a tty, assume the user launched from the command line and
00527                 // therefore wants to see stderr.  Otherwise, assume we've been launched
00528                 // from the finder and shouldn't spam stderr.
00529                 return isatty(0);
00530 #else
00531                 return true;
00532 #endif
00533         }
00534         
00535         bool stderrLogWantsTime()
00536         {
00537 #if LL_WINDOWS
00538                 return false;
00539 #else
00540                 return true;
00541 #endif
00542         }
00543         
00544         
00545         void commonInit(const std::string& dir)
00546         {
00547                 LLError::Settings::reset();
00548                 
00549                 LLError::setDefaultLevel(LLError::LEVEL_INFO);
00550                 LLError::setFatalFunction(LLError::crashAndLoop);
00551                 LLError::setTimeFunction(LLError::utcTime);
00552 
00553                 if (shouldLogToStderr())
00554                 {
00555                         LLError::addRecorder(new RecordToStderr(stderrLogWantsTime()));
00556                 }
00557                 
00558 #if LL_WINDOWS
00559                 LLError::addRecorder(new RecordToWinDebug);
00560 #endif
00561 
00562                 LogControlFile& e = LogControlFile::fromDirectory(dir);
00563 
00564                 // NOTE: We want to explicitly load the file before we add it to the event timer
00565                 // that checks for changes to the file.  Else, we're not actually loading the file yet,
00566                 // and most of the initialization happens without any attention being paid to the
00567                 // log control file.  Not to mention that when it finally gets checked later,
00568                 // all log statements that have been evaluated already become dirty and need to be
00569                 // evaluated for printing again.  So, make sure to call checkAndReload()
00570                 // before addToEventTimer().
00571                 e.checkAndReload();
00572                 e.addToEventTimer();
00573         }
00574 }
00575 
00576 namespace LLError
00577 {
00578         void initForServer(const std::string& identity)
00579         {
00580                 std::string dir = "/opt/linden/etc";
00581                 if (LLApp::instance())
00582                 {
00583                         dir = LLApp::instance()->getOption("configdir").asString();
00584                 }
00585                 commonInit(dir);
00586 #if !LL_WINDOWS
00587                 addRecorder(new RecordToSyslog(identity));
00588 #endif
00589         }
00590 
00591         void initForApplication(const std::string& dir)
00592         {
00593                 commonInit(dir);
00594         }
00595 
00596         void setPrintLocation(bool print)
00597         {
00598                 Settings& s = Settings::get();
00599                 s.printLocation = print;
00600         }
00601 
00602         void setFatalFunction(FatalFunction f)
00603         {
00604                 Settings& s = Settings::get();
00605                 s.crashFunction = f;
00606         }
00607 
00608         void setTimeFunction(TimeFunction f)
00609         {
00610                 Settings& s = Settings::get();
00611                 s.timeFunction = f;
00612         }
00613 
00614         void setDefaultLevel(ELevel level)
00615         {
00616                 Globals& g = Globals::get();
00617                 Settings& s = Settings::get();
00618                 g.invalidateCallSites();
00619                 s.defaultLevel = level;
00620         }
00621 
00622         void setFunctionLevel(const std::string& function_name, ELevel level)
00623         {
00624                 Globals& g = Globals::get();
00625                 Settings& s = Settings::get();
00626                 g.invalidateCallSites();
00627                 s.functionLevelMap[function_name] = level;
00628         }
00629 
00630         void setClassLevel(const std::string& class_name, ELevel level)
00631         {
00632                 Globals& g = Globals::get();
00633                 Settings& s = Settings::get();
00634                 g.invalidateCallSites();
00635                 s.classLevelMap[class_name] = level;
00636         }
00637 
00638         void setFileLevel(const std::string& file_name, ELevel level)
00639         {
00640                 Globals& g = Globals::get();
00641                 Settings& s = Settings::get();
00642                 g.invalidateCallSites();
00643                 s.fileLevelMap[file_name] = level;
00644         }
00645 
00646         void setTagLevel(const std::string& tag_name, ELevel level)
00647         {
00648                 Globals& g = Globals::get();
00649                 Settings& s = Settings::get();
00650                 g.invalidateCallSites();
00651                 s.tagLevelMap[tag_name] = level;
00652         }
00653 }
00654 
00655 namespace {
00656         LLError::ELevel decodeLevel(std::string name)
00657         {
00658                 static LevelMap level_names;
00659                 if (level_names.empty())
00660                 {
00661                         level_names["ALL"]              = LLError::LEVEL_ALL;
00662                         level_names["DEBUG"]    = LLError::LEVEL_DEBUG;
00663                         level_names["INFO"]             = LLError::LEVEL_INFO;
00664                         level_names["WARN"]             = LLError::LEVEL_WARN;
00665                         level_names["ERROR"]    = LLError::LEVEL_ERROR;
00666                         level_names["NONE"]             = LLError::LEVEL_NONE;
00667                 }
00668                 
00669                 std::transform(name.begin(), name.end(), name.begin(), toupper);
00670                 
00671                 LevelMap::const_iterator i = level_names.find(name);
00672                 if (i == level_names.end())
00673                 {
00674                         llwarns << "unrecognized logging level: '" << name << "'" << llendl;
00675                         return LLError::LEVEL_INFO;
00676                 }
00677                 
00678                 return i->second;
00679         }
00680         
00681         void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
00682         {
00683                 LLSD::array_const_iterator i, end;
00684                 for (i = list.beginArray(), end = list.endArray(); i != end; ++i)
00685                 {
00686                         map[*i] = level;
00687                 }
00688         }
00689 }
00690 
00691 namespace LLError
00692 {
00693         void configure(const LLSD& config)
00694         {
00695                 Globals& g = Globals::get();
00696                 Settings& s = Settings::get();
00697                 
00698                 g.invalidateCallSites();
00699                 s.functionLevelMap.clear();
00700                 s.classLevelMap.clear();
00701                 s.fileLevelMap.clear();
00702                 s.tagLevelMap.clear();
00703                 s.uniqueLogMessages.clear();
00704                 
00705                 setPrintLocation(config["print-location"]);
00706                 setDefaultLevel(decodeLevel(config["default-level"]));
00707                 
00708                 LLSD sets = config["settings"];
00709                 LLSD::array_const_iterator a, end;
00710                 for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a)
00711                 {
00712                         const LLSD& entry = *a;
00713                         
00714                         ELevel level = decodeLevel(entry["level"]);
00715                         
00716                         setLevels(s.functionLevelMap,   entry["functions"],     level);
00717                         setLevels(s.classLevelMap,              entry["classes"],       level);
00718                         setLevels(s.fileLevelMap,               entry["files"],         level);
00719                         setLevels(s.tagLevelMap,                entry["tags"],          level);
00720                 }
00721         }
00722 }
00723 
00724 
00725 namespace LLError
00726 {
00727         Recorder::~Recorder()
00728                 { }
00729 
00730         // virtual
00731         bool Recorder::wantsTime()
00732                 { return false; }
00733 
00734 
00735 
00736         void addRecorder(Recorder* recorder)
00737         {
00738                 if (recorder == NULL)
00739                 {
00740                         return;
00741                 }
00742                 Settings& s = Settings::get();
00743                 s.recorders.push_back(recorder);
00744         }
00745 
00746         void removeRecorder(Recorder* recorder)
00747         {
00748                 if (recorder == NULL)
00749                 {
00750                         return;
00751                 }
00752                 Settings& s = Settings::get();
00753                 s.recorders.erase(
00754                         std::remove(s.recorders.begin(), s.recorders.end(), recorder),
00755                         s.recorders.end());
00756         }
00757 }
00758 
00759 namespace LLError
00760 {
00761         void logToFile(const std::string& file_name)
00762         {
00763                 LLError::Settings& s = LLError::Settings::get();
00764 
00765                 removeRecorder(s.fileRecorder);
00766                 delete s.fileRecorder;
00767                 s.fileRecorder = NULL;
00768                 s.fileRecorderFileName.clear();
00769                 
00770                 if (file_name.empty())
00771                 {
00772                         return;
00773                 }
00774                 
00775                 RecordToFile* f = new RecordToFile(file_name);
00776                 if (!f->okay())
00777                 {
00778                         delete f;
00779                         return;
00780                 }
00781 
00782                 s.fileRecorderFileName = file_name;
00783                 s.fileRecorder = f;
00784                 addRecorder(f);
00785         }
00786         
00787         void logToFixedBuffer(LLFixedBuffer* fixedBuffer)
00788         {
00789                 LLError::Settings& s = LLError::Settings::get();
00790 
00791                 removeRecorder(s.fixedBufferRecorder);
00792                 delete s.fixedBufferRecorder;
00793                 s.fixedBufferRecorder = NULL;
00794                 
00795                 if (!fixedBuffer)
00796                 {
00797                         return;
00798                 }
00799                 
00800                 s.fixedBufferRecorder = new RecordToFixedBuffer(*fixedBuffer);
00801                 addRecorder(s.fixedBufferRecorder);
00802         }
00803 
00804         std::string logFileName()
00805         {
00806                 LLError::Settings& s = LLError::Settings::get();
00807                 return s.fileRecorderFileName;
00808         }
00809 }
00810 
00811 namespace
00812 {
00813         void writeToRecorders(LLError::ELevel level, const std::string& message)
00814         {
00815                 LLError::Settings& s = LLError::Settings::get();
00816         
00817                 std::string messageWithTime;
00818                 
00819                 for (Recorders::const_iterator i = s.recorders.begin();
00820                         i != s.recorders.end();
00821                         ++i)
00822                 {
00823                         LLError::Recorder* r = *i;
00824                         
00825                         if (r->wantsTime()  &&  s.timeFunction != NULL)
00826                         {
00827                                 if (messageWithTime.empty())
00828                                 {
00829                                         messageWithTime = s.timeFunction() + " " + message;
00830                                 }
00831                                 
00832                                 r->recordMessage(level, messageWithTime);
00833                         }
00834                         else
00835                         {
00836                                 r->recordMessage(level, message);
00837                         }
00838                 }
00839         }
00840 }
00841 
00842 
00843 /*
00844 Recorder formats:
00845 
00846 $type = "ERROR" | "WARNING" | "ALERT" | "INFO" | "DEBUG"
00847 $loc = "$file($line)"
00848 $msg = "$loc : " if FATAL or printing loc
00849                 "" otherwise
00850 $msg += "$type: "
00851 $msg += contents of stringstream
00852 
00853 $time = "%Y-%m-%dT%H:%M:%SZ" if UTC
00854          or "%Y-%m-%dT%H:%M:%S %Z" if local
00855 
00856 syslog: "$msg"
00857 file: "$time $msg\n"
00858 stderr: "$time $msg\n" except on windows, "$msg\n"
00859 fixedbuf: "$msg"
00860 winddebug: "$msg\n"
00861 
00862 Note: if FATAL, an additional line gets logged first, with $msg set to
00863         "$loc : error"
00864         
00865 You get:
00866         llfoo.cpp(42) : error
00867         llfoo.cpp(42) : ERROR: something
00868         
00869 */
00870 
00871 namespace {
00872         bool checkLevelMap(const LevelMap& map, const std::string& key,
00873                                                 LLError::ELevel& level)
00874         {
00875                 LevelMap::const_iterator i = map.find(key);
00876                 if (i == map.end())
00877                 {
00878                         return false;
00879                 }
00880                 
00881                         level = i->second;
00882                 return true;
00883         }
00884         
00885         class LogLock
00886         {
00887         public:
00888                 LogLock();
00889                 ~LogLock();
00890                 bool ok() const { return mOK; }
00891         private:
00892                 bool mLocked;
00893                 bool mOK;
00894         };
00895         
00896         LogLock::LogLock()
00897                 : mLocked(false), mOK(false)
00898         {
00899                 if (!gLogMutexp)
00900                 {
00901                         mOK = true;
00902                         return;
00903                 }
00904                 
00905                 const int MAX_RETRIES = 5;
00906                 for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
00907                 {
00908                         apr_status_t s = apr_thread_mutex_trylock(gLogMutexp);
00909                         if (!APR_STATUS_IS_EBUSY(s))
00910                         {
00911                                 mLocked = true;
00912                                 mOK = true;
00913                                 return;
00914                         }
00915 
00916                         ms_sleep(1);
00917                         //apr_thread_yield();
00918                                 // Just yielding won't necessarily work, I had problems with
00919                                 // this on Linux - doug 12/02/04
00920                 }
00921 
00922                 // We're hosed, we can't get the mutex.  Blah.
00923                 std::cerr << "LogLock::LogLock: failed to get mutex for log"
00924                                         << std::endl;
00925         }
00926         
00927         LogLock::~LogLock()
00928         {
00929                 if (mLocked)
00930                 {
00931                         apr_thread_mutex_unlock(gLogMutexp);
00932                 }
00933         }
00934 }
00935 
00936 namespace LLError
00937 {
00938         bool Log::shouldLog(CallSite& site)
00939         {
00940                 LogLock lock;
00941                 if (!lock.ok())
00942                 {
00943                         return false;
00944                 }
00945                 
00946                 Globals& g = Globals::get();
00947                 Settings& s = Settings::get();
00948                 
00949                 s.shouldLogCallCounter += 1;
00950                 
00951                 std::string class_name = className(site.mClassInfo);
00952                 std::string function_name = functionName(site.mFunction);
00953                 if (site.mClassInfo != typeid(NoClassInfo))
00954                 {
00955                         function_name = class_name + "::" + function_name;
00956                 }
00957 
00958                 ELevel compareLevel = s.defaultLevel;
00959 
00960                 // The most specific match found will be used as the log level,
00961                 // since the computation short circuits.
00962                 // So, in increasing order of importance:
00963                 // Default < Broad Tag < File < Class < Function < Narrow Tag
00964                 ((site.mNarrowTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mNarrowTag, compareLevel) : false)
00965                 || checkLevelMap(s.functionLevelMap, function_name, compareLevel)
00966                 || checkLevelMap(s.classLevelMap, class_name, compareLevel)
00967                 || checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel)
00968                 || ((site.mBroadTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mBroadTag, compareLevel) : false);
00969 
00970                 site.mCached = true;
00971                 g.addCallSite(site);
00972                 return site.mShouldLog = site.mLevel >= compareLevel;
00973         }
00974 
00975 
00976         std::ostringstream* Log::out()
00977         {
00978                 LogLock lock;
00979                 if (lock.ok())
00980                 {
00981                         Globals& g = Globals::get();
00982 
00983                         if (!g.messageStreamInUse)
00984                         {
00985                                 g.messageStreamInUse = true;
00986                                 return &g.messageStream;
00987                         }
00988                 }
00989                 
00990                 return new std::ostringstream;
00991         }
00992 
00993         void Log::flush(std::ostringstream* out, const CallSite& site)
00994         {
00995                 LogLock lock;
00996                 if (!lock.ok())
00997                 {
00998                         return;
00999                 }
01000                 
01001                 Globals& g = Globals::get();
01002                 Settings& s = Settings::get();
01003 
01004                 std::string message = out->str();
01005                 if (out == &g.messageStream)
01006                 {
01007                         g.messageStream.clear();
01008                         g.messageStream.str("");
01009                         g.messageStreamInUse = false;
01010                 }
01011                 else
01012                 {
01013                         delete out;
01014                 }
01015 
01016                 if (site.mLevel == LEVEL_ERROR)
01017                 {
01018                         std::ostringstream fatalMessage;
01019                         fatalMessage << abbreviateFile(site.mFile)
01020                                                 << "(" << site.mLine << ") : error";
01021                         
01022                         writeToRecorders(site.mLevel, fatalMessage.str());
01023                 }
01024                 
01025                 
01026                 std::ostringstream prefix;
01027 
01028                 switch (site.mLevel)
01029                 {
01030                         case LEVEL_DEBUG:               prefix << "DEBUG: ";    break;
01031                         case LEVEL_INFO:                prefix << "INFO: ";             break;
01032                         case LEVEL_WARN:                prefix << "WARNING: ";  break;
01033                         case LEVEL_ERROR:               prefix << "ERROR: ";    break;
01034                         default:                                prefix << "XXX: ";              break;
01035                 };
01036                 
01037                 if (s.printLocation)
01038                 {
01039                         prefix << abbreviateFile(site.mFile)
01040                                         << "(" << site.mLine << ") : ";
01041                 }
01042                 
01043                 if (message.find(functionName(site.mFunction)) == std::string::npos)
01044                 {
01045         #if LL_WINDOWS
01046                         // DevStudio: __FUNCTION__ already includes the full class name
01047         #else
01048                         if (site.mClassInfo != typeid(NoClassInfo))
01049                         {
01050                                 prefix << className(site.mClassInfo) << "::";
01051                         }
01052         #endif
01053                         prefix << site.mFunction << ": ";
01054                 }
01055 
01056                 if (site.mPrintOnce)
01057                 {
01058                         std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
01059                         if (messageIter != s.uniqueLogMessages.end())
01060                         {
01061                                 messageIter->second++;
01062                                 unsigned int num_messages = messageIter->second;
01063                                 if (num_messages == 10 || num_messages == 50 || (num_messages % 100) == 0)
01064                                 {
01065                                         prefix << "ONCE (" << num_messages << "th time seen): ";
01066                                 } 
01067                                 else
01068                                 {
01069                                         return;
01070                                 }
01071                         }
01072                         else 
01073                         {
01074                                 prefix << "ONCE: ";
01075                                 s.uniqueLogMessages[message] = 1;
01076                         }
01077                 }
01078                 
01079                 prefix << message;
01080                 message = prefix.str();
01081                 
01082                 writeToRecorders(site.mLevel, message);
01083                 
01084                 if (site.mLevel == LEVEL_ERROR  &&  s.crashFunction)
01085                 {
01086                         s.crashFunction(message);
01087                 }
01088         }
01089 }
01090 
01091 
01092 
01093 
01094 namespace LLError
01095 {
01096         Settings* saveAndResetSettings()
01097         {
01098                 return Settings::saveAndReset();
01099         }
01100         
01101         void restoreSettings(Settings* s)
01102         {
01103                 return Settings::restore(s);
01104         }
01105 
01106         std::string removePrefix(std::string& s, const std::string& p)
01107         {
01108                 std::string::size_type where = s.find(p);
01109                 if (where == std::string::npos)
01110                 {
01111                         return s;
01112                 }
01113                 
01114                 return std::string(s, where + p.size());
01115         }
01116         
01117         void replaceChar(std::string& s, char old, char replacement)
01118         {
01119                 std::string::size_type i = 0;
01120                 std::string::size_type len = s.length();
01121                 for ( ; i < len; i++ )
01122                 {
01123                         if (s[i] == old)
01124                         {
01125                                 s[i] = replacement;
01126                         }
01127                 }
01128         }
01129 
01130         std::string abbreviateFile(const std::string& filePath)
01131         {
01132                 std::string f = filePath;
01133 #if LL_WINDOWS
01134                 replaceChar(f, '\\', '/');
01135 #endif
01136                 static std::string indra_prefix = "indra/";
01137                 f = removePrefix(f, indra_prefix);
01138 
01139 #if LL_DARWIN
01140                 static std::string newview_prefix = "newview/../";
01141                 f = removePrefix(f, newview_prefix);
01142 #endif
01143 
01144                 return f;
01145         }
01146 
01147         int shouldLogCallCount()
01148         {
01149                 Settings& s = Settings::get();
01150                 return s.shouldLogCallCounter;
01151         }
01152 
01153         void crashAndLoop(const std::string& message)
01154         {
01155                 // Now, we go kaboom!
01156                 int* crash = NULL;
01157 
01158                 *crash = 0;
01159 
01160                 while(true)
01161                 {
01162                         // Loop forever, in case the crash didn't work?
01163                 }
01164         }
01165 
01166         std::string utcTime()
01167         {
01168                 time_t now = time(NULL);
01169                 const size_t BUF_SIZE = 64;
01170                 char time_str[BUF_SIZE];        /* Flawfinder: ignore */
01171                 
01172                 int chars = strftime(time_str, BUF_SIZE, 
01173                                                                   "%Y-%m-%dT%H:%M:%SZ",
01174                                                                   gmtime(&now));
01175 
01176                 return chars ? time_str : "time error";
01177         }
01178 }
01179 

Generated on Fri May 16 08:32:02 2008 for SecondLife by  doxygen 1.5.5