This reverts commit 39331ea7b9.
This is required to fix a race condition introduced with the logger cache. Adding a locking mechanism might make worthless the speed gain added by the cache itself, push a proper thread-safe implementation if it's worth it.
Helgrind log:
Possible data race during write of size 8 at 0x736F428 by thread #1
Locks held: none
at 0x11872DF: std::_Hashtable<std::string, std::pair<std::string const, Logger const*>, std::allocator<std::pair<std::string const, Logger const*> >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_insert_bucket_begin(unsigned long, std::__detail::_Hash_node<std::pair<std::string const, Logger const*>, true>*) (hashtable.h:1196)
by 0x11865A1: std::_Hashtable<std::string, std::pair<std::string const, Logger const*>, std::allocator<std::pair<std::string const, Logger const*> >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<std::string const, Logger const*>, true>*) (hashtable.h:1342)
by 0x1185A48: std::__detail::_Map_base<std::string, std::pair<std::string const, Logger const*>, std::allocator<std::pair<std::string const, Logger const*> >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::string const&) (hashtable_policy.h:496)
by 0x1184F3A: std::unordered_map<std::string, Logger const*, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, Logger const*> > >::operator[](std::string const&) (unordered_map.h:596)
by 0x1184495: Log::GetLoggerByType(std::string const&) (Log.h:106)
by 0x1184527: Log::ShouldLog(std::string const&, LogLevel) (Log.h:112)
by 0x16E7121: World::LoadConfigSettings(bool) (World.cpp:909)
by 0x16EB623: World::SetInitialWorldSettings() (World.cpp:1276)
by 0x118F0EC: Master::Run() (Master.cpp:169)
by 0x1196AFF: main (Main.cpp:142)
This conflicts with a previous read of size 8 by thread #3
Locks held: none
at 0x1186EA6: std::_Hashtable<std::string, std::pair<std::string const, Logger const*>, std::allocator<std::pair<std::string const, Logger const*> >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_before_node(unsigned long, std::string const&, unsigned long) const (hashtable.h:1162)
by 0x1186263: std::_Hashtable<std::string, std::pair<std::string const, Logger const*>, std::allocator<std::pair<std::string const, Logger const*> >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_node(unsigned long, std::string const&, unsigned long) const (hashtable.h:604)
by 0x11857B9: std::_Hashtable<std::string, std::pair<std::string const, Logger const*>, std::allocator<std::pair<std::string const, Logger const*> >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::find(std::string const&) (hashtable.h:1025)
by 0x1184DE2: std::unordered_map<std::string, Logger const*, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, Logger const*> > >::find(std::string const&) (unordered_map.h:543)
by 0x11842EF: Log::GetLoggerByType(std::string const&) (Log.h:84)
by 0x1184527: Log::ShouldLog(std::string const&, LogLevel) (Log.h:112)
by 0x189AACA: MySQLConnection::Execute(char const*) (MySQLConnection.cpp:175)
by 0x18A3225: BasicStatementTask::Execute() (AdhocStatement.cpp:56)
Address 0x736F428 is 120 bytes inside a block of size 184 alloc'd
at 0x4C2AE3A: operator new(unsigned long, std::nothrow_t const&) (vg_replace_malloc.c:350)
by 0x1185046: ACE_Singleton<Log, ACE_Thread_Mutex>::instance() (Singleton.cpp:91)
by 0x11968E2: main (Main.cpp:135)
403 lines
12 KiB
C++
403 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Log.h"
|
|
#include "Common.h"
|
|
#include "Config.h"
|
|
#include "Util.h"
|
|
#include "AppenderConsole.h"
|
|
#include "AppenderFile.h"
|
|
#include "AppenderDB.h"
|
|
#include "LogOperation.h"
|
|
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <sstream>
|
|
|
|
Log::Log() : worker(NULL)
|
|
{
|
|
m_logsTimestamp = "_" + GetTimestampStr();
|
|
LoadFromConfig();
|
|
}
|
|
|
|
Log::~Log()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
uint8 Log::NextAppenderId()
|
|
{
|
|
return AppenderId++;
|
|
}
|
|
|
|
int32 GetConfigIntDefault(std::string base, const char* name, int32 value)
|
|
{
|
|
base.append(name);
|
|
return sConfigMgr->GetIntDefault(base.c_str(), value);
|
|
}
|
|
|
|
std::string GetConfigStringDefault(std::string base, const char* name, const char* value)
|
|
{
|
|
base.append(name);
|
|
return sConfigMgr->GetStringDefault(base.c_str(), value);
|
|
}
|
|
|
|
Appender* Log::GetAppenderByName(std::string const& name)
|
|
{
|
|
AppenderMap::iterator it = appenders.begin();
|
|
while (it != appenders.end() && it->second && it->second->getName() != name)
|
|
++it;
|
|
|
|
return it == appenders.end() ? NULL : it->second;
|
|
}
|
|
|
|
void Log::CreateAppenderFromConfig(std::string const& appenderName)
|
|
{
|
|
if (appenderName.empty())
|
|
return;
|
|
|
|
// Format=type, level, flags, optional1, optional2
|
|
// if type = File. optional1 = file and option2 = mode
|
|
// if type = Console. optional1 = Color
|
|
std::string options = sConfigMgr->GetStringDefault(appenderName.c_str(), "");
|
|
|
|
Tokenizer tokens(options, ',');
|
|
Tokenizer::const_iterator iter = tokens.begin();
|
|
|
|
size_t size = tokens.size();
|
|
std::string name = appenderName.substr(9);
|
|
|
|
if (size < 2)
|
|
{
|
|
fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong configuration for appender %s. Config line: %s\n", name.c_str(), options.c_str());
|
|
return;
|
|
}
|
|
|
|
AppenderFlags flags = APPENDER_FLAGS_NONE;
|
|
AppenderType type = AppenderType(atoi(*iter++));
|
|
LogLevel level = LogLevel(atoi(*iter++));
|
|
|
|
if (level > LOG_LEVEL_FATAL)
|
|
{
|
|
fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level %d for appender %s\n", level, name.c_str());
|
|
return;
|
|
}
|
|
|
|
if (size > 2)
|
|
flags = AppenderFlags(atoi(*iter++));
|
|
|
|
switch (type)
|
|
{
|
|
case APPENDER_CONSOLE:
|
|
{
|
|
AppenderConsole* appender = new AppenderConsole(NextAppenderId(), name, level, flags);
|
|
appenders[appender->getId()] = appender;
|
|
if (size > 3)
|
|
appender->InitColors(*iter++);
|
|
//fprintf(stdout, "Log::CreateAppenderFromConfig: Created Appender %s (%u), Type CONSOLE, Mask %u\n", appender->getName().c_str(), appender->getId(), appender->getLogLevel());
|
|
break;
|
|
}
|
|
case APPENDER_FILE:
|
|
{
|
|
std::string filename;
|
|
std::string mode = "a";
|
|
|
|
if (size < 4)
|
|
{
|
|
fprintf(stderr, "Log::CreateAppenderFromConfig: Missing file name for appender %s\n", name.c_str());
|
|
return;
|
|
}
|
|
|
|
filename = *iter++;
|
|
|
|
if (size > 4)
|
|
mode = *iter++;
|
|
|
|
if (flags & APPENDER_FLAGS_USE_TIMESTAMP)
|
|
{
|
|
size_t dot_pos = filename.find_last_of(".");
|
|
if (dot_pos != filename.npos)
|
|
filename.insert(dot_pos, m_logsTimestamp);
|
|
else
|
|
filename += m_logsTimestamp;
|
|
}
|
|
|
|
uint64 maxFileSize = 0;
|
|
if (size > 5)
|
|
maxFileSize = atoi(*iter++);
|
|
|
|
uint8 id = NextAppenderId();
|
|
appenders[id] = new AppenderFile(id, name, level, filename.c_str(), m_logsDir.c_str(), mode.c_str(), flags, maxFileSize);
|
|
//fprintf(stdout, "Log::CreateAppenderFromConfig: Created Appender %s (%u), Type FILE, Mask %u, File %s, Mode %s\n", name.c_str(), id, level, filename.c_str(), mode.c_str());
|
|
break;
|
|
}
|
|
case APPENDER_DB:
|
|
{
|
|
uint8 id = NextAppenderId();
|
|
appenders[id] = new AppenderDB(id, name, level);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown type %d for appender %s\n", type, name.c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Log::CreateLoggerFromConfig(std::string const& appenderName)
|
|
{
|
|
if (appenderName.empty())
|
|
return;
|
|
|
|
LogLevel level = LOG_LEVEL_DISABLED;
|
|
uint8 type = uint8(-1);
|
|
|
|
std::string options = sConfigMgr->GetStringDefault(appenderName.c_str(), "");
|
|
std::string name = appenderName.substr(7);
|
|
|
|
if (options.empty())
|
|
{
|
|
fprintf(stderr, "Log::CreateLoggerFromConfig: Missing config option Logger.%s\n", name.c_str());
|
|
return;
|
|
}
|
|
|
|
Tokenizer tokens(options, ',');
|
|
Tokenizer::const_iterator iter = tokens.begin();
|
|
|
|
if (tokens.size() != 2)
|
|
{
|
|
fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong config option Logger.%s=%s\n", name.c_str(), options.c_str());
|
|
return;
|
|
}
|
|
|
|
Logger& logger = loggers[name];
|
|
if (!logger.getName().empty())
|
|
{
|
|
fprintf(stderr, "Error while configuring Logger %s. Already defined\n", name.c_str());
|
|
return;
|
|
}
|
|
|
|
level = LogLevel(atoi(*iter++));
|
|
if (level > LOG_LEVEL_FATAL)
|
|
{
|
|
fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong Log Level %u for logger %s\n", type, name.c_str());
|
|
return;
|
|
}
|
|
|
|
logger.Create(name, level);
|
|
//fprintf(stdout, "Log::CreateLoggerFromConfig: Created Logger %s, Level %u\n", name.c_str(), level);
|
|
|
|
std::istringstream ss(*iter);
|
|
std::string str;
|
|
|
|
ss >> str;
|
|
while (ss)
|
|
{
|
|
if (Appender* appender = GetAppenderByName(str))
|
|
{
|
|
logger.addAppender(appender->getId(), appender);
|
|
//fprintf(stdout, "Log::CreateLoggerFromConfig: Added Appender %s to Logger %s\n", appender->getName().c_str(), name.c_str());
|
|
}
|
|
else
|
|
fprintf(stderr, "Error while configuring Appender %s in Logger %s. Appender does not exist", str.c_str(), name.c_str());
|
|
ss >> str;
|
|
}
|
|
}
|
|
|
|
void Log::ReadAppendersFromConfig()
|
|
{
|
|
std::list<std::string> keys = sConfigMgr->GetKeysByString("Appender.");
|
|
|
|
while (!keys.empty())
|
|
{
|
|
CreateAppenderFromConfig(keys.front());
|
|
keys.pop_front();
|
|
}
|
|
}
|
|
|
|
void Log::ReadLoggersFromConfig()
|
|
{
|
|
std::list<std::string> keys = sConfigMgr->GetKeysByString("Logger.");
|
|
|
|
while (!keys.empty())
|
|
{
|
|
CreateLoggerFromConfig(keys.front());
|
|
keys.pop_front();
|
|
}
|
|
|
|
// Bad config configuration, creating default config
|
|
if (loggers.find(LOGGER_ROOT) == loggers.end())
|
|
{
|
|
fprintf(stderr, "Wrong Loggers configuration. Review your Logger config section.\n"
|
|
"Creating default loggers [root (Error), server (Info)] to console\n");
|
|
|
|
Close(); // Clean any Logger or Appender created
|
|
|
|
AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE);
|
|
appenders[appender->getId()] = appender;
|
|
|
|
Logger& logger = loggers[LOGGER_ROOT];
|
|
logger.Create(LOGGER_ROOT, LOG_LEVEL_ERROR);
|
|
logger.addAppender(appender->getId(), appender);
|
|
|
|
logger = loggers["server"];
|
|
logger.Create("server", LOG_LEVEL_ERROR);
|
|
logger.addAppender(appender->getId(), appender);
|
|
}
|
|
}
|
|
|
|
void Log::vlog(std::string const& filter, LogLevel level, char const* str, va_list argptr)
|
|
{
|
|
char text[MAX_QUERY_LEN];
|
|
vsnprintf(text, MAX_QUERY_LEN, str, argptr);
|
|
write(new LogMessage(level, filter, text));
|
|
}
|
|
|
|
void Log::write(LogMessage* msg) const
|
|
{
|
|
Logger const* logger = GetLoggerByType(msg->type);
|
|
msg->text.append("\n");
|
|
|
|
if (worker)
|
|
worker->enqueue(new LogOperation(logger, msg));
|
|
else
|
|
{
|
|
logger->write(*msg);
|
|
delete msg;
|
|
}
|
|
}
|
|
|
|
std::string Log::GetTimestampStr()
|
|
{
|
|
time_t t = time(NULL);
|
|
tm aTm;
|
|
ACE_OS::localtime_r(&t, &aTm);
|
|
// YYYY year
|
|
// MM month (2 digits 01-12)
|
|
// DD day (2 digits 01-31)
|
|
// HH hour (2 digits 00-23)
|
|
// MM minutes (2 digits 00-59)
|
|
// SS seconds (2 digits 00-59)
|
|
char buf[20];
|
|
snprintf(buf, 20, "%04d-%02d-%02d_%02d-%02d-%02d", aTm.tm_year+1900, aTm.tm_mon+1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec);
|
|
return std::string(buf);
|
|
}
|
|
|
|
bool Log::SetLogLevel(std::string const& name, const char* newLevelc, bool isLogger /* = true */)
|
|
{
|
|
LogLevel newLevel = LogLevel(atoi(newLevelc));
|
|
if (newLevel < 0)
|
|
return false;
|
|
|
|
if (isLogger)
|
|
{
|
|
LoggerMap::iterator it = loggers.begin();
|
|
while (it != loggers.end() && it->second.getName() != name)
|
|
++it;
|
|
|
|
if (it == loggers.end())
|
|
return false;
|
|
|
|
it->second.setLogLevel(newLevel);
|
|
}
|
|
else
|
|
{
|
|
Appender* appender = GetAppenderByName(name);
|
|
if (!appender)
|
|
return false;
|
|
|
|
appender->setLogLevel(newLevel);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Log::outCharDump(char const* str, uint32 accountId, uint32 guid, char const* name)
|
|
{
|
|
if (!str || !ShouldLog("entities.player.dump", LOG_LEVEL_INFO))
|
|
return;
|
|
|
|
std::ostringstream ss;
|
|
ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name
|
|
<< ")\n" << str << "\n== END DUMP ==\n";
|
|
|
|
LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str());
|
|
std::ostringstream param;
|
|
param << guid << '_' << name;
|
|
|
|
msg->param1 = param.str();
|
|
|
|
write(msg);
|
|
}
|
|
|
|
void Log::outCommand(uint32 account, const char * str, ...)
|
|
{
|
|
if (!str || !ShouldLog("commands.gm", LOG_LEVEL_INFO))
|
|
return;
|
|
|
|
va_list ap;
|
|
va_start(ap, str);
|
|
char text[MAX_QUERY_LEN];
|
|
vsnprintf(text, MAX_QUERY_LEN, str, ap);
|
|
va_end(ap);
|
|
|
|
LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, "commands.gm", text);
|
|
|
|
std::ostringstream ss;
|
|
ss << account;
|
|
msg->param1 = ss.str();
|
|
|
|
write(msg);
|
|
}
|
|
|
|
void Log::SetRealmId(uint32 id)
|
|
{
|
|
for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it)
|
|
if (it->second && it->second->getType() == APPENDER_DB)
|
|
((AppenderDB *)it->second)->setRealmId(id);
|
|
}
|
|
|
|
void Log::Close()
|
|
{
|
|
delete worker;
|
|
worker = NULL;
|
|
loggers.clear();
|
|
for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it)
|
|
{
|
|
delete it->second;
|
|
it->second = NULL;
|
|
}
|
|
appenders.clear();
|
|
}
|
|
|
|
void Log::LoadFromConfig()
|
|
{
|
|
Close();
|
|
|
|
if (sConfigMgr->GetBoolDefault("Log.Async.Enable", false))
|
|
worker = new LogWorker();
|
|
|
|
AppenderId = 0;
|
|
m_logsDir = sConfigMgr->GetStringDefault("LogsDir", "");
|
|
if (!m_logsDir.empty())
|
|
if ((m_logsDir.at(m_logsDir.length() - 1) != '/') && (m_logsDir.at(m_logsDir.length() - 1) != '\\'))
|
|
m_logsDir.push_back('/');
|
|
|
|
ReadAppendersFromConfig();
|
|
ReadLoggersFromConfig();
|
|
}
|