summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorMark Lentczner <markl@lindenlab.com>2007-02-06 00:57:33 +0000
committerMark Lentczner <markl@lindenlab.com>2007-02-06 00:57:33 +0000
commitd0d4670f4941dcf7430fb1269c6613140ecf3ff7 (patch)
treee3d6b59c19cac6bc172ec5fb0131ffc8f4923b75 /indra
parent77f04c74eb1603bf2fadc30127d05378bfc7a48a (diff)
merge in of error-refactor-3
concludes (fixes) SL-31187 pair programmed and reviewed by markl and karen
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/linden_common.h1
-rw-r--r--indra/llcommon/llapp.cpp8
-rw-r--r--indra/llcommon/llerror.cpp1031
-rw-r--r--indra/llcommon/llerror.h404
-rw-r--r--indra/llcommon/llerrorcontrol.h123
-rw-r--r--indra/llcommon/llerrorlegacy.h98
-rw-r--r--indra/llcommon/llfile.cpp7
-rw-r--r--indra/llcommon/llfile.h6
-rw-r--r--indra/llcommon/llformat.cpp28
-rw-r--r--indra/llcommon/llformat.h23
-rw-r--r--indra/llcommon/lllivefile.cpp88
-rw-r--r--indra/llcommon/lllivefile.h21
-rw-r--r--indra/llcommon/llsd.cpp1
-rw-r--r--indra/llcommon/llstring.h1
-rw-r--r--indra/llcommon/llstringtable.h2
-rw-r--r--indra/llmessage/message.cpp28
-rw-r--r--indra/llmessage/message.h3
-rw-r--r--indra/llmessage/message_prehash.cpp4
-rw-r--r--indra/llmessage/message_prehash.h2
-rw-r--r--indra/llui/llbutton.cpp2
-rw-r--r--indra/newview/llstartup.cpp4
-rw-r--r--indra/newview/llviewermenu.cpp29
-rw-r--r--indra/newview/llviewerprecompiledheaders.h2
-rw-r--r--indra/newview/llxmlrpctransaction.cpp8
-rw-r--r--indra/test/llhttpclient_tut.cpp1
-rw-r--r--indra/test/llpipeutil.cpp1
-rw-r--r--indra/test/lltut.cpp28
-rw-r--r--indra/test/lltut.h6
-rw-r--r--indra/test/test.cpp11
29 files changed, 1655 insertions, 316 deletions
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
index 3e9ec14eb3..2ecda65d6f 100644
--- a/indra/llcommon/linden_common.h
+++ b/indra/llcommon/linden_common.h
@@ -30,6 +30,7 @@
#include "stdtypes.h"
#include "lldefs.h"
#include "llerror.h"
+#include "llformat.h"
#include "llstring.h"
#include "lltimer.h"
#include "llfasttimer.h"
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 87e7016f39..fe5587365c 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -11,9 +11,11 @@
#include "llcommon.h"
#include "llapr.h"
+#include "llerrorcontrol.h"
#include "llerrorthread.h"
#include "llframetimer.h"
#include "llmemory.h"
+#include "lltimer.h"
//
// Signal handling
@@ -174,10 +176,8 @@ LLSD LLApp::getOptionData(OptionPriority level)
void LLApp::stepFrame()
{
- // Update the static frame timer.
LLFrameTimer::updateFrameTime();
-
- // Run ready runnables
+ LLEventTimer::updateClass();
mRunner.run();
}
@@ -544,7 +544,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
else
{
// Don't log anything, even errors - this is because this signal could happen anywhere.
- gErrorStream.setLevel(LLErrorStream::NONE);
+ LLError::setDefaultLevel(LLError::LEVEL_NONE);
}
// Change the signal that we reraise to SIGABRT, so we generate a core dump.
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 9f643fd9eb..52b37bb05c 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -1,40 +1,1041 @@
/**
* @file llerror.cpp
- * @brief Function to crash.
+ * @date December 2006
+ * @brief error message system
*
- * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
* $License$
*/
+
#include "linden_common.h"
#include "llerror.h"
+#include "llerrorcontrol.h"
+
+#include "llapr.h"
+extern apr_thread_mutex_t *gLogMutexp;
+#include "llfile.h"
+#include "llfixedbuffer.h"
+#include "lllivefile.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+
+#include <algorithm>
+#include <cctype>
+#include <map>
+#include <sstream>
+#if !LL_WINDOWS
+#include <stdio.h>
+#include <syslog.h>
+#endif
+#include <time.h>
+#if LL_WINDOWS
+#include <windows.h>
+#endif
+#include <vector>
+
+
+#ifdef __GNUC__
+#include <cxxabi.h>
+#endif
+
+namespace {
+#if !LL_WINDOWS
+ class RecordToSyslog : public LLError::Recorder
+ {
+ public:
+ RecordToSyslog(const std::string& identity)
+ : mIdentity(identity)
+ {
+ openlog(mIdentity.c_str(), LOG_CONS|LOG_PID, LOG_LOCAL0);
+ // we need to set the string from a local copy of the string
+ // since apparanetly openlog expects the const char* to remain
+ // valid even after it returns (presumably until closelog)
+ }
+
+ ~RecordToSyslog()
+ {
+ closelog();
+ }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message)
+ {
+ int syslogPriority = LOG_CRIT;
+ switch (level) {
+ case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break;
+ case LLError::LEVEL_INFO: syslogPriority = LOG_INFO; break;
+ case LLError::LEVEL_WARN: syslogPriority = LOG_WARNING; break;
+ case LLError::LEVEL_ERROR: syslogPriority = LOG_CRIT; break;
+ default: syslogPriority = LOG_CRIT;
+ }
+
+ syslog(syslogPriority, "%s", message.c_str());
+ }
+ private:
+ std::string mIdentity;
+ };
+#endif
+
+ class RecordToFile : public LLError::Recorder
+ {
+ public:
+ RecordToFile(const std::string& filename)
+ {
+ mFile.open(filename.c_str(), llofstream::out | llofstream::app);
+ if (!mFile)
+ {
+ llinfos << "Error setting log file to " << filename << llendl;
+ }
+ }
+
+ ~RecordToFile()
+ {
+ mFile.close();
+ }
+
+ bool okay() { return mFile; }
+
+ virtual bool wantsTime() { return true; }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message)
+ {
+ mFile << message << std::endl;
+ // mFile.flush();
+ // *FIX: should we do this?
+ }
+
+ private:
+ llofstream mFile;
+ };
+
+
+ class RecordToStderr : public LLError::Recorder
+ {
+ public:
+ RecordToStderr(bool timestamp) : mTimestamp(timestamp) { }
+
+ virtual bool wantsTime() { return mTimestamp; }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message)
+ {
+ fprintf(stderr, "%s\n", message.c_str());
+ }
+
+ private:
+ bool mTimestamp;
+ };
-LLErrorBuffer gErrorBuffer;
-LLErrorStream gErrorStream(&gErrorBuffer);
+ class RecordToFixedBuffer : public LLError::Recorder
+ {
+ public:
+ RecordToFixedBuffer(LLFixedBuffer& buffer) : mBuffer(buffer) { }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message)
+ {
+ mBuffer.addLine(message.c_str());
+ }
+
+ private:
+ LLFixedBuffer& mBuffer;
+ };
+
+#if LL_WINDOWS
+ class RecordToWinDebug: public LLError::Recorder
+ {
+ public:
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message)
+ {
+ llutf16string utf16str =
+ wstring_to_utf16str(utf8str_to_wstring(message));
+ utf16str += '\n';
+ OutputDebugString(utf16str.c_str());
+ }
+ };
+#endif
+}
-void _llcrash_and_loop()
+namespace
{
- // Now, we go kaboom!
- U32* crash = NULL;
+ std::string className(const std::type_info& type)
+ {
+#ifdef __GNUC__
+ // GCC: type_info::name() returns a mangled class name, must demangle
+
+ static size_t abi_name_len = 100;
+ static char* abi_name_buf = (char*)malloc(abi_name_len);
+ // warning: above is voodoo inferred from the GCC manual,
+ // do NOT change
+
+ int status;
+ // We don't use status, and shouldn't have to pass apointer to it
+ // but gcc 3.3 libstc++'s implementation of demangling is broken
+ // and fails without.
+
+ char* name = abi::__cxa_demangle(type.name(),
+ abi_name_buf, &abi_name_len, &status);
+ // this call can realloc the abi_name_buf pointer (!)
+
+ return name ? name : type.name();
+
+#elif LL_WINDOWS
+ // DevStudio: type_info::name() includes the text "class " at the start
+
+ static const std::string class_prefix = "class ";
+
+ std::string name = type.name();
+ std::string::size_type p = name.find(class_prefix);
+ if (p == std::string::npos)
+ {
+ return name;
+ }
+
+ return name.substr(p + class_prefix.size());
+
+#else
+ return type.name();
+#endif
+ }
+
+ std::string functionName(const std::string& preprocessor_name)
+ {
+#if LL_WINDOWS
+ // DevStudio: the __FUNCTION__ macro string includes
+ // the type and/or namespace prefixes
+
+ std::string::size_type p = preprocessor_name.rfind(':');
+ if (p == std::string::npos)
+ {
+ return preprocessor_name;
+ }
+ return preprocessor_name.substr(p + 1);
+
+#else
+ return preprocessor_name;
+#endif
+ }
+
+
+ class LogControlFile : public LLLiveFile
+ {
+ LOG_CLASS(LogControlFile);
+
+ public:
+ static LogControlFile& fromDirectory(const std::string& dir);
+
+ virtual void loadFile();
+
+ private:
+ LogControlFile(const std::string &filename)
+ : LLLiveFile(filename)
+ { }
+ };
+
+ LogControlFile& LogControlFile::fromDirectory(const std::string& dir)
+ {
+ std::string dirBase = dir + "/";
+ // NB: We have no abstraction in llcommon for the "proper"
+ // delimiter but it turns out that "/" works on all three platforms
+
+ std::string file = dirBase + "logcontrol-dev.xml";
+
+ llstat stat_info;
+ if (LLFile::stat(file.c_str(), &stat_info)) {
+ // NB: stat returns non-zero if it can't read the file, for example
+ // if it doesn't exist. LLFile has no better abstraction for
+ // testing for file existence.
+
+ file = dirBase + "logcontrol.xml";
+ }
+ return * new LogControlFile(file);
+ // NB: This instance is never freed
+ }
+
+ void LogControlFile::loadFile()
+ {
+ LLSD configuration;
+
+ {
+ llifstream file(filename().c_str());
+ if (file.is_open())
+ {
+ LLSDSerialize::fromXML(configuration, file);
+ }
+
+ if (configuration.isUndefined())
+ {
+ llwarns << filename() << " missing, ill-formed,"
+ " or simply undefined; not changing configuration"
+ << llendl;
+ return;
+ }
+ }
+
+ LLError::configure(configuration);
+ llwarns << "error logging reconfigured from " << filename() << llendl;
+ }
+
+
+ typedef std::map<std::string, LLError::ELevel> LevelMap;
+ typedef std::vector<LLError::Recorder*> Recorders;
+ typedef std::vector<LLError::CallSite*> CallSiteVector;
+
+ class Globals
+ {
+ public:
+ std::ostringstream messageStream;
+ bool messageStreamInUse;
+
+ void addCallSite(LLError::CallSite&);
+ void invalidateCallSites();
+
+ static Globals& get();
+ // return the one instance of the globals
+
+ private:
+ CallSiteVector callSites;
+
+ Globals()
+ : messageStreamInUse(false)
+ { }
+
+ };
+
+ void Globals::addCallSite(LLError::CallSite& site)
+ {
+ callSites.push_back(&site);
+ }
+
+ void Globals::invalidateCallSites()
+ {
+ for (CallSiteVector::const_iterator i = callSites.begin();
+ i != callSites.end();
+ ++i)
+ {
+ (*i)->invalidate();
+ }
+
+ callSites.clear();
+ }
- *crash = 0;
+ Globals& Globals::get()
+ {
+ /* This pattern, of returning a reference to a static function
+ variable, is to ensure that this global is constructed before
+ it is used, no matter what the global initializeation sequence
+ is.
+ See C++ FAQ Lite, sections 10.12 through 10.14
+ */
+ static Globals* globals = new Globals;
+ return *globals;
+ }
+}
+
+namespace LLError
+{
+ class Settings
+ {
+ public:
+ bool printLocation;
- while(TRUE)
+ LLError::ELevel defaultLevel;
+
+ LevelMap functionLevelMap;
+ LevelMap classLevelMap;
+ LevelMap fileLevelMap;
+
+ LLError::FatalFunction crashFunction;
+ LLError::TimeFunction timeFunction;
+
+ Recorders recorders;
+ Recorder* fileRecorder;
+ Recorder* fixedBufferRecorder;
+ std::string fileRecorderFileName;
+
+ int shouldLogCallCounter;
+
+ static Settings& get();
+
+ static void reset();
+ static Settings* saveAndReset();
+ static void restore(Settings*);
+
+ private:
+ Settings()
+ : printLocation(false),
+ defaultLevel(LLError::LEVEL_DEBUG),
+ crashFunction(NULL),
+ timeFunction(NULL),
+ fileRecorder(NULL),
+ fixedBufferRecorder(NULL),
+ shouldLogCallCounter(0)
+ { }
+
+ static Settings*& getPtr();
+ };
+
+ Settings& Settings::get()
+ {
+ Settings* p = getPtr();
+ if (!p)
+ {
+ reset();
+ p = getPtr();
+ }
+ return *p;
+ }
+
+ void Settings::reset()
+ {
+ Globals::get().invalidateCallSites();
+
+ Settings*& p = getPtr();
+ delete p;
+ p = new Settings();
+ }
+
+ Settings* Settings::saveAndReset()
+ {
+ Globals::get().invalidateCallSites();
+
+ Settings*& p = getPtr();
+ Settings* originalSettings = p;
+ p = new Settings();
+ return originalSettings;
+ }
+
+ void Settings::restore(Settings* originalSettings)
{
+ Globals::get().invalidateCallSites();
+
+ Settings*& p = getPtr();
+ delete p;
+ p = originalSettings;
+ }
- // Loop forever, in case the crash didn't work?
+ Settings*& Settings::getPtr()
+ {
+ static Settings* currentSettings = NULL;
+ return currentSettings;
}
}
-LLScopedErrorLevel::LLScopedErrorLevel(LLErrorBuffer::ELevel error_level)
+namespace LLError
{
- mOrigErrorLevel = gErrorStream.getErrorLevel();
- gErrorStream.setErrorLevel(error_level);
+ CallSite::CallSite(ELevel level,
+ const char* file, int line,
+ const std::type_info& class_info, const char* function)
+ : mLevel(level), mFile(file), mLine(line),
+ mClassInfo(class_info), mFunction(function),
+ mCached(false), mShouldLog(false)
+ { }
+
+
+ void CallSite::invalidate()
+ { mCached = false; }
}
+namespace
+{
+ bool shouldLogToStderr()
+ {
+#if LL_DARWIN
+ // On Mac OS X, stderr from apps launched from the Finder goes to the
+ // console log. It's generally considered bad form to spam too much
+ // there.
+
+ // If stdin is a tty, assume the user launched from the command line and
+ // therefore wants to see stderr. Otherwise, assume we've been launched
+ // from the finder and shouldn't spam stderr.
+ return isatty(0);
+#else
+ return true;
+#endif
+ }
+
+ bool stderrLogWantsTime()
+ {
+#if LL_WINDOWS
+ return false;
+#else
+ return true;
+#endif
+ }
+
+
+ void commonInit(const std::string& dir)
+ {
+ LLError::Settings::reset();
+
+ LLError::setDefaultLevel(LLError::LEVEL_INFO);
+ LLError::setFatalFunction(LLError::crashAndLoop);
+ LLError::setTimeFunction(LLError::utcTime);
+
+ if (shouldLogToStderr())
+ {
+ LLError::addRecorder(new RecordToStderr(stderrLogWantsTime()));
+ }
+
+#if LL_WINDOWS
+ LLError::addRecorder(new RecordToWinDebug);
+#endif
+
+ LogControlFile& e = LogControlFile::fromDirectory(dir);
+ e.addToEventTimer();
+ }
+}
-LLScopedErrorLevel::~LLScopedErrorLevel()
+namespace LLError
{
- gErrorStream.setErrorLevel(mOrigErrorLevel);
+ void initForServer(const std::string& identity)
+ {
+ std::string dir = LLApp::instance()->getOption("configdir");
+ commonInit(dir);
+#if !LL_WINDOWS
+ addRecorder(new RecordToSyslog(identity));
+#endif
+ }
+
+ void initForApplication(const std::string& dir)
+ {
+ commonInit(dir);
+ }
+
+ void setPrintLocation(bool print)
+ {
+ Settings& s = Settings::get();
+ s.printLocation = print;
+ }
+
+ void setFatalFunction(FatalFunction f)
+ {
+ Settings& s = Settings::get();
+ s.crashFunction = f;
+ }
+
+ void setTimeFunction(TimeFunction f)
+ {
+ Settings& s = Settings::get();
+ s.timeFunction = f;
+ }
+
+ void setDefaultLevel(ELevel level)
+ {
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+ g.invalidateCallSites();
+ s.defaultLevel = level;
+ }
+
+ void setFunctionLevel(const std::string& function_name, ELevel level)
+ {
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+ g.invalidateCallSites();
+ s.functionLevelMap[function_name] = level;
+ }
+
+ void setClassLevel(const std::string& class_name, ELevel level)
+ {
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+ g.invalidateCallSites();
+ s.classLevelMap[class_name] = level;
+ }
+
+ void setFileLevel(const std::string& file_name, ELevel level)
+ {
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+ g.invalidateCallSites();
+ s.fileLevelMap[file_name] = level;
+ }
}
+
+namespace {
+ LLError::ELevel decodeLevel(std::string name)
+ {
+ static LevelMap level_names;
+ if (level_names.empty())
+ {
+ level_names["ALL"] = LLError::LEVEL_ALL;
+ level_names["DEBUG"] = LLError::LEVEL_DEBUG;
+ level_names["INFO"] = LLError::LEVEL_INFO;
+ level_names["WARN"] = LLError::LEVEL_WARN;
+ level_names["ERROR"] = LLError::LEVEL_ERROR;
+ level_names["NONE"] = LLError::LEVEL_NONE;
+ }
+
+ std::transform(name.begin(), name.end(), name.begin(), toupper);
+
+ LevelMap::const_iterator i = level_names.find(name);
+ if (i == level_names.end())
+ {
+ llwarns << "unrecognized logging level: '" << name << "'" << llendl;
+ return LLError::LEVEL_INFO;
+ }
+
+ return i->second;
+ }
+
+ void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
+ {
+ LLSD::array_const_iterator i, end;
+ for (i = list.beginArray(), end = list.endArray(); i != end; ++i)
+ {
+ map[*i] = level;
+ }
+ }
+}
+
+namespace LLError
+{
+ void configure(const LLSD& config)
+ {
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+
+ g.invalidateCallSites();
+ s.functionLevelMap.clear();
+ s.classLevelMap.clear();
+ s.fileLevelMap.clear();
+
+ setPrintLocation(config["print-location"]);
+ setDefaultLevel(decodeLevel(config["default-level"]));
+
+ LLSD sets = config["settings"];
+ LLSD::array_const_iterator a, end;
+ for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a)
+ {
+ const LLSD& entry = *a;
+
+ ELevel level = decodeLevel(entry["level"]);
+
+ setLevels(s.functionLevelMap, entry["functions"], level);
+ setLevels(s.classLevelMap, entry["classes"], level);
+ setLevels(s.fileLevelMap, entry["files"], level);
+ }
+ }
+}
+
+
+namespace LLError
+{
+ Recorder::~Recorder()
+ { }
+
+ // virtual
+ bool Recorder::wantsTime()
+ { return false; }
+
+
+
+ void addRecorder(Recorder* recorder)
+ {
+ if (recorder == NULL)
+ {
+ return;
+ }
+ Settings& s = Settings::get();
+ s.recorders.push_back(recorder);
+ }
+
+ void removeRecorder(Recorder* recorder)
+ {
+ if (recorder == NULL)
+ {
+ return;
+ }
+ Settings& s = Settings::get();
+ s.recorders.erase(
+ std::remove(s.recorders.begin(), s.recorders.end(), recorder),
+ s.recorders.end());
+ }
+}
+
+namespace LLError
+{
+ void logToFile(const std::string& file_name)
+ {
+ LLError::Settings& s = LLError::Settings::get();
+
+ removeRecorder(s.fileRecorder);
+ delete s.fileRecorder;
+ s.fileRecorder = NULL;
+ s.fileRecorderFileName.clear();
+
+ if (file_name.empty())
+ {
+ return;
+ }
+
+ RecordToFile* f = new RecordToFile(file_name);
+ if (!f->okay())
+ {
+ delete f;
+ return;
+ }
+
+ s.fileRecorderFileName = file_name;
+ s.fileRecorder = f;
+ addRecorder(f);
+ }
+
+ void logToFixedBuffer(LLFixedBuffer* fixedBuffer)
+ {
+ LLError::Settings& s = LLError::Settings::get();
+
+ removeRecorder(s.fixedBufferRecorder);
+ delete s.fixedBufferRecorder;
+ s.fixedBufferRecorder = NULL;
+
+ if (!fixedBuffer)
+ {
+ return;
+ }
+
+ s.fixedBufferRecorder = new RecordToFixedBuffer(*fixedBuffer);
+ addRecorder(s.fixedBufferRecorder);
+ }
+
+ std::string logFileName()
+ {
+ LLError::Settings& s = LLError::Settings::get();
+ return s.fileRecorderFileName;
+ }
+}
+
+namespace
+{
+ void writeToRecorders(LLError::ELevel level, const std::string& message)
+ {
+ LLError::Settings& s = LLError::Settings::get();
+
+ std::string messageWithTime;
+
+ for (Recorders::const_iterator i = s.recorders.begin();
+ i != s.recorders.end();
+ ++i)
+ {
+ LLError::Recorder* r = *i;
+
+ if (r->wantsTime() && s.timeFunction != NULL)
+ {
+ if (messageWithTime.empty())
+ {
+ messageWithTime = s.timeFunction() + " " + message;
+ }
+
+ r->recordMessage(level, messageWithTime);
+ }
+ else
+ {
+ r->recordMessage(level, message);
+ }
+ }
+ }
+}
+
+
+/*
+Recorder formats:
+
+$type = "ERROR" | "WARNING" | "ALERT" | "INFO" | "DEBUG"
+$loc = "$file($line)"
+$msg = "$loc : " if FATAL or printing loc
+ "" otherwise
+$msg += "$type: "
+$msg += contents of stringstream
+
+$time = "%Y-%m-%dT%H:%M:%SZ" if UTC
+ or "%Y-%m-%dT%H:%M:%S %Z" if local
+
+syslog: "$msg"
+file: "$time $msg\n"
+stderr: "$time $msg\n" except on windows, "$msg\n"
+fixedbuf: "$msg"
+winddebug: "$msg\n"
+
+Note: if FATAL, an additional line gets logged first, with $msg set to
+ "$loc : error"
+
+You get:
+ llfoo.cpp(42) : error
+ llfoo.cpp(42) : ERROR: something
+
+*/
+
+namespace {
+ bool checkLevelMap(const LevelMap& map, const std::string& key,
+ LLError::ELevel& level)
+ {
+ LevelMap::const_iterator i = map.find(key);
+ if (i == map.end())
+ {
+ return false;
+ }
+
+ level = i->second;
+ return true;
+ }
+
+ class LogLock
+ {
+ public:
+ LogLock();
+ ~LogLock();
+ bool ok() const { return mOK; }
+ private:
+ bool mLocked;
+ bool mOK;
+ };
+
+ LogLock::LogLock()
+ : mLocked(false), mOK(false)
+ {
+ if (!gLogMutexp)
+ {
+ mOK = true;
+ return;
+ }
+
+ const int MAX_RETRIES = 5;
+ for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
+ {
+ apr_status_t s = apr_thread_mutex_trylock(gLogMutexp);
+ if (!APR_STATUS_IS_EBUSY(s))
+ {
+ mLocked = true;
+ mOK = true;
+ return;
+ }
+
+ ms_sleep(1);
+ //apr_thread_yield();
+ // Just yielding won't necessarily work, I had problems with
+ // this on Linux - doug 12/02/04
+ }
+
+ // We're hosed, we can't get the mutex. Blah.
+ std::cerr << "LogLock::LogLock: failed to get mutex for log"
+ << std::endl;
+ }
+
+ LogLock::~LogLock()
+ {
+ if (mLocked)
+ {
+ apr_thread_mutex_unlock(gLogMutexp);
+ }
+ }
+}
+
+namespace LLError
+{
+ bool Log::shouldLog(CallSite& site)
+ {
+ LogLock lock;
+ if (!lock.ok())
+ {
+ return false;
+ }
+
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+
+ s.shouldLogCallCounter += 1;
+
+ std::string class_name = className(site.mClassInfo);
+ std::string function_name = functionName(site.mFunction);
+ if (site.mClassInfo != typeid(NoClassInfo))
+ {
+ function_name = class_name + "::" + function_name;
+ }
+
+ ELevel compareLevel = s.defaultLevel;
+
+ checkLevelMap(s.functionLevelMap, function_name, compareLevel)
+ || checkLevelMap(s.classLevelMap, class_name, compareLevel)
+ || checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel);
+
+ site.mCached = true;
+ g.addCallSite(site);
+ return site.mShouldLog = site.mLevel >= compareLevel;
+ }
+
+
+ std::ostringstream* Log::out()
+ {
+ LogLock lock;
+ if (lock.ok())
+ {
+ Globals& g = Globals::get();
+
+ if (!g.messageStreamInUse)
+ {
+ g.messageStreamInUse = true;
+ return &g.messageStream;
+ }
+ }
+
+ return new std::ostringstream;
+ }
+
+ void Log::flush(std::ostringstream* out, const CallSite& site)
+ {
+ LogLock lock;
+ if (!lock.ok())
+ {
+ return;
+ }
+
+ Globals& g = Globals::get();
+ Settings& s = Settings::get();
+
+ std::string message = out->str();
+ if (out == &g.messageStream)
+ {
+ g.messageStream.clear();
+ g.messageStream.str("");
+ g.messageStreamInUse = false;
+ }
+ else
+ {
+ delete out;
+ }
+
+ if (site.mLevel == LEVEL_ERROR)
+ {
+ std::ostringstream fatalMessage;
+ fatalMessage << abbreviateFile(site.mFile)
+ << "(" << site.mLine << ") : error";
+
+ writeToRecorders(site.mLevel, fatalMessage.str());
+ }
+
+
+ std::ostringstream prefix;
+
+ switch (site.mLevel)
+ {
+ case LEVEL_DEBUG: prefix << "DEBUG: "; break;
+ case LEVEL_INFO: prefix << "INFO: "; break;
+ case LEVEL_WARN: prefix << "WARNING: "; break;
+ case LEVEL_ERROR: prefix << "ERROR: "; break;
+ default: prefix << "XXX: "; break;
+ };
+
+ if (s.printLocation)
+ {
+ prefix << abbreviateFile(site.mFile)
+ << "(" << site.mLine << ") : ";
+ }
+
+ if (message.find(functionName(site.mFunction)) == std::string::npos)
+ {
+ #if LL_WINDOWS
+ // DevStudio: __FUNCTION__ already includes the full class name
+ #else
+ if (site.mClassInfo != typeid(NoClassInfo))
+ {
+ prefix << className(site.mClassInfo) << "::";
+ }
+ #endif
+ prefix << site.mFunction << ": ";
+ }
+
+ prefix << message;
+ message = prefix.str();
+
+ writeToRecorders(site.mLevel, message);
+
+ if (site.mLevel == LEVEL_ERROR && s.crashFunction)
+ {
+ s.crashFunction(message);
+ }
+ }
+}
+
+
+
+
+namespace LLError
+{
+ Settings* saveAndResetSettings()
+ {
+ return Settings::saveAndReset();
+ }
+
+ void restoreSettings(Settings* s)
+ {
+ return Settings::restore(s);
+ }
+
+ std::string removePrefix(std::string& s, const std::string& p)
+ {
+ std::string::size_type where = s.find(p);
+ if (where == std::string::npos)
+ {
+ return s;
+ }
+
+ return std::string(s, where + p.size());
+ }
+
+ std::string abbreviateFile(const std::string& filePath)
+ {
+ std::string f = filePath;
+
+#if LL_WINDOWS
+ static std::string indra_prefix = "indra\\";
+#else
+ static std::string indra_prefix = "indra/";
+#endif
+ f = removePrefix(f, indra_prefix);
+
+#if LL_DARWIN
+ static std::string newview_prefix = "newview/../";
+ f = removePrefix(f, newview_prefix);
+#endif
+
+ return f;
+ }
+
+ int shouldLogCallCount()
+ {
+ Settings& s = Settings::get();
+ return s.shouldLogCallCounter;
+ }
+
+ void crashAndLoop(const std::string& message)
+ {
+ // Now, we go kaboom!
+ int* crash = NULL;
+
+ *crash = 0;
+
+ while(true)
+ {
+ // Loop forever, in case the crash didn't work?
+ }
+ }
+
+ std::string utcTime()
+ {
+ time_t now = time(NULL);
+ const size_t BUF_SIZE = 64;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+
+ int chars = strftime(time_str, BUF_SIZE,
+ "%Y-%m-%dT%H:%M:%SZ",
+ gmtime(&now));
+
+ return chars ? time_str : "time error";
+ }
+}
+
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index d3d680ed33..84ac0fa7f0 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -1,8 +1,9 @@
/**
* @file llerror.h
- * @brief Constants, functions, and macros for logging and runtime errors.
+ * @date December 2006
+ * @brief error message system
*
- * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
* $License$
*/
@@ -10,209 +11,208 @@
#define LL_LLERROR_H
#include <sstream>
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "llerrorstream.h"
-#include "llerrorbuffer.h"
-
-// Specific error codes
-const S32 LL_ERR_NOERR = 0;
-const S32 LL_ERR_ASSET_REQUEST_FAILED = -1;
-//const S32 LL_ERR_ASSET_REQUEST_INVALID = -2;
-const S32 LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3;
-const S32 LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4;
-const S32 LL_ERR_INSUFFICIENT_PERMISSIONS = -5;
-const S32 LL_ERR_EOF = -39;
-const S32 LL_ERR_CANNOT_OPEN_FILE = -42;
-const S32 LL_ERR_FILE_NOT_FOUND = -43;
-const S32 LL_ERR_FILE_EMPTY = -44;
-const S32 LL_ERR_TCP_TIMEOUT = -23016;
-const S32 LL_ERR_CIRCUIT_GONE = -23017;
-
-// Error types
-
-#define LLERR_IMAGE (1 << 1) // Image requests
-#define LLERR_MESSAGE (1 << 2) // Messaging
-#define LLERR_PERF (1 << 3) // Performance
-#define LLERR_SQL (1 << 4) // SQL statements
-#define LLERR_DOUG (1 << 5) // Doug's debugging
-#define LLERR_USER_INPUT (1 << 6) // Keyboard and mouse
-#define LLERR_TIMING (1 << 7) // Verbose time info
-#define LLERR_TASK (1 << 8) // Tracking tasks
-#define LLERR_MSG_HANDLER (1 << 9) //
-#define LLERR_CIRCUIT_INFO (1 << 10) // Message system circuit info
-#define LLERR_PHYSICS (1 << 11) // physics
-#define LLERR_VFS (1 << 12) // VFS
-const U32 LLERR_ALL = 0xffff;
-const U32 LLERR_NONE = 0x0;
-
-// Define one of these for different error levels in release...
-// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output.
-#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output
-#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output.
-
-
-//////////////////////////////////////////
-//
-// Implementation - ignore
-//
-//
-#ifdef _DEBUG
-#define SHOW_DEBUG
-#define SHOW_WARN
-#define SHOW_INFO
-#define SHOW_ASSERT
-#else // _DEBUG
-
-#ifdef RELEASE_SHOW_DEBUG
-#define SHOW_DEBUG
-#endif
-
-#ifdef RELEASE_SHOW_WARN
-#define SHOW_WARN
-#endif
-
-#ifdef RELEASE_SHOW_INFO
-#define SHOW_INFO
-#endif
-
-#ifdef RELEASE_SHOW_ASSERT
-#define SHOW_ASSERT
-#endif
-
-#endif // _DEBUG
-
-
-extern LLErrorStream gErrorStream;
-
-
-// LL Error macros
-//
-// Usage:
-//
-// llerrs << "An error, oh my!" << variable << endl;
-// llwarns << "Another error, fuck me!" << variable << endl;
-// llwarnst(LLERR_IMAGE) << "Debug, mother fucker" << endl;
-//
-// NOTE: The output format of filename(lineno): is so that MS DevStudio
-// can parse the output and automatically jump to that location
-
-inline std::string llerrno_string(int errnum)
+#include <typeinfo>
+
+#include "llerrorlegacy.h"
+
+
+/* Error Logging Facility
+
+ Information for most users:
+
+ Code can log messages with constuctions like this:
+
+ llinfos << "request to fizzbip agent " << agent_id
+ << " denied due to timeout" << llendl;
+
+ Messages can be logged to one of four increasing levels of concern,
+ using one of four "streams":
+
+ lldebugs - debug messages that are normally supressed
+ llinfos - informational messages that are normall shown
+ llwarns - warning messages that singal a problem
+ llerrs - error messages that are major, unrecoverable failures
+
+ The later (llerrs) automatically crashes the process after the message
+ is logged.
+
+ Note that these "streams" are actually #define magic. Rules for use:
+ * they cannot be used as normal streams, only to start a message
+ * messages written to them MUST be terminated with llendl
+ * between the opening and closing, the << operator is indeed
+ writing onto a std::ostream, so all conversions and stream
+ formating are available
+
+ These messages are automatically logged with function name, and (if enabled)
+ file and line of the message. (Note: Existing messages that already include
+ the function name don't get name printed twice.)
+
+ If you have a class, adding LOG_CLASS line to the declaration will cause
+ all messages emitted from member functions (normal and static) to be tagged
+ with the proper class name as well as the function name:
+
+ class LLFoo
+ {
+ LOG_CLASS(LLFoo);
+ public:
+ ...
+ };
+
+ void LLFoo::doSomething(int i)
+ {
+ if (i > 100)
+ {
+ llwanrs << "called with a big value for i: " << i << llendl;
+ }
+ ...
+ }
+
+ will result in messages like:
+
+ WARN: LLFoo::doSomething: called with a big value for i: 283
+
+ Which messages are logged and which are supressed can be controled at run
+ time from the live file logcontrol.xml based on function, class and/or
+ source file. See etc/logcontrol-dev.xml for details.
+
+ Lastly, logging is now very efficient in both compiled code and execution
+ when skipped. There is no need to wrap messages, even debugging ones, in
+ #ifdef _DEBUG constructs. lldebugs messages are compiled into all builds,
+ even release. Which means you can use them to help debug even when deployed
+ to a real grid.
+*/
+
+namespace LLError
{
- std::stringstream res;
- res << "error(" << errnum << "):" << strerror(errnum) << " ";
- return res.str();
+ enum ELevel
+ {
+ LEVEL_ALL = 0,
+ // used to indicate that all messagess should be logged
+
+ LEVEL_DEBUG = 0,
+ LEVEL_INFO = 1,
+ LEVEL_WARN = 2,
+ LEVEL_ERROR = 3, // used to be called FATAL
+
+ LEVEL_NONE = 4
+ // not really a level
+ // used to indicate that no messages should be logged
+ };
+
+ /* Macro support
+ The classes CallSite and Log are used by the logging macros below.
+ They are not intended for general use.
+ */
+
+ class CallSite;
+
+ class Log
+ {
+ public:
+ static bool shouldLog(CallSite&);
+ static std::ostringstream* out();
+ static void flush(std::ostringstream*, const CallSite&);
+ };
+
+ class CallSite
+ {
+ // Represents a specific place in the code where a message is logged
+ // This is public because it is used by the macros below. It is not
+ // intended for public use.
+ public:
+ CallSite(ELevel, const char* file, int line,
+ const std::type_info& class_info, const char* function);
+
+ bool shouldLog()
+ { return mCached ? mShouldLog : Log::shouldLog(*this); }
+ // this member function needs to be in-line for efficiency
+
+ void invalidate();
+
+ private:
+ // these describe the call site and never change
+ const ELevel mLevel;
+ const char* const mFile;
+ const int mLine;
+ const std::type_info& mClassInfo;
+ const char* const mFunction;
+
+ // these implement a cache of the call to shouldLog()
+ bool mCached;
+ bool mShouldLog;
+
+ friend class Log;
+ };
+
+
+ class End { };
+ inline std::ostream& operator<<(std::ostream& s, const End&)
+ { return s; }
+ // used to indicate the end of a message
+
+ class NoClassInfo { };
+ // used to indicate no class info known for logging
}
-inline std::string llerror_file_line(const char* file, S32 line)
-{
- std::stringstream res;
- res << file << "(" <<line << ")";
- return res.str();
-}
-// Used to throw an error which is always causes a system halt.
-#define llerrs if (gErrorStream.isEnabledFor(LLErrorBuffer::FATAL)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::FATAL; \
- llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : error\n"; \
- llerror_oss << "ERROR: " << llerror_file_line(__FILE__, __LINE__) << " "
-
-// Used to show warnings
-#define llwarns if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \
- if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : WARNING: "; \
- else llerror_oss << "WARNING: "; \
- llerror_oss
-
-// Alerts are for serious non-fatal situations that are not supposed to happen and need to alert someone
-#define llalerts if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \
- if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : ALERT: "; \
- else llerror_oss << "ALERT: "; \
- llerror_oss
-
-// Used to show informational messages that don't get disabled
-#define llinfos if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \
- if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \
- else llerror_oss << "INFO: "; \
- llerror_oss
-
-#define llinfost(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO, type)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \
- if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \
- else llerror_oss << "INFO: [" << #type << "] "; \
- llerror_oss
-
-// Used for general debugging output
-#define lldebugs if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \
- if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \
- else llerror_oss << "DEBUG: "; \
- llerror_oss
-
-#define lldebugst(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG, type)) \
- { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \
- if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \
- else llerror_oss << "DEBUG: [" << #type << "] "; \
- llerror_oss
-
-#define llendl std::endl; gErrorStream.crashOnError(llerror_oss, llerror_level); }
-#define llendflush std::endl << std::flush; gErrorStream.crashOnError(llerror_oss, llerror_level); }
-#define llcont llerror_oss
-
-#define llerror(msg, num) llerrs << "Error # " << num << ": " << msg << llendl;
-
-#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
-
-#ifdef SHOW_ASSERT
-#define llassert(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
-#else
-#define llassert(func)
-#endif
-#define llassert_always(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
-
-#ifdef SHOW_ASSERT
-#define llverify(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
-#else
-#define llverify(func) (func); // get rid of warning C4189
-#endif
-
-// handy compile-time assert - enforce those template parameters!
-#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1] /* Flawfinder: ignore */
-
-// Makes the app go down in flames, but on purpose!
-void _llcrash_and_loop();
-
-// Use as follows:
-// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl;
-//
-// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun)
-// should perhaps be replaced with boost::format.
-inline std::string llformat(const char *fmt, ...)
-{
- char tstr[1024]; /* Flawfinder: ignore */
- va_list va;
- va_start(va, fmt);
-#if LL_WINDOWS
- _vsnprintf(tstr, 1024, fmt, va);
-#else
- vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */
-#endif
- va_end(va);
- return std::string(tstr);
-}
-// Helper class to temporarily change error level for the current scope.
-class LLScopedErrorLevel
-{
-public:
- LLScopedErrorLevel(LLErrorBuffer::ELevel error_level);
- ~LLScopedErrorLevel();
-
-private:
- LLErrorBuffer::ELevel mOrigErrorLevel;
-};
-
+/*
+ Class type information for logging
+ */
+
+#define LOG_CLASS(s) typedef s _LL_CLASS_TO_LOG
+ // Declares class to tag logged messages with.
+ // See top of file for example of how to use this
+
+typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
+ // Outside a class declartion, or in class without LOG_CLASS(), this
+ // typedef causes the messages to not be associated with any class.
+
+
+
+
+
+/*
+ Error Logging Macros
+ See top of file for common usage.
+*/
+
+#define lllog(level) \
+ { \
+ static LLError::CallSite _site( \
+ level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__);\
+ if (_site.shouldLog()) \
+ { \
+ std::ostringstream* _out = LLError::Log::out(); \
+ (*_out)
+
+#define llendl \
+ LLError::End(); \
+ LLError::Log::flush(_out, _site); \
+ } \
+ }
+
+#define llinfos lllog(LLError::LEVEL_INFO)
+#define lldebugs lllog(LLError::LEVEL_DEBUG)
+#define llwarns lllog(LLError::LEVEL_WARN)
+#define llerrs lllog(LLError::LEVEL_ERROR)
+
+#define llcont (*_out)
+ /*
+ Use this construct if you need to do computation in the middle of a
+ message:
+
+ llinfos << "the agent " << agend_id;
+ switch (f)
+ {
+ case FOP_SHRUGS: llcont << "shrugs"; break;
+ case FOP_TAPS: llcont << "points at " << who; break;
+ case FOP_SAYS: llcont << "says " << message; break;
+ }
+ llcont << " for " << t << " seconds" << llendl;
+
+ Such computation is done iff the message will be logged.
+ */
+
+
#endif // LL_LLERROR_H
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
new file mode 100644
index 0000000000..b1950eebf0
--- /dev/null
+++ b/indra/llcommon/llerrorcontrol.h
@@ -0,0 +1,123 @@
+/**
+ * @file llerrorcontrol.h
+ * @date December 2006
+ * @brief error message system control
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLERRORCONTROL_H
+#define LL_LLERRORCONTROL_H
+
+#include "llerror.h"
+
+#include <string>
+
+class LLFixedBuffer;
+class LLSD;
+
+/*
+ This is the part of the LLError namespace that manages the messages
+ produced by the logging. The logging support is defined in llerror.h.
+ Most files do not need to include this.
+
+ These implementations are in llerror.cpp.
+*/
+
+
+namespace LLError
+{
+ void initForServer(const std::string& identity);
+ // resets all logging settings to defaults needed by server processes
+ // logs to stderr, syslog, and windows debug log
+ // the identity string is used for in the syslog
+
+ void initForApplication(const std::string& dir);
+ // resets all logging settings to defaults needed by applicaitons
+ // logs to stderr and windows debug log
+ // sets up log configuration from the file logcontrol.xml in dir
+
+
+ /*
+ Settings that control what is logged.
+ Setting a level means log messages at that level or above.
+ */
+
+ void setPrintLocation(bool);
+ void setDefaultLevel(LLError::ELevel);
+ void setFunctionLevel(const std::string& function_name, LLError::ELevel);
+ void setClassLevel(const std::string& class_name, LLError::ELevel);
+ void setFileLevel(const std::string& file_name, LLError::ELevel);
+
+ void configure(const LLSD&);
+ // the LLSD can configure all of the settings
+ // usually read automatically from the live errorlog.xml file
+
+
+ /*
+ Control functions.
+ */
+
+ typedef void (*FatalFunction)(const std::string& message);
+ void crashAndLoop(const std::string& message);
+ // Default fatal funtion: divides by zero and loops forever
+
+ void setFatalFunction(FatalFunction);
+ // The fatal function will be called when an message of LEVEL_ERROR
+ // is logged. Note: supressing a LEVEL_ERROR message from being logged
+ // (by, for example, setting a class level to LEVEL_NONE), will keep
+ // the that message from causing the fatal funciton to be invoked.
+
+ typedef std::string (*TimeFunction)();
+ std::string utcTime();
+
+ void setTimeFunction(TimeFunction);
+ // The function is use to return the current time, formatted for
+ // display by those error recorders that want the time included.
+
+
+
+ class Recorder
+ {
+ // An object that handles the actual output or error messages.
+ public:
+ virtual ~Recorder();
+
+ virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;
+ // use the level for better display, not for filtering
+
+ virtual bool wantsTime(); // default returns false
+ // override and return true if the recorder wants the time string
+ // included in the text of the message
+ };
+
+ void addRecorder(Recorder*);
+ void removeRecorder(Recorder*);
+ // each error message is passed to each recorder via recordMessage()
+
+ void logToFile(const std::string& filename);
+ void logToFixedBuffer(LLFixedBuffer*);
+ // Utilities to add recorders for logging to a file or a fixed buffer
+ // A second call to the same function will remove the logger added
+ // with the first.
+ // Passing the empty string or NULL to just removes any prior.
+ std::string logFileName();
+ // returns name of current logging file, empty string if none
+
+
+ /*
+ Utilities for use by the unit tests of LLError itself.
+ */
+
+ class Settings;
+ Settings* saveAndResetSettings();
+ void restoreSettings(Settings *);
+
+ std::string abbreviateFile(const std::string& filePath);
+ int shouldLogCallCount();
+
+};
+
+#endif // LL_LLERRORCONTROL_H
+
diff --git a/indra/llcommon/llerrorlegacy.h b/indra/llcommon/llerrorlegacy.h
new file mode 100644
index 0000000000..19523512a6
--- /dev/null
+++ b/indra/llcommon/llerrorlegacy.h
@@ -0,0 +1,98 @@
+/**
+ * @file llerrorlegacy.h
+ * @date January 2007
+ * @brief old things from the older error system
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLERRORLEGACY_H
+#define LL_LLERRORLEGACY_H
+
+
+
+/*
+ LEGACY -- DO NOT USE THIS STUFF ANYMORE
+*/
+
+// Specific error codes
+const int LL_ERR_NOERR = 0;
+const int LL_ERR_ASSET_REQUEST_FAILED = -1;
+//const int LL_ERR_ASSET_REQUEST_INVALID = -2;
+const int LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3;
+const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4;
+const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5;
+const int LL_ERR_EOF = -39;
+const int LL_ERR_CANNOT_OPEN_FILE = -42;
+const int LL_ERR_FILE_NOT_FOUND = -43;
+const int LL_ERR_FILE_EMPTY = -44;
+const int LL_ERR_TCP_TIMEOUT = -23016;
+const int LL_ERR_CIRCUIT_GONE = -23017;
+
+
+
+// Define one of these for different error levels in release...
+// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output.
+#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output
+#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output.
+
+
+//////////////////////////////////////////
+//
+// Implementation - ignore
+//
+//
+#ifdef _DEBUG
+#define SHOW_DEBUG
+#define SHOW_WARN
+#define SHOW_INFO
+#define SHOW_ASSERT
+#else // _DEBUG
+
+#ifdef RELEASE_SHOW_DEBUG
+#define SHOW_DEBUG
+#endif
+
+#ifdef RELEASE_SHOW_WARN
+#define SHOW_WARN
+#endif
+
+#ifdef RELEASE_SHOW_INFO
+#define SHOW_INFO
+#endif
+
+#ifdef RELEASE_SHOW_ASSERT
+#define SHOW_ASSERT
+#endif
+
+#endif // _DEBUG
+
+
+
+#define lldebugst(type) lldebugs
+#define llendflush llendl
+
+
+#define llerror(msg, num) llerrs << "Error # " << num << ": " << msg << llendl;
+
+#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
+
+#ifdef SHOW_ASSERT
+#define llassert(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+#else
+#define llassert(func)
+#endif
+#define llassert_always(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+
+#ifdef SHOW_ASSERT
+#define llverify(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+#else
+#define llverify(func) (func); // get rid of warning C4189
+#endif
+
+// handy compile-time assert - enforce those template parameters!
+#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1] /* Flawfinder: ignore */
+ //XXX: used in two places in llcommon/llskipmap.h
+
+#endif // LL_LLERRORLEGACY_H
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 16e2f5c5a1..4acd94f943 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -178,6 +178,7 @@ void llifstream::open(const char* _Filename, /* Flawfinder: ignore */
}
llassert(_Filebuffer == NULL);
_Filebuffer = new _Myfb(filep);
+ _ShouldClose = true;
_Myios::init(_Filebuffer);
}
@@ -189,13 +190,17 @@ bool llifstream::is_open() const
}
llifstream::~llifstream()
{
+ if (_ShouldClose)
+ {
+ close();
+ }
delete _Filebuffer;
}
llifstream::llifstream(const char *_Filename,
ios_base::openmode _Mode,
int _Prot)
- : std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL)
+ : std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)
{ // construct with named file and specified mode
open(_Filename, _Mode | ios_base::in, _Prot); /* Flawfinder: ignore */
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 67de0f43fc..2899f51a60 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -68,7 +68,7 @@ public:
typedef std::basic_ios<char,std::char_traits< char > > _Myios;
llifstream()
- : std::basic_istream<char,std::char_traits< char > >(NULL,true),_Filebuffer(NULL)
+ : std::basic_istream<char,std::char_traits< char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)
{ // construct unopened
}
@@ -78,7 +78,8 @@ public:
explicit llifstream(_Filet *_File)
: std::basic_istream<char,std::char_traits< char > >(NULL,true),
- _Filebuffer(new _Myfb(_File))
+ _Filebuffer(new _Myfb(_File)),
+ _ShouldClose(false)
{ // construct with specified C stream
}
virtual ~llifstream();
@@ -95,6 +96,7 @@ public:
private:
_Myfb* _Filebuffer; // the file buffer
+ bool _ShouldClose;
};
diff --git a/indra/llcommon/llformat.cpp b/indra/llcommon/llformat.cpp
new file mode 100644
index 0000000000..0c2a6d3b1e
--- /dev/null
+++ b/indra/llcommon/llformat.cpp
@@ -0,0 +1,28 @@
+/**
+ * @file llformat.cpp
+ * @date January 2007
+ * @brief string formatting utility
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llformat.h"
+
+#include <stdarg.h>
+
+std::string llformat(const char *fmt, ...)
+{
+ char tstr[1024]; /* Flawfinder: ignore */
+ va_list va;
+ va_start(va, fmt);
+#if LL_WINDOWS
+ _vsnprintf(tstr, 1024, fmt, va);
+#else
+ vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */
+#endif
+ va_end(va);
+ return std::string(tstr);
+}
diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h
new file mode 100644
index 0000000000..7079656b72
--- /dev/null
+++ b/indra/llcommon/llformat.h
@@ -0,0 +1,23 @@
+/**
+ * @file llformat.h
+ * @date January 2007
+ * @brief string formatting utility
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFORMAT_H
+#define LL_LLFORMAT_H
+
+#include <string>
+
+// Use as follows:
+// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl;
+//
+// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun)
+// should perhaps be replaced with boost::format.
+
+std::string llformat(const char *fmt, ...);
+
+#endif // LL_LLFORMAT_H
diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp
index 7dad6f82d8..df2e940352 100644
--- a/indra/llcommon/lllivefile.cpp
+++ b/indra/llcommon/lllivefile.cpp
@@ -8,24 +8,56 @@
#include "linden_common.h"
#include "lllivefile.h"
+#include "llframetimer.h"
+#include "lltimer.h"
+class LLLiveFile::Impl
+{
+public:
+ Impl(const std::string &filename, const F32 refresh_period);
+ ~Impl();
+
+ bool check();
+
+
+ bool mForceCheck;
+ F32 mRefreshPeriod;
+ LLFrameTimer mRefreshTimer;
-LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period) :
-mForceCheck(true),
-mRefreshPeriod(refresh_period),
-mFilename(filename),
-mLastModTime(0),
-mLastExists(false)
+ std::string mFilename;
+ time_t mLastModTime;
+ bool mLastExists;
+
+ LLEventTimer* mEventTimer;
+};
+
+LLLiveFile::Impl::Impl(const std::string &filename, const F32 refresh_period)
+ : mForceCheck(true),
+ mRefreshPeriod(refresh_period),
+ mFilename(filename),
+ mLastModTime(0),
+ mLastExists(false),
+ mEventTimer(NULL)
{
}
+LLLiveFile::Impl::~Impl()
+{
+ delete mEventTimer;
+}
+
+LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period)
+ : impl(* new Impl(filename, refresh_period))
+{
+}
LLLiveFile::~LLLiveFile()
{
+ delete &impl;
}
-bool LLLiveFile::checkAndReload()
+bool LLLiveFile::Impl::check()
{
if (!mForceCheck && mRefreshTimer.getElapsedTimeF32() < mRefreshPeriod)
{
@@ -46,9 +78,8 @@ bool LLLiveFile::checkAndReload()
// broken somehow. Clear flags and return.
if (mLastExists)
{
- loadFile(); // Load the file, even though it's missing to allow it to clear state.
mLastExists = false;
- return true;
+ return true; // no longer existing is a change!
}
return false;
}
@@ -68,7 +99,44 @@ bool LLLiveFile::checkAndReload()
mLastExists = true;
mLastModTime = stat_data.st_mtime;
- loadFile();
return true;
}
+bool LLLiveFile::checkAndReload()
+{
+ bool changed = impl.check();
+ if (changed)
+ {
+ loadFile();
+ }
+ return changed;
+}
+
+std::string LLLiveFile::filename() const
+{
+ return impl.mFilename;
+}
+
+namespace
+{
+ class LiveFileEventTimer : public LLEventTimer
+ {
+ public:
+ LiveFileEventTimer(LLLiveFile& f, F32 refresh)
+ : LLEventTimer(refresh), mLiveFile(f)
+ { }
+
+ void tick()
+ { mLiveFile.checkAndReload(); }
+
+ private:
+ LLLiveFile& mLiveFile;
+ };
+
+}
+
+void LLLiveFile::addToEventTimer()
+{
+ impl.mEventTimer = new LiveFileEventTimer(*this, impl.mRefreshPeriod);
+}
+
diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h
index 97c88a5c5c..a71844e5a9 100644
--- a/indra/llcommon/lllivefile.h
+++ b/indra/llcommon/lllivefile.h
@@ -9,7 +9,6 @@
#ifndef LL_LLLIVEFILE_H
#define LL_LLLIVEFILE_H
-#include "llframetimer.h"
class LLLiveFile
{
@@ -17,18 +16,22 @@ public:
LLLiveFile(const std::string &filename, const F32 refresh_period = 5.f);
virtual ~LLLiveFile();
- bool checkAndReload(); // Returns true if the file changed in any way
+ bool checkAndReload();
+ // Returns true if the file changed in any way
+ // Call this before using anything that was read & cached from the file
+
+ std::string filename() const;
+
+ void addToEventTimer();
+ // Normally, just calling checkAndReload() is enough. In some cases
+ // though, you may need to let the live file periodically check itself.
protected:
virtual void loadFile() = 0; // Implement this to load your file if it changed
- bool mForceCheck;
- F32 mRefreshPeriod;
- LLFrameTimer mRefreshTimer;
-
- std::string mFilename;
- time_t mLastModTime;
- bool mLastExists;
+private:
+ class Impl;
+ Impl& impl;
};
#endif //LL_LLLIVEFILE_H
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
index 25bd7ceac8..45d7acd417 100644
--- a/indra/llcommon/llsd.cpp
+++ b/indra/llcommon/llsd.cpp
@@ -10,6 +10,7 @@
#include <math.h>
#include "../llmath/llmath.h"
+#include "llformat.h"
namespace {
class ImplMap;
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 0555231010..a381af74d0 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -11,6 +11,7 @@
#include "stdtypes.h"
#include "llerror.h"
+#include "llfile.h"
#include <algorithm>
#include <map>
#include <stdio.h>
diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h
index ad428ce565..20db115c6e 100644
--- a/indra/llcommon/llstringtable.h
+++ b/indra/llcommon/llstringtable.h
@@ -10,6 +10,8 @@
#ifndef LL_STRING_TABLE_H
#define LL_STRING_TABLE_H
+#include "lldefs.h"
+#include "llformat.h"
#include "llstl.h"
#include <list>
#include <set>
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
index 4ba47e7689..9e1a0ee12c 100644
--- a/indra/llmessage/message.cpp
+++ b/indra/llmessage/message.cpp
@@ -4402,33 +4402,6 @@ void process_secured_template_checksum_request(LLMessageSystem* msg, void**)
send_template_reply(msg, token);
}
-void process_log_control(LLMessageSystem* msg, void**)
-{
- U8 level;
- U32 mask;
- BOOL time;
- BOOL location;
- BOOL remote_infos;
-
- msg->getU8Fast(_PREHASH_Options, _PREHASH_Level, level);
- msg->getU32Fast(_PREHASH_Options, _PREHASH_Mask, mask);
- msg->getBOOLFast(_PREHASH_Options, _PREHASH_Time, time);
- msg->getBOOLFast(_PREHASH_Options, _PREHASH_Location, location);
- msg->getBOOLFast(_PREHASH_Options, _PREHASH_RemoteInfos, remote_infos);
-
- gErrorStream.setLevel(LLErrorStream::ELevel(level));
- gErrorStream.setDebugMask(mask);
- gErrorStream.setTime(time);
- gErrorStream.setPrintLocation(location);
- gErrorStream.setElevatedRemote(remote_infos);
-
- llinfos << "Logging set to level " << gErrorStream.getLevel()
- << " mask " << std::hex << gErrorStream.getDebugMask() << std::dec
- << " time " << gErrorStream.getTime()
- << " loc " << gErrorStream.getPrintLocation()
- << llendl;
-}
-
void process_log_messages(LLMessageSystem* msg, void**)
{
U8 log_message;
@@ -4749,7 +4722,6 @@ BOOL start_messaging_system(
gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck, process_packet_ack, NULL);
gMessageSystem->setHandlerFuncFast(_PREHASH_TemplateChecksumRequest, process_template_checksum_request, NULL);
gMessageSystem->setHandlerFuncFast(_PREHASH_SecuredTemplateChecksumRequest, process_secured_template_checksum_request, NULL);
- gMessageSystem->setHandlerFuncFast(_PREHASH_LogControl, process_log_control, NULL);
gMessageSystem->setHandlerFuncFast(_PREHASH_LogMessages, process_log_messages, NULL);
gMessageSystem->setHandlerFuncFast(_PREHASH_CreateTrustedCircuit,
process_create_trusted_circuit,
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
index ec76e1f755..48d950e377 100644
--- a/indra/llmessage/message.h
+++ b/indra/llmessage/message.h
@@ -187,6 +187,8 @@ class LLMessagePollInfo;
class LLMessageSystem
{
+ LOG_CLASS(LLMessageSystem);
+
public:
U8 mSendBuffer[MAX_BUFFER_SIZE];
// Encoded send buffer needs to be slightly larger since the zero
@@ -757,7 +759,6 @@ BOOL start_messaging_system(
void end_messaging_system();
void null_message_callback(LLMessageSystem *msg, void **data);
-void process_log_control(LLMessageSystem* msg, void**);
//
// Inlines
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 18be31af58..c2d7c9c2a3 100644
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -489,7 +489,6 @@ char * _PREHASH_NVPair;
char * _PREHASH_ObjectSpinStart;
char * _PREHASH_UseEstateSun;
char * _PREHASH_LogoutBlock;
-char * _PREHASH_RelayLogControl;
char * _PREHASH_RegionID;
char * _PREHASH_Creator;
char * _PREHASH_ProposalText;
@@ -1294,7 +1293,6 @@ char * _PREHASH_AssetBlock;
char * _PREHASH_AcceptNotices;
char * _PREHASH_SetGroupAcceptNotices;
char * _PREHASH_CloseCircuit;
-char * _PREHASH_LogControl;
char * _PREHASH_TeleportFinish;
char * _PREHASH_PathRevolutions;
char * _PREHASH_ClassifiedInfoReply;
@@ -1963,7 +1961,6 @@ void init_prehash_data()
_PREHASH_ObjectSpinStart = gMessageStringTable.getString("ObjectSpinStart");
_PREHASH_UseEstateSun = gMessageStringTable.getString("UseEstateSun");
_PREHASH_LogoutBlock = gMessageStringTable.getString("LogoutBlock");
- _PREHASH_RelayLogControl = gMessageStringTable.getString("RelayLogControl");
_PREHASH_RegionID = gMessageStringTable.getString("RegionID");
_PREHASH_Creator = gMessageStringTable.getString("Creator");
_PREHASH_ProposalText = gMessageStringTable.getString("ProposalText");
@@ -2768,7 +2765,6 @@ void init_prehash_data()
_PREHASH_AcceptNotices = gMessageStringTable.getString("AcceptNotices");
_PREHASH_SetGroupAcceptNotices = gMessageStringTable.getString("SetGroupAcceptNotices");
_PREHASH_CloseCircuit = gMessageStringTable.getString("CloseCircuit");
- _PREHASH_LogControl = gMessageStringTable.getString("LogControl");
_PREHASH_TeleportFinish = gMessageStringTable.getString("TeleportFinish");
_PREHASH_PathRevolutions = gMessageStringTable.getString("PathRevolutions");
_PREHASH_ClassifiedInfoReply = gMessageStringTable.getString("ClassifiedInfoReply");
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index 0da2e02f83..63e23237f5 100644
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -489,7 +489,6 @@ extern char * _PREHASH_NVPair;
extern char * _PREHASH_ObjectSpinStart;
extern char * _PREHASH_UseEstateSun;
extern char * _PREHASH_LogoutBlock;
-extern char * _PREHASH_RelayLogControl;
extern char * _PREHASH_RegionID;
extern char * _PREHASH_Creator;
extern char * _PREHASH_ProposalText;
@@ -1294,7 +1293,6 @@ extern char * _PREHASH_AssetBlock;
extern char * _PREHASH_AcceptNotices;
extern char * _PREHASH_SetGroupAcceptNotices;
extern char * _PREHASH_CloseCircuit;
-extern char * _PREHASH_LogControl;
extern char * _PREHASH_TeleportFinish;
extern char * _PREHASH_PathRevolutions;
extern char * _PREHASH_ClassifiedInfoReply;
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index c02be6bb8d..ffa9262e21 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -584,7 +584,7 @@ void LLButton::draw()
else
{
// no image
- llalerts << "No image for button " << mName << llendl;
+ llwarns << "No image for button " << mName << llendl;
// draw it in pink so we can find it
gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4::pink1, FALSE);
}
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index cf80a72282..4ff07e9976 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -28,6 +28,7 @@
#include "llcrypto.h"
#include "lldir.h"
#include "lleconomy.h"
+#include "llerrorcontrol.h"
#include "llfiltersd2xmlrpc.h"
#include "llfocusmgr.h"
#include "imageids.h"
@@ -421,7 +422,6 @@ BOOL idle_startup()
invalid_message_callback,
NULL);
- gErrorStream.setUTCTimestamp(gLogUTC);
if (gSavedSettings.getBOOL("LogMessages") || gLogMessages)
{
llinfos << "Message logging activated!" << llendl;
@@ -1679,7 +1679,7 @@ BOOL idle_startup()
// Move the progress view in front of the UI
gViewerWindow->moveProgressViewToFront();
- gErrorStream.setFixedBuffer(gDebugView->mDebugConsolep);
+ LLError::logToFixedBuffer(gDebugView->mDebugConsolep);
// set initial visibility of debug console
gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));
gDebugView->mStatViewp->setVisible(gSavedSettings.getBOOL("ShowDebugStats"));
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 7ef1fd82c6..49578b186d 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -430,8 +430,6 @@ void handle_dump_image_list(void*);
void handle_fullscreen_debug(void*);
void handle_crash(void*);
void handle_dump_followcam(void*);
-void handle_viewer_enable_circuit_log(void*);
-void handle_viewer_disable_circuit_log(void*);
void handle_viewer_enable_message_log(void*);
void handle_viewer_disable_message_log(void*);
void handle_send_postcard(void*);
@@ -818,10 +816,6 @@ void init_client_menu(LLMenuGL* menu)
LLMenuGL* sub = NULL;
sub = new LLMenuGL("Network");
- sub->append(new LLMenuItemCallGL("Enable Circuit Log",
- &handle_viewer_enable_circuit_log, NULL));
- sub->append(new LLMenuItemCallGL("Disable Circuit Log",
- &handle_viewer_disable_circuit_log, NULL));
sub->append(new LLMenuItemCallGL("Enable Message Log",
&handle_viewer_enable_message_log, NULL));
sub->append(new LLMenuItemCallGL("Disable Message Log",
@@ -1307,11 +1301,6 @@ void init_server_menu(LLMenuGL* menu)
&handle_normal_llinfo_log, &enable_god_customer_service));
sub_menu->appendSeparator();
- sub_menu->append(new LLMenuItemCallGL("Enable Circuit Log",
- &handle_sim_enable_circuit_log, &enable_god_customer_service));
- sub_menu->append(new LLMenuItemCallGL("Disable Circuit Log",
- &handle_sim_disable_circuit_log, &enable_god_customer_service));
- sub_menu->appendSeparator();
sub_menu->append(new LLMenuItemCallGL("Enable Message Log",
&handle_sim_enable_message_log, &enable_god_customer_service));
sub_menu->append(new LLMenuItemCallGL("Disable Message Log",
@@ -6375,24 +6364,6 @@ void handle_dump_followcam(void*)
LLFollowCamMgr::dump();
}
-void handle_viewer_enable_circuit_log(void*)
-{
- llinfos << "Showing circuit information every " << gMessageSystem->mCircuitPrintFreq << " seconds" << llendl;
- gErrorStream.setLevel( LLErrorStream::DEBUG );
- gErrorStream.setDebugFlag( LLERR_CIRCUIT_INFO );
- // and dump stuff out immediately
- gMessageSystem->dumpCircuitInfo();
-}
-
-void handle_viewer_disable_circuit_log(void*)
-{
- llinfos << "Hiding circuit information" << llendl;
-#if !LL_DEBUG
- gErrorStream.setLevel( LLErrorStream::INFO );
-#endif
- gErrorStream.clearDebugFlag( LLERR_CIRCUIT_INFO );
-}
-
void handle_viewer_enable_message_log(void*)
{
gMessageSystem->startLogging();
diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h
index a2ef97fc6b..50b84babd1 100644
--- a/indra/newview/llviewerprecompiledheaders.h
+++ b/indra/newview/llviewerprecompiledheaders.h
@@ -63,8 +63,6 @@
#include "lldqueueptr.h"
#include "llendianswizzle.h"
#include "llerror.h"
-#include "llerrorbuffer.h"
-#include "llerrorstream.h"
#include "llfasttimer.h"
#include "llfixedbuffer.h"
#include "llframetimer.h"
diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp
index c2726997b2..cc8f955bc6 100644
--- a/indra/newview/llxmlrpctransaction.cpp
+++ b/indra/newview/llxmlrpctransaction.cpp
@@ -324,9 +324,9 @@ bool LLXMLRPCTransaction::Impl::process()
if (curl_msg->data.result != CURLE_OK)
{
setCurlStatus(curl_msg->data.result);
- llalerts << "LLXMLRPCTransaction CURL error "
+ llwarns << "LLXMLRPCTransaction CURL error "
<< mCurlCode << ": " << mCurlErrorBuffer << llendl;
- llalerts << "LLXMLRPCTransaction request URI: "
+ llwarns << "LLXMLRPCTransaction request URI: "
<< mURI << llendl;
return true;
@@ -360,11 +360,11 @@ bool LLXMLRPCTransaction::Impl::process()
{
setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
- llalerts << "LLXMLRPCTransaction XMLRPC "
+ llwarns << "LLXMLRPCTransaction XMLRPC "
<< (hasError ? "error " : "fault ")
<< faultCode << ": "
<< faultString << llendl;
- llalerts << "LLXMLRPCTransaction request URI: "
+ llwarns << "LLXMLRPCTransaction request URI: "
<< mURI << llendl;
}
diff --git a/indra/test/llhttpclient_tut.cpp b/indra/test/llhttpclient_tut.cpp
index 98f24c1bdd..40cde1fd6c 100644
--- a/indra/test/llhttpclient_tut.cpp
+++ b/indra/test/llhttpclient_tut.cpp
@@ -16,6 +16,7 @@
#include "lltut.h"
#include "llhttpclient.h"
+#include "llformat.h"
#include "llpipeutil.h"
#include "llpumpio.h"
diff --git a/indra/test/llpipeutil.cpp b/indra/test/llpipeutil.cpp
index 56789cfae8..3a15bfdb7e 100644
--- a/indra/test/llpipeutil.cpp
+++ b/indra/test/llpipeutil.cpp
@@ -12,6 +12,7 @@
#include <stdlib.h>
#include "llbufferstream.h"
+#include "lldefs.h"
#include "llframetimer.h"
#include "llpumpio.h"
#include "llrand.h"
diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp
index 96aad3da58..592d61137d 100644
--- a/indra/test/lltut.cpp
+++ b/indra/test/lltut.cpp
@@ -9,6 +9,8 @@
*/
#include "lltut.h"
+
+#include "llformat.h"
#include "llsd.h"
namespace tut
@@ -135,6 +137,20 @@ namespace tut
}
}
+ void ensure_ends_with(const std::string& msg,
+ const std::string& actual, const std::string& expectedEnd)
+ {
+ if( actual.size() < expectedEnd.size()
+ || actual.rfind(expectedEnd)
+ != (actual.size() - expectedEnd.size()) )
+ {
+ std::stringstream ss;
+ ss << msg << ": " << "expected to find " << expectedEnd
+ << " at end of actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+
void ensure_contains(const std::string& msg,
const std::string& actual, const std::string& expectedSubString)
{
@@ -146,4 +162,16 @@ namespace tut
throw failure(ss.str().c_str());
}
}
+
+ void ensure_does_not_contain(const std::string& msg,
+ const std::string& actual, const std::string& expectedSubString)
+ {
+ if( actual.find(expectedSubString, 0) != std::string::npos )
+ {
+ std::stringstream ss;
+ ss << msg << ": " << "expected not to find " << expectedSubString
+ << " in actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
}
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
index c750a99b8d..c2ec504857 100644
--- a/indra/test/lltut.h
+++ b/indra/test/lltut.h
@@ -68,8 +68,14 @@ namespace tut
void ensure_starts_with(const std::string& msg,
const std::string& actual, const std::string& expectedStart);
+ void ensure_ends_with(const std::string& msg,
+ const std::string& actual, const std::string& expectedEnd);
+
void ensure_contains(const std::string& msg,
const std::string& actual, const std::string& expectedSubString);
+
+ void ensure_does_not_contain(const std::string& msg,
+ const std::string& actual, const std::string& expectedSubString);
}
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index 2d727daaa5..312c52dc77 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -17,6 +17,7 @@
*/
#include "linden_common.h"
+#include "llerrorcontrol.h"
#include "lltut.h"
#include <apr-1/apr_pools.h>
@@ -159,8 +160,18 @@ void stream_groups(std::ostream& s, const char* app)
}
}
+void wouldHaveCrashed(const std::string& message)
+{
+ tut::fail("llerrs message: " + message);
+}
+
int main(int argc, char **argv)
{
+ LLError::initForApplication(".");
+ LLError::setFatalFunction(wouldHaveCrashed);
+ LLError::setDefaultLevel(LLError::LEVEL_ERROR);
+ // *FIX: should come from error config file
+
#ifdef CTYPE_WORKAROUND
ctype_workaround();
#endif