Commit 4e4a6a7a authored by Eric Cano's avatar Eric Cano
Browse files

Simplified to the looger classes and gathered more commonalities in the base class.

Created an StdoutLogger class.
Created a CommandLineParams class that will allow early parsing, before the daemon object is actually created.
parent e5984e5e
......@@ -57,6 +57,7 @@ set (COMMON_LIB_SRC_FILES
log/Param.cpp
log/StringLogger.cpp
log/SyslogLogger.cpp
log/StdoutLogger.cpp
priorities/DriveQuota.cpp
priorities/MountCriteria.cpp
priorities/UserGroup.cpp
......
......@@ -27,26 +27,22 @@
// constructor
//------------------------------------------------------------------------------
cta::log::DummyLogger::DummyLogger(const std::string &programName):
Logger(programName) {
Logger(programName, DEBUG) {
}
//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
cta::log::DummyLogger::~DummyLogger() throw() {
cta::log::DummyLogger::~DummyLogger() {
}
//------------------------------------------------------------------------------
// prepareForFork
//------------------------------------------------------------------------------
void cta::log::DummyLogger::prepareForFork() {
}
void cta::log::DummyLogger::prepareForFork() {}
//------------------------------------------------------------------------------
// operator()
// reducedSyslog
//------------------------------------------------------------------------------
void cta::log::DummyLogger::operator() (
const int priority,
const std::string &msg,
const std::list<Param> &params) throw() {
}
void cta::log::DummyLogger::reducedSyslog(std::string msg) {}
......@@ -52,7 +52,7 @@ public:
/**
* Destructor.
*/
~DummyLogger() throw();
virtual ~DummyLogger();
/**
* Prepares the logger object for a call to fork().
......@@ -61,18 +61,9 @@ public:
* method until the call to fork() has completed.
*/
void prepareForFork() ;
/**
* Dummy operator() method that does nothing.
*
* @param priority the priority of the message as defined by the syslog API.
* @param msg the message.
* @param params the parameters of the message.
*/
void operator() (
const int priority,
const std::string &msg,
const std::list<Param> &params = std::list<Param>()) throw();
protected:
virtual void reducedSyslog(std::string msg);
}; // class DummyLogger
......
......@@ -22,13 +22,19 @@
*****************************************************************************/
#include "common/log/Logger.hpp"
#include "common/utils/Utils.hpp"
#include "common/exception/Exception.hpp"
#include <sys/time.h>
#include <sys/syslog.h>
#include <sys/syscall.h>
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
cta::log::Logger::Logger(const std::string &programName):
m_programName(programName) {
}
cta::log::Logger::Logger(const std::string &programName, const int logMask):
m_programName(programName), m_logMask(logMask),
m_maxMsgLen(determineMaxMsgLen()),
m_priorityToText(generatePriorityToTextMap()) {}
//------------------------------------------------------------------------------
// getProgramName
......@@ -42,3 +48,263 @@ const std::string &cta::log::Logger::getProgramName() const {
//------------------------------------------------------------------------------
cta::log::Logger::~Logger() {
}
//-----------------------------------------------------------------------------
// operator()
//-----------------------------------------------------------------------------
void cta::log::Logger::operator() (
const int priority,
const std::string &msg,
const std::list<Param> &params) {
const std::string rawParams;
struct timeval timeStamp;
gettimeofday(&timeStamp, NULL);
const int pid = getpid();
//-------------------------------------------------------------------------
// Note that we do here part of the work of the real syslog call, by
// building the message ourselves. We then only call a reduced version of
// syslog (namely reducedSyslog). The reason behind it is to be able to set
// the message timestamp ourselves, in case we log messages asynchronously,
// as we do when retrieving logs from the DB
//-------------------------------------------------------------------------
// Ignore messages whose priority is not of interest
if(priority > m_logMask) {
return;
}
// Try to find the textual representation of the syslog priority
std::map<int, std::string>::const_iterator priorityTextPair =
m_priorityToText.find(priority);
// Do nothing if the log priority is not valid
if(m_priorityToText.end() == priorityTextPair) {
return;
}
// Safe to get a reference to the textual representation of the priority
const std::string &priorityText = priorityTextPair->second;
std::ostringstream os;
writeLogMsg(
os,
priority,
priorityText,
msg,
params,
rawParams,
timeStamp,
m_programName,
pid);
reducedSyslog(os.str());
}
//-----------------------------------------------------------------------------
// writeLogMsg
//-----------------------------------------------------------------------------
void cta::log::Logger::writeLogMsg(
std::ostringstream &os,
const int priority,
const std::string &priorityText,
const std::string &msg,
const std::list<Param> &params,
const std::string &rawParams,
const struct timeval &timeStamp,
const std::string &programName,
const int pid) {
//-------------------------------------------------------------------------
// Note that we do here part of the work of the real syslog call, by
// building the message ourselves. We then only call a reduced version of
// syslog (namely reducedSyslog). The reason behind it is to be able to set
// the message timestamp ourselves, in case we log messages asynchronously,
// as we do when retrieving logs from the DB
//-------------------------------------------------------------------------
// Start message with priority, time, program and PID (syslog standard
// format)
writeHeader(os, priority | LOG_LOCAL3, timeStamp, programName, pid);
const int tid = syscall(__NR_gettid);
// Append the log level, the thread id and the message text
os << "LVL=\"" << priorityText << "\" TID=\"" << tid << "\" MSG=\"" <<
msg << "\" ";
// Process parameters
for(auto itor = params.cbegin(); itor != params.cend(); itor++) {
const Param &param = *itor;
// Check the parameter name, if it's an empty string set the value to
// "Undefined".
const std::string name = param.getName() == "" ? "Undefined" :
cleanString(param.getName(), true);
// Process the parameter value
const std::string value = cleanString(param.getValue(), false);
// Write the name and value to the buffer
os << name << "=\"" << value << "\" ";
}
// Append raw parameters
os << rawParams;
// Terminate the string
os << "\n";
}
//-----------------------------------------------------------------------------
// writeHeader
//-----------------------------------------------------------------------------
void cta::log::Logger::writeHeader(
std::ostringstream &os,
const int priority,
const struct timeval &timeStamp,
const std::string &programName,
const int pid) {
char buf[80];
int bufLen = sizeof(buf);
int len = 0;
os << "<" << priority << ">";
struct tm localTime;
localtime_r(&(timeStamp.tv_sec), &localTime);
len += strftime(buf, bufLen, "%Y-%m-%dT%T", &localTime);
len += snprintf(buf + len, bufLen - len, ".%06ld",
(unsigned long)timeStamp.tv_usec);
len += strftime(buf + len, bufLen - len, "%z: ", &localTime);
// dirty trick to have the proper timezone format (':' between hh and mm)
buf[len-2] = buf[len-3];
buf[len-3] = buf[len-4];
buf[len-4] = ':';
buf[sizeof(buf) - 1] = '\0';
os << buf << programName << "[" << pid << "]: ";
}
//-----------------------------------------------------------------------------
// cleanString
//-----------------------------------------------------------------------------
std::string cta::log::Logger::cleanString(const std::string &s,
const bool replaceUnderscores) {
// Trim both left and right white-space
std::string result = utils::trimString(s);
for (std::string::iterator it = result.begin(); it != result.end(); ++it) {
// Replace double quote with single quote
if ('"' == *it) {
*it = '\'';
}
// Replace newline and tab with a space
if ('\t' == *it || '\n' == *it) {
*it = ' ';
}
// If requested, replace spaces with underscores
if(replaceUnderscores && ' ' == *it) {
*it = '_';
}
}
return result;
}
//------------------------------------------------------------------------------
// determineMaxMsgLen
//------------------------------------------------------------------------------
size_t cta::log::Logger::determineMaxMsgLen() {
size_t msgSize = 0;
// Determine the size automatically, this is not guaranteed to work!
FILE *const fp = fopen("/etc/rsyslog.conf", "r");
if(fp) {
char buffer[1024];
// The /etc/rsyslog.conf file exists so we assume the default message
// size of 2K.
msgSize = DEFAULT_RSYSLOG_MSGLEN;
// In rsyslog versions >= 3.21.4, the maximum size of a message became
// configurable through the $MaxMessageSize global config directive.
// Here we attempt to find out if the user has increased the size!
while(fgets(buffer, sizeof(buffer), fp) != NULL) {
if(strncasecmp(buffer, "$MaxMessageSize", 15)) {
continue; // Option not of interest
}
msgSize = atol(&buffer[15]);
}
fclose(fp);
}
// If the /etc/rsyslog.conf file is missing which implies that we are
// running on a stock syslogd system, therefore the message size is
// governed by the syslog RFC: http://www.faqs.org/rfcs/rfc3164.html
// Check that the size of messages falls within acceptable limits
if((msgSize >= DEFAULT_SYSLOG_MSGLEN) && (msgSize <= LOG_MAX_LINELEN)) {
return msgSize;
} else {
return DEFAULT_SYSLOG_MSGLEN;
}
}
//------------------------------------------------------------------------------
// generatePriorityToTextMap
//------------------------------------------------------------------------------
std::map<int, std::string>
cta::log::Logger::generatePriorityToTextMap() {
std::map<int, std::string> m;
try {
m[LOG_EMERG] = "Emerg";
m[LOG_ALERT] = "Alert";
m[LOG_CRIT] = "Crit";
m[LOG_ERR] = "Error";
m[LOG_WARNING] = "Warn";
m[LOG_NOTICE] = "Notice";
m[LOG_INFO] = "Info";
m[LOG_DEBUG] = "Debug";
} catch(std::exception &se) {
exception::Exception ex;
ex.getMessage() << "Failed to generate priority to text mapping: " <<
se.what();
throw ex;
}
return m;
}
//------------------------------------------------------------------------------
// generateConfigTextToPriorityMap
//------------------------------------------------------------------------------
std::map<std::string, int>
cta::log::Logger::generateConfigTextToPriorityMap() {
std::map<std::string, int> m;
try {
m["LOG_EMERG"] = LOG_EMERG;
m["LOG_ALERT"] = LOG_ALERT;
m["LOG_CRIT"] = LOG_CRIT;
m["LOG_ERR"] = LOG_ERR;
m["LOG_WARNING"] = LOG_WARNING;
m["LOG_NOTICE"] = LOG_NOTICE;
m["LOG_INFO"] = LOG_INFO;
m["LOG_DEBUG"] = LOG_DEBUG;
} catch(std::exception &se) {
exception::Exception ex;
ex.getMessage() <<
"Failed to generate configuration text to priority mapping: " <<
se.what();
throw ex;
}
return m;
}
\ No newline at end of file
......@@ -28,6 +28,7 @@
#include "common/log/Param.hpp"
#include <list>
#include <map>
/**
* It is a convention of CASTOR to use syslog level of LOG_NOTICE to label
......@@ -88,9 +89,10 @@ public:
* Constructor
*
* @param programName The name of the program to be prepended to every log
* @param logMask The log mask.
* message.
*/
Logger(const std::string &programName);
Logger(const std::string &programName, const int logMask);
/**
* Destructor.
......@@ -112,7 +114,7 @@ public:
const std::string &getProgramName() const;
/**
* Writes a message into the CASTOR logging system. Note that no exception
* Writes a message into the CTA logging system. Note that no exception
* will ever be thrown in case of failure. Failures will actually be
* silently ignored in order to not impact the processing.
*
......@@ -126,7 +128,36 @@ public:
virtual void operator() (
const int priority,
const std::string &msg,
const std::list<Param> &params = std::list<Param>()) = 0;
const std::list<Param> &params = std::list<Param>());
/**
* Writes the header of a syslog message to teh specifed output stream.
*
* @param os The output stream to which the header will be written.
* @param priority The priority of the message.
* @param timeStamp The time stamp of the message.
* @param programName the program name of the log message.
* @param pid The process ID of the process logging the message.
* @return The header of the syslog message.
*/
static void writeHeader(
std::ostringstream &os,
const int priority,
const struct timeval &timeStamp,
const std::string &programName,
const int pid);
/**
* Creates a clean version of the specified string ready for use with syslog.
*
* @param s The string to be cleaned.
* @param replaceUnderscores Set to true if spaces should be replaced by
* underscores.
* @return A cleaned version of the string.
*/
static std::string cleanString(const std::string &s,
const bool replaceUnderscores);
protected:
......@@ -134,6 +165,97 @@ protected:
* The name of the program to be prepended to every log message.
*/
const std::string m_programName;
/**
* A reduced version of syslog. This method is able to set the message
* timestamp. This is necessary when logging messages asynchronously of there
* creation, such as when retrieving logs from the DB.
*
* @param msg The message to be logged.
*/
virtual void reducedSyslog(std::string msg) = 0;
/**
* The log mask.
*/
int m_logMask;
/**
* The maximum message length that the client syslog server can handle.
*/
const size_t m_maxMsgLen;
/**
* Map from syslog integer priority to textual representation.
*/
const std::map<int, std::string> m_priorityToText;
/**
* Map from the possible string values of the LogMask parameters of
* /etc/castor.conf and their equivalent syslog priorities.
*/
const std::map<std::string, int> m_configTextToPriority;
/**
* Writes a log message to the specified output stream.
*
* @param logMsg The output stream to which the log message is to be written.
* @param priority the priority of the message as defined by the syslog API.
* @param msg the message.
* @param params the parameters of the message.
* @param rawParams preprocessed parameters of the message.
* @param timeStamp the time stamp of the log message.
* @param programName the program name of the log message.
* @param pid the pid of the log message.
*/
static void writeLogMsg(
std::ostringstream &os,
const int priority,
const std::string &priorityText,
const std::string &msg,
const std::list<Param> &params,
const std::string &rawParams,
const struct timeval &timeStamp,
const std::string &programName,
const int pid);
/**
* Default size of a syslog message.
*/
static const size_t DEFAULT_SYSLOG_MSGLEN = 1024;
/**
* Default size of a rsyslog message.
*/
static const size_t DEFAULT_RSYSLOG_MSGLEN = 2000;
/**
* Maximum length of a log message.
*/
static const size_t LOG_MAX_LINELEN = 8192;
/**
* Determines the maximum message length that the client syslog server can
* handle.
*
* @return The maximum message length that the client syslog server can
* handle.
*/
static size_t determineMaxMsgLen();
/**
* Generates and returns the mapping between syslog priorities and their
* textual representations.
*/
static std::map<int, std::string> generatePriorityToTextMap();
/**
* Generates and returns the mapping between the possible string values
* of the LogMask parameters of /etc/castor.conf and their equivalent
* syslog priorities.
*/
static std::map<std::string, int> generateConfigTextToPriorityMap();
}; // class Logger
......
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/log/StdoutLogger.hpp"
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
cta::log::StdoutLogger::StdoutLogger(const std::string &programName):
Logger(programName, DEBUG) {
}
//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
cta::log::StdoutLogger::~StdoutLogger() {
}
//------------------------------------------------------------------------------
// prepareForFork
//------------------------------------------------------------------------------
void cta::log::StdoutLogger::prepareForFork() {}
//------------------------------------------------------------------------------
// reducedSyslog
//------------------------------------------------------------------------------
void cta::log::StdoutLogger::reducedSyslog(std::string msg) {
printf("%s", msg.c_str());
}
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/log/Logger.hpp"
namespace cta {
namespace log {
/**
* Class implementaing the API of the CASTOR logging system.
*/
class StdoutLogger: public Logger {
public:
/**
* Constructor
*
* @param programName The name of the program to be prepended to every log
* message.
*/
StdoutLogger(const std::string &programName);
/**
* Destructor.
*/
~StdoutLogger();
/**
* Prepares the logger object for a call to fork().
*
* No further calls to operator() should be made after calling this
* method until the call to fork() has completed.
*/
void prepareForFork() ;
protected:
/**