Merge branch 'buffer-log-messages'
This commit is contained in:
commit
eea6196231
@ -594,6 +594,7 @@ void cmdLineHelp()
|
||||
" -h, --help Show this help.\n"
|
||||
" --log=N Set the verbosity to a value between\n"
|
||||
" 0 (Debug) and 5 (Only Fatal messages)\n"
|
||||
" --logbuffer=N Buffers up to N lines log lines before writing.\n"
|
||||
" --root=DIR Path to add to the list of STK root directories.\n"
|
||||
" You can specify more than one by separating them\n"
|
||||
" with colons (:).\n"
|
||||
@ -664,6 +665,8 @@ int handleCmdLineOutputModifier()
|
||||
int n;
|
||||
if(CommandLine::has("--log", &n))
|
||||
Log::setLogLevel(n);
|
||||
if (CommandLine::has("--logbuffer", &n))
|
||||
Log::setBufferSize(n);
|
||||
|
||||
if(CommandLine::has("--log=nocolor"))
|
||||
{
|
||||
@ -1807,8 +1810,10 @@ int main(int argc, char *argv[] )
|
||||
} // try
|
||||
catch (std::exception &e)
|
||||
{
|
||||
Log::flushBuffers();
|
||||
Log::error("main", "Exception caught : %s.",e.what());
|
||||
Log::error("main", "Aborting SuperTuxKart.");
|
||||
Log::flushBuffers();
|
||||
}
|
||||
|
||||
/* Program closing...*/
|
||||
@ -1832,6 +1837,8 @@ int main(int argc, char *argv[] )
|
||||
MemoryLeaks::checkForLeaks();
|
||||
#endif
|
||||
|
||||
Log::flushBuffers();
|
||||
|
||||
#ifndef WIN32
|
||||
if (user_config) //close logfiles
|
||||
{
|
||||
|
@ -36,6 +36,8 @@ Log::LogLevel Log::m_min_log_level = Log::LL_VERBOSE;
|
||||
bool Log::m_no_colors = false;
|
||||
FILE* Log::m_file_stdout = NULL;
|
||||
std::string Log::m_prefix = "";
|
||||
size_t Log::m_buffer_size = 1;
|
||||
Synchronised<std::vector<struct Log::LineInfo> > Log::m_line_buffer;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Selects background/foreground colors for the message depending on
|
||||
@ -107,25 +109,22 @@ void Log::setTerminalColor(LogLevel level)
|
||||
*/
|
||||
void Log::resetTerminalColor()
|
||||
{
|
||||
if(m_no_colors)
|
||||
{
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_no_colors) return;
|
||||
|
||||
#ifdef WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
/*TERM_BLACK*/0 << 4 | /*TERM_LIGHTGRAY*/7);
|
||||
printf("\n");
|
||||
#else
|
||||
printf("%c[0;;m\n", 0x1B);
|
||||
#endif
|
||||
} // resetTerminalColor
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This actually prints the log message. If log messages are not redirected
|
||||
* to a file, it tries to select a terminal colour.
|
||||
/** This actually creates a log message. If the messages are to be buffered,
|
||||
* it will be appended to the output buffer. If the buffer is full, it will
|
||||
* be flushed. If the message is not to be buffered, it will be immediately
|
||||
* written using writeLine().
|
||||
|
||||
* \param level Log level of the message to print.
|
||||
* \param format A printf-like format string.
|
||||
* \param va_list The values to be printed for the format.
|
||||
@ -137,6 +136,63 @@ void Log::printMessage(int level, const char *component, const char *format,
|
||||
|
||||
if (level < m_min_log_level) return;
|
||||
|
||||
static const char *names[] = { "debug", "verbose ", "info ",
|
||||
"warn ", "error ", "fatal " };
|
||||
const int MAX_LENGTH = 2048;
|
||||
char line[MAX_LENGTH + 1];
|
||||
int index = 0;
|
||||
if (!m_prefix.empty())
|
||||
index += snprintf(line+index, MAX_LENGTH-index, "%s ", m_prefix.c_str());
|
||||
|
||||
index += snprintf (line + index, MAX_LENGTH - index, "[%s] %s: ", names[level], component);
|
||||
index += vsnprintf(line + index, MAX_LENGTH - index, format, args);
|
||||
va_end(args);
|
||||
index = snprintf (line + index, MAX_LENGTH - index, "\n");
|
||||
|
||||
// If the data is not buffered, immediately print it:
|
||||
if (m_buffer_size <= 1)
|
||||
{
|
||||
writeLine(line, level);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now the data needs to be buffered. Add an entry to the buffer,
|
||||
// and if necessary flush the buffers.
|
||||
struct LineInfo li;
|
||||
li.m_level = level;
|
||||
li.m_line = std::string(line);
|
||||
m_line_buffer.lock();
|
||||
m_line_buffer.getData().push_back(li);
|
||||
if (m_line_buffer.getData().size() < m_buffer_size)
|
||||
{
|
||||
// Buffer not yet full, don't flush data.
|
||||
m_line_buffer.unlock();
|
||||
return;
|
||||
}
|
||||
m_line_buffer.unlock();
|
||||
// Because of the unlock above it can happen that another thread adds
|
||||
// another line and calls flushBuffers() first before this thread can
|
||||
// call it, but that doesn't really matter, when this thread will finally
|
||||
// call flushBuffer() it will then just print nothing if another thread
|
||||
// had been actively flushing it in between.
|
||||
flushBuffers();
|
||||
} // printMessage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Writes the specified line to the various output devices, e.g. terminal,
|
||||
* log file etc. If log messages are not redirected to a file, it tries to
|
||||
* select a terminal colour.
|
||||
* \param line The line to write.
|
||||
* \param level Message level. Only used to select terminal colour.
|
||||
*/
|
||||
void Log::writeLine(const char *line, int level)
|
||||
{
|
||||
|
||||
// If we don't have a console file, write to stdout and hope for the best
|
||||
if (!m_file_stdout || level >= LL_WARN ||
|
||||
UserConfigParams::m_log_errors_to_console) // log to console & file
|
||||
{
|
||||
setTerminalColor((LogLevel)level);
|
||||
#ifdef ANDROID
|
||||
android_LogPriority alp;
|
||||
switch (level)
|
||||
@ -154,93 +210,41 @@ void Log::printMessage(int level, const char *component, const char *format,
|
||||
case LL_FATAL: alp = ANDROID_LOG_FATAL; break;
|
||||
default: alp = ANDROID_LOG_FATAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *names[] = {"debug", "verbose ", "info ",
|
||||
"warn ", "error ", "fatal "};
|
||||
|
||||
// Using a va_list twice produces undefined results, ie crash.
|
||||
// So make a copy if we're going to use it twice.
|
||||
VALIST copy;
|
||||
if (m_file_stdout)
|
||||
{
|
||||
va_copy(copy, args);
|
||||
}
|
||||
#if defined(_MSC_FULL_VER) && defined(_DEBUG)
|
||||
VALIST copy2;
|
||||
va_copy(copy2, args);
|
||||
#endif
|
||||
|
||||
// If we don't have a console file, write to stdout and hope for the best
|
||||
if(!m_file_stdout || level >= LL_WARN ||
|
||||
UserConfigParams::m_log_errors_to_console) // log to console & file
|
||||
{
|
||||
VALIST out;
|
||||
va_copy(out, args);
|
||||
|
||||
setTerminalColor((LogLevel)level);
|
||||
#ifdef ANDROID
|
||||
__android_log_vprint(alp, "SuperTuxKart", format, out);
|
||||
__android_log_vprint(alp, "SuperTuxKart", line);
|
||||
#else
|
||||
if(!m_prefix.empty())
|
||||
printf("%s ", m_prefix.c_str());
|
||||
printf("[%s] %s: ", names[level], component);
|
||||
vprintf(format, out);
|
||||
printf(line);
|
||||
#endif
|
||||
resetTerminalColor(); // this prints a \n
|
||||
|
||||
va_end(out);
|
||||
}
|
||||
|
||||
#if defined(_MSC_FULL_VER) && defined(_DEBUG)
|
||||
static char szBuff[2048];
|
||||
vsnprintf(szBuff, sizeof(szBuff), format, copy2);
|
||||
|
||||
if (!m_prefix.empty())
|
||||
{
|
||||
OutputDebugString(m_prefix.c_str());
|
||||
OutputDebugString(" ");
|
||||
}
|
||||
OutputDebugString("[");
|
||||
OutputDebugString(names[level]);
|
||||
OutputDebugString("] ");
|
||||
OutputDebugString(component);
|
||||
OutputDebugString(": ");
|
||||
OutputDebugString(szBuff);
|
||||
OutputDebugString("\r\n");
|
||||
OutputDebugString(line);
|
||||
#endif
|
||||
|
||||
|
||||
if(m_file_stdout)
|
||||
{
|
||||
if(!m_prefix.empty())
|
||||
fprintf(m_file_stdout, "%s ", m_prefix.c_str());
|
||||
fprintf (m_file_stdout, "[%s] %s: ", names[level], component);
|
||||
vfprintf(m_file_stdout, format, copy);
|
||||
fprintf (m_file_stdout, "\n");
|
||||
va_end(copy);
|
||||
}
|
||||
if (m_file_stdout) fprintf(m_file_stdout, line);
|
||||
|
||||
#ifdef WIN32
|
||||
if (level >= LL_FATAL)
|
||||
{
|
||||
std::string message;
|
||||
|
||||
char tmp[2048];
|
||||
sprintf(tmp, "[%s] %s: ", names[level], component);
|
||||
message += tmp;
|
||||
|
||||
VALIST out;
|
||||
va_copy(out, args);
|
||||
vsprintf(tmp, format, out);
|
||||
message += tmp;
|
||||
va_end(out);
|
||||
|
||||
MessageBoxA(NULL, message.c_str(), "SuperTuxKart - Fatal error", MB_OK);
|
||||
MessageBoxA(NULL, line, "SuperTuxKart - Fatal error", MB_OK);
|
||||
}
|
||||
#endif
|
||||
} // printMessage
|
||||
} // _fluhBuffers
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Flushes all stored log messages to the various output devices (thread safe).
|
||||
*/
|
||||
void Log::flushBuffers()
|
||||
{
|
||||
m_line_buffer.lock();
|
||||
for (unsigned int i = 0; i < m_line_buffer.getData().size(); i++)
|
||||
{
|
||||
const LineInfo &li = m_line_buffer.getData()[i];
|
||||
writeLine(li.m_line.c_str(), li.m_level);
|
||||
}
|
||||
m_line_buffer.getData().clear();
|
||||
m_line_buffer.unlock();
|
||||
} // flushBuffers
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function opens the files that will contain the output.
|
||||
|
@ -20,11 +20,15 @@
|
||||
#ifndef HEADER_LOG_HPP
|
||||
#define HEADER_LOG_HPP
|
||||
|
||||
#include "utils/synchronised.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define VALIST __gnuc_va_list
|
||||
@ -59,16 +63,30 @@ private:
|
||||
/** The file where stdout output will be written */
|
||||
static FILE* m_file_stdout;
|
||||
|
||||
/** An optional buffer for lines to be output. */
|
||||
struct LineInfo
|
||||
{
|
||||
std::string m_line;
|
||||
int m_level;
|
||||
};
|
||||
static Synchronised<std::vector<struct LineInfo> > m_line_buffer;
|
||||
|
||||
/** <0 if no buffered logging is to be used, otherwise this is
|
||||
** the maximum number of lines the buffer should hold. */
|
||||
static size_t m_buffer_size;
|
||||
|
||||
/** An optional prefix to be printed. */
|
||||
static std::string m_prefix;
|
||||
|
||||
static void setTerminalColor(LogLevel level);
|
||||
static void resetTerminalColor();
|
||||
|
||||
public:
|
||||
static void writeLine(const char *line, int level);
|
||||
|
||||
static void printMessage(int level, const char *component,
|
||||
const char *format, VALIST va_list);
|
||||
|
||||
public:
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** A simple macro to define the various log functions.
|
||||
* Note that an assert is added so that a debugger is triggered
|
||||
@ -98,7 +116,12 @@ public:
|
||||
static void openOutputFiles(const std::string &logout);
|
||||
|
||||
static void closeOutputFiles();
|
||||
static void flushBuffers();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the number of lines to buffer. Setting the buffer size to a
|
||||
* a value <=1 means no buffering, lines will be immediately printed. */
|
||||
static void setBufferSize(size_t n) { m_buffer_size = n; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Defines the minimum log level to be displayed. */
|
||||
static void setLogLevel(int n)
|
||||
|
Loading…
Reference in New Issue
Block a user