X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=src%2Flog.cpp;h=54442c39b1669561a5fa9b6a686395d9416d4c0a;hb=79414aa3e5591fdaffa0956a08610a2228042941;hp=8ed1f7694eaff9aa909383396b9ebe95699e953c;hpb=093b1b47d9c874b5d7d17e720f1f68f00d7c000e;p=minetest.git diff --git a/src/log.cpp b/src/log.cpp index 8ed1f7694..54442c39b 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -19,175 +19,441 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" -#include -#include -#include -#include -#include "threads.h" +#include "threading/mutex_auto_lock.h" #include "debug.h" #include "gettime.h" #include "porting.h" +#include "settings.h" #include "config.h" +#include "exceptions.h" +#include "util/numeric.h" +#include "log.h" + +#include +#include +#include +#include +#include + +const int BUFFER_LENGTH = 256; + +class StringBuffer : public std::streambuf { +public: + StringBuffer() { + buffer_index = 0; + } + + int overflow(int c); + virtual void flush(const std::string &buf) = 0; + std::streamsize xsputn(const char *s, std::streamsize n); + void push_back(char c); + +private: + char buffer[BUFFER_LENGTH]; + int buffer_index; +}; + + +class LogBuffer : public StringBuffer { +public: + LogBuffer(Logger &logger, LogLevel lev) : + logger(logger), + level(lev) + {} + + void flush(const std::string &buffer); + +private: + Logger &logger; + LogLevel level; +}; + + +class RawLogBuffer : public StringBuffer { +public: + void flush(const std::string &buffer); +}; + +//// +//// Globals +//// + +Logger g_logger; + +StreamLogOutput stdout_output(std::cout); +StreamLogOutput stderr_output(std::cerr); +std::ostream null_stream(NULL); -std::list log_outputs[LMT_NUM_VALUES]; -std::map log_threadnames; -JMutex log_threadnamemutex; +RawLogBuffer raw_buf; -void log_add_output(ILogOutput *out, enum LogMessageLevel lev) +LogBuffer none_buf(g_logger, LL_NONE); +LogBuffer error_buf(g_logger, LL_ERROR); +LogBuffer warning_buf(g_logger, LL_WARNING); +LogBuffer action_buf(g_logger, LL_ACTION); +LogBuffer info_buf(g_logger, LL_INFO); +LogBuffer verbose_buf(g_logger, LL_VERBOSE); + +// Connection +std::ostream *dout_con_ptr = &null_stream; +std::ostream *derr_con_ptr = &verbosestream; + +// Server +std::ostream *dout_server_ptr = &infostream; +std::ostream *derr_server_ptr = &errorstream; + +#ifndef SERVER +// Client +std::ostream *dout_client_ptr = &infostream; +std::ostream *derr_client_ptr = &errorstream; +#endif + +std::ostream rawstream(&raw_buf); +std::ostream dstream(&none_buf); +std::ostream errorstream(&error_buf); +std::ostream warningstream(&warning_buf); +std::ostream actionstream(&action_buf); +std::ostream infostream(&info_buf); +std::ostream verbosestream(&verbose_buf); + +// Android +#ifdef __ANDROID__ + +static unsigned int g_level_to_android[] = { + ANDROID_LOG_INFO, // LL_NONE + //ANDROID_LOG_FATAL, + ANDROID_LOG_ERROR, // LL_ERROR + ANDROID_LOG_WARN, // LL_WARNING + ANDROID_LOG_WARN, // LL_ACTION + //ANDROID_LOG_INFO, + ANDROID_LOG_DEBUG, // LL_INFO + ANDROID_LOG_VERBOSE, // LL_VERBOSE +}; + +class AndroidSystemLogOutput : public ICombinedLogOutput { + public: + AndroidSystemLogOutput() + { + g_logger.addOutput(this); + } + ~AndroidSystemLogOutput() + { + g_logger.removeOutput(this); + } + void logRaw(LogLevel lev, const std::string &line) + { + STATIC_ASSERT(ARRLEN(g_level_to_android) == LL_MAX, + mismatch_between_android_and_internal_loglevels); + __android_log_print(g_level_to_android[lev], + PROJECT_NAME_C, "%s", line.c_str()); + } +}; + +AndroidSystemLogOutput g_android_log_output; + +#endif + +/////////////////////////////////////////////////////////////////////////////// + + +//// +//// Logger +//// + +LogLevel Logger::stringToLevel(const std::string &name) { - log_outputs[lev].push_back(out); + if (name == "none") + return LL_NONE; + else if (name == "error") + return LL_ERROR; + else if (name == "warning") + return LL_WARNING; + else if (name == "action") + return LL_ACTION; + else if (name == "info") + return LL_INFO; + else if (name == "verbose") + return LL_VERBOSE; + else + return LL_MAX; } -void log_add_output_maxlev(ILogOutput *out, enum LogMessageLevel lev) +void Logger::addOutput(ILogOutput *out) { - for(int i=0; i<=lev; i++) - log_outputs[i].push_back(out); + addOutputMaxLevel(out, (LogLevel)(LL_MAX - 1)); } -void log_add_output_all_levs(ILogOutput *out) +void Logger::addOutput(ILogOutput *out, LogLevel lev) { - for(int i=0; i::iterator it = - std::find(log_outputs[i].begin(), log_outputs[i].end(), out); - if(it != log_outputs[i].end()) - log_outputs[i].erase(it); + for (size_t i = 0; i < LL_MAX; i++) { + if (mask & LOGLEVEL_TO_MASKLEVEL(i)) + m_outputs[i].push_back(out); } } -void log_set_lev_silence(enum LogMessageLevel lev, bool silence) +void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev) { - log_threadnamemutex.Lock(); + assert(lev < LL_MAX); + for (size_t i = 0; i <= lev; i++) + m_outputs[i].push_back(out); +} + +LogLevelMask Logger::removeOutput(ILogOutput *out) +{ + LogLevelMask ret_mask = 0; + for (size_t i = 0; i < LL_MAX; i++) { + std::vector::iterator it; - for (std::list::iterator - it = log_outputs[lev].begin(); - it != log_outputs[lev].end(); - ++it) { - ILogOutput *out = *it; - out->silence = silence; + it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out); + if (it != m_outputs[i].end()) { + ret_mask |= LOGLEVEL_TO_MASKLEVEL(i); + m_outputs[i].erase(it); + } } + return ret_mask; +} - log_threadnamemutex.Unlock(); +void Logger::setLevelSilenced(LogLevel lev, bool silenced) +{ + m_silenced_levels[lev] = silenced; } -void log_register_thread(const std::string &name) +void Logger::registerThread(const std::string &name) { - threadid_t id = get_current_thread_id(); - log_threadnamemutex.Lock(); - log_threadnames[id] = name; - log_threadnamemutex.Unlock(); + std::thread::id id = std::this_thread::get_id(); + MutexAutoLock lock(m_mutex); + m_thread_names[id] = name; } -void log_deregister_thread() +void Logger::deregisterThread() { - threadid_t id = get_current_thread_id(); - log_threadnamemutex.Lock(); - log_threadnames.erase(id); - log_threadnamemutex.Unlock(); + std::thread::id id = std::this_thread::get_id(); + MutexAutoLock lock(m_mutex); + m_thread_names.erase(id); } -static std::string get_lev_string(enum LogMessageLevel lev) +const std::string Logger::getLevelLabel(LogLevel lev) { - switch(lev){ - case LMT_ERROR: - return "ERROR"; - case LMT_ACTION: - return "ACTION"; - case LMT_INFO: - return "INFO"; - case LMT_VERBOSE: - return "VERBOSE"; - case LMT_NUM_VALUES: - break; - } - return "(unknown level)"; + static const std::string names[] = { + "", + "ERROR", + "WARNING", + "ACTION", + "INFO", + "VERBOSE", + }; + assert(lev < LL_MAX && lev >= 0); + STATIC_ASSERT(ARRLEN(names) == LL_MAX, + mismatch_between_loglevel_names_and_enum); + return names[lev]; } -void log_printline(enum LogMessageLevel lev, const std::string &text) +LogColor Logger::color_mode = LOG_COLOR_AUTO; + +const std::string Logger::getThreadName() { - log_threadnamemutex.Lock(); - std::string threadname = "(unknown thread)"; - std::map::const_iterator i; - i = log_threadnames.find(get_current_thread_id()); - if(i != log_threadnames.end()) - threadname = i->second; - std::string levelname = get_lev_string(lev); + std::map::const_iterator it; + + std::thread::id id = std::this_thread::get_id(); + it = m_thread_names.find(id); + if (it != m_thread_names.end()) + return it->second; + + std::ostringstream os; + os << "#0x" << std::hex << id; + return os.str(); +} + +void Logger::log(LogLevel lev, const std::string &text) +{ + if (m_silenced_levels[lev]) + return; + + const std::string thread_name = getThreadName(); + const std::string label = getLevelLabel(lev); + const std::string timestamp = getTimestamp(); std::ostringstream os(std::ios_base::binary); - os<::iterator i = log_outputs[lev].begin(); - i != log_outputs[lev].end(); i++){ - ILogOutput *out = *i; - if (out->silence) - continue; - - out->printLog(os.str()); - out->printLog(os.str(), lev); - out->printLog(lev, text); - } - log_threadnamemutex.Unlock(); + os << timestamp << ": " << label << "[" << thread_name << "]: " << text; + + logToOutputs(lev, os.str(), timestamp, thread_name, text); } -class Logbuf : public std::streambuf +void Logger::logRaw(LogLevel lev, const std::string &text) { -public: - Logbuf(enum LogMessageLevel lev): - m_lev(lev) - { + if (m_silenced_levels[lev]) + return; + + logToOutputsRaw(lev, text); +} + +void Logger::logToOutputsRaw(LogLevel lev, const std::string &line) +{ + MutexAutoLock lock(m_mutex); + for (size_t i = 0; i != m_outputs[lev].size(); i++) + m_outputs[lev][i]->logRaw(lev, line); +} + +void Logger::logToOutputs(LogLevel lev, const std::string &combined, + const std::string &time, const std::string &thread_name, + const std::string &payload_text) +{ + MutexAutoLock lock(m_mutex); + for (size_t i = 0; i != m_outputs[lev].size(); i++) + m_outputs[lev][i]->log(lev, combined, time, thread_name, payload_text); +} + + +//// +//// *LogOutput methods +//// + +void FileLogOutput::setFile(const std::string &filename, s64 file_size_max) +{ + // Only move debug.txt if there is a valid maximum file size + bool is_too_large = false; + if (file_size_max > 0) { + std::ifstream ifile(filename, std::ios::binary | std::ios::ate); + is_too_large = ifile.tellg() > file_size_max; + ifile.close(); } - ~Logbuf() - { + if (is_too_large) { + std::string filename_secondary = filename + ".1"; + actionstream << "The log file grew too big; it is moved to " << + filename_secondary << std::endl; + remove(filename_secondary.c_str()); + rename(filename.c_str(), filename_secondary.c_str()); } + m_stream.open(filename, std::ios::app | std::ios::ate); + + if (!m_stream.good()) + throw FileNotGoodException("Failed to open log file " + + filename + ": " + strerror(errno)); + m_stream << "\n\n" + "-------------" << std::endl << + " Separator" << std::endl << + "-------------\n" << std::endl; +} - int overflow(int c) - { - bufchar(c); - return c; +void StreamLogOutput::logRaw(LogLevel lev, const std::string &line) +{ + bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) || + (Logger::color_mode == LOG_COLOR_AUTO && is_tty); + if (colored_message) { + switch (lev) { + case LL_ERROR: + // error is red + m_stream << "\033[91m"; + break; + case LL_WARNING: + // warning is yellow + m_stream << "\033[93m"; + break; + case LL_INFO: + // info is a bit dark + m_stream << "\033[37m"; + break; + case LL_VERBOSE: + // verbose is darker than info + m_stream << "\033[2m"; + break; + default: + // action is white + colored_message = false; + } } - std::streamsize xsputn(const char *s, std::streamsize n) - { - for(int i=0; iget("chat_log_level"); + LogLevel log_level = Logger::stringToLevel(conf_loglev); + if (log_level == LL_MAX) { + warningstream << "Supplied unrecognized chat_log_level; " + "showing none." << std::endl; + log_level = LL_NONE; } - void bufchar(char c) - { - if(c == '\n' || c == '\r'){ - if(m_buf != "") - printbuf(); - m_buf = ""; - return; + m_logger.removeOutput(this); + m_logger.addOutputMaxLevel(this, log_level); +} + +void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line) +{ + std::string color; + + if (!g_settings->getBool("disable_escape_sequences")) { + switch (lev) { + case LL_ERROR: // red + color = "\x1b(c@#F00)"; + break; + case LL_WARNING: // yellow + color = "\x1b(c@#EE0)"; + break; + case LL_INFO: // grey + color = "\x1b(c@#BBB)"; + break; + case LL_VERBOSE: // dark grey + color = "\x1b(c@#888)"; + break; + default: break; } - m_buf += c; } -private: - enum LogMessageLevel m_lev; - std::string m_buf; -}; + m_buffer.push(color.append(line)); +} + +//// +//// *Buffer methods +//// + +int StringBuffer::overflow(int c) +{ + push_back(c); + return c; +} + + +std::streamsize StringBuffer::xsputn(const char *s, std::streamsize n) +{ + for (int i = 0; i < n; ++i) + push_back(s[i]); + return n; +} + +void StringBuffer::push_back(char c) +{ + if (c == '\n' || c == '\r') { + if (buffer_index) + flush(std::string(buffer, buffer_index)); + buffer_index = 0; + } else { + buffer[buffer_index++] = c; + if (buffer_index >= BUFFER_LENGTH) { + flush(std::string(buffer, buffer_index)); + buffer_index = 0; + } + } +} -Logbuf errorbuf(LMT_ERROR); -Logbuf actionbuf(LMT_ACTION); -Logbuf infobuf(LMT_INFO); -Logbuf verbosebuf(LMT_VERBOSE); -std::ostream errorstream(&errorbuf); -std::ostream actionstream(&actionbuf); -std::ostream infostream(&infobuf); -std::ostream verbosestream(&verbosebuf); -bool log_trace_level_enabled = false; +void LogBuffer::flush(const std::string &buffer) +{ + logger.log(level, buffer); +} +void RawLogBuffer::flush(const std::string &buffer) +{ + g_logger.logRaw(LL_NONE, buffer); +}