Commit 4af6fc12 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[3427] Logging configuration implemented.

parent 0a00f963
......@@ -18,6 +18,7 @@
#include <d2/d_controller.h>
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
#include <dhcpsrv/configuration.h>
#include <sstream>
......@@ -217,6 +218,28 @@ DControllerBase::configFromFile() {
isc::data::ConstElementPtr whole_config =
isc::data::Element::fromJSONFile(config_file, true);
// Let's configure logging before applying the configuration,
// so we can log things during configuration process.
// Temporary storage for logging configuration
isc::dhcp::ConfigurationPtr storage(new isc::dhcp::Configuration());
// Get 'Logging' element from the config
isc::data::ConstElementPtr logger = whole_config->get("Logging");
if (logger) {
// Configure logger first, so it can be applied to DHCPv6
// configuration. If we don't have a logger, just pass
// empty configuration.
Daemon::configureLogger(logger, storage);
} else {
// There was no Logging element defined in the config file.
// Let's pass an empty pointer that will remove any current
// configuration.
Daemon::configureLogger(isc::data::ConstElementPtr(),
storage);
}
// Extract derivation-specific portion of the configuration.
module_config = whole_config->get(getAppName());
if (!module_config) {
......
......@@ -17,6 +17,7 @@
#include <dhcp4/json_config_parser.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcpsrv/cfgmgr.h>
#include <exceptions/exceptions.h>
#include <string>
......@@ -42,6 +43,7 @@ void configure(const std::string& file_name) {
isc::data::ConstElementPtr json;
isc::data::ConstElementPtr dhcp4;
isc::data::ConstElementPtr logger;
isc::data::ConstElementPtr result;
// Basic sanity check: file name must not be empty.
......@@ -62,6 +64,23 @@ void configure(const std::string& file_name) {
" file: " << file_name);
}
// Let's configure logging before applying the configuration,
// so we can log things during configuration process.
logger = json->get("Logging");
if (logger) {
// Configure logger first, so it can be applied to DHCPv6
// configuration. If we don't have a logger, just pass
// empty configuration.
Daemon::configureLogger(logger, CfgMgr::instance().getConfiguration());
} else {
// There was no Logging element defined in the config file.
// Let's pass an empty pointer that will remove any current
// configuration.
Daemon::configureLogger(isc::data::ConstElementPtr(),
CfgMgr::instance().getConfiguration());
}
// Get Dhcp4 component from the config
dhcp4 = json->get("Dhcp4");
......
......@@ -47,6 +47,7 @@ void configure(const std::string& file_name) {
isc::data::ConstElementPtr json;
isc::data::ConstElementPtr dhcp6;
isc::data::ConstElementPtr logger;
isc::data::ConstElementPtr result;
// Basic sanity check: file name must not be empty.
......@@ -67,6 +68,22 @@ void configure(const std::string& file_name) {
+ file_name);
}
// Let's configure logging before applying the configuration,
// so we can log things during configuration process.
logger = json->get("Logging");
if (logger) {
// Configure logger first, so it can be applied to DHCPv6
// configuration. If we don't have a logger, just pass
// empty configuration.
Daemon::configureLogger(logger, CfgMgr::instance().getConfiguration());
} else {
// There was no Logging element defined in the config file.
// Let's pass an empty pointer that will remove any current
// configuration.
Daemon::configureLogger(isc::data::ConstElementPtr(),
CfgMgr::instance().getConfiguration());
}
// Get Dhcp6 component from the config
dhcp6 = json->get("Dhcp6");
......
......@@ -59,6 +59,8 @@ libkea_dhcpsrv_la_SOURCES += key_from_key.h
libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
libkea_dhcpsrv_la_SOURCES += logging.cc logging.h
libkea_dhcpsrv_la_SOURCES += configuration.h
libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
if HAVE_MYSQL
......
......@@ -437,10 +437,15 @@ CfgMgr::getD2ClientMgr() {
return (d2_client_mgr_);
}
ConfigurationPtr
CfgMgr::getConfiguration() {
return (configuration_);
}
CfgMgr::CfgMgr()
: datadir_(DHCP_DATA_DIR),
all_ifaces_active_(false), echo_v4_client_id_(true),
d2_client_mgr_() {
d2_client_mgr_(), configuration_(new Configuration()) {
// DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
// Note: the definition of DHCP_DATA_DIR needs to include quotation marks
// See AM_CPPFLAGS definition in Makefile.am
......
......@@ -24,6 +24,7 @@
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/configuration.h>
#include <util/buffer.h>
#include <boost/shared_ptr.hpp>
......@@ -420,6 +421,12 @@ public:
/// @return a reference to the DHCP-DDNS manager.
D2ClientMgr& getD2ClientMgr();
/// @brief Returns the current configuration.
///
/// @return a pointer to the current configuration.
ConfigurationPtr getConfiguration();
protected:
/// @brief Protected constructor.
......@@ -516,6 +523,14 @@ private:
/// @brief Manages the DHCP-DDNS client and its configuration.
D2ClientMgr d2_client_mgr_;
/// @brief Configuration
///
/// This is a structure that will hold all configuration.
/// @todo: migrate all other parameters to that structure.
/// @todo: maybe this should be a vector<Configuration>, so we could keep
/// previous configurations and do a rollback if needed?
ConfigurationPtr configuration_;
};
} // namespace isc::dhcp
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DHCPSRV_CONFIG_H
#define DHCPSRV_CONFIG_H
#include <log/logger_level.h>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace dhcp {
/// @brief Defines single logging destination
///
/// This structure is used to keep log4cplus configuration parameters.
struct LoggingDestination {
/// @brief defines logging destination output
///
/// Values accepted are: stdout, stderr, syslog, syslog:name.
/// Any other destination will be considered a file name.
std::string output_;
/// @brief Maximum number of log files in rotation
int maxver_;
/// @brief Maximum log file size
uint64_t maxsize_;
};
/// @brief structure that describes one logging entry
///
/// This is a structure that conveys one logger entry configuration.
/// The structure in JSON form has the following syntax:
/// {
/// "name": "*",
/// "output_options": [
/// {
/// "output": "/path/to/the/logfile.log",
/// "maxver": 8,
/// "maxsize": 204800
/// }
/// ],
/// "severity": "WARN",
/// "debuglevel": 99
/// },
struct LoggingInfo {
/// @brief logging name
std::string name_;
/// @brief describes logging severity
isc::log::Severity severity_;
/// @brief debuglevel (used when severity_ == DEBUG)
///
/// We use range 0(least verbose)..99(most verbose)
int debuglevel_;
/// @brief specific logging destinations
std::vector<LoggingDestination> destinations_;
};
/// @brief storage for logging information in log4cplus format
typedef std::vector<isc::dhcp::LoggingInfo> LoggingInfoStorage;
/// @brief Specifies current DHCP configuration
///
/// @todo Migrate all other configuration parameters from cfgmgr.h here
struct Configuration {
/// @brief logging specific information
LoggingInfoStorage logging_info_;
};
/// @brief pointer to the configuration
typedef boost::shared_ptr<Configuration> ConfigurationPtr;
} // namespace isc::dhcp
} // namespace isc
#endif
......@@ -15,11 +15,13 @@
#include <config.h>
#include <dhcpsrv/daemon.h>
#include <exceptions/exceptions.h>
#include <cc/data.h>
#include <boost/bind.hpp>
#include <logging.h>
#include <errno.h>
/// @brief provides default implementation for basic daemon operations
///
///
/// This file provides stub implementations that are expected to be redefined
/// in derived classes (e.g. ControlledDhcpv6Srv)
namespace isc {
......@@ -53,6 +55,41 @@ void Daemon::handleSignal() {
}
}
void Daemon::configureLogger(isc::data::ConstElementPtr log_config,
const ConfigurationPtr& storage) {
// This is utility class that translates JSON structures into formats
// understandable by log4cplus.
LogConfigParser parser(storage);
if (!log_config) {
// There was no logger configuration. Let's clear any config
// and revert to the default.
parser.defaultLogging(); // Set up default logging
return;
}
isc::data::ConstElementPtr loggers;
loggers = log_config->get("loggers");
if (!loggers) {
// There is Logging structure, but it doesn't have loggers
// array in it. Let's clear any old logging configuration
// we may have and revert to the default.
parser.defaultLogging(); // Set up default logging
return;
}
// Translate JSON structures into log4cplus formats
parser.parseConfiguration(loggers);
// Apply the configuration
/// @todo: Once configuration unrolling is implemented,
/// this call will be moved to a separate method.
parser.applyConfiguration();
}
};
};
......@@ -12,12 +12,16 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DAEMON_H
#define DAEMON_H
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/configuration.h>
#include <util/signal_set.h>
#include <boost/noncopyable.hpp>
#include <string>
namespace isc {
namespace dhcp {
......@@ -105,7 +109,7 @@ public:
return (config_file_);
}
/// Initializes logger
/// @brief Initializes logger
///
/// This method initializes logger. Currently its implementation is specific
/// to each configuration backend.
......@@ -114,6 +118,19 @@ public:
/// @param verbose verbose mode (true usually enables DEBUG messages)
static void loggerInit(const char* log_name, bool verbose);
/// @brief Configures logger
///
/// Applies configuration stored in "Logging" structure in the
/// configuration file. This structure has a "loggers" array that
/// contains 0 or more entries, each configuring one logging source
/// (name, severity, debuglevel), each with zero or more outputs (file,
/// maxsize, maximum number of files).
///
/// @param log_config JSON structures that describe logging
/// @param storage configuration will be stored here
static void configureLogger(isc::data::ConstElementPtr log_config,
const isc::dhcp::ConfigurationPtr& storage);
protected:
/// @brief Invokes handler for the next received signal.
......@@ -150,3 +167,5 @@ private:
}; // end of isc::dhcp namespace
}; // end of isc namespace
#endif
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <cc/data.h>
#include <dhcpsrv/logging.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <log/logger_specification.h>
#include <log/logger_manager.h>
using namespace isc::data;
using namespace isc::log;
namespace isc {
namespace dhcp {
static const char* DEFAULT_SYSLOG_NAME = "kea";
LogConfigParser::LogConfigParser(const ConfigurationPtr& storage)
:config_(storage) {
if (!storage) {
isc_throw(InvalidOperation, "LogConfigParser needs a pointer to the "
"configuration, so parsed data can be stored there");
}
}
void LogConfigParser::parseConfiguration(isc::data::ConstElementPtr loggers) {
// Iterate over all entries in "Logging/loggers" list
BOOST_FOREACH(ConstElementPtr logger, loggers->listValue()) {
parseConfigEntry(logger);
}
}
void LogConfigParser::parseConfigEntry(isc::data::ConstElementPtr entry) {
if (!entry) {
// This should not happen, but let's be on the safe side and check
return;
}
if (!config_) {
isc_throw(BadValue, "configuration storage not set, can't parse logger config.");
}
LoggingInfo info;
// Get a name
isc::data::ConstElementPtr name_ptr = entry->get("name");
if (!name_ptr) {
isc_throw(BadValue, "loggers entry does not have a mandatory 'name' "
"element (" << entry->getPosition() << ")");
}
info.name_ = name_ptr->stringValue();
// Get severity
isc::data::ConstElementPtr severity_ptr = entry->get("severity");
if (!name_ptr) {
isc_throw(BadValue, "loggers entry does not have a mandatory "
"'severity' element (" << entry->getPosition() << ")");
}
try {
info.severity_ = isc::log::getSeverity(severity_ptr->stringValue().c_str());
} catch (const std::exception& ex) {
isc_throw(BadValue, "Unable to convert '" << severity_ptr->stringValue()
<< "' into allowed severity (" << severity_ptr->getPosition()
<< ")");
}
// Get debug logging level
info.debuglevel_ = 0;
isc::data::ConstElementPtr debuglevel_ptr = entry->get("debuglevel");
// It's ok to not have debuglevel, we'll just assume its least verbose
// (0) level.
if (debuglevel_ptr) {
try {
info.debuglevel_ = boost::lexical_cast<int>(debuglevel_ptr->str());
if ( (info.debuglevel_ < 0) || (info.debuglevel_ > 99) ) {
// Comment doesn't matter, it is caught several lines below
isc_throw(BadValue, "");
}
} catch (...) {
isc_throw(BadValue, "Unable to convert '" << debuglevel_ptr->stringValue()
<< "' into allowed debuglevel range (0-99) ("
<< debuglevel_ptr->getPosition() << ")");
}
}
isc::data::ConstElementPtr output_options = entry->get("output_options");
if (output_options) {
parseOutputOptions(info.destinations_, output_options);
}
config_->logging_info_.push_back(info);
}
void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination,
isc::data::ConstElementPtr output_options) {
if (!output_options) {
isc_throw(BadValue, "Missing 'output_options' structure in 'loggers'");
}
BOOST_FOREACH(ConstElementPtr output_option, output_options->listValue()) {
LoggingDestination dest;
isc::data::ConstElementPtr output = output_option->get("output");
if (!output) {
isc_throw(BadValue, "output_options entry does not have a mandatory 'output' "
"element (" << output_option->getPosition() << ")");
}
dest.output_ = output->stringValue();
isc::data::ConstElementPtr maxver_ptr = output_option->get("maxver");
if (maxver_ptr) {
dest.maxver_ = boost::lexical_cast<int>(maxver_ptr->str());
}
isc::data::ConstElementPtr maxsize_ptr = output_option->get("maxsize");
if (maxsize_ptr) {
dest.maxsize_ = boost::lexical_cast<uint64_t>(maxsize_ptr->str());
}
destination.push_back(dest);
}
}
void LogConfigParser::applyConfiguration() {
// Constants: not declared static as this is function is expected to be
// called once only
static const std::string STDOUT = "stdout";
static const std::string STDERR = "stderr";
static const std::string SYSLOG = "syslog";
static const std::string SYSLOG_COLON = "syslog:";
// Set locking directory to /tmp
setenv("B10_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
// Now iterate through all specified loggers
for (LoggingInfoStorage::const_iterator it = config_->logging_info_.begin();
it != config_->logging_info_.end(); ++it) {
// Prepare the objects to define the logging specification
LoggerSpecification spec(it->name_,
it->severity_,
it->debuglevel_);
OutputOption option;
for (std::vector<LoggingDestination>::const_iterator dest = it->destinations_.begin();
dest != it->destinations_.end(); ++dest) {
// Set up output option according to destination specification
if (dest->output_ == STDOUT) {
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDOUT;
} else if (dest->output_ == STDERR) {
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDERR;
} else if (dest->output_ == SYSLOG) {
option.destination = OutputOption::DEST_SYSLOG;
// Use default specified in OutputOption constructor for the
// syslog destination
} else if (dest->output_.find(SYSLOG_COLON) == 0) {
option.destination = OutputOption::DEST_SYSLOG;
// Must take account of the string actually being "syslog:"
if (dest->output_ == SYSLOG_COLON) {
// The expected syntax is syslog:facility. User skipped
// the logging name, so we'll just use DEFAULT_SYSLOG_NAME.
option.facility = DEFAULT_SYSLOG_NAME;
} else {
// Everything else in the string is the facility name
option.facility = dest->output_.substr(SYSLOG_COLON.size());
}
} else {
// Not a recognised destination, assume a file.
option.destination = OutputOption::DEST_FILE;
option.filename = dest->output_;
}
// ... and set the destination
spec.addOutputOption(option);
}
LoggerManager manager;
manager.process(spec);
}
}
void LogConfigParser::defaultLogging() {
LoggerSpecification spec("kea", isc::log::INFO, 0);
OutputOption option;
option.destination = OutputOption::DEST_CONSOLE;
option.stream = OutputOption::STR_STDOUT;
spec.addOutputOption(option);
LoggerManager manager;
manager.process(spec);
}
} // namespace isc::dhcp
} // namespace isc
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DHCPSRV_LOGGING_H
#define DHCPSRV_LOGGING_H
#include <cc/data.h>
#include <dhcpsrv/configuration.h>
#include <dhcpsrv/cfgmgr.h>
#include <stdint.h>
#include <vector>
namespace isc {
namespace dhcp {
/// @brief Interprets JSON structures and translates them to log4cplus
///
/// This parser iterates over provided JSON structures and translates them
/// into values applicable to log4cplus.
///
/// This class uses Configuration structure to store logging configuration.
class LogConfigParser {
public:
/// @brief Constructor
///
/// @param storage parsed logging configuration will be stored here
LogConfigParser(const ConfigurationPtr& storage);
/// @brief Parses specified configuration
///
/// Walks over specified logging configuration JSON structures and store
/// parsed information in config_->logging_info_.
///
/// @param log_config JSON structures to be parsed
void parseConfiguration(isc::data::ConstElementPtr log_config);
/// @brief Applies stored configuration
void applyConfiguration();
/// @brief Configures default logging
static void defaultLogging();
protected:
/// @brief Parses one JSON structure in Logging/loggers" array
///
/// The structure has the following syntax:
/// {
/// "name": "*",
/// "output_options": [
/// {
/// "output": "/home/thomson/kea-inst/kea-warn.log",