diff options
-rw-r--r-- | icetray/dryice/Jamfile.jam | 1 | ||||
-rw-r--r-- | icetray/icetray/Jamfile.jam | 11 | ||||
-rw-r--r-- | icetray/icetray/icetrayService.cpp | 5 | ||||
-rw-r--r-- | icetray/icetray/icetrayService.h | 4 | ||||
-rw-r--r-- | icetray/icetray/logWriter.ice | 26 | ||||
-rw-r--r-- | icetray/icetray/logger.cpp | 136 | ||||
-rw-r--r-- | icetray/icetray/logger.h | 89 | ||||
-rw-r--r-- | icetray/unittests/Jamfile.jam | 7 | ||||
-rw-r--r-- | icetray/unittests/testIceTrayLogger.cpp | 209 |
9 files changed, 481 insertions, 7 deletions
diff --git a/icetray/dryice/Jamfile.jam b/icetray/dryice/Jamfile.jam index 5f2da34..6df0569 100644 --- a/icetray/dryice/Jamfile.jam +++ b/icetray/dryice/Jamfile.jam @@ -13,6 +13,7 @@ lib dryice : <library>..//boost_system <library>..//boost_thread <library>..//dl + <implicit-dependency>../icetray//icetray : : <include>. ; diff --git a/icetray/icetray/Jamfile.jam b/icetray/icetray/Jamfile.jam index cc7d204..42f5389 100644 --- a/icetray/icetray/Jamfile.jam +++ b/icetray/icetray/Jamfile.jam @@ -1,12 +1,7 @@ import package ; lib icetray : - sqlSource.cpp - staticSqlSource.cpp - abstractDatabaseClient.cpp - abstractCachingDatabaseClient.cpp - defaultPool.cpp - icetrayService.cpp + [ glob-tree *.cpp *.ice : bin ] : <library>..//adhocutil <library>..//dbppcore @@ -19,6 +14,7 @@ lib icetray : <library>..//boost_system <library>..//boost_thread <library>../..//glibmm + <allow-ice>yes : : <include>. <library>..//dbppcore @@ -28,5 +24,6 @@ lib icetray : <library>..//boost_thread ; -package.install install-main : <install-source-root>. : : icetray : [ glob *.h : defaultPool.h ] ; +package.install install-main : : : icetray : [ glob-tree *.h : defaultPool.h ] ; +package.install-data install-ice : icetray/ice : [ glob *.ice ] ; diff --git a/icetray/icetray/icetrayService.cpp b/icetray/icetray/icetrayService.cpp index 7f66cd1..8353fd2 100644 --- a/icetray/icetray/icetrayService.cpp +++ b/icetray/icetray/icetrayService.cpp @@ -46,6 +46,11 @@ namespace IceTray { p->getPropertyWithDefault("DryIce.PoolProvider", "DefaultPool"), name, type, p)); } + + Logging::LogManager * Service::getLogManager() + { + return &logManager; + } } extern "C" { diff --git a/icetray/icetray/icetrayService.h b/icetray/icetray/icetrayService.h index 75902b7..ffc4251 100644 --- a/icetray/icetray/icetrayService.h +++ b/icetray/icetray/icetrayService.h @@ -5,6 +5,7 @@ #include <factory.h> #include <visibility.h> #include "database.h" +#include "logger.h" namespace IceTray { class DLL_PUBLIC Service : public IceBox::Service, public AdHoc::AbstractPluginImplementation { @@ -20,11 +21,14 @@ namespace IceTray { DatabasePoolPtr getConnectionPool(const Ice::CommunicatorPtr & ic, const std::string & type, const std::string & prefix); + Logging::LogManager * getLogManager(); + static Service * getCurrent(); private: friend class DryIce; Ice::ObjectAdapterPtr adp; + Logging::LogManager logManager; static Service * current; }; diff --git a/icetray/icetray/logWriter.ice b/icetray/icetray/logWriter.ice new file mode 100644 index 0000000..141d8c3 --- /dev/null +++ b/icetray/icetray/logWriter.ice @@ -0,0 +1,26 @@ +#ifndef ICETRAY_LOGWRITER +#define ICETRAY_LOGWRITER + +module IceTray { + module Logging { + enum LogLevel { + /* These match those defined in syslog.h */ + EMERG = 0, + ALERT = 1, + CRIT = 2, + ERR = 3, + WARNING = 4, + NOTICE = 5, + INFO = 6, + DEBUG = 7 + }; + + interface LogWriter { + optional(0) LogLevel level(string domain); + void message(LogLevel level, string domain, string message); + }; + }; +}; + +#endif + diff --git a/icetray/icetray/logger.cpp b/icetray/icetray/logger.cpp new file mode 100644 index 0000000..b4cf629 --- /dev/null +++ b/icetray/icetray/logger.cpp @@ -0,0 +1,136 @@ +#include "logger.h" +#include <factory.impl.h> +#include <buffer.h> +#include <lockHelpers.h> + +INSTANTIATEFACTORY(IceTray::Logging::LogWriter, Ice::CommunicatorPtr); + +namespace IceTray { + namespace Logging { + LoggerBase::LoggerBase(LogManager * manager, const std::string & domain) : + logs(manager->getLogsForDomain(domain)), + domain(domain) + { + } + + LoggerBase::~LoggerBase() + { + } + + Logger::Logger(LogManager * manager, const std::string & domain) : LoggerBase(manager, domain) { } + + void + Logger::message(LogLevel priority, const std::string & msg) const + { + const auto fl = firstFor(priority); + if (fl != logs.end()) { + message(fl, priority, msg); + } + } + + void + Logger::message(LogLevelWriters::const_iterator fl, LogLevel priority, const std::string & msg) const + { + SharedLock(_lock); + while (fl != logs.end()) { + for (auto log : *fl) { + log->message(priority, domain, msg); + } + fl++; + } + } + + void + Logger::messagebf(const LogLevelWriters::const_iterator fl, LogLevel priority, const boost::format & f) const + { + message(fl, priority, f.str()); + } + + void + Logger::messagef(LogLevel priority, const char * msgfmt, ...) const + { + const auto fl = firstFor(priority); + if (fl != logs.end()) { + va_list v; + va_start(v, msgfmt); + vmessagef(fl, priority, msgfmt, v); + va_end(v); + } + } + + void + Logger::vmessagef(LogLevelWriters::const_iterator fl, LogLevel priority, const char * msgfmt, va_list va) const + { + char * msg; + int len = vasprintf(&msg, msgfmt, va); + if (len > 0) { + message(fl, priority, msg); + } + free(msg); + } + + LogLevelWriters::const_iterator + Logger::firstFor(LogLevel priority) const + { + SharedLock(_lock); + auto i = logs.begin(); + i += priority; + while (i != logs.end()) { + if (!i->empty()) { + return i; + } + i++; + } + return logs.end(); + } + + LoggerPtr + LogManager::getLogger(const std::string & domain) + { + auto logger = LoggerPtr(new Logger(this, domain)); + loggers.insert(logger.get()); + return logger; + } + + LogLevelWriters + LogManager::getLogsForDomain(const std::string & domain) const + { + SharedLock(_lock); + LogLevelWriters logs; + for (const auto & log : logWriters) { + auto level = log->level(domain); + if (level) { + logs[*level].insert(log); + } + } + return logs; + } + + void + LogManager::addWriter(LogWriterPrx writer) + { + UpgradableLock(_lock, l); + UpgradeScopeLock(l) { + logWriters.insert(writer); + } + for (auto logger : loggers) { + Lock(logger->_lock); + logger->logs = getLogsForDomain(logger->domain); + } + } + + void + LogManager::removeWriter(LogWriterPrx writer) + { + UpgradableLock(_lock, l); + UpgradeScopeLock(l) { + logWriters.erase(writer); + } + for (auto logger : loggers) { + Lock(logger->_lock); + logger->logs = getLogsForDomain(logger->domain); + } + } + } +} + diff --git a/icetray/icetray/logger.h b/icetray/icetray/logger.h new file mode 100644 index 0000000..cb16852 --- /dev/null +++ b/icetray/icetray/logger.h @@ -0,0 +1,89 @@ +#ifndef ICETRAY_LOGGING_H +#define ICETRAY_LOGGING_H + +#include <factory.h> +#include <visibility.h> +#include <logWriter.h> +#include <boost/format/format_fwd.hpp> +#include <map> +#include <buffer.h> +#include <boost/thread/shared_mutex.hpp> + +namespace IceTray { + namespace Logging { + class LogManager; + class LoggerBase; + + typedef std::set<LogWriterPrx> LogWriters; + typedef std::array<LogWriters, 8> LogLevelWriters; + typedef std::set<LoggerBase *> Loggers; + + class DLL_PUBLIC LoggerBase { + public: + LoggerBase(LogManager *, const std::string & domain); + ~LoggerBase(); + + protected: + friend class LogManager; + mutable boost::shared_mutex _lock; + LogLevelWriters logs; + const std::string domain; + }; + + class DLL_PUBLIC Logger : public LoggerBase { + public: + Logger(LogManager *, const std::string & domain); + + void message(LogLevel priority, const std::string & msg) const; + void messagef(LogLevel priority, const char * msgfmt, ...) const __attribute__ ((format (printf, 3, 4))); + template <typename fmt, typename... Args> + void messagectf(LogLevel priority, const Args & ... args) const { + const auto fl = firstFor(priority); + if (fl != logs.end()) { + message(fl, priority, fmt::get(args...)); + } + } + template <typename... Args> + void messagebf(LogLevel priority, const std::string & msgfmt, const Args & ... args) const { + const auto fl = firstFor(priority); + if (fl != logs.end()) { + auto fmt = AdHoc::Buffer::getFormat(msgfmt); + messagebf(fl, priority, *fmt, args...); + } + } + + private: + void message(LogLevelWriters::const_iterator fl, LogLevel priority, const std::string & msg) const; + void vmessagef(LogLevelWriters::const_iterator fl, LogLevel priority, const char * msgfmt, va_list) const; + template <typename Arg, typename... OtherArgs> + void messagebf(LogLevelWriters::const_iterator fl, LogLevel priority, boost::format & f, const Arg & arg, const OtherArgs & ... otherargs) const { + f % arg; + messagebf(fl, priority, f, otherargs...); + } + void messagebf(LogLevelWriters::const_iterator fl, LogLevel priority, const boost::format & f) const; + + LogLevelWriters::const_iterator firstFor(LogLevel priority) const; + }; + typedef std::shared_ptr<Logger> LoggerPtr; + + class DLL_PUBLIC LogManager { + public: + LoggerPtr getLogger(const std::string &); + LogLevelWriters getLogsForDomain(const std::string &) const; + void addWriter(LogWriterPrx writer); + void removeWriter(LogWriterPrx writer); + + private: + mutable boost::shared_mutex _lock; + Loggers loggers; + LogWriters logWriters; + }; + + typedef AdHoc::Factory<LogWriter, Ice::CommunicatorPtr> LogWriterFactory; + } +} + +#define LOGMANAGER() (::IceTray::Service::getCurrent()->getLogManager()) + +#endif + diff --git a/icetray/unittests/Jamfile.jam b/icetray/unittests/Jamfile.jam index 7403ae0..7b25761 100644 --- a/icetray/unittests/Jamfile.jam +++ b/icetray/unittests/Jamfile.jam @@ -25,6 +25,7 @@ alias testCommon : : : : <icetray.sql.tool>../tool//icetray-sql <dependency>../tool//icetray-sql <icetray.sql.basedir>$(me) + <implicit-dependency>../icetray//icetray ; generators.register-standard $(__name__).icetray.c++.sql : SQL : CPP(%.sql) H(%.sql) ; @@ -82,5 +83,11 @@ run : testDefaultPool ; +run + testIceTrayLogger.cpp + : : : + <library>testCommon + ; + IMPORT $(__name__) : icetray.c++.sql : : icetray.c++.sql ; diff --git a/icetray/unittests/testIceTrayLogger.cpp b/icetray/unittests/testIceTrayLogger.cpp new file mode 100644 index 0000000..321a878 --- /dev/null +++ b/icetray/unittests/testIceTrayLogger.cpp @@ -0,0 +1,209 @@ +#define BOOST_TEST_MODULE TestIceTrayLogger +#include <boost/test/unit_test.hpp> + +#include <logger.h> +#include <icetrayService.h> +#include <Ice/Initialize.h> +#include <Ice/Communicator.h> +#include <Ice/ObjectAdapter.h> +#include <compileTimeFormatter.h> +#include <boost/format.hpp> + +using namespace IceTray::Logging; + +struct LogEntry { + std::string domain; + std::string message; +}; + +class TestLogWriter : public LogWriter { + public: + TestLogWriter(LogLevel ll) : + calls(0), + l(ll) + { + } + + IceUtil::Optional<LogLevel> level(const std::string &, const Ice::Current &) override + { + return l; + } + + void message(LogLevel priority, const std::string & domain, const std::string & message, const Ice::Current &) override + { + calls += 1; + BOOST_REQUIRE(l >= priority); + msgs.push_back({domain, message}); + } + + std::vector<LogEntry> msgs; + unsigned int calls; + + private: + const LogLevel l; +}; + +class TestLogImpl { + public: + TestLogImpl() : + ic(Ice::initialize()), + adp(ic->createObjectAdapterWithEndpoints("test", "default")) + { + adp->activate(); + log = manager.getLogger("test.domain"); + } + + ~TestLogImpl() + { + adp->deactivate(); + adp->destroy(); + ic->destroy(); + } + + LogWriterPrx add(LogWriter * w) + { + return LogWriterPrx::uncheckedCast(adp->addWithUUID(w)); + } + + protected: + LoggerPtr log; + LogManager manager; + + private: + Ice::CommunicatorPtr ic; + Ice::ObjectAdapterPtr adp; +}; + +BOOST_FIXTURE_TEST_SUITE(li, TestLogImpl); + +BOOST_AUTO_TEST_CASE(no_writers) { + log->message(EMERG, ""); + log->message(DEBUG, ""); +} + +BOOST_AUTO_TEST_CASE(priority_filtering) { + auto w = new TestLogWriter(WARNING); + auto e = new TestLogWriter(ERR); + auto wp = add(w); + auto ep = add(e); + manager.addWriter(wp); + manager.addWriter(ep); + BOOST_REQUIRE(w->msgs.empty()); + BOOST_REQUIRE(e->msgs.empty()); + + log->message(DEBUG, "debug"); + BOOST_REQUIRE(w->msgs.empty()); + BOOST_REQUIRE(e->msgs.empty()); + BOOST_REQUIRE_EQUAL(0, w->calls); + BOOST_REQUIRE_EQUAL(0, e->calls); + + log->message(INFO, "into"); + BOOST_REQUIRE(w->msgs.empty()); + BOOST_REQUIRE(e->msgs.empty()); + BOOST_REQUIRE_EQUAL(0, w->calls); + BOOST_REQUIRE_EQUAL(0, e->calls); + + log->message(NOTICE, "notice"); + BOOST_REQUIRE(w->msgs.empty()); + BOOST_REQUIRE(e->msgs.empty()); + BOOST_REQUIRE_EQUAL(0, w->calls); + BOOST_REQUIRE_EQUAL(0, e->calls); + + log->message(WARNING, "warning"); + BOOST_REQUIRE_EQUAL(1, w->msgs.size()); + BOOST_REQUIRE(e->msgs.empty()); + BOOST_REQUIRE_EQUAL(1, w->calls); + BOOST_REQUIRE_EQUAL(0, e->calls); + + log->message(ERR, "err"); + BOOST_REQUIRE_EQUAL(2, w->msgs.size()); + BOOST_REQUIRE_EQUAL(1, e->msgs.size()); + BOOST_REQUIRE_EQUAL(2, w->calls); + BOOST_REQUIRE_EQUAL(1, e->calls); + + log->message(CRIT, "crit"); + BOOST_REQUIRE_EQUAL(3, w->msgs.size()); + BOOST_REQUIRE_EQUAL(2, e->msgs.size()); + BOOST_REQUIRE_EQUAL(3, w->calls); + BOOST_REQUIRE_EQUAL(2, e->calls); + + log->message(ALERT, "alert"); + BOOST_REQUIRE_EQUAL(4, w->msgs.size()); + BOOST_REQUIRE_EQUAL(3, e->msgs.size()); + BOOST_REQUIRE_EQUAL(4, w->calls); + BOOST_REQUIRE_EQUAL(3, e->calls); + + log->message(EMERG, "emerg"); + BOOST_REQUIRE_EQUAL(5, w->msgs.size()); + BOOST_REQUIRE_EQUAL(4, e->msgs.size()); + BOOST_REQUIRE_EQUAL(5, w->calls); + BOOST_REQUIRE_EQUAL(4, e->calls); + + manager.removeWriter(wp); + log->message(ERR, "err2"); + BOOST_REQUIRE_EQUAL(5, w->msgs.size()); + BOOST_REQUIRE_EQUAL(5, e->msgs.size()); + BOOST_REQUIRE_EQUAL(5, w->calls); + BOOST_REQUIRE_EQUAL(5, e->calls); +} + +BOOST_AUTO_TEST_CASE(formatter_plain) +{ + auto d = new TestLogWriter(DEBUG); + manager.addWriter(add(d)); + log->message(DEBUG, "plain message."); + BOOST_REQUIRE_EQUAL(1, d->msgs.size()); + BOOST_REQUIRE_EQUAL("plain message.", d->msgs.front().message); + BOOST_REQUIRE_EQUAL("test.domain", d->msgs.front().domain); +} + +BOOST_AUTO_TEST_CASE(formatter_libc) +{ + auto d = new TestLogWriter(DEBUG); + manager.addWriter(add(d)); + log->messagef(DEBUG, "plain %s.", "message"); + BOOST_REQUIRE_EQUAL(1, d->msgs.size()); + BOOST_REQUIRE_EQUAL("plain message.", d->msgs.front().message); + BOOST_REQUIRE_EQUAL("test.domain", d->msgs.front().domain); +} + +BOOST_AUTO_TEST_CASE(formatter_boost_format) +{ + auto d = new TestLogWriter(DEBUG); + manager.addWriter(add(d)); + log->messagebf(DEBUG, "plain %s", std::string("message")); + BOOST_REQUIRE_EQUAL(1, d->msgs.size()); + BOOST_REQUIRE_EQUAL("plain message", d->msgs.front().message); + BOOST_REQUIRE_EQUAL("test.domain", d->msgs.front().domain); +} + +AdHocFormatter(Plain, "plain %?."); +BOOST_AUTO_TEST_CASE(formatter_adhoc_compiletime) +{ + auto d = new TestLogWriter(DEBUG); + manager.addWriter(add(d)); + log->messagectf<Plain>(DEBUG, "message"); + BOOST_REQUIRE_EQUAL(1, d->msgs.size()); + BOOST_REQUIRE_EQUAL("plain message.", d->msgs.front().message); + BOOST_REQUIRE_EQUAL("test.domain", d->msgs.front().domain); +} + +BOOST_AUTO_TEST_SUITE_END(); + +class TestService : public IceTray::Service { + public: + void addObjects(const std::string &, const Ice::CommunicatorPtr &, const Ice::StringSeq &, const Ice::ObjectAdapterPtr &) + { + } +}; + +BOOST_FIXTURE_TEST_SUITE( ts, TestService ); + +BOOST_AUTO_TEST_CASE( getLogger ) +{ + auto logger = LOGMANAGER()->getLogger("test.domain"); + BOOST_REQUIRE(logger); +} + +BOOST_AUTO_TEST_SUITE_END(); + |