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