barrier/lib/base/CLog.cpp

315 lines
6.5 KiB
C++

#include "CLog.h"
#include "CString.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if WINDOWS_LIKE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define vsnprintf _vsnprintf
#endif
// names of priorities
static const char* g_priority[] = {
"FATAL",
"ERROR",
"WARNING",
"NOTE",
"INFO",
"DEBUG",
"DEBUG1",
"DEBUG2"
};
// number of priorities
static const int g_numPriority = (int)(sizeof(g_priority) /
sizeof(g_priority[0]));
// the default priority
#if defined(NDEBUG)
static const int g_defaultMaxPriority = 4;
#else
static const int g_defaultMaxPriority = 5;
#endif
// length of longest string in g_priority
static const int g_maxPriorityLength = 7;
// length of suffix string (": ")
static const int g_prioritySuffixLength = 2;
// amount of padded required to fill in the priority prefix
static const int g_priorityPad = g_maxPriorityLength +
g_prioritySuffixLength;
// platform newline sequence
#if WINDOWS_LIKE
static const char* g_newline = "\r\n";
#else
static const char* g_newline = "\n";
#endif
// minimum length of a newline sequence
static const int g_newlineLength = 2;
//
// CLog
//
CLog::Outputter CLog::s_outputter = NULL;
CLog::Lock CLog::s_lock = &CLog::dummyLock;
int CLog::s_maxPriority = -1;
void
CLog::print(const char* fmt, ...)
{
// check if fmt begins with a priority argument
int priority = 4;
if (fmt[0] == '%' && fmt[1] == 'z') {
priority = fmt[2] - '\060';
fmt += 3;
}
// done if below priority threshold
if (priority > getMaxPriority()) {
return;
}
// compute prefix padding length
int pad = g_priorityPad;
// print to buffer
char stack[1024];
va_list args;
va_start(args, fmt);
char* buffer = CStringUtil::vsprint(stack,
sizeof(stack) / sizeof(stack[0]),
pad, g_newlineLength, fmt, args);
va_end(args);
// output buffer
output(priority, buffer);
// clean up
if (buffer != stack)
delete[] buffer;
}
void
CLog::printt(const char* file, int line, const char* fmt, ...)
{
// check if fmt begins with a priority argument
int priority = 4;
if (fmt[0] == '%' && fmt[1] == 'z') {
priority = fmt[2] - '\060';
fmt += 3;
}
// done if below priority threshold
if (priority > getMaxPriority()) {
return;
}
// compute prefix padding length
char stack[1024];
sprintf(stack, "%d", line);
int pad = strlen(file) + 1 /* comma */ +
strlen(stack) + 1 /* colon */ + 1 /* space */ +
g_priorityPad;
// print to buffer, leaving space for a newline at the end
va_list args;
va_start(args, fmt);
char* buffer = CStringUtil::vsprint(stack,
sizeof(stack) / sizeof(stack[0]),
pad, g_newlineLength, fmt, args);
va_end(args);
// print the prefix to the buffer. leave space for priority label.
sprintf(buffer + g_priorityPad, "%s,%d:", file, line);
buffer[pad - 1] = ' ';
// discard file and line if priority < 0
char* message = buffer;
if (priority < 0) {
message += pad - g_priorityPad;
}
// output buffer
output(priority, message);
// clean up
if (buffer != stack)
delete[] buffer;
}
void
CLog::setOutputter(Outputter outputter)
{
CHoldLock lock(s_lock);
s_outputter = outputter;
}
CLog::Outputter
CLog::getOutputter()
{
CHoldLock lock(s_lock);
return s_outputter;
}
void
CLog::setLock(Lock newLock)
{
CHoldLock lock(s_lock);
s_lock = (newLock == NULL) ? dummyLock : newLock;
}
CLog::Lock
CLog::getLock()
{
CHoldLock lock(s_lock);
return (s_lock == dummyLock) ? NULL : s_lock;
}
bool
CLog::setFilter(const char* maxPriority)
{
if (maxPriority != NULL) {
for (int i = 0; i < g_numPriority; ++i) {
if (strcmp(maxPriority, g_priority[i]) == 0) {
setFilter(i);
return true;
}
}
return false;
}
return true;
}
void
CLog::setFilter(int maxPriority)
{
CHoldLock lock(s_lock);
s_maxPriority = maxPriority;
}
int
CLog::getFilter()
{
CHoldLock lock(s_lock);
return getMaxPriority();
}
void
CLog::dummyLock(bool)
{
// do nothing
}
int
CLog::getMaxPriority()
{
CHoldLock lock(s_lock);
if (s_maxPriority == -1) {
s_maxPriority = g_defaultMaxPriority;
setFilter(getenv("SYN_LOG_PRI"));
}
return s_maxPriority;
}
void
CLog::output(int priority, char* msg)
{
assert(priority >= -1 && priority < g_numPriority);
assert(msg != NULL);
// insert priority label
int n = -g_prioritySuffixLength;
if (priority >= 0) {
n = strlen(g_priority[priority]);
strcpy(msg + g_maxPriorityLength - n, g_priority[priority]);
msg[g_maxPriorityLength + 0] = ':';
msg[g_maxPriorityLength + 1] = ' ';
msg[g_maxPriorityLength + 1] = ' ';
}
// put a newline at the end
strcat(msg + g_priorityPad, g_newline);
// print it
CHoldLock lock(s_lock);
if (s_outputter == NULL ||
!s_outputter(priority, msg + g_maxPriorityLength - n)) {
#if WINDOWS_LIKE
openConsole();
#endif
fprintf(stderr, "%s", msg + g_maxPriorityLength - n);
}
}
#if WINDOWS_LIKE
static DWORD s_thread = 0;
static
BOOL WINAPI
CLogSignalHandler(DWORD)
{
// terminate cleanly and skip remaining handlers
PostThreadMessage(s_thread, WM_QUIT, 0, 0);
return TRUE;
}
void
CLog::openConsole()
{
static bool s_hasConsole = false;
// ignore if already created
if (s_hasConsole)
return;
// remember the current thread. when we get a ctrl+break or the
// console is closed we'll post WM_QUIT to this thread to shutdown
// cleanly.
// note -- win95/98/me are broken and will not receive a signal
// when the console is closed nor during logoff or shutdown,
// see microsoft articles Q130717 and Q134284. we could work
// around this in a painful way using hooks and hidden windows
// (as apache does) but it's not worth it. the app will still
// quit, just not cleanly. users in-the-know can use ctrl+c.
s_thread = GetCurrentThreadId();
// open a console
if (!AllocConsole())
return;
// get the handle for error output
HANDLE herr = GetStdHandle(STD_ERROR_HANDLE);
// prep console. windows 95 and its ilk have braindead
// consoles that can't even resize independently of the
// buffer size. use a 25 line buffer for those systems.
OSVERSIONINFO osInfo;
COORD size = { 80, 1000 };
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
if (GetVersionEx(&osInfo) &&
osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
size.Y = 25;
SetConsoleScreenBufferSize(herr, size);
SetConsoleTextAttribute(herr,
FOREGROUND_RED |
FOREGROUND_GREEN |
FOREGROUND_BLUE);
SetConsoleCtrlHandler(CLogSignalHandler, TRUE);
// reopen stderr to point at console
freopen("con", "w", stderr);
s_hasConsole = true;
}
#endif