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