/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "CLog.h" #include "CString.h" #include "CStringUtil.h" #include "LogOutputters.h" #include "CArch.h" #include "Version.h" #include #include #include #include // names of priorities static const char* g_priority[] = { "FATAL", "ERROR", "WARNING", "NOTE", "INFO", "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4", "DEBUG5" }; // 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; // // CLog // CLog* CLog::s_log = NULL; CLog::CLog() { assert(s_log == NULL); // create mutex for multithread safe operation m_mutex = ARCH->newMutex(); // other initalization m_maxPriority = g_defaultMaxPriority; m_maxNewlineLength = 0; insert(new CConsoleLogOutputter); } CLog::~CLog() { // clean up for (COutputterList::iterator index = m_outputters.begin(); index != m_outputters.end(); ++index) { delete *index; } for (COutputterList::iterator index = m_alwaysOutputters.begin(); index != m_alwaysOutputters.end(); ++index) { delete *index; } ARCH->closeMutex(m_mutex); s_log = NULL; } CLog* CLog::getInstance() { // note -- not thread safe; client must initialize log safely if (s_log == NULL) { s_log = new CLog; } return s_log; } const char* CLog::getFilterName() const { return getFilterName(getFilter()); } const char* CLog::getFilterName(int level) const { return g_priority[level]; } void CLog::print(const char* file, int line, const char* fmt, ...) { // check if fmt begins with a priority argument ELevel priority = kINFO; if (fmt[0] == '%' && fmt[1] == 'z') { // 060 in octal is 0 (48 in decimal), so subtracting this converts ascii // number it a true number. we could use atoi instead, but this is how // it was done originally. priority = (ELevel)(fmt[2] - '\060'); // TODO: fix this shit // move the pointer on past the debug priority char fmt += 3; } // done if below priority threshold if (priority > getFilter()) { return; } // compute prefix padding length char stack[1024]; // compute suffix padding length int sPad = m_maxNewlineLength; // print to buffer, leaving space for a newline at the end and prefix // at the beginning. char* buffer = stack; int len = (int)(sizeof(stack) / sizeof(stack[0])); while (true) { // try printing into the buffer va_list args; va_start(args, fmt); int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again if (n < 0 || n > (int)len) { if (buffer != stack) { delete[] buffer; } len *= 2; buffer = new char[len]; } // if the buffer was big enough then continue else { break; } } // print the prefix to the buffer. leave space for priority label. // do not prefix time and file for kPRINT (CLOG_PRINT) if ((file != NULL) && (priority != kPRINT)) { char message[2048]; struct tm *tm; char tmp[220]; time_t t; time(&t); tm = localtime(&t); sprintf(tmp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); //strcpy(msg, tmp); sprintf(message, "%s %s: %s\n\t%s,%d", tmp, g_priority[priority], buffer, file, line); // buffer[pPad - 1] = ' '; // discard file and line if priority < 0 /*if (priority < 0) { message += pPad - g_priorityPad; } */ // output buffer output(priority, message); } else { output(priority, buffer); } // clean up if (buffer != stack) { delete[] buffer; } } void CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) { assert(outputter != NULL); CArchMutexLock lock(m_mutex); if (alwaysAtHead) { m_alwaysOutputters.push_front(outputter); } else { m_outputters.push_front(outputter); } outputter->open(kAppVersion); // Issue 41 // don't show log unless user requests it, as some users find this // feature irritating (i.e. when they lose network connectivity). // in windows the log window can be displayed by selecting "show log" // from the synergy system tray icon. // if this causes problems for other architectures, then a different // work around should be attempted. //outputter->show(false); } void CLog::remove(ILogOutputter* outputter) { CArchMutexLock lock(m_mutex); m_outputters.remove(outputter); m_alwaysOutputters.remove(outputter); } void CLog::pop_front(bool alwaysAtHead) { CArchMutexLock lock(m_mutex); COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; if (!list->empty()) { delete list->front(); list->pop_front(); } } 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) { CArchMutexLock lock(m_mutex); m_maxPriority = maxPriority; } int CLog::getFilter() const { CArchMutexLock lock(m_mutex); return m_maxPriority; } void CLog::output(ELevel priority, char* msg) { assert(priority >= -1 && priority < g_numPriority); assert(msg != NULL); if (!msg) return; CArchMutexLock lock(m_mutex); COutputterList::const_iterator i; for (i = m_alwaysOutputters.begin(); i != m_alwaysOutputters.end(); ++i) { // write to outputter (*i)->write(priority, msg); } for (i = m_outputters.begin(); i != m_outputters.end(); ++i) { // write to outputter and break out of loop if it returns false if (!(*i)->write(priority, msg)) { break; } } }