DRDE/ACTK1_0/common/Logger.cpp

238 lines
5.9 KiB
C++

#include "SystemProperties.hpp"
#include "common/Logger.hpp"
#include <sys/time.h>
#include <pthread.h>
#include "common/AutoMutex.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
namespace {
// s_loggerMap and s_loggerProperties are protected by s_logPoolLock
class LoggerMap
{
public:
// This is primarily a thin wrapper around std::map to release the heap
// allocated Logger objects. (Bring on map<string, tr1::shared_ptr > ... ).
~LoggerMap()
{
for (Map::iterator i = loggerMap.begin(); i != loggerMap.end(); ++i)
{
delete i->second;
}
}
typedef map<string, Logger *> Map;
typedef Map::const_iterator const_iterator;
Map::const_iterator find(const string& name) const
{
return loggerMap.find(name);
}
Map::const_iterator end() const { return loggerMap.end(); }
void addLog(const string& name, Logger::LoggerLevel lvl, const string& fname)
{
loggerMap.insert(make_pair(name, new Logger(name, lvl, fname)));
}
private:
Map loggerMap;
};
static LoggerMap s_loggerMap;
static auto_ptr<Properties> s_loggerProperties;
pthread_mutex_t s_logPoolLock = PTHREAD_MUTEX_INITIALIZER;
const char * LoggerLevelStringReps[Logger::__INVALID_LEVEL + 1] =
{
"OFF",
"FINEST",
"FINER",
"FINE",
"CONFIG",
"INFO",
"WARNING",
"SEVERE",
"__INVALID_LEVEL"
};
string findKey(const string& parent, const string& key, const string& def)
{
const string delim(".");
string::size_type i;
for (i = parent.size(); i != string::npos; i = parent.rfind(delim, i))
{
try
{
return s_loggerProperties->getProperty(parent.substr(0, i) + delim + key);
}
catch (PropertyNotFoundException& e)
{ }
if (i == 0) break;
i -= delim.size();
}
return def;
}
string getFileNameForLogger(const string& logName)
{
try
{
return findKey(logName, "file", "");
}
catch (EPPException& e)
{
return "";
}
}
Logger::LoggerLevel getLevelForLogger(
const string& logName,
const Logger::LoggerLevel defaultLevel)
{
try
{
const string lvl(findKey(logName, "level", "WARNING"));
for (int i = 0; i < Logger::__INVALID_LEVEL; i++)
{
if (lvl == LoggerLevelStringReps[i])
{
return (Logger::LoggerLevel)i;
}
}
return defaultLevel;
}
catch (EPPException& e)
{
return defaultLevel;
}
}
// Add a time string to str.
ostream& logTime(ostream& str)
{
struct timeval tv;
struct tm tm;
const char BUFSZ=32;
char tmpbuf[BUFSZ];
gettimeofday(&tv, NULL);
gmtime_r(&(tv.tv_sec), &tm);
strftime(tmpbuf, BUFSZ, "%Y%m%d %H:%M:%S", &tm);
str << tmpbuf;
snprintf(tmpbuf, BUFSZ, ".%03ld", tv.tv_usec / 1000);
str << tmpbuf;
return str;
}
} // anonymous namespace
Logger::Logger(const string& name, const LoggerLevel ll, const string& fileName)
: myName(name), level(ll), stream(NULL)
{
pthread_mutex_init(&mtx, NULL);
if (fileName.size() > 0)
{
stream = new ofstream(fileName.c_str(), ios_base::app);
if (!stream->good())
{
cerr << "Logger: failed open file '" << fileName
<< "' for logger '" << name << "'." << endl;
// Note if the new worked, but the stream is 'invalid', the object
// still exists but is of no use. Let's delete it and treat it as
// for the default logging case.
delete stream;
stream = NULL;
}
}
}
Logger::~Logger()
{
pthread_mutex_destroy(&mtx);
if (stream) delete stream;
}
void Logger::init() throw (PropertyConfigException)
{
s_loggerMap = LoggerMap();
s_loggerProperties = auto_ptr<Properties>(new Properties);
try
{
s_loggerProperties->load(SystemProperties::getProperty("logging.config.file"));
}
catch (PropertyConfigException& e)
{
// We explicitly send this to cerr as the logging system itself is not working!
cerr << "Logger::init failed to load config: "
<< e.getMessage() << endl;
throw;
}
catch (PropertyNotFoundException& e)
{
PropertyConfigException pce("Could not initialise the logging system.");
pce.causedBy(e);
// We explicitly send this to cerr as the logging system itself is not
// working!
cerr << e.getMessage() << endl;
throw pce;
}
}
Logger* Logger::getLogger(const string &name)
{
AutoMutex lock(&s_logPoolLock);
LoggerMap::const_iterator p = s_loggerMap.find(name);
if (p != s_loggerMap.end()) return p->second;
const string fname = getFileNameForLogger(name);
const LoggerLevel ll = getLevelForLogger(name, WARNING);
s_loggerMap.addLog(name, ll, fname);
// Redundant find, but this only happens once per log name.
return s_loggerMap.find(name)->second;
}
void Logger::log(LoggerLevel lvl,
const string& msg,
const string& unit,
int line)
{
if (lvl >= this->level)
{
ostringstream str;
logTime(str) << " | " << myName << " | ";
if (unit != "")
{
str << unit;
if (line > 0) str << "[" << line << "]";
str << " | ";
}
str << LoggerLevelStringReps[lvl] << ": " << msg << "\n";
AutoMutex lock(&mtx);
if (stream)
{
*stream << str.str();
stream->flush();
}
else
{
clog << str.str();
clog.flush();
}
}
}