3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "threading/mutex_auto_lock.h"
28 #include "exceptions.h"
29 #include "util/numeric.h"
38 class LevelTarget : public LogTarget {
40 LevelTarget(Logger &logger, LogLevel level, bool raw = false) :
46 virtual bool hasOutput() override {
47 return m_logger.hasOutput(m_level);
50 virtual void log(const std::string &buf) override {
52 m_logger.log(m_level, buf);
54 m_logger.logRaw(m_level, buf);
71 AndroidLogOutput stdout_output;
72 AndroidLogOutput stderr_output;
74 StreamLogOutput stdout_output(std::cout);
75 StreamLogOutput stderr_output(std::cerr);
78 LevelTarget none_target_raw(g_logger, LL_NONE, true);
79 LevelTarget none_target(g_logger, LL_NONE);
80 LevelTarget error_target(g_logger, LL_ERROR);
81 LevelTarget warning_target(g_logger, LL_WARNING);
82 LevelTarget action_target(g_logger, LL_ACTION);
83 LevelTarget info_target(g_logger, LL_INFO);
84 LevelTarget verbose_target(g_logger, LL_VERBOSE);
85 LevelTarget trace_target(g_logger, LL_TRACE);
87 thread_local LogStream dstream(none_target);
88 thread_local LogStream rawstream(none_target_raw);
89 thread_local LogStream errorstream(error_target);
90 thread_local LogStream warningstream(warning_target);
91 thread_local LogStream actionstream(action_target);
92 thread_local LogStream infostream(info_target);
93 thread_local LogStream verbosestream(verbose_target);
94 thread_local LogStream tracestream(trace_target);
95 thread_local LogStream derr_con(verbose_target);
96 thread_local LogStream dout_con(trace_target);
101 static unsigned int g_level_to_android[] = {
102 ANDROID_LOG_INFO, // LL_NONE
104 ANDROID_LOG_ERROR, // LL_ERROR
105 ANDROID_LOG_WARN, // LL_WARNING
106 ANDROID_LOG_WARN, // LL_ACTION
108 ANDROID_LOG_DEBUG, // LL_INFO
109 ANDROID_LOG_VERBOSE, // LL_VERBOSE
110 ANDROID_LOG_VERBOSE, // LL_TRACE
113 void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line) {
114 STATIC_ASSERT(ARRLEN(g_level_to_android) == LL_MAX,
115 mismatch_between_android_and_internal_loglevels);
116 __android_log_print(g_level_to_android[lev],
117 PROJECT_NAME_C, "%s", line.c_str());
121 ///////////////////////////////////////////////////////////////////////////////
128 LogLevel Logger::stringToLevel(const std::string &name)
132 else if (name == "error")
134 else if (name == "warning")
136 else if (name == "action")
138 else if (name == "info")
140 else if (name == "verbose")
142 else if (name == "trace")
148 void Logger::addOutput(ILogOutput *out)
150 addOutputMaxLevel(out, (LogLevel)(LL_MAX - 1));
153 void Logger::addOutput(ILogOutput *out, LogLevel lev)
155 m_outputs[lev].push_back(out);
156 m_has_outputs[lev] = true;
159 void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
161 for (size_t i = 0; i < LL_MAX; i++) {
162 if (mask & LOGLEVEL_TO_MASKLEVEL(i)) {
163 m_outputs[i].push_back(out);
164 m_has_outputs[i] = true;
169 void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
171 assert(lev < LL_MAX);
172 for (size_t i = 0; i <= lev; i++) {
173 m_outputs[i].push_back(out);
174 m_has_outputs[i] = true;
178 LogLevelMask Logger::removeOutput(ILogOutput *out)
180 LogLevelMask ret_mask = 0;
181 for (size_t i = 0; i < LL_MAX; i++) {
182 std::vector<ILogOutput *>::iterator it;
184 it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out);
185 if (it != m_outputs[i].end()) {
186 ret_mask |= LOGLEVEL_TO_MASKLEVEL(i);
187 m_outputs[i].erase(it);
188 m_has_outputs[i] = !m_outputs[i].empty();
194 void Logger::setLevelSilenced(LogLevel lev, bool silenced)
196 m_silenced_levels[lev] = silenced;
199 void Logger::registerThread(const std::string &name)
201 std::thread::id id = std::this_thread::get_id();
202 MutexAutoLock lock(m_mutex);
203 m_thread_names[id] = name;
206 void Logger::deregisterThread()
208 std::thread::id id = std::this_thread::get_id();
209 MutexAutoLock lock(m_mutex);
210 m_thread_names.erase(id);
213 const std::string Logger::getLevelLabel(LogLevel lev)
215 static const std::string names[] = {
224 assert(lev < LL_MAX && lev >= 0);
225 STATIC_ASSERT(ARRLEN(names) == LL_MAX,
226 mismatch_between_loglevel_names_and_enum);
230 LogColor Logger::color_mode = LOG_COLOR_AUTO;
232 const std::string Logger::getThreadName()
234 std::map<std::thread::id, std::string>::const_iterator it;
236 std::thread::id id = std::this_thread::get_id();
237 it = m_thread_names.find(id);
238 if (it != m_thread_names.end())
241 std::ostringstream os;
242 os << "#0x" << std::hex << id;
246 void Logger::log(LogLevel lev, const std::string &text)
248 if (m_silenced_levels[lev])
251 const std::string thread_name = getThreadName();
252 const std::string label = getLevelLabel(lev);
253 const std::string timestamp = getTimestamp();
254 std::ostringstream os(std::ios_base::binary);
255 os << timestamp << ": " << label << "[" << thread_name << "]: " << text;
257 logToOutputs(lev, os.str(), timestamp, thread_name, text);
260 void Logger::logRaw(LogLevel lev, const std::string &text)
262 if (m_silenced_levels[lev])
265 logToOutputsRaw(lev, text);
268 void Logger::logToOutputsRaw(LogLevel lev, const std::string &line)
270 MutexAutoLock lock(m_mutex);
271 for (size_t i = 0; i != m_outputs[lev].size(); i++)
272 m_outputs[lev][i]->logRaw(lev, line);
275 void Logger::logToOutputs(LogLevel lev, const std::string &combined,
276 const std::string &time, const std::string &thread_name,
277 const std::string &payload_text)
279 MutexAutoLock lock(m_mutex);
280 for (size_t i = 0; i != m_outputs[lev].size(); i++)
281 m_outputs[lev][i]->log(lev, combined, time, thread_name, payload_text);
285 //// *LogOutput methods
288 void FileLogOutput::setFile(const std::string &filename, s64 file_size_max)
290 // Only move debug.txt if there is a valid maximum file size
291 bool is_too_large = false;
292 if (file_size_max > 0) {
293 std::ifstream ifile(filename, std::ios::binary | std::ios::ate);
294 is_too_large = ifile.tellg() > file_size_max;
299 std::string filename_secondary = filename + ".1";
300 actionstream << "The log file grew too big; it is moved to " <<
301 filename_secondary << std::endl;
302 remove(filename_secondary.c_str());
303 rename(filename.c_str(), filename_secondary.c_str());
305 m_stream.open(filename, std::ios::app | std::ios::ate);
307 if (!m_stream.good())
308 throw FileNotGoodException("Failed to open log file " +
309 filename + ": " + strerror(errno));
311 "-------------" << std::endl <<
312 " Separator" << std::endl <<
313 "-------------\n" << std::endl;
316 void StreamLogOutput::logRaw(LogLevel lev, const std::string &line)
318 bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) ||
319 (Logger::color_mode == LOG_COLOR_AUTO && is_tty);
320 if (colored_message) {
324 m_stream << "\033[91m";
328 m_stream << "\033[93m";
331 // info is a bit dark
332 m_stream << "\033[37m";
336 // verbose is darker than info
337 m_stream << "\033[2m";
341 colored_message = false;
345 m_stream << line << std::endl;
347 if (colored_message) {
348 // reset to white color
349 m_stream << "\033[0m";
353 void LogOutputBuffer::updateLogLevel()
355 const std::string &conf_loglev = g_settings->get("chat_log_level");
356 LogLevel log_level = Logger::stringToLevel(conf_loglev);
357 if (log_level == LL_MAX) {
358 warningstream << "Supplied unrecognized chat_log_level; "
359 "showing none." << std::endl;
363 m_logger.removeOutput(this);
364 m_logger.addOutputMaxLevel(this, log_level);
367 void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line)
371 if (!g_settings->getBool("disable_escape_sequences")) {
373 case LL_ERROR: // red
374 color = "\x1b(c@#F00)";
376 case LL_WARNING: // yellow
377 color = "\x1b(c@#EE0)";
379 case LL_INFO: // grey
380 color = "\x1b(c@#BBB)";
382 case LL_VERBOSE: // dark grey
384 color = "\x1b(c@#888)";
390 m_buffer.push(color.append(line));