summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--icetray/dryice/Jamfile.jam1
-rw-r--r--icetray/icetray/Jamfile.jam11
-rw-r--r--icetray/icetray/icetrayService.cpp5
-rw-r--r--icetray/icetray/icetrayService.h4
-rw-r--r--icetray/icetray/logWriter.ice26
-rw-r--r--icetray/icetray/logger.cpp136
-rw-r--r--icetray/icetray/logger.h89
-rw-r--r--icetray/unittests/Jamfile.jam7
-rw-r--r--icetray/unittests/testIceTrayLogger.cpp209
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();
+