Mercurial > projects > hoofbaby
diff deps/Platinum/ThirdParty/Neptune/Source/Core/NptLogging.cpp @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/Platinum/ThirdParty/Neptune/Source/Core/NptLogging.cpp Mon Jul 06 08:06:28 2009 -0700 @@ -0,0 +1,1185 @@ +/***************************************************************** +| +| Neptune - Logging Support +| +| Copyright (c) 2002-2008, Axiomatic Systems, LLC. +| All rights reserved. +| +| Redistribution and use in source and binary forms, with or without +| modification, are permitted provided that the following conditions are met: +| * Redistributions of source code must retain the above copyright +| notice, this list of conditions and the following disclaimer. +| * Redistributions in binary form must reproduce the above copyright +| notice, this list of conditions and the following disclaimer in the +| documentation and/or other materials provided with the distribution. +| * Neither the name of Axiomatic Systems nor the +| names of its contributors may be used to endorse or promote products +| derived from this software without specific prior written permission. +| +| THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY +| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +| DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY +| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +| +****************************************************************/ +/** @file +* Implementation file for logging +*/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include <stdarg.h> + +#include "NptLogging.h" +#include "NptList.h" +#include "NptStreams.h" +#include "NptSockets.h" +#include "NptUtils.h" +#include "NptFile.h" +#include "NptSystem.h" +#include "NptConsole.h" +#include "NptThreads.h" +//#include "NptDirectory.h" + +/*---------------------------------------------------------------------- +| types ++---------------------------------------------------------------------*/ +class NPT_LogConsoleHandler : public NPT_LogHandler { +public: + // class methods + static NPT_Result Create(const char* logger_name, NPT_LogHandler*& handler); + + // methods + void Log(const NPT_LogRecord& record); + +private: + // members + bool m_UseColors; + NPT_Flags m_FormatFilter; +}; + +class NPT_LogFileHandler : public NPT_LogHandler { +public: + // class methods + static NPT_Result Create(const char* logger_name, NPT_LogHandler*& handler); + + // methods + void Log(const NPT_LogRecord& record); + +private: + NPT_Result Open(bool append = true); + +private: + // members + NPT_String m_Filename; + NPT_Flags m_FormatFilter; + NPT_Mutex m_RecycleLock; + NPT_LargeSize m_Recycle; + NPT_InputStreamReference m_InputStream; + NPT_OutputStreamReference m_OutputStream; +}; + +class NPT_LogTcpHandler : public NPT_LogHandler { +public: + // class methods + static NPT_Result Create(const char* logger_name, NPT_LogHandler*& handler); + + // methods + void Log(const NPT_LogRecord& record); + +private: + // methods + NPT_Result Connect(); + + // members + NPT_String m_Host; + NPT_UInt16 m_Port; + NPT_OutputStreamReference m_Stream; +}; + +class NPT_LogNullHandler : public NPT_LogHandler { +public: + // class methods + static NPT_Result Create(NPT_LogHandler*& handler); + + // methods + void Log(const NPT_LogRecord& record); +}; + +/*---------------------------------------------------------------------- +| constants ++---------------------------------------------------------------------*/ +#define NPT_LOG_HEAP_BUFFER_INCREMENT 4096 +#define NPT_LOG_STACK_BUFFER_MAX_SIZE 512 +#define NPT_LOG_HEAP_BUFFER_MAX_SIZE 65536 + +#if !defined(NPT_LOG_CONFIG_ENV) +#define NPT_LOG_CONFIG_ENV "NEPTUNE_LOG_CONFIG" +#endif + +#if !defined(NPT_LOG_DEFAULT_CONFIG_SOURCE) +#define NPT_LOG_DEFAULT_CONFIG_SOURCE "file:neptune-logging.properties" +#endif + +#define NPT_LOG_ROOT_DEFAULT_LOG_LEVEL NPT_LOG_LEVEL_INFO +#define NPT_LOG_ROOT_DEFAULT_HANDLER "ConsoleHandler" +#if !defined(NPT_LOG_ROOT_DEFAULT_FILE_HANDLER_FILENAME) +#define NPT_LOG_ROOT_DEFAULT_FILE_HANDLER_FILENAME "_neptune.log" +#endif + +#define NPT_LOG_TCP_HANDLER_DEFAULT_PORT 7723 +#define NPT_LOG_TCP_HANDLER_DEFAULT_CONNECT_TIMEOUT 5000 /* 5 seconds */ + +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__APPLE__) +#define NPT_LOG_CONSOLE_HANDLER_DEFAULT_COLOR_MODE false +#else +#define NPT_LOG_CONSOLE_HANDLER_DEFAULT_COLOR_MODE true +#endif + +#define NPT_LOG_FILE_HANDLER_MIN_RECYCLE_SIZE 20000000 + +#define NPT_LOG_FORMAT_FILTER_NO_SOURCE 1 +#define NPT_LOG_FORMAT_FILTER_NO_TIMESTAMP 2 +#define NPT_LOG_FORMAT_FILTER_NO_LOGGER_NAME 4 +#define NPT_LOG_FORMAT_FILTER_NO_FUNCTION_NAME 8 +#define NPT_LOG_FORMAT_FILTER_NO_SOURCEPATH 16 + +/*---------------------------------------------------------------------- +| globals ++---------------------------------------------------------------------*/ +static NPT_LogManager LogManager; +const char* LogManagerConfig = NULL; +bool LogManagerEnabled = true; + +/*---------------------------------------------------------------------- +| NPT_LogHandler::Create ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogHandler::Create(const char* logger_name, + const char* handler_name, + NPT_LogHandler*& handler) +{ + handler = NULL; + + if (NPT_StringsEqual(handler_name, "NullHandler")) { + return NPT_LogNullHandler::Create(handler); + } else if (NPT_StringsEqual(handler_name, "FileHandler")) { + return NPT_LogFileHandler::Create(logger_name, handler); + } else if (NPT_StringsEqual(handler_name, "ConsoleHandler")) { + return NPT_LogConsoleHandler::Create(logger_name, handler); + } else if (NPT_StringsEqual(handler_name, "TcpHandler")) { + return NPT_LogTcpHandler::Create(logger_name, handler); + } + + return NPT_ERROR_NO_SUCH_CLASS; +} + +/*---------------------------------------------------------------------- +| NPT_Log::GetLogLevel ++---------------------------------------------------------------------*/ +int +NPT_Log::GetLogLevel(const char* name) +{ + if ( NPT_StringsEqual(name, "FATAL")) { + return NPT_LOG_LEVEL_SEVERE; + } else if (NPT_StringsEqual(name, "SEVERE")) { + return NPT_LOG_LEVEL_WARNING; + } else if (NPT_StringsEqual(name, "WARNING")) { + return NPT_LOG_LEVEL_WARNING; + } else if (NPT_StringsEqual(name, "INFO")) { + return NPT_LOG_LEVEL_INFO; + } else if (NPT_StringsEqual(name, "FINE")) { + return NPT_LOG_LEVEL_FINE; + } else if (NPT_StringsEqual(name, "FINER")) { + return NPT_LOG_LEVEL_FINER; + } else if (NPT_StringsEqual(name, "FINEST")) { + return NPT_LOG_LEVEL_FINEST; + } else if (NPT_StringsEqual(name, "ALL")) { + return NPT_LOG_LEVEL_ALL; + } else if (NPT_StringsEqual(name, "OFF")) { + return NPT_LOG_LEVEL_OFF; + } else { + return -1; + } +} + +/*---------------------------------------------------------------------- +| NPT_Log::GetLogLevelName ++---------------------------------------------------------------------*/ +const char* +NPT_Log::GetLogLevelName(int level) +{ + switch (level) { + case NPT_LOG_LEVEL_FATAL: return "FATAL"; + case NPT_LOG_LEVEL_SEVERE: return "SEVERE"; + case NPT_LOG_LEVEL_WARNING: return "WARNING"; + case NPT_LOG_LEVEL_INFO: return "INFO"; + case NPT_LOG_LEVEL_FINE: return "FINE"; + case NPT_LOG_LEVEL_FINER: return "FINER"; + case NPT_LOG_LEVEL_FINEST: return "FINEST"; + case NPT_LOG_LEVEL_OFF: return "OFF"; + default: return ""; + } +} + +/*---------------------------------------------------------------------- +| NPT_Log::GetLogLevelAnsiColor ++---------------------------------------------------------------------*/ +const char* +NPT_Log::GetLogLevelAnsiColor(int level) +{ + switch (level) { + case NPT_LOG_LEVEL_FATAL: return "31"; + case NPT_LOG_LEVEL_SEVERE: return "31"; + case NPT_LOG_LEVEL_WARNING: return "33"; + case NPT_LOG_LEVEL_INFO: return "32"; + case NPT_LOG_LEVEL_FINE: return "34"; + case NPT_LOG_LEVEL_FINER: return "35"; + case NPT_LOG_LEVEL_FINEST: return "36"; + default: return NULL; + } +} + +/*---------------------------------------------------------------------- +| NPT_Log::FormatRecordToStream ++---------------------------------------------------------------------*/ +void +NPT_Log::FormatRecordToStream(const NPT_LogRecord& record, + NPT_OutputStream& stream, + bool use_colors, + NPT_Flags format_filter) +{ + const char* level_name = GetLogLevelName(record.m_Level); + NPT_String level_string; + + /* format and emit the record */ + if (level_name[0] == '\0') { + level_string = NPT_String::FromInteger(record.m_Level); + level_name = level_string; + } + if ((format_filter & NPT_LOG_FORMAT_FILTER_NO_SOURCE) == 0) { + int start = 0; + /* remove source file path if requested */ + if (format_filter & NPT_LOG_FORMAT_FILTER_NO_SOURCEPATH) { + for (start = NPT_StringLength(record.m_SourceFile); + start; + --start) { + if (record.m_SourceFile[start-1] == '\\' || + record.m_SourceFile[start-1] == '/') { + break; + } + } + } + stream.WriteString(record.m_SourceFile + start); + stream.Write("(", 1, NULL); + stream.WriteString(NPT_String::FromIntegerU(record.m_SourceLine)); + stream.Write("): ", 3, NULL); + } + if ((format_filter & NPT_LOG_FORMAT_FILTER_NO_LOGGER_NAME) == 0) { + stream.Write("[", 1, NULL); + stream.WriteString(record.m_LoggerName); + stream.Write("] ", 2, NULL); + } + if ((format_filter & NPT_LOG_FORMAT_FILTER_NO_TIMESTAMP) == 0) { + stream.WriteString(NPT_String::FromIntegerU(record.m_TimeStamp.m_Seconds)); + stream.WriteString(":"); + NPT_String ms = NPT_String::FromIntegerU(record.m_TimeStamp.m_NanoSeconds/1000000L); + if (ms.GetLength() < 3) stream.Write("0", 1); + if (ms.GetLength() < 2) stream.Write("0", 1); + stream.WriteString(ms); + stream.Write(" ", 1); + } + if ((format_filter & NPT_LOG_FORMAT_FILTER_NO_FUNCTION_NAME) == 0) { + stream.WriteFully("[",1); + if (record.m_SourceFunction) { + stream.WriteString(record.m_SourceFunction); + } + stream.WriteFully("] ",2); + } + const char* ansi_color = NULL; + if (use_colors) { + ansi_color = GetLogLevelAnsiColor(record.m_Level); + if (ansi_color) { + stream.Write("\033[", 2, NULL); + stream.WriteString(ansi_color); + stream.Write(";1m", 3, NULL); + } + } + stream.WriteString(level_name); + if (use_colors && ansi_color) { + stream.Write("\033[0m", 4, NULL); + } + stream.Write(": ", 2, NULL); + stream.WriteString(record.m_Message); + stream.Write("\n", 1, NULL); +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::NPT_LogManager ++---------------------------------------------------------------------*/ +NPT_LogManager::NPT_LogManager() : + m_Configured(false), + m_Configuring(false), + m_Root(NULL) +{ +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::~NPT_LogManager ++---------------------------------------------------------------------*/ +NPT_LogManager::~NPT_LogManager() +{ + /* destroy everything we've created */ + for (NPT_List<NPT_Logger*>::Iterator i = m_Loggers.GetFirstItem(); + i; + ++i) { + NPT_Logger* logger = *i; + delete logger; + } + + /* destroy the root logger */ + delete m_Root; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::EnableLogging ++---------------------------------------------------------------------*/ +void +NPT_LogManager::EnableLogging(bool value) +{ + LogManagerEnabled = value; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::SetConfig ++---------------------------------------------------------------------*/ +void +NPT_LogManager::SetConfig(const char* config) +{ + LogManagerConfig = config; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::Configure ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogManager::Configure() +{ + NPT_String config_sources_env; + const char* config_sources = NPT_LOG_DEFAULT_CONFIG_SOURCE; + + // exit if we're already initialized + if (m_Configured) return NPT_SUCCESS; + + // we're starting to configure ourselves + m_Configuring = true; + + /* set some default config values */ + SetConfigValue(".handlers", NPT_LOG_ROOT_DEFAULT_HANDLER); + + if (!LogManagerConfig) { + /* see if the config sources have been set to non-default values */ + if (NPT_SUCCEEDED(NPT_GetEnvironment(NPT_LOG_CONFIG_ENV, config_sources_env))) { + config_sources = config_sources_env; + } + } else { + config_sources = LogManagerConfig; + } + + /* load all configs */ + NPT_String config_source; + const char* cursor = config_sources; + const char* source = config_sources; + for (;;) { + if (*cursor == '\0' || *cursor == '|') { + if (cursor != source) { + config_source.Assign(source, (NPT_Size)(cursor-source)); + config_source.Trim(" \t"); + ParseConfigSource(config_source); + } + if (*cursor == '\0') break; + } + cursor++; + } + + /* create the root logger */ + LogManager.m_Root = new NPT_Logger(""); + LogManager.m_Root->m_Level = NPT_LOG_ROOT_DEFAULT_LOG_LEVEL; + LogManager.m_Root->m_LevelIsInherited = false; + ConfigureLogger(LogManager.m_Root); + + // we're initialized now + m_Configured = true; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::ConfigValueIsBooleanTrue ++---------------------------------------------------------------------*/ +bool +NPT_LogManager::ConfigValueIsBooleanTrue(NPT_String& value) +{ + return + value.Compare("true", true) == 0 || + value.Compare("yes", true) == 0 || + value.Compare("on", true) == 0 || + value.Compare("1", true) == 0; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::ConfigValueIsBooleanFalse ++---------------------------------------------------------------------*/ +bool +NPT_LogManager::ConfigValueIsBooleanFalse(NPT_String& value) +{ + return + value.Compare("false", true) == 0 || + value.Compare("no", true) == 0 || + value.Compare("off", true) == 0 || + value.Compare("0", true) == 0; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::GetConfigValue ++---------------------------------------------------------------------*/ +NPT_String* +NPT_LogManager::GetConfigValue(const char* prefix, const char* suffix) +{ + NPT_Size prefix_length = prefix?NPT_StringLength(prefix):0; + NPT_Size suffix_length = suffix?NPT_StringLength(suffix):0; + NPT_Size key_length = prefix_length+suffix_length; + for (NPT_List<NPT_LogConfigEntry>::Iterator i = LogManager.m_Config.GetFirstItem(); + i; + ++i) { + NPT_LogConfigEntry& entry = *i; + if ((entry.m_Key.GetLength() == key_length) && + (prefix == NULL || entry.m_Key.StartsWith(prefix)) && + (suffix == NULL || entry.m_Key.EndsWith(suffix )) ) { + return &entry.m_Value; + } + } + + // not found + return NULL; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::SetConfigValue ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogManager::SetConfigValue(const char* key, const char* value) +{ + NPT_String* value_string = GetConfigValue(key, NULL); + if (value_string) { + /* the key already exists, replace the value */ + *value_string = value; + } else { + /* the value does not already exist, create a new one */ + NPT_CHECK(LogManager.m_Config.Add(NPT_LogConfigEntry(key, value))); + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::ParseConfig ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogManager::ParseConfig(const char* config, + NPT_Size config_size) +{ + const char* cursor = config; + const char* line = config; + const char* separator = NULL; + NPT_String key; + NPT_String value; + + /* parse all entries */ + while (cursor <= config+config_size) { + /* separators are newlines, ';' or end of buffer */ + if ( cursor == config+config_size || + *cursor == '\n' || + *cursor == '\r' || + *cursor == ';') { + /* newline or end of buffer */ + if (separator && line[0] != '#') { + /* we have a property */ + key.Assign(line, (NPT_Size)(separator-line)); + value.Assign(line+(separator+1-line), (NPT_Size)(cursor-(separator+1))); + key.Trim(" \t"); + value.Trim(" \t"); + + SetConfigValue((const char*)key, (const char*)value); + } + line = cursor+1; + separator = NULL; + } else if (*cursor == '=' && separator == NULL) { + separator = cursor; + } + cursor++; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::ParseConfigFile ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogManager::ParseConfigFile(const char* filename) +{ + NPT_Result result; + + /* load the file */ + NPT_DataBuffer buffer; + result = NPT_File::Load(filename, buffer); + if (NPT_FAILED(result)) return result; + + /* parse the config */ + return ParseConfig((const char*)buffer.GetData(), buffer.GetDataSize()); +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::ParseConfigSource ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogManager::ParseConfigSource(NPT_String& source) +{ + if (source.StartsWith("file:")) { + /* file source */ + ParseConfigFile(source.GetChars()+5); + } else if (source.StartsWith("plist:")) { + /* property list source */ + ParseConfig(source.GetChars()+6, source.GetLength()-6); + } else { + return NPT_ERROR_INVALID_SYNTAX; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::HaveLoggerConfig ++---------------------------------------------------------------------*/ +bool +NPT_LogManager::HaveLoggerConfig(const char* name) +{ + NPT_Size name_length = NPT_StringLength(name); + for (NPT_List<NPT_LogConfigEntry>::Iterator i = m_Config.GetFirstItem(); + i; + ++i) { + NPT_LogConfigEntry& entry = *i; + if (entry.m_Key.StartsWith(name)) { + const char* suffix = entry.m_Key.GetChars()+name_length; + if (NPT_StringsEqual(suffix, ".level") || + NPT_StringsEqual(suffix, ".handlers") || + NPT_StringsEqual(suffix, ".forward")) { + return true; + } + } + } + + /* no config found */ + return false; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::ConfigureLogger ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogManager::ConfigureLogger(NPT_Logger* logger) +{ + /* configure the level */ + NPT_String* level_value = GetConfigValue(logger->m_Name,".level"); + if (level_value) { + NPT_Int32 value; + /* try a symbolic name */ + value = NPT_Log::GetLogLevel(*level_value); + if (value < 0) { + /* try a numeric value */ + if (NPT_FAILED(level_value->ToInteger(value, false))) { + value = -1; + } + } + if (value >= 0) { + logger->m_Level = value; + logger->m_LevelIsInherited = false; + } + } + + /* configure the handlers */ + NPT_String* handlers = GetConfigValue(logger->m_Name,".handlers"); + if (handlers) { + const char* handlers_list = handlers->GetChars(); + const char* cursor = handlers_list; + const char* name_start = handlers_list; + NPT_String handler_name; + NPT_LogHandler* handler; + for (;;) { + if (*cursor == '\0' || *cursor == ',') { + if (cursor != name_start) { + handler_name.Assign(name_start, (NPT_Size)(cursor-name_start)); + handler_name.Trim(" \t"); + + /* create a handler */ + if (NPT_SUCCEEDED( + NPT_LogHandler::Create(logger->m_Name, handler_name, handler))) { + logger->AddHandler(handler); + } + + } + if (*cursor == '\0') break; + name_start = cursor+1; + } + ++cursor; + } + } + + /* configure the forwarding */ + NPT_String* forward = GetConfigValue(logger->m_Name,".forward"); + if (forward && !ConfigValueIsBooleanTrue(*forward)) { + logger->m_ForwardToParent = false; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::FindLogger ++---------------------------------------------------------------------*/ +NPT_Logger* +NPT_LogManager::FindLogger(const char* name) +{ + for (NPT_List<NPT_Logger*>::Iterator i = LogManager.m_Loggers.GetFirstItem(); + i; + ++i) { + NPT_Logger* logger = *i; + if (logger->m_Name == name) { + return logger; + } + } + + return NULL; +} + +/*---------------------------------------------------------------------- +| NPT_LogManager::GetLogger ++---------------------------------------------------------------------*/ +NPT_Logger* +NPT_LogManager::GetLogger(const char* name) +{ + NPT_Logger* logger; + + /* check that LogManager was not turned off */ + if (!LogManagerEnabled) return NULL; + + /* check that the manager is initialized */ + if (!LogManager.m_Configured) { + /* check that we're not in the middle of configuration */ + if (LogManager.m_Configuring) return NULL; + + /* init the manager */ + LogManager.Configure(); + NPT_ASSERT(LogManager.m_Configured); + } + + /* check if this logger is already configured */ + logger = LogManager.FindLogger(name); + if (logger) return logger; + + /* create a new logger */ + logger = new NPT_Logger(name); + if (logger == NULL) return NULL; + + /* configure the logger */ + LogManager.ConfigureLogger(logger); + + /* find which parent to attach to */ + NPT_Logger* parent = LogManager.m_Root; + NPT_String parent_name = name; + for (;;) { + NPT_Logger* candidate_parent; + + /* find the last dot */ + int dot = parent_name.ReverseFind('.'); + if (dot < 0) break; + parent_name.SetLength(dot); + + /* see if the parent exists */ + candidate_parent = LogManager.FindLogger(parent_name); + if (candidate_parent) { + parent = candidate_parent; + break; + } + + /* this parent name does not exist, see if we need to create it */ + if (LogManager.HaveLoggerConfig(parent_name)) { + parent = GetLogger(parent_name); + break; + } + } + + /* attach to the parent */ + logger->SetParent(parent); + + /* add this logger to the list */ + LogManager.m_Loggers.Add(logger); + + return logger; +} + +/*---------------------------------------------------------------------- +| NPT_Logger::NPT_Logger ++---------------------------------------------------------------------*/ +NPT_Logger::NPT_Logger(const char* name) : + m_Name(name), + m_Level(NPT_LOG_LEVEL_OFF), + m_LevelIsInherited(true), + m_ForwardToParent(true), + m_Parent(NULL) +{ +} + +/*---------------------------------------------------------------------- +| NPT_Logger::~NPT_Logger ++---------------------------------------------------------------------*/ +NPT_Logger::~NPT_Logger() +{ + /* destroy all handlers */ + for (NPT_List<NPT_LogHandler*>::Iterator i = m_Handlers.GetFirstItem(); + i; + ++i) { + NPT_LogHandler* handler = *i; + delete handler; + } +} + +/*---------------------------------------------------------------------- +| NPT_Logger::Log ++---------------------------------------------------------------------*/ +void +NPT_Logger::Log(int level, + const char* source_file, + unsigned int source_line, + const char* source_function, + const char* msg, + ...) +{ + char buffer[NPT_LOG_STACK_BUFFER_MAX_SIZE]; + NPT_Size buffer_size = sizeof(buffer); + char* message = buffer; + int result; + va_list args; + + va_start(args, msg); + + /* check the log level (in case filtering has not already been done) */ + if (level < m_Level) return; + + for(;;) { + /* try to format the message (it might not fit) */ + result = NPT_FormatStringVN(message, buffer_size-1, msg, args); + if (result >= (int)(buffer_size-1)) result = -1; + message[buffer_size-1] = 0; /* force a NULL termination */ + if (result >= 0) break; + + /* the buffer was too small, try something bigger */ + buffer_size = (buffer_size+NPT_LOG_HEAP_BUFFER_INCREMENT)*2; + if (buffer_size > NPT_LOG_HEAP_BUFFER_MAX_SIZE) break; + if (message != buffer) delete[] message; + message = new char[buffer_size]; + if (message == NULL) return; + } + + /* the message is formatted, publish it to the handlers */ + NPT_LogRecord record; + NPT_Logger* logger = this; + + /* setup the log record */ + record.m_LoggerName = logger->m_Name, + record.m_Level = level; + record.m_Message = message; + record.m_SourceFile = source_file; + record.m_SourceLine = source_line; + record.m_SourceFunction = source_function; + NPT_System::GetCurrentTimeStamp(record.m_TimeStamp); + + /* call all handlers for this logger and parents */ + while (logger) { + /* call all handlers for the current logger */ + for (NPT_List<NPT_LogHandler*>::Iterator i = logger->m_Handlers.GetFirstItem(); + i; + ++i) { + NPT_LogHandler* handler = *i; + handler->Log(record); + } + + /* forward to the parent unless this logger does not forward */ + if (logger->m_ForwardToParent) { + logger = logger->m_Parent; + } else { + break; + } + } + + /* free anything we may have allocated */ + if (message != buffer) delete[] message; + + va_end(args); +} + +/*---------------------------------------------------------------------- +| NPT_Logger::AddHandler ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Logger::AddHandler(NPT_LogHandler* handler) +{ + /* check parameters */ + if (handler == NULL) return NPT_ERROR_INVALID_PARAMETERS; + + return m_Handlers.Add(handler); +} + +/*---------------------------------------------------------------------- +| NPT_Logger::SetParent ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Logger::SetParent(NPT_Logger* parent) +{ + /* set our new parent */ + m_Parent = parent; + + /* find the first ancestor with its own log level */ + NPT_Logger* logger = this; + while (logger->m_LevelIsInherited && logger->m_Parent) { + logger = logger->m_Parent; + } + if (logger != this) m_Level = logger->m_Level; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogNullHandler::Create ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogNullHandler::Create(NPT_LogHandler*& handler) +{ + handler = new NPT_LogNullHandler(); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogNullHandler::Log ++---------------------------------------------------------------------*/ +void +NPT_LogNullHandler::Log(const NPT_LogRecord& /*record*/) +{ +} + +/*---------------------------------------------------------------------- +| NPT_LogConsoleHandler::Create ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogConsoleHandler::Create(const char* logger_name, + NPT_LogHandler*& handler) +{ + /* compute a prefix for the configuration of this handler */ + NPT_String logger_prefix = logger_name; + logger_prefix += ".ConsoleHandler"; + + /* allocate a new object */ + NPT_LogConsoleHandler* instance = new NPT_LogConsoleHandler(); + handler = instance; + + /* configure the object */ + NPT_String* colors; + instance->m_UseColors = NPT_LOG_CONSOLE_HANDLER_DEFAULT_COLOR_MODE; + colors = LogManager.GetConfigValue(logger_prefix,".colors"); + if (colors) { + if (NPT_LogManager::ConfigValueIsBooleanTrue(*colors)) { + instance->m_UseColors = true; + } else if (NPT_LogManager::ConfigValueIsBooleanFalse(*colors)) { + instance->m_UseColors = false; + } + } + NPT_String* filter; + instance->m_FormatFilter = 30; // default to nothing + filter = LogManager.GetConfigValue(logger_prefix,".filter"); + if (filter) { + NPT_Int32 flags = 0; + filter->ToInteger(flags, true); + instance->m_FormatFilter = flags; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogConsoleHandler::Log ++---------------------------------------------------------------------*/ +void +NPT_LogConsoleHandler::Log(const NPT_LogRecord& record) +{ + NPT_MemoryStream memory_stream(4096); + + NPT_Log::FormatRecordToStream(record, memory_stream, m_UseColors, m_FormatFilter); + memory_stream.Write("\0", 1); + NPT_Console::Output((const char*)memory_stream.GetData()); +} + +/*---------------------------------------------------------------------- +| NPT_LogFileHandler::Log ++---------------------------------------------------------------------*/ +void +NPT_LogFileHandler::Log(const NPT_LogRecord& record) +{ + if (m_Recycle > 0) { + m_RecycleLock.Lock(); + + /* get log size */ + NPT_LargeSize size; + if (!m_InputStream.IsNull()) m_InputStream->GetSize(size); + + /* time to recycle ? */ + if (size > m_Recycle) { + /* release streams to force a reopen later */ + m_OutputStream = NULL; + m_InputStream = NULL; + + /* move file */ + NPT_TimeStamp now; + NPT_System::GetCurrentTimeStamp(now); + + NPT_String new_name = NPT_FilePath::Create( + NPT_FilePath::DirectoryName(m_Filename), + NPT_FilePath::BaseName(m_Filename) + "-" + NPT_String::FromIntegerU(now.m_Seconds) + ".log"); + + NPT_File::Rename(m_Filename, new_name); + } + } + + /* try to reopen the file if it failed to open previously or if we recycled it */ + if (m_InputStream.IsNull() || m_OutputStream.IsNull()) { + Open(); + } + + if (m_InputStream.AsPointer() && m_OutputStream.AsPointer()) { + /* seek output stream to end of file */ + NPT_LargeSize size; + m_InputStream->GetSize(size); + m_OutputStream->Seek(size); + + NPT_Debug("NPT_LogFileHandler told to seek to position %d\n", size); + + NPT_Log::FormatRecordToStream(record, *m_OutputStream, false, m_FormatFilter); + + /* force flushing */ + m_OutputStream->Flush(); + } + + if (m_Recycle > 0) m_RecycleLock.Unlock(); +} + +/*---------------------------------------------------------------------- +| NPT_LogFileHandler::Open ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogFileHandler::Open(bool append /* = true */) +{ + /* reset streams just in case */ + m_OutputStream = NULL; + m_InputStream = NULL; + + /* open the log file */ + NPT_File file(m_Filename); + NPT_Result result = file.Open(NPT_FILE_OPEN_MODE_CREATE | + NPT_FILE_OPEN_MODE_READ | + NPT_FILE_OPEN_MODE_WRITE | + (append?NPT_FILE_OPEN_MODE_APPEND:NPT_FILE_OPEN_MODE_TRUNCATE)); + if (NPT_FAILED(result)) { + NPT_Debug("NPT_LogFileHandler::Open - cannot open log file '%s' (%d)\n", + m_Filename.GetChars(), result); + return result; + } + + NPT_CHECK(file.GetInputStream(m_InputStream)); + NPT_CHECK(file.GetOutputStream(m_OutputStream)); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogFileHandler::Create ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogFileHandler::Create(const char* logger_name, + NPT_LogHandler*& handler) +{ + /* compute a prefix for the configuration of this handler */ + NPT_String logger_prefix = logger_name; + logger_prefix += ".FileHandler"; + + /* allocate a new object */ + NPT_LogFileHandler* instance = new NPT_LogFileHandler(); + handler = instance; + + /* filename */ + NPT_String* filename_conf = LogManager.GetConfigValue(logger_prefix, ".filename"); + if (filename_conf) { + instance->m_Filename = *filename_conf; + } else if (logger_name[0]) { + NPT_String filename_synth = logger_name; + filename_synth += ".log"; + instance->m_Filename = filename_synth; + } else { + /* default name for the root logger */ + instance->m_Filename = NPT_LOG_ROOT_DEFAULT_FILE_HANDLER_FILENAME; + } + + /* append mode */ + bool append = true; + NPT_String* append_mode = LogManager.GetConfigValue(logger_prefix, ".append"); + if (append_mode && NPT_LogManager::ConfigValueIsBooleanFalse(*append_mode)) { + append = false; + } + + /* filter */ + NPT_String* filter; + instance->m_FormatFilter = 0; + filter = LogManager.GetConfigValue(logger_prefix,".filter"); + if (filter) { + NPT_Int32 flags = 0; + filter->ToInteger(flags, true); + instance->m_FormatFilter = flags; + } + + /* recycle */ + NPT_String* recycle; + instance->m_Recycle = 0; + recycle = LogManager.GetConfigValue(logger_prefix,".recycle"); + if (recycle) { + NPT_Int32 size = 0; + recycle->ToInteger(size, true); + if (size > NPT_LOG_FILE_HANDLER_MIN_RECYCLE_SIZE) { + instance->m_Recycle = size; + } else { + instance->m_Recycle = NPT_LOG_FILE_HANDLER_MIN_RECYCLE_SIZE; + } + } + + /* open the log file */ + return instance->Open(append); +} + +/*---------------------------------------------------------------------- +| NPT_LogTcpHandler::Create ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogTcpHandler::Create(const char* logger_name, NPT_LogHandler*& handler) +{ + /* compute a prefix for the configuration of this handler */ + NPT_String logger_prefix = logger_name; + logger_prefix += ".TcpHandler"; + + /* allocate a new object */ + NPT_LogTcpHandler* instance = new NPT_LogTcpHandler(); + handler = instance; + + /* configure the object */ + const NPT_String* hostname = LogManager.GetConfigValue(logger_prefix, ".hostname"); + if (hostname) { + instance->m_Host = *hostname; + } else { + /* default hostname */ + instance->m_Host = "localhost"; + } + const NPT_String* port = LogManager.GetConfigValue(logger_prefix, ".port"); + if (port) { + NPT_Int32 port_int; + if (NPT_SUCCEEDED(port->ToInteger(port_int, true))) { + instance->m_Port = (NPT_UInt16)port_int; + } else { + instance->m_Port = NPT_LOG_TCP_HANDLER_DEFAULT_PORT; + } + } else { + /* default port */ + instance->m_Port = NPT_LOG_TCP_HANDLER_DEFAULT_PORT; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_LogTcpHandler::Connect ++---------------------------------------------------------------------*/ +NPT_Result +NPT_LogTcpHandler::Connect() +{ + /* create a socket */ + NPT_Socket tcp_socket = new NPT_TcpClientSocket(); + + /* connect to the host */ + NPT_IpAddress ip_address; + NPT_CHECK(ip_address.ResolveName(m_Host)); + NPT_Result result = tcp_socket.Connect(NPT_SocketAddress(ip_address, m_Port), + NPT_LOG_TCP_HANDLER_DEFAULT_CONNECT_TIMEOUT); + if (NPT_FAILED(result)) { + return result; + } + + /* get the stream */ + return tcp_socket.GetOutputStream(m_Stream); +} + +/*---------------------------------------------------------------------- +| NPT_LogTcpHandler::Log ++---------------------------------------------------------------------*/ +void +NPT_LogTcpHandler::Log(const NPT_LogRecord& record) +{ + /* ensure we're connected */ + if (m_Stream.IsNull()) { + if (NPT_FAILED(Connect())) return; + } + + /* format the record */ + NPT_String msg; + const char* level_name = NPT_Log::GetLogLevelName(record.m_Level); + NPT_String level_string; + + /* format and emit the record */ + if (level_name[0] == '\0') { + level_string = NPT_String::FromIntegerU(record.m_Level); + level_name = level_string; + } + msg.Reserve(2048); + msg += "Logger: "; + msg += record.m_LoggerName; + msg += "\r\nLevel: "; + msg += level_name; + msg += "\r\nSource-File: "; + msg += record.m_SourceFile; + msg += "\r\nSource-Line: "; + msg += NPT_String::FromIntegerU(record.m_SourceLine); + msg += "\r\nTimeStamp: "; + msg += NPT_String::FromIntegerU(record.m_TimeStamp.m_Seconds); + msg += ":"; + msg += NPT_String::FromIntegerU(record.m_TimeStamp.m_NanoSeconds/1000000L); + msg += "\r\nContent-Length: "; + msg += NPT_String::FromIntegerU(NPT_StringLength(record.m_Message)); + msg += "\r\n\r\n"; + msg += record.m_Message; + + /* emit the formatted record */ + if (NPT_FAILED(m_Stream->WriteString(msg))) { + m_Stream = NULL; + } +}