212 lines
5.4 KiB
C++
212 lines
5.4 KiB
C++
/*
|
|
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
* See the COPYING file in the main directory for details.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
|
|
#include "Configuration.h"
|
|
#include "Sockets.h"
|
|
#include "Logger.h"
|
|
#include "Timeval.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
// Reference to a global config table, used all over the system.
|
|
extern ConfigurationTable gConfig;
|
|
|
|
|
|
/** The global logging lock. */
|
|
static Mutex gLogLock;
|
|
|
|
|
|
|
|
/**@ The global alarms table. */
|
|
//@{
|
|
Mutex alarmsLock;
|
|
list<string> alarmsList;
|
|
void addAlarm(const string&);
|
|
//@}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Names of the logging levels. */
|
|
const char* levelNames[] =
|
|
{ "FORCE", "ERROR", "ALARM", "WARN", "NOTICE", "INFO", "DEBUG", "DEEPDEBUG" };
|
|
const unsigned numLevels = 8;
|
|
|
|
ostream& operator<<(ostream& os, Log::Level level)
|
|
{
|
|
unsigned il = (unsigned)level;
|
|
assert(il<numLevels);
|
|
os << levelNames[il];
|
|
return os;
|
|
}
|
|
|
|
|
|
/** Given a string, return the corresponding level name. */
|
|
Log::Level gLookupLevel(const char* name)
|
|
{
|
|
for (unsigned i=0; i<numLevels; i++) {
|
|
if (strcmp(levelNames[i],name)==0) return (Log::Level)i;
|
|
}
|
|
// This should never be called with a bogus name.
|
|
LOG(ERROR) << "undefined logging level " << name << "defaulting to FORCE";
|
|
return Log::LOG_FORCE;
|
|
}
|
|
|
|
/** Return the current logging level for a given source file. */
|
|
Log::Level gLoggingLevel(const char* filename)
|
|
{
|
|
const string keyName = string("Log.Level.") + string(filename);
|
|
if (gConfig.defines(keyName)) return gLookupLevel(gConfig.getStr(keyName));
|
|
return gLookupLevel(gConfig.getStr("Log.Level"));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** The current global log sink. */
|
|
static FILE *gLoggingFile = stdout;
|
|
|
|
void gSetLogFile(FILE *wFile)
|
|
{
|
|
gLogLock.lock();
|
|
gLoggingFile = wFile;
|
|
gLogLock.unlock();
|
|
}
|
|
|
|
|
|
bool gSetLogFile(const char *name)
|
|
{
|
|
assert(name);
|
|
LOG(DEEPDEBUG) << "setting log path to " << name;
|
|
bool retVal = true;
|
|
gLogLock.lock();
|
|
FILE* newLoggingFile = fopen(name,"a+");
|
|
if (!newLoggingFile) {
|
|
LOG(ERROR) << "cannot open \"" << name << "\" for logging.";
|
|
retVal = false;
|
|
} else {
|
|
gLoggingFile = newLoggingFile;
|
|
}
|
|
gLogLock.unlock();
|
|
LOG(FORCE) << "new log path " << name;
|
|
return retVal;
|
|
}
|
|
|
|
|
|
// copies the alarm list and returns it. list supposed to be small.
|
|
std::list<std::string> gGetLoggerAlarms()
|
|
{
|
|
alarmsLock.lock();
|
|
std::list<std::string> ret;
|
|
// excuse the "complexity", but to use std::copy with a list you need
|
|
// an insert_iterator - copy technically overwrites, doesn't insert.
|
|
std::insert_iterator< std::list<std::string> > ii(ret, ret.begin());
|
|
std::copy(alarmsList.begin(), alarmsList.end(), ii);
|
|
alarmsLock.unlock();
|
|
return ret;
|
|
}
|
|
|
|
// Add an alarm to the alarm list, and send it out via udp
|
|
//
|
|
// On the first call we read the ip and port from the configuration
|
|
// TODO - is there any global setup function where this should be done? -- Alon
|
|
void addAlarm(const string& s)
|
|
{
|
|
// Socket open and close on every alarm - wise?
|
|
// Probably. That way we are sure to pick up changes in the target address.
|
|
// Alarms should not happen often.
|
|
if (gConfig.defines("Log.Alarms.TargetIP")) {
|
|
UDPSocket alarmsocket(0,
|
|
gConfig.getStr("Log.Alarms.TargetIP"),
|
|
gConfig.getNum("Log.Alarms.TargetPort"));
|
|
alarmsocket.write(s.c_str());
|
|
}
|
|
// append to list and reduce list to max alarm count
|
|
alarmsLock.lock();
|
|
alarmsList.push_back(s);
|
|
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
|
|
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
|
|
alarmsLock.unlock();
|
|
}
|
|
|
|
|
|
Log::~Log()
|
|
{
|
|
// XXX always handle alarms, even if the logging level is too low
|
|
if (mReportLevel == LOG_ALARM) {
|
|
addAlarm(mStream.str().c_str());
|
|
std::cerr << mStream.str() << std::endl;
|
|
}
|
|
// Current logging level was already checked by the macro.
|
|
// So just log.
|
|
gLogLock.lock();
|
|
mStream << std::endl;
|
|
|
|
if (gLoggingFile == NULL)
|
|
gLoggingFile = stdout;
|
|
|
|
fprintf(gLoggingFile, "%s", mStream.str().c_str());
|
|
fflush(gLoggingFile);
|
|
gLogLock.unlock();
|
|
}
|
|
|
|
|
|
ostringstream& Log::get()
|
|
{
|
|
Timeval now;
|
|
mStream.precision(4);
|
|
mStream << now << ' ' << mReportLevel << ' ';
|
|
return mStream;
|
|
}
|
|
|
|
|
|
|
|
void gLogInit(const char* defaultLevel)
|
|
{
|
|
// Define defaults in the global config
|
|
if (!gConfig.defines("Log.Level")) {
|
|
gConfig.set("Log.Level",defaultLevel);
|
|
LOG(FORCE) << "Setting initial global logging level to " << defaultLevel;
|
|
}
|
|
if (!gConfig.defines("Log.Alarms.TargetPort")) {
|
|
gConfig.set("Log.Alarms.TargetPort",DEFAULT_ALARM_PORT);
|
|
}
|
|
if (!gConfig.defines("Log.Alarms.Max")) {
|
|
gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// vim: ts=4 sw=4
|