Commit 8216a6b1 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3198'

parents 2340a0ff 8aaf38b8
/user_chk_messages.cc
/user_chk_messages.h
/s-messages
......@@ -10,26 +10,21 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS)
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
# Until logging in dynamically loaded libraries is fixed,
# Define rule to build logging source files from message file
# user_chk_messages.h user_chk_messages.cc: s-messages
# Until logging in dynamically loaded libraries is fixed, exclude these.
# s-messages: user_chk_messages.mes
# $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes
# touch $@
user_chk_messages.h user_chk_messages.cc: s-messages
s-messages: user_chk_messages.mes
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes
touch $@
# Tell automake that the message files are built as part of the build process
# (so that they are built before the main library is built).
# BUILT_SOURCES = user_chk_messages.h user_chk_messages.cc
BUILT_SOURCES =
BUILT_SOURCES = user_chk_messages.h user_chk_messages.cc
# Ensure that the message file is included in the distribution
EXTRA_DIST = libdhcp_user_chk.dox
# Get rid of generated message files on a clean
#CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
CLEANFILES = *.gcno *.gcda
CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
# convenience archive
......@@ -42,15 +37,13 @@ libduc_la_SOURCES += pkt_send_co.cc
libduc_la_SOURCES += subnet_select_co.cc
libduc_la_SOURCES += user.cc user.h
libduc_la_SOURCES += user_chk.h
# Until logging in dynamically loaded libraries is fixed, exclude these.
#libduc_la_SOURCES += user_chk_log.cc user_chk_log.h
libduc_la_SOURCES += user_chk_log.cc user_chk_log.h
libduc_la_SOURCES += user_data_source.h
libduc_la_SOURCES += user_file.cc user_file.h
libduc_la_SOURCES += user_registry.cc user_registry.h
libduc_la_SOURCES += version.cc
# Until logging in dynamically loaded libraries is fixed, exclude these.
#nodist_libduc_la_SOURCES = user_chk_messages.cc user_chk_messages.h
nodist_libduc_la_SOURCES = user_chk_messages.cc user_chk_messages.h
libduc_la_CXXFLAGS = $(AM_CXXFLAGS)
libduc_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -15,6 +15,7 @@
/// @file load_unload.cc Defines the load and unload hooks library functions.
#include <hooks/hooks.h>
#include <user_chk_log.h>
#include <user_registry.h>
#include <user_file.h>
......@@ -98,8 +99,8 @@ int load(LibraryHandle&) {
}
catch (const std::exception& ex) {
// Log the error and return failure.
std::cout << "DHCP UserCheckHook could not be loaded: "
<< ex.what() << std::endl;
LOG_ERROR(user_chk_logger, USER_CHK_HOOK_LOAD_ERROR)
.arg(ex.what());
ret_val = 1;
}
......@@ -120,8 +121,8 @@ int unload() {
} catch (const std::exception& ex) {
// On the off chance something goes awry, catch it and log it.
// @todo Not sure if we should return a non-zero result or not.
std::cout << "DHCP UserCheckHook could not be unloaded: "
<< ex.what() << std::endl;
LOG_ERROR(user_chk_logger, USER_CHK_HOOK_UNLOAD_ERROR)
.arg(ex.what());
}
return (0);
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -20,6 +20,7 @@
#include <dhcp/pkt6.h>
#include <dhcpsrv/subnet.h>
#include <user_chk.h>
#include <user_chk_log.h>
using namespace isc::dhcp;
using namespace isc::hooks;
......@@ -49,8 +50,7 @@ extern "C" {
/// @return 0 upon success, non-zero otherwise.
int subnet4_select(CalloutHandle& handle) {
if (!user_registry) {
std::cout << "DHCP UserCheckHook : subnet4_select UserRegistry is null"
<< std::endl;
LOG_ERROR(user_chk_logger, USER_CHK_SUBNET4_SELECT_REGISTRY_NULL);
return (1);
}
......@@ -78,8 +78,8 @@ int subnet4_select(CalloutHandle& handle) {
handle.setArgument("subnet4", subnet);
}
} catch (const std::exception& ex) {
std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
<< ex.what() << std::endl;
LOG_ERROR(user_chk_logger, USER_CHK_SUBNET4_SELECT_ERROR)
.arg(ex.what());
return (1);
}
......@@ -104,8 +104,7 @@ int subnet4_select(CalloutHandle& handle) {
/// @return 0 upon success, non-zero otherwise.
int subnet6_select(CalloutHandle& handle) {
if (!user_registry) {
std::cout << "DHCP UserCheckHook : subnet6_select UserRegistry is null"
<< std::endl;
LOG_ERROR(user_chk_logger, USER_CHK_SUBNET6_SELECT_REGISTRY_NULL);
return (1);
}
......@@ -133,8 +132,8 @@ int subnet6_select(CalloutHandle& handle) {
handle.setArgument("subnet6", subnet);
}
} catch (const std::exception& ex) {
std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
<< ex.what() << std::endl;
LOG_ERROR(user_chk_logger, USER_CHK_SUBNET6_SELECT_ERROR)
.arg(ex.what());
return (1);
}
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -19,6 +19,8 @@
#include <log/macros.h>
#include <user_chk_messages.h>
namespace user_chk {
/// @brief User Check Logger
///
/// Define the logger used to log messages. We could define it in multiple
......@@ -26,4 +28,6 @@
/// space.
extern isc::log::Logger user_chk_logger;
} // end of namespace user_chk
#endif // USER_CHK_LOG_H
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -20,6 +20,8 @@
#include <hooks/library_manager.h>
#include <hooks/pointer_converter.h>
#include <hooks/server_hooks.h>
#include <log/logger_manager.h>
#include <log/message_initializer.h>
#include <string>
#include <vector>
......@@ -263,6 +265,19 @@ LibraryManager::loadLibrary() {
// Open the library (which is a check that it exists and is accessible).
if (openLibrary()) {
// The hook libraries provide their own log messages and logger
// instances. This step is required to register log messages for
// the library being loaded in the global dictionary. Ideally, this
// should be called after all libraries have been loaded but we're
// going to call the version() and load() functions here and these
// functions may already contain logging statements.
isc::log::MessageInitializer::loadDictionary();
// The log messages registered by the new hook library may duplicate
// some of the existing messages. Log warning for each duplicated
// message now.
isc::log::LoggerManager::logDuplicatedMessages();
// Library opened OK, see if a version function is present and if so,
// check what value it returns.
if (checkVersion()) {
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -120,6 +120,10 @@ public:
/// Open the library and check the version. If all is OK, load all standard
/// symbols then call "load" if present.
///
/// It also calls the @c isc::log::MessageInitializer::loadDictionary, prior
/// to invoking the @c version function of the library, to update the global
/// logging dictionary with the log messages registered by the loaded library.
///
/// @return true if the library loaded successfully, false otherwise. In the
/// latter case, the library will be unloaded if possible.
bool loadLibrary();
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -34,13 +34,39 @@
/// ...where data_1, data_2 and data_3 are the values passed in arguments of
/// the same name to the three callouts (data_1 passed to hookpt_one, data_2
/// to hookpt_two etc.) and the result is returned in the argument "result".
///
/// - The logger instance is created and some log messages are defined. Some
/// log messages are duplicated purposely, to check that the logger handles
/// the duplicates correctly.
#include <hooks/hooks.h>
#include <fstream>
#include <log/logger.h>
#include <log/macros.h>
#include <log/message_initializer.h>
using namespace isc::hooks;
using namespace isc::log;
using namespace std;
namespace {
/// @brief Logger used by the library.
isc::log::Logger logger("bcl");
/// @brief Log messages.
const char* log_messages[] = {
"BCL_LOAD_START", "basic callout load %1",
"BCL_LOAD_END", "basic callout load end",
"BCL_LOAD_END", "duplicate of basic callout load end",
NULL
};
/// @brief Initializer for log messages.
const MessageInitializer message_initializer(log_messages);
}
extern "C" {
// Callouts. All return their result through the "result" argument.
......@@ -117,6 +143,8 @@ load(isc::hooks::LibraryHandle&) {
#ifdef USE_STATIC_LINK
hooksStaticLinkInit();
#endif
LOG_INFO(logger, "BCL_LOAD_START").arg("argument");
LOG_INFO(logger, "BCL_LOAD_END");
return (0);
}
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013,2015 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
......@@ -21,6 +21,9 @@
#include <hooks/tests/marker_file.h>
#include <hooks/tests/test_libraries.h>
#include <log/message_dictionary.h>
#include <log/message_initializer.h>
#include <gtest/gtest.h>
#include <algorithm>
......@@ -32,6 +35,7 @@
using namespace isc;
using namespace isc::hooks;
using namespace isc::log;
using namespace std;
namespace {
......@@ -566,4 +570,30 @@ TEST_F(LibraryManagerTest, validateLibraries) {
EXPECT_TRUE(LibraryManager::validateLibrary(UNLOAD_CALLOUT_LIBRARY));
}
// Check that log messages are properly registered and unregistered.
TEST_F(LibraryManagerTest, libraryLoggerSetup) {
// Load a library with all framework functions.
LibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY), 0,
callout_manager_);
EXPECT_TRUE(lib_manager.loadLibrary());
// After loading the library, the global logging dictionary should
// contain log messages registerd for this library.
const MessageDictionaryPtr& dict = MessageDictionary::globalDictionary();
EXPECT_EQ("basic callout load %1", dict->getText("BCL_LOAD_START"));
EXPECT_EQ("basic callout load end", dict->getText("BCL_LOAD_END"));
// Some of the messages defined by the hook library are duplicates. But,
// the loadLibrary function should have logged the duplicates and clear
// the duplicates list. By checking that the list of duplicates is empty
// we test that the LibraryManager handles the duplicates (logs and
// clears them).
EXPECT_TRUE(MessageInitializer::getDuplicates().empty());
// After unloading the library, the messages should be unregistered.
EXPECT_TRUE(lib_manager.unloadLibrary());
EXPECT_TRUE(dict->getText("BCL_LOAD_START").empty());
EXPECT_TRUE(dict->getText("BCL_LOAD_END").empty());
}
} // Anonymous namespace
......@@ -619,10 +619,10 @@ main(int argc, char* argv[]) {
}
catch (const MessageException& e) {
// Create an error message from the ID and the text
MessageDictionary& global = MessageDictionary::globalDictionary();
const MessageDictionaryPtr& global = MessageDictionary::globalDictionary();
string text = e.id();
text += ", ";
text += global.getText(e.id());
text += global->getText(e.id());
// Format with arguments
vector<string> args(e.arguments());
for (size_t i(0); i < args.size(); ++ i) {
......
// Copyright (C) 2011,2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,2014-2015 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
......@@ -126,7 +126,7 @@ LoggerImpl::getEffectiveDebugLevel() {
string*
LoggerImpl::lookupMessage(const MessageID& ident) {
return (new string(string(ident) + " " +
MessageDictionary::globalDictionary().getText(ident)));
MessageDictionary::globalDictionary()->getText(ident)));
}
// Replace the interprocess synchronization object
......
......@@ -121,26 +121,31 @@ LoggerManager::init(const std::string& root, isc::log::Severity severity,
// Check if there were any duplicate message IDs in the default dictionary
// and if so, log them. Log using the logging facility logger.
const vector<string>& duplicates = MessageInitializer::getDuplicates();
logDuplicatedMessages();
// Replace any messages with local ones (if given)
if (file) {
readLocalMessageFile(file);
}
// Ensure that the mutex is constructed and ready at this point.
(void) getMutex();
}
void
LoggerManager::logDuplicatedMessages() {
const list<string>& duplicates = MessageInitializer::getDuplicates();
if (!duplicates.empty()) {
// There are duplicates present. This list itself may contain
// duplicates; if so, the message ID is listed as many times as
// there are duplicates.
for (vector<string>::const_iterator i = duplicates.begin();
for (list<string>::const_iterator i = duplicates.begin();
i != duplicates.end(); ++i) {
LOG_WARN(logger, LOG_DUPLICATE_MESSAGE_ID).arg(*i);
}
MessageInitializer::clearDuplicates();
}
// Replace any messages with local ones (if given)
if (file) {
readLocalMessageFile(file);
}
// Ensure that the mutex is constructed and ready at this point.
(void) getMutex();
}
......@@ -150,8 +155,8 @@ LoggerManager::init(const std::string& root, isc::log::Severity severity,
void
LoggerManager::readLocalMessageFile(const char* file) {
MessageDictionary& dictionary = MessageDictionary::globalDictionary();
MessageReader reader(&dictionary);
const MessageDictionaryPtr& dictionary = MessageDictionary::globalDictionary();
MessageReader reader(dictionary.get());
// Turn off use of any lock files. This is because this logger can
// be used by standalone programs which may not have write access to
......
......@@ -120,6 +120,14 @@ public:
int dbglevel = 0, const char* file = NULL,
bool buffer = false);
/// \brief List duplicated log messages.
///
/// Lists the duplicated log messages using warning severity. Then, it
/// clears the list of duplicated messages. This method is called by the
/// \c init method and by the \c isc::hooks::LibraryManager when the new
/// hooks library is loaded.
static void logDuplicatedMessages();
/// \brief Reset logging
///
/// Resets logging to whatever was set in the call to init(), expect for
......
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2015 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
......@@ -704,6 +704,34 @@ In the case of "stdout", "stderr" and "syslog", they must be written exactly
as is - no leading or trailing spaces, and in lower-case.</dd>
</dl>
@subsection logInitializationHooks Hooks Specific Notes
All hooks libraries should use Kea logging mechanisms. The loggers and the
library specific log messages are created in the same way as for the core
Kea modules. The loggers created within the hook library belong to the
logging hierarchy of the Kea process and their configuration can be
controlled from the Kea configuration file. If the configuration file doesn't
contain the specific configuration for the logger used in the library,
this logger is given the configuration of the root Kea logger.
The hook libraries are loaded dynamically. This requires that the global log
messages dictionary, holding the mapping of specific log message
identifiers to the actual messages, is updated to include the messages
specified in the hook library when the library is loaded. Conversely, the
messages have to be removed from the dictionary when the library is unloaded.
The new messages are added to the global dictionary using the
@c isc::log::MessageInitializer::loadDictionary static function. It is
called by the @c isc::hooks::LibraryManager::loadLibrary for each loaded
library.
When the library is unloaded, the instance of the
@c isc::log::MessageInitializer defined in the library is destroyed
and its destructor removes the messages registered by the destroyed
instance from the global dictionary.
The hook library itself must not perform any action to register or
unregister log messages in the global dictionary!
@subsection logInitializationPython Python Initialization
To initialize the logger in a Python program, the "init" method must be
called:
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,2015 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
......@@ -56,6 +56,17 @@ MessageDictionary::replace(const std::string& ident, const std::string& text) {
return (found);
}
bool
MessageDictionary::erase(const std::string& ident, const std::string& text) {
Dictionary::iterator mes = dictionary_.find(ident);
// Both the ID and the text must match.
bool found = (mes != dictionary_.end() && (mes->second == text));
if (found) {
dictionary_.erase(mes);
}
return (found);
}
// Load a set of messages
vector<std::string>
......@@ -100,9 +111,9 @@ MessageDictionary::getText(const std::string& ident) const {
// Return global dictionary
MessageDictionary&
const MessageDictionaryPtr&
MessageDictionary::globalDictionary() {
static MessageDictionary global;
static MessageDictionaryPtr global(new MessageDictionary());
return (global);
}
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,2015 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
......@@ -21,12 +21,19 @@
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <log/message_types.h>
namespace isc {
namespace log {
/// \brief Forward declaration of \c MessageDictionary
class MessageDictionary;
/// \brief Shared pointer to the \c MessageDictionary.
typedef boost::shared_ptr<MessageDictionary> MessageDictionaryPtr;
/// \brief Message Dictionary
///
/// The message dictionary is a wrapper around a std::map object, and allows
......@@ -96,7 +103,6 @@ public:
return (replace(boost::lexical_cast<std::string>(ident), text));
}
/// \brief Replace Message
///
/// Alternate signature.
......@@ -109,6 +115,25 @@ public:
virtual bool replace(const std::string& ident, const std::string& text);
/// \brief Removes the specified message from the dictionary.
///
/// Checks if both the message identifier and the text match the message
/// in the dictionary before removal. If the text doesn't match it is
/// an indication that the message which removal is requested is a
/// duplicate of another message. This may occur when two Kea modules
/// register messages with the same identifier. When one of the modules
/// is unloaded and the relevant messages are unregistered, there is a
/// need to make sure that the message registered by the other module
/// is not accidentally removed. Hence, the additional check for the
/// text match is needed.
///
/// \param ident Identification of the message to remove.
/// \param text Message text
///
/// \return true of the message has been removed, false if the message
/// couldn't be found.
virtual bool erase(const std::string& ident, const std::string& text);
/// \brief Load Dictionary
///
/// Designed to be used during the initialization of programs, this
......@@ -126,7 +151,6 @@ public:
/// empty.
virtual std::vector<std::string> load(const char* elements[]);
/// \brief Get Message Text
///
/// Given an ID, retrieve associated message text.
......@@ -178,7 +202,7 @@ public:
/// Returns a pointer to the singleton global dictionary.
///
/// \return Pointer to global dictionary.
static MessageDictionary& globalDictionary();
static const MessageDictionaryPtr& globalDictionary();
private:
Dictionary dictionary_; ///< Holds the ID to text lookups
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011,2015 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
......@@ -12,10 +12,13 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <cassert>
#include <cstdlib>
#include <log/message_dictionary.h>
#include <log/message_initializer.h>
#include <boost/array.hpp>
#include <algorithm>
#include <cassert>
#include <cstdlib>
// As explained in the header file, initialization of the message dictionary
......@@ -32,20 +35,19 @@
namespace {
// Declare the array of pointers to value arrays.
const char** logger_values[isc::log::MessageInitializer::MAX_MESSAGE_ARRAYS];
/// Type definition for the list of pointers to messages.
typedef std::list<const char**> LoggerValuesList;
// Declare the index used to access the array. As this needs to be initialized
// at first used, it is accessed it via a function.
size_t& getIndex() {
static size_t index = 0;
return (index);
/// Return reference to the list of log messages.
LoggerValuesList& getNonConstLoggerValues() {
static LoggerValuesList logger_values;
return (logger_values);
}
// Return the duplicates singleton version (non-const for local use)
std::vector<std::string>&
std::list<std::string>&
getNonConstDuplicates() {
static std::vector<std::string> duplicates;
static std::list<std::string> duplicates;