TrinityCore
Log.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS 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 "Config.h"
22#include "Duration.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 <sstream>
31
32Log::Log() : AppenderId(0), lowestLogLevel(LOG_LEVEL_FATAL), _ioContext(nullptr), _strand(nullptr)
33{
35 RegisterAppender<AppenderConsole>();
36 RegisterAppender<AppenderFile>();
37}
38
40{
41 delete _strand;
42 Close();
43}
44
46{
47 return AppenderId++;
48}
49
50Appender* Log::GetAppenderByName(std::string_view name)
51{
52 auto it = appenders.begin();
53 while (it != appenders.end() && it->second && it->second->getName() != name)
54 ++it;
55
56 return it == appenders.end() ? nullptr : it->second.get();
57}
58
59void Log::CreateAppenderFromConfigLine(std::string const& appenderName, std::string const& options)
60{
61 if (appenderName.empty())
62 return;
63
64 // Format = type, level, flags, optional1, optional2
65 // if type = File. optional1 = file and option2 = mode
66 // if type = Console. optional1 = Color
67
68 std::vector<std::string_view> tokens = Trinity::Tokenize(options, ',', true);
69
70 size_t const size = tokens.size();
71 std::string name = appenderName.substr(9);
72
73 if (size < 2)
74 {
75 fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong configuration for appender %s. Config line: %s\n", name.c_str(), options.c_str());
76 return;
77 }
78
80 AppenderType type = AppenderType(Trinity::StringTo<uint8>(tokens[0]).value_or(APPENDER_INVALID));
81 LogLevel level = LogLevel(Trinity::StringTo<uint8>(tokens[1]).value_or(LOG_LEVEL_INVALID));
82
83 auto factoryFunction = appenderFactory.find(type);
84 if (factoryFunction == appenderFactory.end())
85 {
86 fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown type '%s' for appender %s\n", std::string(tokens[0]).c_str(), name.c_str());
87 return;
88 }
89
90 if (level > NUM_ENABLED_LOG_LEVELS)
91 {
92 fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level '%s' for appender %s\n", std::string(tokens[1]).c_str(), name.c_str());
93 return;
94 }
95
96 if (size > 2)
97 {
98 if (Optional<uint8> flagsVal = Trinity::StringTo<uint8>(tokens[2]))
99 flags = AppenderFlags(*flagsVal);
100 else
101 {
102 fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown flags '%s' for appender %s\n", std::string(tokens[2]).c_str(), name.c_str());
103 return;
104 }
105 }
106
107 try
108 {
109 Appender* appender = factoryFunction->second(NextAppenderId(), name, level, flags, tokens);
110 appenders[appender->getId()].reset(appender);
111 }
112 catch (InvalidAppenderArgsException const& iaae)
113 {
114 fprintf(stderr, "%s\n", iaae.what());
115 }
116}
117
118void Log::CreateAppenderFromConfig(std::string const& appenderName)
119{
120 CreateAppenderFromConfigLine(appenderName, sConfigMgr->GetStringDefault(appenderName, ""));
121}
122
123void Log::CreateLoggerFromConfigLine(std::string const& loggerName, std::string const& options)
124{
125 if (loggerName.empty())
126 return;
127
129
130 std::string name = loggerName.substr(7);
131
132 if (options.empty())
133 {
134 fprintf(stderr, "Log::CreateLoggerFromConfig: Missing config option Logger.%s\n", name.c_str());
135 return;
136 }
137
138 std::vector<std::string_view> tokens = Trinity::Tokenize(options, ',', true);
139
140 if (tokens.size() != 2)
141 {
142 fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong config option Logger.%s=%s\n", name.c_str(), options.c_str());
143 return;
144 }
145
146 if (loggers.find(name) != loggers.end())
147 {
148 fprintf(stderr, "Error while configuring Logger %s. Already defined\n", name.c_str());
149 return;
150 }
151
152 level = LogLevel(Trinity::StringTo<uint8>(tokens[0]).value_or(LOG_LEVEL_INVALID));
153 if (level > NUM_ENABLED_LOG_LEVELS)
154 {
155 fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong Log Level '%s' for logger %s\n", std::string(tokens[0]).c_str(), name.c_str());
156 return;
157 }
158
159 if (level < lowestLogLevel)
160 lowestLogLevel = level;
161
162 Logger* logger = new Logger(name, level);
163 loggers[logger->getName()].reset(logger);
164 //fprintf(stdout, "Log::CreateLoggerFromConfig: Created Logger %s, Level %u\n", name.c_str(), level);
165
166 for (std::string_view appenderName : Trinity::Tokenize(tokens[1], ' ', false))
167 {
168 if (Appender* appender = GetAppenderByName(appenderName))
169 {
170 logger->addAppender(appender->getId(), appender);
171 //fprintf(stdout, "Log::CreateLoggerFromConfig: Added Appender %s to Logger %s\n", appender->getName().c_str(), name.c_str());
172 }
173 else
174 fprintf(stderr, "Error while configuring Appender %s in Logger %s. Appender does not exist\n", std::string(appenderName).c_str(), name.c_str());
175 }
176}
177
178void Log::CreateLoggerFromConfig(std::string const& loggerName)
179{
180 CreateLoggerFromConfigLine(loggerName, sConfigMgr->GetStringDefault(loggerName, ""));
181}
182
184{
185 std::vector<std::string> keys = sConfigMgr->GetKeysByString("Appender.");
186 for (std::string const& appenderName : keys)
187 CreateAppenderFromConfig(appenderName);
188}
189
191{
192 std::vector<std::string> keys = sConfigMgr->GetKeysByString("Logger.");
193 for (std::string const& loggerName : keys)
194 CreateLoggerFromConfig(loggerName);
195
196 // Bad config configuration, creating default config
197 if (loggers.find(LOGGER_ROOT) == loggers.end())
198 {
199 fprintf(stderr, "Wrong Loggers configuration. Review your Logger config section.\n"
200 "Creating default loggers [root (Error), server (Info)] to console\n");
201
202 Close(); // Clean any Logger or Appender created
203
205 appenders[appender->getId()].reset(appender);
206
207 Logger* rootLogger = new Logger(LOGGER_ROOT, LOG_LEVEL_ERROR);
208 rootLogger->addAppender(appender->getId(), appender);
209 loggers[rootLogger->getName()].reset(rootLogger);
210
211 Logger* serverLogger = new Logger("server", LOG_LEVEL_INFO);
212 serverLogger->addAppender(appender->getId(), appender);
213 loggers[serverLogger->getName()].reset(serverLogger);
214 }
215}
216
217void Log::RegisterAppender(uint8 index, AppenderCreatorFn appenderCreateFn)
218{
219 auto itr = appenderFactory.find(index);
220 ASSERT(itr == appenderFactory.end());
221 appenderFactory[index] = appenderCreateFn;
222}
223
224void Log::OutMessageImpl(std::string_view filter, LogLevel level, Trinity::FormatStringView messageFormat, Trinity::FormatArgs messageFormatArgs)
225{
226 write(std::make_unique<LogMessage>(level, filter, Trinity::StringVFormat(messageFormat, messageFormatArgs)));
227}
228
229void Log::OutCommandImpl(uint32 account, Trinity::FormatStringView messageFormat, Trinity::FormatArgs messageFormatArgs)
230{
231 write(std::make_unique<LogMessage>(LOG_LEVEL_INFO, "commands.gm", Trinity::StringVFormat(messageFormat, messageFormatArgs), Trinity::ToString(account)));
232}
233
234void Log::write(std::unique_ptr<LogMessage> msg) const
235{
236 Logger const* logger = GetLoggerByType(msg->type);
237
238 if (_ioContext)
239 {
240 std::shared_ptr<LogOperation> logOperation = std::make_shared<LogOperation>(logger, std::move(msg));
241 Trinity::Asio::post(*_strand, [logOperation]() { logOperation->call(); });
242 }
243 else
244 logger->write(msg.get());
245}
246
247Logger const* Log::GetLoggerByType(std::string_view type) const
248{
249 auto it = loggers.find(type);
250 if (it != loggers.end())
251 return it->second.get();
252
253 if (type == LOGGER_ROOT)
254 return nullptr;
255
256 std::string_view parentLogger = LOGGER_ROOT;
257 size_t found = type.find_last_of('.');
258 if (found != std::string::npos)
259 parentLogger = type.substr(0, found);
260
261 return GetLoggerByType(parentLogger);
262}
263
265{
266 time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
267
268 std::tm aTm;
269 localtime_r(&tt, &aTm);
270
271 // YYYY year
272 // MM month (2 digits 01-12)
273 // DD day (2 digits 01-31)
274 // HH hour (2 digits 00-23)
275 // MM minutes (2 digits 00-59)
276 // SS seconds (2 digits 00-59)
277 try
278 {
279 return Trinity::StringFormat("{:04}-{:02}-{:02}_{:02}-{:02}-{:02}",
280 aTm.tm_year + 1900, aTm.tm_mon + 1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec);
281 }
282 catch (std::exception const& ex)
283 {
284 fprintf(stderr, "Failed to initialize timestamp part of log filename! %s", ex.what());
285 fflush(stderr);
286 ABORT();
287 }
288}
289
290bool Log::SetLogLevel(std::string const& name, int32 newLeveli, bool isLogger /* = true */)
291{
292 if (newLeveli < 0)
293 return false;
294
295 LogLevel newLevel = LogLevel(newLeveli);
296
297 if (isLogger)
298 {
299 auto it = loggers.begin();
300 while (it != loggers.end() && it->second->getName() != name)
301 ++it;
302
303 if (it == loggers.end())
304 return false;
305
306 it->second->setLogLevel(newLevel);
307
308 if (newLevel != LOG_LEVEL_DISABLED && newLevel < lowestLogLevel)
309 lowestLogLevel = newLevel;
310 }
311 else
312 {
313 Appender* appender = GetAppenderByName(name);
314 if (!appender)
315 return false;
316
317 appender->setLogLevel(newLevel);
318 }
319
320 return true;
321}
322
323void Log::OutCharDump(char const* str, uint32 accountId, uint64 guid, char const* name)
324{
325 if (!str || !ShouldLog("entities.player.dump", LOG_LEVEL_INFO))
326 return;
327
328 std::ostringstream ss;
329 ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name
330 << ")\n" << str << "\n== END DUMP ==\n";
331
332 std::unique_ptr<LogMessage> msg(new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str()));
333 std::ostringstream param;
334 param << guid << '_' << name;
335
336 msg->param1 = param.str();
337
338 write(std::move(msg));
339}
340
342{
343 for (std::pair<uint8 const, std::unique_ptr<Appender>>& appender : appenders)
344 appender.second->setRealmId(id);
345}
346
348{
349 loggers.clear();
350 appenders.clear();
351}
352
353bool Log::ShouldLog(std::string_view type, LogLevel level) const
354{
355 // TODO: Use cache to store "Type.sub1.sub2": "Type" equivalence, should
356 // Speed up in cases where requesting "Type.sub1.sub2" but only configured
357 // Logger "Type"
358
359 // Don't even look for a logger if the LogLevel is lower than lowest log levels across all loggers
360 if (level < lowestLogLevel)
361 return false;
362
363 Logger const* logger = GetLoggerByType(type);
364 if (!logger)
365 return false;
366
367 LogLevel logLevel = logger->getLogLevel();
368 return logLevel != LOG_LEVEL_DISABLED && logLevel <= level;
369}
370
372{
373 static Log instance;
374 return &instance;
375}
376
378{
379 if (ioContext)
380 {
381 _ioContext = ioContext;
382 _strand = new Trinity::Asio::Strand(*ioContext);
383 }
384
386}
387
389{
390 delete _strand;
391 _strand = nullptr;
392 _ioContext = nullptr;
393}
394
396{
397 Close();
398
400 AppenderId = 0;
401 m_logsDir = sConfigMgr->GetStringDefault("LogsDir", "");
402 if (!m_logsDir.empty())
403 if ((m_logsDir.at(m_logsDir.length() - 1) != '/') && (m_logsDir.at(m_logsDir.length() - 1) != '\\'))
404 m_logsDir.push_back('/');
405
408}
#define sConfigMgr
Definition: Config.h:61
uint8_t uint8
Definition: Define.h:144
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint32_t uint32
Definition: Define.h:142
uint16 flags
Definition: DisableMgr.cpp:49
#define ABORT
Definition: Errors.h:74
#define ASSERT
Definition: Errors.h:68
AppenderFlags
Definition: LogCommon.h:50
@ APPENDER_FLAGS_NONE
Definition: LogCommon.h:51
AppenderType
Definition: LogCommon.h:40
@ APPENDER_INVALID
Definition: LogCommon.h:46
LogLevel
Definition: LogCommon.h:25
@ NUM_ENABLED_LOG_LEVELS
Definition: LogCommon.h:34
@ LOG_LEVEL_INVALID
Definition: LogCommon.h:35
@ LOG_LEVEL_DEBUG
Definition: LogCommon.h:28
@ LOG_LEVEL_ERROR
Definition: LogCommon.h:31
@ LOG_LEVEL_FATAL
Definition: LogCommon.h:32
@ LOG_LEVEL_DISABLED
Definition: LogCommon.h:26
@ LOG_LEVEL_INFO
Definition: LogCommon.h:29
Appender *(* AppenderCreatorFn)(uint8 id, std::string const &name, LogLevel level, AppenderFlags flags, std::vector< std::string_view > const &extraArgs)
Definition: Log.h:43
#define LOGGER_ROOT
Definition: Log.h:41
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
uint8 getId() const
Definition: Appender.cpp:28
void setLogLevel(LogLevel)
Definition: Appender.cpp:48
Definition: Log.h:52
void OutMessageImpl(std::string_view filter, LogLevel level, Trinity::FormatStringView messageFormat, Trinity::FormatArgs messageFormatArgs)
Definition: Log.cpp:224
std::unordered_map< std::string_view, std::unique_ptr< Logger > > loggers
Definition: Log.h:119
Trinity::Asio::Strand * _strand
Definition: Log.h:127
~Log()
Definition: Log.cpp:39
uint8 NextAppenderId()
Definition: Log.cpp:45
void write(std::unique_ptr< LogMessage > msg) const
Definition: Log.cpp:234
bool SetLogLevel(std::string const &name, int32 level, bool isLogger=true)
Definition: Log.cpp:290
void CreateAppenderFromConfig(std::string const &name)
Definition: Log.cpp:118
void OutCharDump(char const *str, uint32 account_id, uint64 guid, char const *name)
Definition: Log.cpp:323
void SetRealmId(uint32 id)
Definition: Log.cpp:341
void OutCommandImpl(uint32 account, Trinity::FormatStringView messageFormat, Trinity::FormatArgs messageFormatArgs)
Definition: Log.cpp:229
std::unordered_map< uint8, std::unique_ptr< Appender > > appenders
Definition: Log.h:118
void CreateAppenderFromConfigLine(std::string const &name, std::string const &options)
Definition: Log.cpp:59
Logger const * GetLoggerByType(std::string_view type) const
Definition: Log.cpp:247
static Log * instance()
Definition: Log.cpp:371
void LoadFromConfig()
Definition: Log.cpp:395
std::string m_logsTimestamp
Definition: Log.h:124
Trinity::Asio::IoContext * _ioContext
Definition: Log.h:126
void CreateLoggerFromConfigLine(std::string const &name, std::string const &options)
Definition: Log.cpp:123
void Initialize(Trinity::Asio::IoContext *ioContext)
Definition: Log.cpp:377
void ReadAppendersFromConfig()
Definition: Log.cpp:183
uint8 AppenderId
Definition: Log.h:120
static std::string GetTimestampStr()
Definition: Log.cpp:264
void SetSynchronous()
Definition: Log.cpp:388
void RegisterAppender()
Definition: Log.h:91
std::string m_logsDir
Definition: Log.h:123
bool ShouldLog(std::string_view type, LogLevel level) const
Definition: Log.cpp:353
void Close()
Definition: Log.cpp:347
void CreateLoggerFromConfig(std::string const &name)
Definition: Log.cpp:178
Appender * GetAppenderByName(std::string_view name)
Definition: Log.cpp:50
std::unordered_map< uint8, AppenderCreatorFn > appenderFactory
Definition: Log.h:117
LogLevel lowestLogLevel
Definition: Log.h:121
Log()
Definition: Log.cpp:32
void ReadLoggersFromConfig()
Definition: Log.cpp:190
Definition: Logger.h:30
void write(LogMessage *message) const
Definition: Logger.cpp:49
void addAppender(uint8 type, Appender *appender)
Definition: Logger.cpp:34
LogLevel getLogLevel() const
Definition: Logger.cpp:29
std::string const & getName() const
Definition: Logger.cpp:24
decltype(auto) post(boost::asio::io_context &ioContext, T &&t)
Definition: IoContext.h:54
std::string ToString(Type &&val, Params &&... params)
fmt::format_args FormatArgs
Definition: StringFormat.h:31
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
std::string StringVFormat(FormatStringView fmt, FormatArgs args)
Definition: StringFormat.h:63
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
fmt::string_view FormatStringView
Definition: StringFormat.h:29
constexpr std::size_t size()
Definition: UpdateField.h:796