Legends-of-Azeroth-Pandaria-5.4.8
392 строки · 11.7 Кб
1/*
2* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
3*
4* This program is free software; you can redistribute it and/or modify it
5* under the terms of the GNU General Public License as published by the
6* Free Software Foundation; either version 2 of the License, or (at your
7* option) any later version.
8*
9* This program is distributed in the hope that it will be useful, but WITHOUT
10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12* more details.
13*
14* You should have received a copy of the GNU General Public License along
15* with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "Log.h"
19#include "AppenderConsole.h"
20#include "AppenderFile.h"
21#include "Common.h"
22#include "Config.h"
23#include "Errors.h"
24#include "Logger.h"
25#include "LogMessage.h"
26#include "LogOperation.h"
27#include "Strand.h"
28#include "StringConvert.h"
29#include "Util.h"
30#include <chrono>
31#include <sstream>
32
33Log::Log() : AppenderId(0), lowestLogLevel(LOG_LEVEL_FATAL), _ioContext(nullptr), _strand(nullptr)
34{
35m_logsTimestamp = "_" + GetTimestampStr();
36RegisterAppender<AppenderConsole>();
37RegisterAppender<AppenderFile>();
38}
39
40Log::~Log()
41{
42delete _strand;
43Close();
44}
45
46uint8 Log::NextAppenderId()
47{
48return AppenderId++;
49}
50
51Appender* Log::GetAppenderByName(std::string_view name)
52{
53auto it = appenders.begin();
54while (it != appenders.end() && it->second && it->second->getName() != name)
55++it;
56
57return it == appenders.end() ? nullptr : it->second.get();
58}
59
60void Log::CreateAppenderFromConfig(std::string const& appenderName)
61{
62if (appenderName.empty())
63return;
64
65// Format = type, level, flags, optional1, optional2
66// if type = File. optional1 = file and option2 = mode
67// if type = Console. optional1 = Color
68std::string options = sConfigMgr->GetStringDefault(appenderName, "");
69
70std::vector<std::string_view> tokens = Trinity::Tokenize(options, ',', true);
71
72size_t const size = tokens.size();
73std::string name = appenderName.substr(9);
74
75if (size < 2)
76{
77fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong configuration for appender %s. Config line: %s\n", name.c_str(), options.c_str());
78return;
79}
80
81AppenderFlags flags = APPENDER_FLAGS_NONE;
82AppenderType type = AppenderType(Trinity::StringTo<uint8>(tokens[0]).value_or(APPENDER_INVALID));
83LogLevel level = LogLevel(Trinity::StringTo<uint8>(tokens[1]).value_or(LOG_LEVEL_INVALID));
84
85auto factoryFunction = appenderFactory.find(type);
86if (factoryFunction == appenderFactory.end())
87{
88fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown type '%s' for appender %s\n", std::string(tokens[0]).c_str(), name.c_str());
89return;
90}
91
92if (level > NUM_ENABLED_LOG_LEVELS)
93{
94fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level '%s' for appender %s\n", std::string(tokens[1]).c_str(), name.c_str());
95return;
96}
97
98if (size > 2)
99{
100if (Optional<uint8> flagsVal = Trinity::StringTo<uint8>(tokens[2]))
101flags = AppenderFlags(*flagsVal);
102else
103{
104fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown flags '%s' for appender %s\n", std::string(tokens[2]).c_str(), name.c_str());
105return;
106}
107}
108
109try
110{
111Appender* appender = factoryFunction->second(NextAppenderId(), name, level, flags, tokens);
112appenders[appender->getId()].reset(appender);
113}
114catch (InvalidAppenderArgsException const& iaae)
115{
116fprintf(stderr, "%s\n", iaae.what());
117}
118}
119
120void Log::CreateLoggerFromConfig(std::string const& appenderName)
121{
122if (appenderName.empty())
123return;
124
125LogLevel level = LOG_LEVEL_DISABLED;
126
127std::string options = sConfigMgr->GetStringDefault(appenderName, "");
128std::string name = appenderName.substr(7);
129
130if (options.empty())
131{
132fprintf(stderr, "Log::CreateLoggerFromConfig: Missing config option Logger.%s\n", name.c_str());
133return;
134}
135
136std::vector<std::string_view> tokens = Trinity::Tokenize(options, ',', true);
137
138if (tokens.size() != 2)
139{
140fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong config option Logger.%s=%s\n", name.c_str(), options.c_str());
141return;
142}
143
144std::unique_ptr<Logger>& logger = loggers[name];
145if (logger)
146{
147fprintf(stderr, "Error while configuring Logger %s. Already defined\n", name.c_str());
148return;
149}
150
151level = LogLevel(Trinity::StringTo<uint8>(tokens[0]).value_or(LOG_LEVEL_INVALID));
152if (level > NUM_ENABLED_LOG_LEVELS)
153{
154fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong Log Level '%s' for logger %s\n", std::string(tokens[0]).c_str(), name.c_str());
155return;
156}
157
158if (level < lowestLogLevel)
159lowestLogLevel = level;
160
161logger = std::make_unique<Logger>(name, level);
162//fprintf(stdout, "Log::CreateLoggerFromConfig: Created Logger %s, Level %u\n", name.c_str(), level);
163
164for (std::string_view appenderName : Trinity::Tokenize(tokens[1], ' ', false))
165{
166if (Appender* appender = GetAppenderByName(appenderName))
167{
168logger->addAppender(appender->getId(), appender);
169//fprintf(stdout, "Log::CreateLoggerFromConfig: Added Appender %s to Logger %s\n", appender->getName().c_str(), name.c_str());
170}
171else
172fprintf(stderr, "Error while configuring Appender %s in Logger %s. Appender does not exist\n", std::string(appenderName).c_str(), name.c_str());
173}
174}
175
176void Log::ReadAppendersFromConfig()
177{
178std::vector<std::string> keys = sConfigMgr->GetKeysByString("Appender.");
179for (std::string const& appenderName : keys)
180CreateAppenderFromConfig(appenderName);
181}
182
183void Log::ReadLoggersFromConfig()
184{
185std::vector<std::string> keys = sConfigMgr->GetKeysByString("Logger.");
186for (std::string const& loggerName : keys)
187CreateLoggerFromConfig(loggerName);
188
189// Bad config configuration, creating default config
190if (loggers.find(LOGGER_ROOT) == loggers.end())
191{
192fprintf(stderr, "Wrong Loggers configuration. Review your Logger config section.\n"
193"Creating default loggers [root (Error), server (Info)] to console\n");
194
195Close(); // Clean any Logger or Appender created
196
197AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, {});
198appenders[appender->getId()].reset(appender);
199
200Logger* rootLogger = new Logger(LOGGER_ROOT, LOG_LEVEL_ERROR);
201rootLogger->addAppender(appender->getId(), appender);
202loggers[LOGGER_ROOT].reset(rootLogger);
203
204Logger* serverLogger = new Logger("server", LOG_LEVEL_INFO);
205serverLogger->addAppender(appender->getId(), appender);
206loggers["server"].reset(serverLogger);
207}
208}
209
210void Log::RegisterAppender(uint8 index, AppenderCreatorFn appenderCreateFn)
211{
212auto itr = appenderFactory.find(index);
213ASSERT(itr == appenderFactory.end());
214appenderFactory[index] = appenderCreateFn;
215}
216
217void Log::outMessage(std::string const& filter, LogLevel level, std::string&& message)
218{
219write(std::make_unique<LogMessage>(level, filter, std::move(message)));
220}
221
222void Log::outCommand(std::string&& message, std::string&& param1)
223{
224write(std::make_unique<LogMessage>(LOG_LEVEL_INFO, "commands.gm", std::move(message), std::move(param1)));
225}
226
227void Log::write(std::unique_ptr<LogMessage>&& msg) const
228{
229Logger const* logger = GetLoggerByType(msg->type);
230
231if (_ioContext)
232{
233std::shared_ptr<LogOperation> logOperation = std::make_shared<LogOperation>(logger, std::move(msg));
234Trinity::Asio::post(*_ioContext, Trinity::Asio::bind_executor(*_strand, [logOperation]() { logOperation->call(); }));
235}
236else
237logger->write(msg.get());
238}
239
240Logger const* Log::GetLoggerByType(std::string const& type) const
241{
242auto it = loggers.find(type);
243if (it != loggers.end())
244return it->second.get();
245
246if (type == LOGGER_ROOT)
247return nullptr;
248
249std::string parentLogger = LOGGER_ROOT;
250size_t found = type.find_last_of('.');
251if (found != std::string::npos)
252parentLogger = type.substr(0, found);
253
254return GetLoggerByType(parentLogger);
255}
256
257std::string Log::GetTimestampStr()
258{
259time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
260
261std::tm aTm;
262localtime_r(&tt, &aTm);
263
264// YYYY year
265// MM month (2 digits 01-12)
266// DD day (2 digits 01-31)
267// HH hour (2 digits 00-23)
268// MM minutes (2 digits 00-59)
269// SS seconds (2 digits 00-59)
270return Trinity::StringFormat("%04d-%02d-%02d_%02d-%02d-%02d",
271aTm.tm_year + 1900, aTm.tm_mon + 1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec);
272}
273
274bool Log::SetLogLevel(std::string const& name, int32 newLeveli, bool isLogger /* = true */)
275{
276if (newLeveli < 0)
277return false;
278
279LogLevel newLevel = LogLevel(newLeveli);
280
281if (isLogger)
282{
283auto it = loggers.begin();
284while (it != loggers.end() && it->second->getName() != name)
285++it;
286
287if (it == loggers.end())
288return false;
289
290it->second->setLogLevel(newLevel);
291
292if (newLevel != LOG_LEVEL_DISABLED && newLevel < lowestLogLevel)
293lowestLogLevel = newLevel;
294}
295else
296{
297Appender* appender = GetAppenderByName(name);
298if (!appender)
299return false;
300
301appender->setLogLevel(newLevel);
302}
303
304return true;
305}
306
307void Log::outCharDump(char const* str, uint32 accountId, uint64 guid, char const* name)
308{
309if (!str || !ShouldLog("entities.player.dump", LOG_LEVEL_INFO))
310return;
311
312std::ostringstream ss;
313ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name
314<< ")\n" << str << "\n== END DUMP ==\n";
315
316std::unique_ptr<LogMessage> msg(new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str()));
317std::ostringstream param;
318param << guid << '_' << name;
319
320msg->param1 = param.str();
321
322write(std::move(msg));
323}
324
325void Log::SetRealmId(uint32 id)
326{
327for (std::pair<uint8 const, std::unique_ptr<Appender>>& appender : appenders)
328appender.second->setRealmId(id);
329}
330
331void Log::Close()
332{
333loggers.clear();
334appenders.clear();
335}
336
337bool Log::ShouldLog(std::string const& type, LogLevel level) const
338{
339// TODO: Use cache to store "Type.sub1.sub2": "Type" equivalence, should
340// Speed up in cases where requesting "Type.sub1.sub2" but only configured
341// Logger "Type"
342
343// Don't even look for a logger if the LogLevel is lower than lowest log levels across all loggers
344if (level < lowestLogLevel)
345return false;
346
347Logger const* logger = GetLoggerByType(type);
348if (!logger)
349return false;
350
351LogLevel logLevel = logger->getLogLevel();
352return logLevel != LOG_LEVEL_DISABLED && logLevel <= level;
353}
354
355Log* Log::instance()
356{
357static Log instance;
358return &instance;
359}
360
361void Log::Initialize(Trinity::Asio::IoContext* ioContext)
362{
363if (ioContext)
364{
365_ioContext = ioContext;
366_strand = new Trinity::Asio::Strand(*ioContext);
367}
368
369LoadFromConfig();
370}
371
372void Log::SetSynchronous()
373{
374delete _strand;
375_strand = nullptr;
376_ioContext = nullptr;
377}
378
379void Log::LoadFromConfig()
380{
381Close();
382
383lowestLogLevel = LOG_LEVEL_FATAL;
384AppenderId = 0;
385m_logsDir = sConfigMgr->GetStringDefault("LogsDir", "");
386if (!m_logsDir.empty())
387if ((m_logsDir.at(m_logsDir.length() - 1) != '/') && (m_logsDir.at(m_logsDir.length() - 1) != '\\'))
388m_logsDir.push_back('/');
389
390ReadAppendersFromConfig();
391ReadLoggersFromConfig();
392}
393