summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2013-04-09 21:32:35 +0200
committerPatrick Ohly <patrick.ohly@intel.com>2013-05-06 16:28:13 +0200
commit649837c2c2d3858116a0e422dd890d082f7f99ac (patch)
tree88061f3c8f84a21a26535c4e7d6c532b52f71ced
parent2f6f880910f36703b96995270dac5a6d2f8e6e56 (diff)
Logging: thread-safe
Logging must be thread-safe, because the glib log callback may be called from arbitrary threads. This becomes more important with EDS 3.8, because it shifts the execution of synchronous calls into threads. Thread-safe logging will also be required for running the Synthesis engine multithreaded, to overlap SyncML client communication with preparing the sources. To achieve this, the core Logging module protects its global data with a recursive mutex. A recursive mutes is used because logging calls themselves may be recursive, so ensuring single-lock semantic would be hard. Ref-counted boost pointers are used to track usage of Logger instances. This allows removal of an instance from the logging stack while it may still be in use. Destruction then will be delayed until the last user of the instance drops it. The instance itself must be prepared to handle this. The Logging mutex is available to users of the Logging module. Code which holds the logging mutex should not lock any other mutex, to avoid deadlocks. The new code is a bit fuzzy on that, because it calls other modules (glib, Synthesis engine) while holding the mutex. If that becomes a problem, then the mutex can be unlocked, at the risk of leading to reordered log messages in different channels (see ServerLogger). Making all loggers follow the new rules uses different approaches. Loggers like the one in the local transport child which use a parent logger and an additional ref-counted class like the D-Bus helper keep a weak reference to the helper and lock it before use. If it is gone already, the second logging part is skipped. This is the recommended approach. In cases where introducing ref-counting for the second class would have been too intrusive (Server and SessionHelper), a fake boost::shared_ptr without a destructor is used as an intermediate step towards the recommended approach. To avoid race conditions while the instance these fake pointers refer to destructs, an explicit "remove()" method is necessary which must hold the Logging mutex. Using the potentially removed pointer must do the same. Such fake ref-counted Loggers cannot be used as parent logger of other loggers, because then remove() would not be able to drop the last reference to the fake boost::shared_ptr. Loggers with fake boost::shared_ptr must keep a strong reference, because no-one else does. The goal is to turn this into weak references eventually. LogDir must protect concurrent access to m_report and the Synthesis engine. The LogRedirectLogger assumes that it is still the active logger while disabling itself. The remove() callback method will always be invoked before removing a logger from the stack.
-rw-r--r--src/dbus/server/main.cpp9
-rw-r--r--src/dbus/server/server.cpp116
-rw-r--r--src/dbus/server/server.h34
-rw-r--r--src/dbus/server/session-helper.cpp122
-rw-r--r--src/dbus/server/session-helper.h12
-rw-r--r--src/dbus/server/session.cpp80
-rw-r--r--src/dbus/server/session.h10
-rw-r--r--src/dbus/server/sync-helper.cpp11
-rw-r--r--src/syncevo/Cmdline.cpp6
-rw-r--r--src/syncevo/ForkExec.cpp9
-rw-r--r--src/syncevo/LocalTransportAgent.cpp74
-rw-r--r--src/syncevo/LogRedirect.cpp82
-rw-r--r--src/syncevo/LogRedirect.h22
-rw-r--r--src/syncevo/LogSyslog.cpp2
-rw-r--r--src/syncevo/LogSyslog.h2
-rw-r--r--src/syncevo/Logging.cpp259
-rw-r--r--src/syncevo/Logging.h205
-rw-r--r--src/syncevo/SuspendFlags.h1
-rw-r--r--src/syncevo/SyncContext.cpp240
-rw-r--r--src/syncevo/syncevo.am4
-rw-r--r--src/syncevolution.cpp2
-rw-r--r--test/client-test-main.cpp12
22 files changed, 848 insertions, 466 deletions
diff --git a/src/dbus/server/main.cpp b/src/dbus/server/main.cpp
index ed424efd..a8d8c6d9 100644
--- a/src/dbus/server/main.cpp
+++ b/src/dbus/server/main.cpp
@@ -152,9 +152,9 @@ int main(int argc, char **argv, char **envp)
setvbuf(stdout, NULL, _IONBF, 0);
// Redirect output and optionally log to syslog.
- LogRedirect redirect(true);
- redirect.setLevel(stdoutEnabled ? level : Logger::NONE);
- std::auto_ptr<Logger> syslogger;
+ PushLogger<LogRedirect> redirect(new LogRedirect(LogRedirect::STDERR_AND_STDOUT));
+ redirect->setLevel(stdoutEnabled ? level : Logger::NONE);
+ PushLogger<LoggerSyslog> syslogger;
if (syslogEnabled && level > Logger::NONE) {
syslogger.reset(new LoggerSyslog(execName));
syslogger->setLevel(level);
@@ -206,6 +206,9 @@ int main(int argc, char **argv, char **envp)
SE_LOG_DEBUG(NULL, "flushing D-Bus connection");
conn.flush();
conn.reset();
+ SE_LOG_INFO(NULL, "terminating, closing logging");
+ syslogger.reset();
+ redirect.reset();
SE_LOG_INFO(NULL, "terminating");
return 0;
} catch ( const std::exception &ex ) {
diff --git a/src/dbus/server/server.cpp b/src/dbus/server/server.cpp
index 4f885d6e..da96c860 100644
--- a/src/dbus/server/server.cpp
+++ b/src/dbus/server/server.cpp
@@ -48,6 +48,87 @@ static void logIdle(bool idle)
SE_LOG_DEBUG(NULL, "server is %s", idle ? "idle" : "not idle");
}
+class ServerLogger : public Logger
+{
+ Logger::Handle m_parentLogger;
+ // Currently a strong reference. Would be a weak reference
+ // if we had proper reference counting for Server.
+ boost::shared_ptr<Server> m_server;
+
+public:
+ ServerLogger(const boost::shared_ptr<Server> &server) :
+ m_parentLogger(Logger::instance()),
+ m_server(server)
+ {
+ }
+
+ virtual void remove() throw ()
+ {
+ // Hold the Logger mutex while cutting our connection to the
+ // server. The code using m_server below does the same and
+ // holds the mutex while logging. That way we prevent threads
+ // from holding onto the server while it tries to shut down.
+ //
+ // This is important because the server's live time is not
+ // really controlled via the boost::shared_ptr, it may
+ // destruct while there are still references. See
+ // Server::m_logger instantiation below.
+ RecMutex::Guard guard = lock();
+ m_server.reset();
+ }
+
+ virtual void messagev(const MessageOptions &options,
+ const char *format,
+ va_list args)
+ {
+ // Ensure that remove() cannot proceed while we have the
+ // server in use.
+ RecMutex::Guard guard = lock();
+ Server *server = m_server.get();
+ message2DBus(server,
+ options,
+ format,
+ args,
+ server ? server->getPath() : "",
+ getProcessName());
+ }
+
+ /**
+ * @param server may be NULL, in which case logging only goes to parent
+ */
+ void message2DBus(Server *server,
+ const MessageOptions &options,
+ const char *format,
+ va_list args,
+ const std::string &dbusPath,
+ const std::string &procname)
+ {
+ // Keeps logging consistent: otherwise thread A might log to
+ // parent, thread B to parent and D-Bus, then thread A
+ // finishes its logging via D-Bus. The order of log messages
+ // would then not be the same in the parent and D-Bus.
+ RecMutex::Guard guard = lock();
+
+ // iterating over args in messagev() is destructive, must make a copy first
+ va_list argsCopy;
+ va_copy(argsCopy, args);
+ m_parentLogger.messagev(options, format, args);
+
+ if (server) {
+ try {
+ if (options.m_level <= server->getDBusLogLevel()) {
+ string log = StringPrintfV(format, argsCopy);
+ server->logOutput(dbusPath, options.m_level, log, procname);
+ }
+ } catch (...) {
+ remove();
+ // Give up on server logging silently.
+ }
+ }
+ va_end(argsCopy);
+ }
+};
+
void Server::clientGone(Client *c)
{
for (Clients_t::iterator it = m_clients.begin();
@@ -282,7 +363,11 @@ Server::Server(GMainLoop *loop,
m_logOutputSignal(*this, "LogOutput"),
m_autoTerm(m_loop, m_shutdownRequested, duration),
m_dbusLogLevel(Logger::INFO),
- m_parentLogger(Logger::instance())
+ // TODO (?): turn Server into a proper reference counted instance.
+ // This would help with dangling references to it when other threads
+ // use it for logging, see ServerLogger. However, with mutex locking
+ // in ServerLogger that shouldn't be a problem.
+ m_logger(new ServerLogger(boost::shared_ptr<Server>(this, NopDestructor())))
{
struct timeval tv;
gettimeofday(&tv, NULL);
@@ -327,8 +412,9 @@ void Server::activate()
// out object isn't visible to it yet.
GDBusCXX::DBusObjectHelper::activate();
- Logger::pushLogger(this);
- setLevel(Logger::DEBUG);
+ // Push ourselves as logger for the time being.
+ m_logger->setLevel(Logger::DEBUG);
+ m_pushLogger.reset(m_logger);
m_presence.reset(new PresenceStatus(*this));
@@ -363,7 +449,9 @@ Server::~Server()
m_connman.reset();
m_networkManager.reset();
m_presence.reset();
- Logger::popLogger();
+
+ m_pushLogger.reset();
+ m_logger.reset();
}
bool Server::shutdown()
@@ -909,24 +997,16 @@ void Server::updateDevice(const string &deviceId,
}
}
-void Server::messagev(const MessageOptions &options,
- const char *format,
- va_list args,
- const std::string &dbusPath,
- const std::string &procname)
+void Server::message2DBus(const Logger::MessageOptions &options,
+ const char *format,
+ va_list args,
+ const std::string &dbusPath,
+ const std::string &procname)
{
- // iterating over args in messagev() is destructive, must make a copy first
- va_list argsCopy;
- va_copy(argsCopy, args);
- m_parentLogger.messagev(options, format, args);
// prefix is used to set session path
// for general server output, the object path field is dbus server
// the object path can't be empty for object paths prevent using empty string.
- if (options.m_level <= m_dbusLogLevel) {
- string log = StringPrintfV(format, argsCopy);
- logOutput(dbusPath, options.m_level, log, procname);
- }
- va_end(argsCopy);
+ m_logger->message2DBus(this, options, format, args, dbusPath, procname);
}
void Server::logOutput(const GDBusCXX::DBusObject_t &path,
diff --git a/src/dbus/server/server.h b/src/dbus/server/server.h
index 95163331..673958b4 100644
--- a/src/dbus/server/server.h
+++ b/src/dbus/server/server.h
@@ -53,14 +53,15 @@ class NetworkManagerClient;
// TODO: avoid polluting namespace
using namespace std;
+class ServerLogger;
+
/**
* Implements the main org.syncevolution.Server interface.
*
* The Server class is responsible for listening to clients and
* spinning of sync sessions as requested by clients.
*/
-class Server : public GDBusCXX::DBusObjectHelper,
- public Logger
+class Server : public GDBusCXX::DBusObjectHelper
{
GMainLoop *m_loop;
bool &m_shutdownRequested;
@@ -391,9 +392,10 @@ class Server : public GDBusCXX::DBusObjectHelper,
// The level of detail for D-Bus logging signals.
Logger::Level m_dbusLogLevel;
- //records the parent logger, dbus server acts as logger to
- //send signals to clients and put logs in the parent logger.
- Logger &m_parentLogger;
+ // Created in constructor and captures parent logger there,
+ // then pushed as default logger in activate().
+ boost::shared_ptr<ServerLogger> m_logger;
+ PushLogger<ServerLogger> m_pushLogger;
/**
* All active timeouts created by addTimeout().
@@ -653,23 +655,11 @@ public:
*/
bool notificationsEnabled();
- /**
- * implement virtual method from LogStdout.
- * Not only print the message in the console
- * but also send them as signals to clients
- */
- virtual void messagev(const MessageOptions &options,
- const char *format,
- va_list args) {
- messagev(options, format, args,
- getPath(),
- getProcessName());
- }
- void messagev(const MessageOptions &options,
- const char *format,
- va_list args,
- const std::string &dbusPath,
- const std::string &procname);
+ void message2DBus(const Logger::MessageOptions &options,
+ const char *format,
+ va_list args,
+ const std::string &dbusPath,
+ const std::string &procname);
};
// extensions to the D-Bus server, created dynamically by main()
diff --git a/src/dbus/server/session-helper.cpp b/src/dbus/server/session-helper.cpp
index cffd8e52..0df0004b 100644
--- a/src/dbus/server/session-helper.cpp
+++ b/src/dbus/server/session-helper.cpp
@@ -30,10 +30,82 @@
SE_BEGIN_CXX
+static void dumpString(const std::string &output)
+{
+ fputs(output.c_str(), stdout);
+}
+
+/**
+ * Same logging approach as in Server class: pretend that we
+ * have reference counting for the SessionHelper class and
+ * use mutex locking to prevent dangling pointers.
+ */
+class SessionHelperLogger : public Logger
+{
+ boost::shared_ptr<LogRedirect> m_parentLogger;
+ boost::shared_ptr<SessionHelper> m_helper;
+
+public:
+ SessionHelperLogger(const boost::shared_ptr<LogRedirect> &parentLogger,
+ const boost::shared_ptr<SessionHelper> &helper):
+ m_parentLogger(parentLogger),
+ m_helper(helper)
+ {
+ }
+
+ virtual void remove() throw ()
+ {
+ RecMutex::Guard guard = lock();
+ m_helper.reset();
+ }
+
+ virtual void messagev(const MessageOptions &options,
+ const char *format,
+ va_list args)
+ {
+ RecMutex::Guard guard = lock();
+ static bool dbg = getenv("SYNCEVOLUTION_DEBUG");
+
+ if (dbg) {
+ // let parent LogRedirect or utility function handle the output *in addition* to
+ // logging via D-Bus
+ va_list argsCopy;
+ va_copy(argsCopy, args);
+ if (m_parentLogger) {
+ m_parentLogger->messagev(options, format, argsCopy);
+ } else {
+ formatLines(options.m_level, DEBUG,
+ options.m_processName,
+ options.m_prefix,
+ format, argsCopy,
+ boost::bind(dumpString, _1));
+ }
+ va_end(argsCopy);
+ } else if (m_parentLogger) {
+ // Only flush parent logger, to capture output sent to
+ // stdout/stderr by some library and send it via D-Bus
+ // (recursively!) before printing out own, new output.
+ m_parentLogger->flush();
+ }
+
+ if (m_helper) {
+ // send to parent
+ string log = StringPrintfV(format, args);
+ string strLevel = Logger::levelToStr(options.m_level);
+ try {
+ m_helper->emitLogOutput(strLevel, log, options.m_processName ? *options.m_processName : getProcessName());
+ } catch (...) {
+ // Give up forwarding output.
+ m_helper.reset();
+ }
+ }
+ }
+};
+
SessionHelper::SessionHelper(GMainLoop *loop,
const GDBusCXX::DBusConnectionPtr &conn,
const boost::shared_ptr<ForkExecChild> &forkexec,
- LogRedirect *parentLogger) :
+ const boost::shared_ptr<LogRedirect> &parentLogger) :
GDBusCXX::DBusObjectHelper(conn,
SessionCommon::HELPER_PATH,
SessionCommon::HELPER_IFACE,
@@ -42,7 +114,7 @@ SessionHelper::SessionHelper(GMainLoop *loop,
m_loop(loop),
m_conn(conn),
m_forkexec(forkexec),
- m_parentLogger(parentLogger),
+ m_logger(new SessionHelperLogger(parentLogger, boost::shared_ptr<SessionHelper>(this, NopDestructor()))),
emitLogOutput(*this, "LogOutput"),
emitSyncProgress(*this, "SyncProgress"),
emitSourceProgress(*this, "SourceProgress"),
@@ -68,52 +140,18 @@ SessionHelper::SessionHelper(GMainLoop *loop,
add(emitPasswordRequest);
add(emitMessage);
add(emitShutdown);
- activate();
- Logger::pushLogger(this);
-}
-
-SessionHelper::~SessionHelper()
-{
- Logger::popLogger();
}
-static void dumpString(const std::string &output)
+void SessionHelper::activate()
{
- fputs(output.c_str(), stdout);
+ GDBusCXX::DBusObjectHelper::activate();
+ m_pushLogger.reset(m_logger);
}
-void SessionHelper::messagev(const MessageOptions &options,
- const char *format,
- va_list args)
+SessionHelper::~SessionHelper()
{
- static bool dbg = getenv("SYNCEVOLUTION_DEBUG");
-
- if (dbg) {
- // let parent LogRedirect or utility function handle the output *in addition* to
- // logging via D-Bus
- va_list argsCopy;
- va_copy(argsCopy, args);
- if (m_parentLogger) {
- m_parentLogger->messagev(options, format, argsCopy);
- } else {
- formatLines(options.m_level, DEBUG,
- options.m_processName,
- options.m_prefix,
- format, argsCopy,
- boost::bind(dumpString, _1));
- }
- va_end(argsCopy);
- } else {
- // Only flush parent logger, to capture output sent to
- // stdout/stderr by some library and send it via D-Bus
- // (recursively!) before printing out own, new output.
- m_parentLogger->flush();
- }
-
- // send to parent
- string log = StringPrintfV(format, args);
- string strLevel = Logger::levelToStr(options.m_level);
- emitLogOutput(strLevel, log, options.m_processName ? *options.m_processName : getProcessName());
+ m_pushLogger.reset();
+ m_logger.reset();
}
void SessionHelper::run()
diff --git a/src/dbus/server/session-helper.h b/src/dbus/server/session-helper.h
index 380ff209..e06b28fd 100644
--- a/src/dbus/server/session-helper.h
+++ b/src/dbus/server/session-helper.h
@@ -43,14 +43,14 @@ class ForkExecChild;
* traditional syncevo-dbus-server did.
*/
class SessionHelper : public GDBusCXX::DBusObjectHelper,
- private Logger,
private boost::noncopyable
{
GMainLoop *m_loop;
GDBusCXX::DBusConnectionPtr m_conn;
boost::shared_ptr<ForkExecChild> m_forkexec;
- LogRedirect *m_parentLogger;
boost::function<bool ()> m_operation;
+ boost::shared_ptr<Logger> m_logger;
+ PushLogger<Logger> m_pushLogger;
/** valid during doSync() */
boost::scoped_ptr<DBusSync> m_sync;
@@ -81,18 +81,14 @@ class SessionHelper : public GDBusCXX::DBusObjectHelper,
/** SessionHelper.PasswordResponse */
void passwordResponse(bool timedOut, bool aborted, const std::string &password);
- // Logger implementation -> output via D-Bus emitLogOutput
- virtual void messagev(const MessageOptions &options,
- const char *format,
- va_list args);
-
public:
SessionHelper(GMainLoop *loop,
const GDBusCXX::DBusConnectionPtr &conn,
const boost::shared_ptr<ForkExecChild> &forkexec,
- LogRedirect *parentLogger);
+ const boost::shared_ptr<LogRedirect> &parentLogger);
~SessionHelper();
+ void activate();
void run();
GMainLoop *getLoop() const { return m_loop; }
diff --git a/src/dbus/server/session.cpp b/src/dbus/server/session.cpp
index 64819b63..acae499c 100644
--- a/src/dbus/server/session.cpp
+++ b/src/dbus/server/session.cpp
@@ -204,7 +204,7 @@ void Session::setNamedConfig(const std::string &configName,
bool update, bool temporary,
const ReadOperations::Config_t &config)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_runOperation != SessionCommon::OP_NULL) {
string msg = StringPrintf("%s started, cannot change configuration at this time", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
@@ -323,7 +323,7 @@ void Session::setNamedConfig(const std::string &configName,
void Session::initServer(SharedBuffer data, const std::string &messageType)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
m_serverMode = true;
m_initialMessage = data;
m_initialMessageType = messageType;
@@ -331,7 +331,7 @@ void Session::initServer(SharedBuffer data, const std::string &messageType)
void Session::sync(const std::string &mode, const SessionCommon::SourceModes_t &sourceModes)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_runOperation == SessionCommon::OP_SYNC) {
string msg = StringPrintf("%s started, cannot start again", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
@@ -352,7 +352,7 @@ void Session::sync(const std::string &mode, const SessionCommon::SourceModes_t &
void Session::sync2(const std::string &mode, const SessionCommon::SourceModes_t &sourceModes)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (!m_forkExecParent || !m_helper) {
SE_THROW("syncing cannot continue, helper died");
}
@@ -415,7 +415,7 @@ void Session::sync2(const std::string &mode, const SessionCommon::SourceModes_t
void Session::abort()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_runOperation != SessionCommon::OP_SYNC && m_runOperation != SessionCommon::OP_CMDLINE) {
SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot abort at this time");
}
@@ -436,7 +436,7 @@ void Session::abort()
void Session::suspend()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_runOperation != SessionCommon::OP_SYNC && m_runOperation != SessionCommon::OP_CMDLINE) {
SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot suspend at this time");
}
@@ -453,7 +453,7 @@ void Session::suspend()
void Session::abortAsync(const SimpleResult &result)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (!m_forkExecParent) {
result.done();
} else {
@@ -470,7 +470,7 @@ void Session::getStatus(std::string &status,
uint32_t &error,
SourceStatuses_t &sources)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
status = syncStatusToString(m_syncStatus);
if (m_stepIsWaiting) {
status += ";waiting";
@@ -483,14 +483,14 @@ void Session::getStatus(std::string &status,
void Session::getProgress(int32_t &progress,
SourceProgresses_t &sources)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
progress = m_progData.getProgress();
sources = m_sourceProgress;
}
void Session::fireStatus(bool flush)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
std::string status;
uint32_t error;
SourceStatuses_t sources;
@@ -507,7 +507,7 @@ void Session::fireStatus(bool flush)
void Session::fireProgress(bool flush)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
int32_t progress;
SourceProgresses_t sources;
@@ -595,13 +595,13 @@ Session::Session(Server &server,
void Session::passwordRequest(const std::string &descr, const ConfigPasswordKey &key)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
m_passwordRequest = m_server.passwordRequest(descr, key, m_me);
}
void Session::dbusResultCb(const std::string &operation, bool success, const std::string &error) throw()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
SE_LOG_DEBUG(NULL, "%s helper call completed, %s",
operation.c_str(),
@@ -625,7 +625,7 @@ void Session::dbusResultCb(const std::string &operation, bool success, const std
void Session::failureCb() throw()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
if (m_status == SESSION_DONE) {
// ignore errors that happen after session already closed,
@@ -666,7 +666,7 @@ void Session::failureCb() throw()
void Session::doneCb(bool success) throw()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
if (m_status == SESSION_DONE) {
return;
@@ -730,7 +730,7 @@ static void raiseChildTermError(int status, const SimpleResult &result)
void Session::runOperationAsync(SessionCommon::RunOperation op,
const SuccessCb_t &helperReady)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
m_server.addSyncSession(this);
m_runOperation = op;
m_status = SESSION_RUNNING;
@@ -743,7 +743,7 @@ void Session::runOperationAsync(SessionCommon::RunOperation op,
void Session::useHelperAsync(const SimpleResult &result)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
if (m_helper) {
// exists already, invoke callback directly
@@ -810,9 +810,9 @@ void Session::messagev(const MessageOptions &options,
{
// log with session path and empty process name,
// just like the syncevo-dbus-helper does
- m_server.messagev(options,
- format, args,
- getPath(), "");
+ m_server.message2DBus(options,
+ format, args,
+ getPath(), "");
}
static void Logging2Server(Server &server,
@@ -829,7 +829,7 @@ static void Logging2Server(Server &server,
void Session::useHelper2(const SimpleResult &result, const boost::signals2::connection &c)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
// helper is running, don't call result.failed() when it quits
// sometime in the future
@@ -872,7 +872,7 @@ void Session::useHelper2(const SimpleResult &result, const boost::signals2::conn
void Session::onConnect(const GDBusCXX::DBusConnectionPtr &conn) throw ()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
SE_LOG_DEBUG(NULL, "helper has connected");
m_helper.reset(new SessionProxy(conn));
@@ -891,7 +891,7 @@ void Session::onConnect(const GDBusCXX::DBusConnectionPtr &conn) throw ()
void Session::onQuit(int status) throw ()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
SE_LOG_DEBUG(NULL, "helper quit with return code %d, was %s",
status,
@@ -936,7 +936,7 @@ void Session::onQuit(int status) throw ()
void Session::onFailure(SyncMLStatus status, const std::string &explanation) throw ()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
SE_LOG_DEBUG(NULL, "helper failed, status code %d = %s, %s",
status,
@@ -949,7 +949,7 @@ void Session::onFailure(SyncMLStatus status, const std::string &explanation) thr
void Session::onOutput(const char *buffer, size_t length)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
// treat null-bytes inside the buffer like line breaks
size_t off = 0;
do {
@@ -960,7 +960,7 @@ void Session::onOutput(const char *buffer, size_t length)
void Session::activateSession()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_status != SESSION_IDLE) {
SE_THROW("internal error, session changing from non-idle to active");
}
@@ -981,7 +981,7 @@ void Session::activateSession()
void Session::passwordResponse(bool timedOut, bool aborted, const std::string &password)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_helper) {
// Ignore communicaton failures with helper here,
// we'll notice that elsewhere
@@ -994,7 +994,7 @@ void Session::passwordResponse(bool timedOut, bool aborted, const std::string &p
void Session::syncProgress(sysync::TProgressEventEnum type,
int32_t extra1, int32_t extra2, int32_t extra3)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
switch(type) {
case sysync::PEV_CUSTOM_START:
m_cmdlineOp = (RunOperation)extra1;
@@ -1047,7 +1047,7 @@ void Session::sourceProgress(sysync::TProgressEventEnum type,
SyncMode sourceSyncMode,
int32_t extra1, int32_t extra2, int32_t extra3)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
// a command line operation can be many things, helper must have told us
SessionCommon::RunOperation op = m_runOperation == SessionCommon::OP_CMDLINE ?
m_cmdlineOp :
@@ -1165,7 +1165,7 @@ void Session::sourceProgress(sysync::TProgressEventEnum type,
bool Session::setFilters(SyncConfig &config)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
/** apply temporary configs to config */
config.setConfigFilter(true, "", m_syncFilter);
// set all sources in the filter to config
@@ -1177,7 +1177,7 @@ bool Session::setFilters(SyncConfig &config)
void Session::setWaiting(bool isWaiting)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
// if stepInfo doesn't change, then ignore it to avoid duplicate status info
if(m_stepIsWaiting != isWaiting) {
m_stepIsWaiting = isWaiting;
@@ -1187,7 +1187,7 @@ void Session::setWaiting(bool isWaiting)
void Session::restore(const string &dir, bool before, const std::vector<std::string> &sources)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_runOperation == SessionCommon::OP_RESTORE) {
string msg = StringPrintf("restore started, cannot restore again");
SE_THROW_EXCEPTION(InvalidCall, msg);
@@ -1207,7 +1207,7 @@ void Session::restore(const string &dir, bool before, const std::vector<std::str
void Session::restore2(const string &dir, bool before, const std::vector<std::string> &sources)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (!m_forkExecParent || !m_helper) {
SE_THROW("syncing cannot continue, helper died");
}
@@ -1219,7 +1219,7 @@ void Session::restore2(const string &dir, bool before, const std::vector<std::st
void Session::execute(const vector<string> &args, const map<string, string> &vars)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (m_runOperation == SessionCommon::OP_CMDLINE) {
SE_THROW_EXCEPTION(InvalidCall, "cmdline started, cannot start again");
} else if (m_runOperation != SessionCommon::OP_NULL) {
@@ -1238,7 +1238,7 @@ void Session::execute(const vector<string> &args, const map<string, string> &var
void Session::execute2(const vector<string> &args, const map<string, string> &vars)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
if (!m_forkExecParent || !m_helper) {
SE_THROW("syncing cannot continue, helper died");
}
@@ -1251,7 +1251,7 @@ void Session::execute2(const vector<string> &args, const map<string, string> &va
/*Implementation of Session.CheckPresence */
void Session::checkPresence (string &status)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
vector<string> transport;
m_server.checkPresence(m_configName, status, transport);
}
@@ -1260,7 +1260,7 @@ void Session::sendViaConnection(const DBusArray<uint8_t> buffer,
const std::string &type,
const std::string &url)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
boost::shared_ptr<Connection> connection = m_connection.lock();
@@ -1279,7 +1279,7 @@ void Session::sendViaConnection(const DBusArray<uint8_t> buffer,
void Session::shutdownConnection()
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
try {
boost::shared_ptr<Connection> connection = m_connection.lock();
@@ -1299,7 +1299,7 @@ void Session::shutdownConnection()
void Session::storeMessage(const DBusArray<uint8_t> &message,
const std::string &type)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
// ignore errors
if (m_helper) {
m_helper->m_storeMessage.start(message, type,
@@ -1309,7 +1309,7 @@ void Session::storeMessage(const DBusArray<uint8_t> &message,
void Session::connectionState(const std::string &error)
{
- Session::LoggingGuard guard(this);
+ PushLogger<Logger> guard(m_me);
// ignore errors
if (m_helper) {
m_helper->m_connectionState.start(error,
diff --git a/src/dbus/server/session.h b/src/dbus/server/session.h
index c7053e69..f2062eb8 100644
--- a/src/dbus/server/session.h
+++ b/src/dbus/server/session.h
@@ -351,16 +351,6 @@ private:
const char *format,
va_list args);
- class LoggingGuard {
- public:
- LoggingGuard(Session *session) {
- Logger::pushLogger(session);
- }
- ~LoggingGuard() {
- Logger::popLogger();
- }
- };
-
public:
enum {
PRI_CMDLINE = -10,
diff --git a/src/dbus/server/sync-helper.cpp b/src/dbus/server/sync-helper.cpp
index fc7d4fae..5ad57516 100644
--- a/src/dbus/server/sync-helper.cpp
+++ b/src/dbus/server/sync-helper.cpp
@@ -44,11 +44,12 @@ namespace {
}
void onConnect(const DBusConnectionPtr &conn,
- LogRedirect *parentLogger,
+ const boost::shared_ptr<LogRedirect> &parentLogger,
const boost::shared_ptr<ForkExecChild> &forkexec,
boost::shared_ptr<SessionHelper> &helper)
{
helper.reset(new SessionHelper(loop, conn, forkexec, parentLogger));
+ helper->activate();
}
void onAbort()
@@ -87,9 +88,11 @@ int main(int argc, char **argv, char **envp)
// which are unaware of the SyncEvolution logging system.
// Redirecting is useful to get such output into our
// sync logfile, once we have one.
- boost::scoped_ptr<LogRedirect> redirect;
+ boost::shared_ptr<LogRedirect> redirect;
+ PushLogger<LogRedirect> pushRedirect;
if (!debug) {
- redirect.reset(new LogRedirect(true));
+ redirect.reset(new LogRedirect(LogRedirect::STDERR_AND_STDOUT));
+ pushRedirect.reset(redirect);
}
setvbuf(stderr, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
@@ -110,7 +113,7 @@ int main(int argc, char **argv, char **envp)
boost::shared_ptr<SessionHelper> helper;
bool failed = false;
- forkexec->m_onConnect.connect(boost::bind(onConnect, _1, redirect.get(),
+ forkexec->m_onConnect.connect(boost::bind(onConnect, _1, redirect,
boost::cref(forkexec),
boost::ref(helper)));
forkexec->m_onFailure.connect(boost::bind(onFailure, _2, boost::ref(failed)));
diff --git a/src/syncevo/Cmdline.cpp b/src/syncevo/Cmdline.cpp
index b0e1cda1..63afdfef 100644
--- a/src/syncevo/Cmdline.cpp
+++ b/src/syncevo/Cmdline.cpp
@@ -4362,9 +4362,9 @@ private:
* vararg constructor with NULL termination,
* out and error stream into stringstream members
*/
- class TestCmdline : private Logger {
+ class TestCmdline : public Logger {
void init() {
- pushLogger(this);
+ addLogger(boost::shared_ptr<Logger>(this, NopDestructor()));
m_argv.reset(new const char *[m_argvstr.size() + 1]);
m_argv[0] = "client-test";
@@ -4398,7 +4398,7 @@ private:
}
~TestCmdline() {
- popLogger();
+ removeLogger(this);
}
boost::shared_ptr<SyncContext> parse()
diff --git a/src/syncevo/ForkExec.cpp b/src/syncevo/ForkExec.cpp
index 649b5b0c..46cd1d67 100644
--- a/src/syncevo/ForkExec.cpp
+++ b/src/syncevo/ForkExec.cpp
@@ -128,14 +128,7 @@ void ForkExecParent::forked(gpointer data) throw()
// any output is printed directly, instead of going through
// the parent's output processing in LogRedirect.
if (getenv("SYNCEVOLUTION_DEBUG")) {
- int index = Logger::numLoggers();
- LogRedirect *redirect = NULL;
- while (--index >= 0 &&
- !(redirect = dynamic_cast<LogRedirect *>(Logger::loggerAt(index)))) {
- }
- if (redirect) {
- redirect->reset();
- }
+ LogRedirect::removeRedirect();
}
if (me->m_mergedStdoutStderr) {
diff --git a/src/syncevo/LocalTransportAgent.cpp b/src/syncevo/LocalTransportAgent.cpp
index 08b20d45..179c0ea9 100644
--- a/src/syncevo/LocalTransportAgent.cpp
+++ b/src/syncevo/LocalTransportAgent.cpp
@@ -550,7 +550,42 @@ public:
true /* ignore transmission failures */> m_logOutput;
};
-class LocalTransportAgentChild : public TransportAgent, private Logger
+class ChildLogger : public Logger
+{
+ std::auto_ptr<LogRedirect> m_parentLogger;
+ boost::weak_ptr<LocalTransportChildImpl> m_child;
+
+public:
+ ChildLogger(const boost::shared_ptr<LocalTransportChildImpl> &child) :
+ m_parentLogger(new LogRedirect(LogRedirect::STDERR_AND_STDOUT)),
+ m_child(child)
+ {}
+ ~ChildLogger()
+ {
+ m_parentLogger.reset();
+ }
+
+ /**
+ * Write message into our own log and send to parent.
+ */
+ virtual void messagev(const MessageOptions &options,
+ const char *format,
+ va_list args)
+ {
+ m_parentLogger->process();
+ boost::shared_ptr<LocalTransportChildImpl> child = m_child.lock();
+ if (child) {
+ // prefix is used to set session path
+ // for general server output, the object path field is dbus server
+ // the object path can't be empty for object paths prevent using empty string.
+ string strLevel = Logger::levelToStr(options.m_level);
+ string log = StringPrintfV(format, args);
+ child->m_logOutput(strLevel, log);
+ }
+ }
+};
+
+class LocalTransportAgentChild : public TransportAgent
{
/** final return code of our main(): non-zero indicates that we need to shut down */
int m_ret;
@@ -576,7 +611,7 @@ class LocalTransportAgentChild : public TransportAgent, private Logger
/**
* our D-Bus interface, created in onConnect()
*/
- boost::scoped_ptr<LocalTransportChildImpl> m_child;
+ boost::shared_ptr<LocalTransportChildImpl> m_child;
/**
* sync context, created in Sync() D-Bus call
@@ -834,36 +869,13 @@ class LocalTransportAgentChild : public TransportAgent, private Logger
}
}
- /**
- * Write message into our own log and send to parent.
- */
- virtual void messagev(const MessageOptions &options,
- const char *format,
- va_list args)
- {
- if (m_parentLogger) {
- m_parentLogger->process();
- }
- if (m_child) {
- // prefix is used to set session path
- // for general server output, the object path field is dbus server
- // the object path can't be empty for object paths prevent using empty string.
- string strLevel = Logger::levelToStr(options.m_level);
- string log = StringPrintfV(format, args);
- m_child->m_logOutput(strLevel, log);
- }
- }
-
public:
LocalTransportAgentChild() :
m_ret(0),
- m_parentLogger(new LogRedirect(true)), // redirect all output via D-Bus
m_forkexec(SyncEvo::ForkExecChild::create()),
m_reportSent(false),
m_status(INACTIVE)
{
- Logger::pushLogger(this);
-
m_forkexec->m_onConnect.connect(boost::bind(&LocalTransportAgentChild::onConnect, this, _1));
m_forkexec->m_onFailure.connect(boost::bind(&LocalTransportAgentChild::onFailure, this, _1, _2));
// When parent quits, we need to abort whatever we do and shut
@@ -880,9 +892,9 @@ public:
m_forkexec->connect();
}
- ~LocalTransportAgentChild()
+ boost::shared_ptr<ChildLogger> createLogger()
{
- Logger::popLogger();
+ return boost::shared_ptr<ChildLogger>(new ChildLogger(m_child));
}
void run()
@@ -1125,8 +1137,16 @@ int LocalTransportMain(int argc, char **argv)
Logger::setProcessName("syncevo-local-sync");
boost::shared_ptr<LocalTransportAgentChild> child(new LocalTransportAgentChild);
+ PushLogger<Logger> logger;
+ // Temporary handle is necessary to avoid compiler issue with
+ // clang (ambiguous brackets).
+ {
+ Logger::Handle handle(child->createLogger());
+ logger.reset(handle);
+ }
child->run();
int ret = child->getReturnCode();
+ logger.reset();
child.reset();
return ret;
} catch ( const std::exception &ex ) {
diff --git a/src/syncevo/LogRedirect.cpp b/src/syncevo/LogRedirect.cpp
index 89475c7f..65ced69b 100644
--- a/src/syncevo/LogRedirect.cpp
+++ b/src/syncevo/LogRedirect.cpp
@@ -57,8 +57,11 @@ void LogRedirect::abortHandler(int sig) throw()
// SE_LOG_ERROR(NULL, "caught signal %d, shutting down", sig);
// shut down redirection, also flushes to log
- if (m_redirect) {
- m_redirect->restore();
+ {
+ RecMutex::Guard guard = lock();
+ if (m_redirect) {
+ m_redirect->restore();
+ }
}
// Raise same signal again. Because our handler
@@ -103,13 +106,13 @@ void LogRedirect::init()
m_knownErrors.insert("Qt: Session management error: None of the authentication protocols specified are supported");
}
-LogRedirect::LogRedirect(bool both, const char *filename) throw()
+LogRedirect::LogRedirect(Mode mode, const char *filename)
{
init();
m_processing = true;
if (!getenv("SYNCEVOLUTION_DEBUG")) {
redirect(STDERR_FILENO, m_stderr);
- if (both) {
+ if (mode == STDERR_AND_STDOUT) {
redirect(STDOUT_FILENO, m_stdout);
m_out = filename ?
fopen(filename, "w") :
@@ -132,7 +135,12 @@ LogRedirect::LogRedirect(bool both, const char *filename) throw()
fileno(m_out) :
m_stderr.m_copy), "w");
}
- Logger::pushLogger(this);
+
+ // Modify process state while holding the Logger mutex.
+ RecMutex::Guard guard = lock();
+ if (m_redirect) {
+ SE_LOG_WARNING(NULL, "LogRedirect already instantiated?!");
+ }
m_redirect = this;
if (!getenv("SYNCEVOLUTION_DEBUG")) {
@@ -158,6 +166,8 @@ LogRedirect::LogRedirect(ExecuteFlags flags)
{
init();
+ // This instance does not modify process state and
+ // doesn't have to be thread-safe.
m_streams = true;
if (!(flags & EXECUTE_NO_STDERR)) {
redirect(STDERR_FILENO, m_stderr);
@@ -169,10 +179,12 @@ LogRedirect::LogRedirect(ExecuteFlags flags)
LogRedirect::~LogRedirect() throw()
{
- bool pop = false;
+ RecMutex::Guard guard;
+ if (!m_streams) {
+ guard = lock();
+ }
if (m_redirect == this) {
m_redirect = NULL;
- pop = true;
}
process();
restore();
@@ -186,28 +198,36 @@ LogRedirect::~LogRedirect() throw()
if (m_buffer) {
free(m_buffer);
}
- if (pop) {
- Logger::popLogger();
- }
}
-void LogRedirect::redoRedirect() throw()
+void LogRedirect::remove() throw()
{
- bool doStdout = m_stdout.m_copy >= 0;
- bool doStderr = m_stderr.m_copy >= 0;
+ restore();
+}
- if (doStdout) {
- restore(m_stdout);
- redirect(STDOUT_FILENO, m_stdout);
- }
- if (doStderr) {
- restore(m_stderr);
- redirect(STDERR_FILENO, m_stderr);
+void LogRedirect::removeRedirect() throw()
+{
+ if (m_redirect) {
+ // We were forked. Ignore mutex (might be held by thread which was not
+ // forked) and restore the forked process' state to the one it was
+ // before setting up redirection.
+ //
+ // Do the minimal amount of work possible in restore(), i.e.,
+ // suppress the processing of streams.
+ m_redirect->m_streams = false;
+
+ m_redirect->restore(m_redirect->m_stdout);
+ m_redirect->restore(m_redirect->m_stderr);
}
}
void LogRedirect::restore() throw()
{
+ RecMutex::Guard guard;
+ if (!m_streams) {
+ guard = lock();
+ }
+
if (m_processing) {
return;
}
@@ -223,6 +243,8 @@ void LogRedirect::messagev(const MessageOptions &options,
const char *format,
va_list args)
{
+ RecMutex::Guard guard = lock();
+
// check for other output first
process();
// Choose output channel: SHOW goes to original stdout,
@@ -524,6 +546,8 @@ bool LogRedirect::ignoreError(const std::string &text)
void LogRedirect::process()
{
+ RecMutex::Guard guard;
+
if (m_streams) {
// iterate until both sockets are closed by peer
while (true) {
@@ -589,6 +613,8 @@ void LogRedirect::process()
break;
}
}
+ } else {
+ guard = lock();
}
if (m_processing) {
@@ -613,6 +639,8 @@ void LogRedirect::process()
void LogRedirect::flush() throw()
{
+ RecMutex::Guard guard = lock();
+
process();
if (!m_stdoutData.empty()) {
std::string buffer;
@@ -645,17 +673,17 @@ class LogRedirectTest : public CppUnit::TestFixture {
{
public:
std::stringstream m_streams[DEBUG + 1];
- LogRedirect *m_redirect;
+ PushLogger<LogRedirect> m_redirect;
- LogBuffer(bool both = true)
+ LogBuffer(LogRedirect::Mode mode = LogRedirect::STDERR_AND_STDOUT)
{
- m_redirect = new LogRedirect(both);
- pushLogger(this);
+ m_redirect.reset(new LogRedirect(mode));
+ addLogger(boost::shared_ptr<Logger>(this, NopDestructor()));
}
~LogBuffer()
{
- popLogger();
- delete m_redirect;
+ removeLogger(this);
+ m_redirect.reset();
}
virtual void messagev(const MessageOptions &options,
@@ -746,7 +774,7 @@ public:
orig_stdout = dup(STDOUT_FILENO);
dup2(new_stdout, STDOUT_FILENO);
- LogBuffer buffer(false);
+ LogBuffer buffer(LogRedirect::STDERR);
fprintf(stdout, "normal message stdout\n");
fflush(stdout);
diff --git a/src/syncevo/LogRedirect.h b/src/syncevo/LogRedirect.h
index 5b762ab4..65f7498f 100644
--- a/src/syncevo/LogRedirect.h
+++ b/src/syncevo/LogRedirect.h
@@ -132,6 +132,11 @@ class LogRedirect : public LoggerStdout
void init();
public:
+ enum Mode {
+ STDERR_AND_STDOUT,
+ STDERR
+ };
+
/**
* Redirect both stderr and stdout or just stderr,
* using UDP so that we don't block when not reading
@@ -140,16 +145,23 @@ class LogRedirect : public LoggerStdout
* messagev() only writes messages to the previous stdout
* or the optional file which pass the filtering (relevant,
* suppress known errors, ...).
+ *
+ * May only be called when there is no other active LogRedirect
+ * instance. Not thread-safe, in contrast to the actual logging
+ * method and redirect handling.
+ *
+ * Does not add or remove the logger from the logger stack.
+ * That must be done by the caller.
*/
- LogRedirect(bool both = true, const char *filename = NULL) throw();
+ LogRedirect(Mode mode, const char *filename = NULL);
~LogRedirect() throw();
+ virtual void remove() throw();
+
/**
- * re-initialize redirection after a fork:
- * - closes inherited file descriptors, except for the original output file descriptor
- * - sets up new sockets
+ * Remove redirection (if any) after a fork and before an exec.
*/
- void redoRedirect() throw();
+ static void removeRedirect() throw();
/**
* Meant to be used for redirecting output of a specific command
diff --git a/src/syncevo/LogSyslog.cpp b/src/syncevo/LogSyslog.cpp
index a24d7737..2b178171 100644
--- a/src/syncevo/LogSyslog.cpp
+++ b/src/syncevo/LogSyslog.cpp
@@ -35,13 +35,11 @@ LoggerSyslog::LoggerSyslog(const std::string &processName) :
// valgrind tells us that openlog() does not copy the string.
// Must provide pointer to a permanent copy.
openlog(m_processName.c_str(), LOG_CONS | LOG_NDELAY | LOG_PID, LOG_USER);
- Logger::pushLogger(this);
}
LoggerSyslog::~LoggerSyslog()
{
closelog();
- Logger::popLogger();
}
static void printToSyslog(int sysloglevel, std::string &chunk, size_t expectedTotal)
diff --git a/src/syncevo/LogSyslog.h b/src/syncevo/LogSyslog.h
index 3f72132c..9e8f3c9a 100644
--- a/src/syncevo/LogSyslog.h
+++ b/src/syncevo/LogSyslog.h
@@ -35,7 +35,7 @@ SE_BEGIN_CXX
class LoggerSyslog : public Logger
{
const std::string m_processName;
- Logger &m_parentLogger;
+ Handle m_parentLogger;
public:
/**
diff --git a/src/syncevo/Logging.cpp b/src/syncevo/Logging.cpp
index 82a063a5..01794f8a 100644
--- a/src/syncevo/Logging.cpp
+++ b/src/syncevo/Logging.cpp
@@ -27,7 +27,32 @@
#include <syncevo/declarations.h>
SE_BEGIN_CXX
-std::string Logger::m_processName;
+static RecMutex logMutex;
+/**
+ * POD to have it initialized without relying on a constructor to run.
+ */
+static std::string *logProcessName;
+
+void Logger::setProcessName(const std::string &name)
+{
+ RecMutex::Guard guard = logMutex.lock();
+ if (!logProcessName) {
+ logProcessName = new std::string(name);
+ } else {
+ *logProcessName = name;
+ }
+}
+
+std::string Logger::getProcessName()
+{
+ RecMutex::Guard guard = logMutex.lock();
+ return logProcessName ? *logProcessName : "";
+}
+
+RecMutex::Guard Logger::lock()
+{
+ return logMutex.lock();
+}
Logger::Logger() :
m_level(INFO)
@@ -38,53 +63,55 @@ Logger::~Logger()
{
}
-static std::vector<Logger *> &loggers()
+/**
+ * Create (if necessary) and return the logger stack.
+ * It has at least one entry.
+ *
+ * logMutex must be locked when calling this.
+ */
+static std::vector<Logger::Handle> &LoggersSingleton()
{
// allocate array once and never free it because it might be needed till
// the very end of the application life cycle
- static std::vector<Logger *> *loggers = new std::vector<Logger *>;
+ static std::vector<Logger::Handle> *loggers;
+ if (!loggers) {
+ loggers = new std::vector<Logger::Handle>;
+ // Ensure that the array is never empty.
+ boost::shared_ptr<Logger> logger(new LoggerStdout);
+ loggers->push_back(logger);
+ }
return *loggers;
}
-Logger &Logger::instance()
+Logger::Handle Logger::instance()
{
- // prevent destructing this instance as part of the executable's
- // shutdown by allocating it dynamically, because it may be
- // needed by other instances that get destructed later
- // (order of global instance construction/destruction is
- // undefined)
- static LoggerStdout *DefaultLogger = new LoggerStdout;
- if (!loggers().empty()) {
- return *loggers()[loggers().size() - 1];
- } else {
- return *DefaultLogger;
- }
+ RecMutex::Guard guard = logMutex.lock();
+ std::vector<Handle> &loggers = LoggersSingleton();
+ return loggers.back();
}
-void Logger::pushLogger(Logger *logger)
+void Logger::addLogger(const Handle &logger)
{
- loggers().push_back(logger);
-}
+ RecMutex::Guard guard = logMutex.lock();
+ std::vector<Handle> &loggers = LoggersSingleton();
-void Logger::popLogger()
-{
- if (loggers().empty()) {
- throw "too many popLogger() calls";
- } else {
- loggers().pop_back();
- }
+ loggers.push_back(logger);
}
-int Logger::numLoggers()
+void Logger::removeLogger(Logger *logger)
{
- return (int)loggers().size();
-}
+ RecMutex::Guard guard = logMutex.lock();
+ std::vector<Handle> &loggers = LoggersSingleton();
-Logger *Logger::loggerAt(int index)
-{
- return index < 0 || index >= (int)loggers().size() ?
- NULL :
- loggers()[index];
+ for (ssize_t i = loggers.size() - 1;
+ i >= 0;
+ --i) {
+ if (loggers[i] == logger) {
+ loggers[i].remove();
+ loggers.erase(loggers.begin() + i);
+ break;
+ }
+ }
}
void Logger::formatLines(Level msglevel,
@@ -101,56 +128,69 @@ void Logger::formatLines(Level msglevel,
if (msglevel != SHOW) {
std::string reltime;
std::string procname;
- const std::string *realProcname;
+ std::string firstLine;
- if (processName) {
- realProcname = processName;
- } else {
- realProcname = &m_processName;
- }
- if (!realProcname->empty()) {
- procname.reserve(realProcname->size() + 1);
- procname += " ";
- procname += *realProcname;
- }
+ // Run non-blocking operations on shared data while
+ // holding the mutex.
+ {
+ RecMutex::Guard guard = logMutex.lock();
+ const std::string *realProcname;
- if (outputlevel >= DEBUG) {
- // add relative time stamp
- Timespec now = Timespec::monotonic();
- if (!m_startTime) {
- // first message, start counting time
- m_startTime = now;
- time_t nowt = time(NULL);
- struct tm tm_gm, tm_local;
- char buffer[2][80];
- gmtime_r(&nowt, &tm_gm);
- localtime_r(&nowt, &tm_local);
- reltime = " 00:00:00";
- strftime(buffer[0], sizeof(buffer[0]),
- "%a %Y-%m-%d %H:%M:%S",
- &tm_gm);
- strftime(buffer[1], sizeof(buffer[1]),
- "%H:%M %z %Z",
- &tm_local);
- std::string line =
- StringPrintf("[DEBUG%s%s] %s UTC = %s\n",
- procname.c_str(),
- reltime.c_str(),
- buffer[0],
- buffer[1]);
- print(line, 1);
+ if (processName) {
+ realProcname = processName;
} else {
- if (now >= m_startTime) {
- Timespec delta = now - m_startTime;
- reltime = StringPrintf(" %02ld:%02ld:%02ld",
- delta.tv_sec / (60 * 60),
- (delta.tv_sec % (60 * 60)) / 60,
- delta.tv_sec % 60);
+ if (!logProcessName) {
+ logProcessName = new std::string;
+ }
+ realProcname = logProcessName;
+ }
+ if (!realProcname->empty()) {
+ procname.reserve(realProcname->size() + 1);
+ procname += " ";
+ procname += *realProcname;
+ }
+
+ if (outputlevel >= DEBUG) {
+ // add relative time stamp
+ Timespec now = Timespec::monotonic();
+ if (!m_startTime) {
+ // first message, start counting time
+ m_startTime = now;
+ time_t nowt = time(NULL);
+ struct tm tm_gm, tm_local;
+ char buffer[2][80];
+ gmtime_r(&nowt, &tm_gm);
+ localtime_r(&nowt, &tm_local);
+ reltime = " 00:00:00";
+ strftime(buffer[0], sizeof(buffer[0]),
+ "%a %Y-%m-%d %H:%M:%S",
+ &tm_gm);
+ strftime(buffer[1], sizeof(buffer[1]),
+ "%H:%M %z %Z",
+ &tm_local);
+ std::string line =
+ StringPrintf("[DEBUG%s%s] %s UTC = %s\n",
+ procname.c_str(),
+ reltime.c_str(),
+ buffer[0],
+ buffer[1]);
} else {
- reltime = " ??:??:??";
+ if (now >= m_startTime) {
+ Timespec delta = now - m_startTime;
+ reltime = StringPrintf(" %02ld:%02ld:%02ld",
+ delta.tv_sec / (60 * 60),
+ (delta.tv_sec % (60 * 60)) / 60,
+ delta.tv_sec % 60);
+ } else {
+ reltime = " ??:??:??";
+ }
}
}
}
+
+ if (!firstLine.empty()) {
+ print(firstLine, 1);
+ }
tag = StringPrintf("[%s%s%s] %s%s",
levelToStr(msglevel),
procname.c_str(),
@@ -224,41 +264,68 @@ Logger::MessageOptions::MessageOptions(Level level,
{
}
-void Logger::message(Level level,
- const std::string *prefix,
- const char *file,
- int line,
- const char *function,
- const char *format,
- ...)
+Logger::Handle::Handle() throw ()
+{
+}
+
+Logger::Handle::Handle(Logger *logger) throw ()
+{
+ m_logger.reset(logger);
+}
+
+Logger::Handle::Handle(const Handle &other) throw ()
+{
+ m_logger = other.m_logger;
+}
+
+Logger::Handle & Logger::Handle::operator = (const Handle &other) throw ()
+{
+ if (this != &other) {
+ m_logger = other.m_logger;
+ }
+ return *this;
+}
+
+Logger::Handle::~Handle() throw ()
+{
+ m_logger.reset();
+}
+
+void Logger::Handle::message(Level level,
+ const std::string *prefix,
+ const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...)
{
va_list args;
va_start(args, format);
- messagev(MessageOptions(level, prefix, file, line, function), format, args);
+ m_logger->messagev(MessageOptions(level, prefix, file, line, function), format, args);
va_end(args);
}
-void Logger::message(Level level,
- const std::string &prefix,
- const char *file,
- int line,
- const char *function,
- const char *format,
- ...)
+void Logger::Handle::message(Level level,
+ const std::string &prefix,
+ const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...)
{
va_list args;
va_start(args, format);
- messagev(MessageOptions(level, &prefix, file, line, function), format, args);
+ m_logger->messagev(MessageOptions(level, &prefix, file, line, function), format, args);
va_end(args);
}
-void Logger::messageWithOptions(const MessageOptions &options,
- const char *format,
- ...)
+void Logger::Handle::messageWithOptions(const MessageOptions &options,
+ const char *format,
+ ...)
{
va_list args;
va_start(args, format);
- messagev(options, format, args);
+ m_logger->messagev(options, format, args);
va_end(args);
}
diff --git a/src/syncevo/Logging.h b/src/syncevo/Logging.h
index 283efa8d..dde9e325 100644
--- a/src/syncevo/Logging.h
+++ b/src/syncevo/Logging.h
@@ -33,8 +33,11 @@
#endif
#include <syncevo/Timespec.h>
+#include <syncevo/ThreadSupport.h>
#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
@@ -130,8 +133,17 @@ class Logger
*
* Included by LoggerStdout in the [INFO/DEBUG/...] tag.
*/
- static void setProcessName(const std::string &name) { m_processName = name; }
- static std::string getProcessName() { return m_processName; }
+ static void setProcessName(const std::string &name);
+ static std::string getProcessName();
+
+ /**
+ * Obtains the recursive logging mutex.
+ *
+ * All calls offered by the Logger class already lock that mutex
+ * internally, but sometimes it may be necessary to protect a larger
+ * region of logging related activity.
+ */
+ static RecMutex::Guard lock();
#ifdef HAVE_GLIB
/**
@@ -161,6 +173,15 @@ class Logger
virtual ~Logger();
/**
+ * Prepare logger for removal from logging stack. May be called
+ * multiple times.
+ *
+ * The logger should stop doing anything right away and just pass
+ * on messages until it gets deleted eventually.
+ */
+ virtual void remove() throw () {}
+
+ /**
* Collects all the parameters which may get passed to
* messagev.
*/
@@ -199,36 +220,69 @@ class Logger
const char *format,
va_list args) = 0;
- /** redirect into messagev() */
- void message(Level level,
- const std::string *prefix,
- const char *file,
- int line,
- const char *function,
- const char *format,
- ...)
+ /**
+ * A convenience and backwards-compatibility class which allows
+ * calling some methods of the underlying pointer directly similar
+ * to the Logger reference returned in previous SyncEvolution
+ * releases.
+ */
+ class Handle
+ {
+ boost::shared_ptr<Logger> m_logger;
+
+ public:
+ Handle() throw ();
+ Handle(Logger *logger) throw ();
+ template<class L> Handle(const boost::shared_ptr<L> &logger) throw () : m_logger(logger) {}
+ template<class L> Handle(const boost::weak_ptr<L> &logger) throw () : m_logger(logger.lock()) {}
+ Handle(const Handle &other) throw ();
+ Handle &operator = (const Handle &other) throw ();
+ ~Handle() throw ();
+
+ operator bool () const { return m_logger; }
+ bool operator == (Logger *logger) const { return m_logger.get() == logger; }
+ Logger *get() const { return m_logger.get(); }
+
+ void messagev(const MessageOptions &options,
+ const char *format,
+ va_list args)
+ {
+ m_logger->messagev(options, format, args);
+ }
+
+ void message(Level level,
+ const std::string *prefix,
+ const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...)
#ifdef __GNUC__
- __attribute__((format(printf, 7, 8)))
+ __attribute__((format(printf, 7, 8)))
#endif
- ;
- void message(Level level,
- const std::string &prefix,
- const char *file,
- int line,
- const char *function,
- const char *format,
- ...)
+ ;
+ void message(Level level,
+ const std::string &prefix,
+ const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...)
#ifdef __GNUC__
- __attribute__((format(printf, 7, 8)))
+ __attribute__((format(printf, 7, 8)))
#endif
- ;
- void messageWithOptions(const MessageOptions &options,
- const char *format,
- ...)
+ ;
+ void messageWithOptions(const MessageOptions &options,
+ const char *format,
+ ...)
#ifdef __GNUC__
- __attribute__((format(printf, 3, 4)))
+ __attribute__((format(printf, 3, 4)))
#endif
- ;
+ ;
+ void setLevel(Level level) { m_logger->setLevel(level); }
+ Level getLevel() { return m_logger->getLevel(); }
+ void remove() throw () { m_logger->remove(); }
+ };
/**
* Grants access to the singleton which implements logging.
@@ -236,31 +290,25 @@ class Logger
* class itself is platform specific: if no Log instance
* has been set yet, then this call has to create one.
*/
- static Logger &instance();
+ static Handle instance();
/**
- * Overrides the default Logger implementation. The Logger class
- * itself will never delete the active logger.
+ * Overrides the current default Logger implementation.
*
* @param logger will be used for all future logging activities
*/
- static void pushLogger(Logger *logger);
+ static void addLogger(const Handle &logger);
/**
- * Remove the current logger and restore previous one.
- * Must match a pushLogger() call.
- */
- static void popLogger();
-
- /** total number of active loggers */
- static int numLoggers();
-
- /**
- * access to active logger
- * @param index 0 for oldest (inner-most) logger
- * @return pointer or NULL for invalid index
+ * Remove the specified logger.
+ *
+ * Note that the logger might still be in use afterwards, for
+ * example when a different thread currently uses it. Therefore
+ * loggers should be small stub classes. If they need access to
+ * more expensive classes to do their work, they shold hold weak
+ * reference to those and only lock them when logging.
*/
- static Logger *loggerAt(int index);
+ static void removeLogger(Logger *logger);
virtual void setLevel(Level level) { m_level = level; }
virtual Level getLevel() { return m_level; }
@@ -288,7 +336,6 @@ class Logger
boost::function<void (std::string &chunk, size_t expectedTotal)> print);
private:
- static std::string m_processName;
Level m_level;
/**
@@ -299,6 +346,76 @@ class Logger
Timespec m_startTime;
};
+/**
+ * Takes a logger and adds it to the stack
+ * as long as the instance exists.
+ */
+template<class L> class PushLogger : boost::noncopyable
+{
+ Logger::Handle m_logger;
+
+ public:
+ PushLogger() {}
+ /**
+ * Can use Handle directly here.
+ */
+ PushLogger(const Logger::Handle &logger) : m_logger(logger)
+ {
+ if (m_logger) {
+ Logger::addLogger(m_logger);
+ }
+ }
+ /**
+ * Take any type that a Handle constructor accepts, then use it as
+ * Handle.
+ */
+ template <class M> PushLogger(M logger) : m_logger(Logger::Handle(logger))
+ {
+ if (m_logger) {
+ Logger::addLogger(m_logger);
+ }
+ }
+ ~PushLogger() throw ()
+ {
+ if (m_logger) {
+ Logger::removeLogger(m_logger.get());
+ }
+ }
+
+ operator bool () const { return m_logger; }
+
+ void reset(const Logger::Handle &logger)
+ {
+ if (m_logger) {
+ Logger::removeLogger(m_logger.get());
+ }
+ m_logger = logger;
+ if (m_logger) {
+ Logger::addLogger(m_logger);
+ }
+ }
+ template<class M> void reset(M logger)
+ {
+ if (m_logger) {
+ Logger::removeLogger(m_logger.get());
+ }
+ m_logger = Logger::Handle(logger);
+ if (m_logger) {
+ Logger::addLogger(m_logger);
+ }
+ }
+
+ void reset()
+ {
+ if (m_logger) {
+ Logger::removeLogger(m_logger.get());
+ }
+ m_logger = Logger::Handle();
+ }
+
+ L *get() { return static_cast<L *>(m_logger.get()); }
+ L * operator -> () { return get(); }
+};
/**
* Wraps Logger::message() in the current default logger.
diff --git a/src/syncevo/SuspendFlags.h b/src/syncevo/SuspendFlags.h
index f527dfa4..a45c1154 100644
--- a/src/syncevo/SuspendFlags.h
+++ b/src/syncevo/SuspendFlags.h
@@ -21,6 +21,7 @@
#define INCL_SUSPENDFLAGS
#include <signal.h>
+#include <stdint.h>
#include <boost/smart_ptr.hpp>
#include <boost/function.hpp>
#include <boost/signals2.hpp>
diff --git a/src/syncevo/SyncContext.cpp b/src/syncevo/SyncContext.cpp
index fe8e9183..9624c383 100644
--- a/src/syncevo/SyncContext.cpp
+++ b/src/syncevo/SyncContext.cpp
@@ -267,12 +267,30 @@ public:
}
};
-// this class owns the logging directory and is responsible
+class LogDir;
+
+/**
+ * Helper class for LogDir: acts as proxy for logging into
+ * the LogDir's reports and log file.
+ */
+class LogDirLogger : public Logger
+{
+ Logger::Handle m_parentLogger; /**< the logger which was active before we started to intercept messages */
+ boost::weak_ptr<LogDir> m_logdir; /**< grants access to report and Synthesis engine */
+
+public:
+ LogDirLogger(const boost::weak_ptr<LogDir> &logdir);
+ virtual void remove() throw ();
+ virtual void messagev(const MessageOptions &options,
+ const char *format,
+ va_list args);
+};
+
+// This class owns the logging directory. It is responsible
// for redirecting output at the start and end of sync (even
-// in case of exceptions thrown!)
-class LogDir : public Logger, private boost::noncopyable, private LogDirNames {
+// in case of exceptions thrown!).
+class LogDir : private boost::noncopyable, private LogDirNames {
SyncContext &m_client;
- Logger &m_parentLogger; /**< the logger which was active before we started to intercept messages */
string m_logdir; /**< configured backup root dir */
int m_maxlogdirs; /**< number of backup dirs to preserve, 0 if unlimited */
string m_prefix; /**< common prefix of backup dirs */
@@ -286,11 +304,18 @@ class LogDir : public Logger, private boost::noncopyable, private LogDirNames {
that this class still is the central point to ask
for the name of the log file. */
boost::scoped_ptr<SafeConfigNode> m_info; /**< key/value representation of sync information */
+
+ // Access to m_report and m_client must be thread-safe as soon as
+ // LogDirLogger is active, because they are shared between main
+ // thread and any thread which might log errors.
+ friend class LogDirLogger;
bool m_readonly; /**< m_info is not to be written to */
SyncReport *m_report; /**< record start/end times here */
-public:
- LogDir(SyncContext &client) : m_client(client), m_parentLogger(Logger::instance()), m_info(NULL), m_readonly(false), m_report(NULL)
+ boost::weak_ptr<LogDir> m_self;
+ PushLogger<LogDirLogger> m_logger; /**< active logger */
+
+ LogDir(SyncContext &client) : m_client(client), m_info(NULL), m_readonly(false), m_report(NULL)
{
// Set default log directory. This will be overwritten with a user-specified
// location later on, if one was selected by the user. SyncEvolution >= 0.9 alpha
@@ -312,6 +337,14 @@ public:
setLogdir(path);
}
+public:
+ static boost::shared_ptr<LogDir> create(SyncContext &client)
+ {
+ boost::shared_ptr<LogDir> logdir(new LogDir(client));
+ logdir->m_self = logdir;
+ return logdir;
+ }
+
/**
* Finds previous log directories for context. Reports errors via exceptions.
*
@@ -512,35 +545,36 @@ public:
}
// update log level of default logger and our own replacement
- Level level;
+ Logger::Level level;
switch (logLevel) {
case 0:
// default for console output
- level = INFO;
+ level = Logger::INFO;
break;
case 1:
- level = ERROR;
+ level = Logger::ERROR;
break;
case 2:
- level = INFO;
+ level = Logger::INFO;
break;
default:
if (m_logfile.empty() || getenv("SYNCEVOLUTION_DEBUG")) {
// no log file or user wants to see everything:
// print all information to the console
- level = DEBUG;
+ level = Logger::DEBUG;
} else {
// have log file: avoid excessive output to the console,
// full information is in the log file
- level = INFO;
+ level = Logger::INFO;
}
break;
}
if (mode != SESSION_USE_PATH) {
Logger::instance().setLevel(level);
}
- setLevel(level);
- Logger::pushLogger(this);
+ boost::shared_ptr<Logger> logger(new LogDirLogger(m_self));
+ logger->setLevel(level);
+ m_logger.reset(logger);
time_t start = time(NULL);
if (m_report) {
@@ -710,6 +744,7 @@ public:
if (!m_readonly) {
writeTimestamp("end", end);
if (m_report) {
+ RecMutex::Guard guard = Logger::lock();
writeReport(*m_report);
}
m_info->flush();
@@ -718,11 +753,9 @@ public:
}
}
- // remove redirection of logging (safe for destructor)
+ // Remove redirection of logging.
void restore() {
- if (&Logger::instance() == this) {
- Logger::popLogger();
- }
+ m_logger.reset();
}
~LogDir() {
@@ -730,47 +763,6 @@ public:
}
- virtual void messagev(const MessageOptions &options,
- const char *format,
- va_list args)
- {
- // always to parent first (usually stdout):
- // if the parent is a LogRedirect instance, then
- // it'll flush its own output first, which ensures
- // that the new output comes later (as desired)
- {
- va_list argscopy;
- va_copy(argscopy, args);
- m_parentLogger.messagev(options, format, argscopy);
- va_end(argscopy);
- }
-
- if (m_report &&
- options.m_level <= ERROR &&
- m_report->getError().empty()) {
- va_list argscopy;
- va_copy(argscopy, args);
- string error = StringPrintfV(format, argscopy);
- va_end(argscopy);
-
- m_report->setError(error);
- }
-
- if (m_client.getEngine().get()) {
- va_list argscopy;
- va_copy(argscopy, args);
- // once to Synthesis log, with full debugging
- m_client.getEngine().doDebug(options.m_level,
- options.m_prefix ? options.m_prefix->c_str() : NULL,
- options.m_file,
- options.m_line,
- options.m_function,
- format,
- argscopy);
- va_end(argscopy);
- }
- }
-
#if 0
/**
* A quick check for level = SHOW text dumps whether the text dump
@@ -933,6 +925,67 @@ private:
}
};
+LogDirLogger::LogDirLogger(const boost::weak_ptr<LogDir> &logdir) :
+ m_parentLogger(Logger::instance()),
+ m_logdir(logdir)
+{
+}
+
+void LogDirLogger::remove() throw ()
+{
+ // Forget reference to LogDir. This prevents accessing it in
+ // future messagev() calls.
+ RecMutex::Guard guard = Logger::lock();
+ m_logdir.reset();
+}
+
+void LogDirLogger::messagev(const MessageOptions &options,
+ const char *format,
+ va_list args)
+{
+ // Protects ordering of log messages and access to shared
+ // variables like m_report and m_engine.
+ RecMutex::Guard guard = Logger::lock();
+
+ // always to parent first (usually stdout):
+ // if the parent is a LogRedirect instance, then
+ // it'll flush its own output first, which ensures
+ // that the new output comes later (as desired)
+ va_list argscopy;
+ va_copy(argscopy, args);
+ m_parentLogger.messagev(options, format, argscopy);
+ va_end(argscopy);
+
+ boost::shared_ptr<LogDir> logdir = m_logdir.lock();
+ if (logdir) {
+ if (logdir->m_report &&
+ options.m_level <= ERROR &&
+ logdir->m_report->getError().empty()) {
+ va_list argscopy;
+ va_copy(argscopy, args);
+ string error = StringPrintfV(format, argscopy);
+ va_end(argscopy);
+
+ logdir->m_report->setError(error);
+ }
+
+ if (logdir->m_client.getEngine().get()) {
+ va_list argscopy;
+ va_copy(argscopy, args);
+ // once to Synthesis log, with full debugging
+ logdir->m_client.getEngine().doDebug(options.m_level,
+ options.m_prefix ? options.m_prefix->c_str() : NULL,
+ options.m_file,
+ options.m_line,
+ options.m_function,
+ format,
+ argscopy);
+ va_end(argscopy);
+ }
+ }
+}
+
+
const char* const LogDirNames::DIR_PREFIX = "SyncEvolution-";
/**
@@ -1001,7 +1054,7 @@ public:
private:
VirtualSyncSources_t m_virtualSources; /**< all configured virtual data sources (aka Synthesis <superdatastore>) */
- LogDir m_logdir; /**< our logging directory */
+ boost::shared_ptr<LogDir> m_logdir; /**< our logging directory */
SyncContext &m_client; /**< the context in which we were instantiated */
set<string> m_prepared; /**< remember for which source we dumped databases successfully */
string m_intro; /**< remembers the dumpLocalChanges() intro and only prints it again
@@ -1015,7 +1068,7 @@ private:
/** create name in current (if set) or previous logdir */
string databaseName(SyncSource &source, const string suffix, string logdir = "") {
if (!logdir.size()) {
- logdir = m_logdir.getLogdir();
+ logdir = m_logdir->getLogdir();
}
return logdir + "/" +
source.getName() + "." + suffix;
@@ -1076,9 +1129,9 @@ public:
// to search for previous backups of each source, if
// necessary.
SyncContext context(m_client.getContextName());
- LogDir logdir(context);
+ boost::shared_ptr<LogDir> logdir(LogDir::create(context));
vector<string> dirs;
- logdir.previousLogdirs(dirs);
+ logdir->previousLogdirs(dirs);
BOOST_FOREACH(SyncSource *source, *this) {
if ((!excludeSource.empty() && excludeSource != source->getName()) ||
@@ -1150,7 +1203,7 @@ public:
}
SourceList(SyncContext &client, bool doLogging) :
- m_logdir(client),
+ m_logdir(LogDir::create(client)),
m_client(client),
m_doLogging(doLogging),
m_reportTodo(true),
@@ -1160,37 +1213,37 @@ public:
// call as soon as logdir settings are known
void startSession(const string &logDirPath, int maxlogdirs, int logLevel, SyncReport *report) {
- m_logdir.setLogdir(logDirPath);
- m_previousLogdir = m_logdir.previousLogdir();
+ m_logdir->setLogdir(logDirPath);
+ m_previousLogdir = m_logdir->previousLogdir();
if (m_doLogging) {
- m_logdir.startSession(logDirPath, LogDir::SESSION_CREATE, maxlogdirs, logLevel, report);
+ m_logdir->startSession(logDirPath, LogDir::SESSION_CREATE, maxlogdirs, logLevel, report);
} else {
// Run debug session without paying attention to
// the normal logdir handling. The log level here
// refers to stdout. The log file will be as complete
// as possible.
- m_logdir.startSession(logDirPath, LogDir::SESSION_USE_PATH, 0, 1, report);
+ m_logdir->startSession(logDirPath, LogDir::SESSION_USE_PATH, 0, 1, report);
}
}
/** read-only access to existing session, identified in logDirPath */
void accessSession(const string &logDirPath) {
- m_logdir.setLogdir(logDirPath);
- m_previousLogdir = m_logdir.previousLogdir();
- m_logdir.startSession(logDirPath, LogDir::SESSION_READ_ONLY, 0, 0, NULL);
+ m_logdir->setLogdir(logDirPath);
+ m_previousLogdir = m_logdir->previousLogdir();
+ m_logdir->startSession(logDirPath, LogDir::SESSION_READ_ONLY, 0, 0, NULL);
}
/** return log directory, empty if not enabled */
const string &getLogdir() {
- return m_logdir.getLogdir();
+ return m_logdir->getLogdir();
}
/** return previous log dir found in startSession() */
const string &getPrevLogdir() const { return m_previousLogdir; }
/** set directory for database files without actually redirecting the logging */
- void setPath(const string &path) { m_logdir.setPath(path); }
+ void setPath(const string &path) { m_logdir->setPath(path); }
/**
* If possible (directory to compare against available) and enabled,
@@ -1214,7 +1267,7 @@ public:
vector<string> dirs;
if (oldSession.empty()) {
- m_logdir.previousLogdirs(dirs);
+ m_logdir->previousLogdirs(dirs);
}
BOOST_FOREACH(SyncSource *source, *this) {
@@ -1237,10 +1290,10 @@ public:
it != dirs.rend();
++it) {
const string &sessiondir = *it;
- LogDir oldsession(m_client);
- oldsession.openLogdir(sessiondir);
+ boost::shared_ptr<LogDir> oldsession(LogDir::create(m_client));
+ oldsession->openLogdir(sessiondir);
SyncReport report;
- oldsession.readReport(report);
+ oldsession->readReport(report);
if (report.find(source->getName()) != report.end()) {
// source was active in that session, use dump
// made there
@@ -1283,7 +1336,7 @@ public:
return;
}
- if (m_logdir.getLogfile().size() &&
+ if (m_logdir->getLogfile().size() &&
m_doLogging &&
(m_client.getDumpData() || m_client.getPrintChanges())) {
// dump initial databases
@@ -1340,17 +1393,17 @@ public:
}
// ensure that stderr is seen again
- m_logdir.restore();
+ m_logdir->restore();
// write out session status
- m_logdir.endSession();
+ m_logdir->endSession();
if (m_reportTodo) {
// haven't looked at result of sync yet;
// don't do it again
m_reportTodo = false;
- string logfile = m_logdir.getLogfile();
+ string logfile = m_logdir->getLogfile();
if (status == STATUS_OK) {
SE_LOG_SHOW(NULL, "\nSynchronization successful.");
} else if (logfile.size()) {
@@ -1380,7 +1433,7 @@ public:
// compare databases?
if (m_client.getPrintChanges()) {
- dumpLocalChanges(m_logdir.getLogdir(),
+ dumpLocalChanges(m_logdir->getLogdir(),
"before", "after", "",
StringPrintf("\nData modified %s during synchronization:\n",
m_client.isLocalSync() ? m_client.getContextName().c_str() : "locally"),
@@ -1388,12 +1441,12 @@ public:
}
// now remove some old logdirs
- m_logdir.expire();
+ m_logdir->expire();
}
} else {
// finish debug session
- m_logdir.restore();
- m_logdir.endSession();
+ m_logdir->restore();
+ m_logdir->endSession();
}
}
@@ -4136,16 +4189,15 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
void SyncContext::getSessions(vector<string> &dirs)
{
- LogDir logging(*this);
- logging.previousLogdirs(dirs);
+ LogDir::create(*this)->previousLogdirs(dirs);
}
string SyncContext::readSessionInfo(const string &dir, SyncReport &report)
{
- LogDir logging(*this);
- logging.openLogdir(dir);
- logging.readReport(report);
- return logging.getPeerNameFromLogdir(dir);
+ boost::shared_ptr<LogDir> logging(LogDir::create(*this));
+ logging->openLogdir(dir);
+ logging->readReport(report);
+ return logging->getPeerNameFromLogdir(dir);
}
#ifdef ENABLE_UNIT_TESTS
@@ -4157,7 +4209,7 @@ string SyncContext::readSessionInfo(const string &dir, SyncReport &report)
* With that setup and a fake SyncContext it is possible to simulate
* sessions and test the resulting logdirs.
*/
-class LogDirTest : public CppUnit::TestFixture, private SyncContext, private Logger
+class LogDirTest : public CppUnit::TestFixture, private SyncContext, public Logger
{
public:
LogDirTest() :
@@ -4165,11 +4217,11 @@ public:
m_maxLogDirs(10)
{
// suppress output by redirecting into m_out
- pushLogger(this);
+ addLogger(boost::shared_ptr<Logger>(this, NopDestructor()));
}
~LogDirTest() {
- popLogger();
+ removeLogger(this);
}
void setUp() {
diff --git a/src/syncevo/syncevo.am b/src/syncevo/syncevo.am
index 6b027df1..cc2e30cb 100644
--- a/src/syncevo/syncevo.am
+++ b/src/syncevo/syncevo.am
@@ -36,7 +36,7 @@ src_syncevo_sources = \
src/syncevo/eds_abi_wrapper.cpp \
src/syncevo/GLibSupport.h \
src/syncevo/GLibSupport.cpp \
- src/syncevo/ThreadingSupport.h \
+ src/syncevo/ThreadSupport.h \
\
src/syncevo/SyncML.h \
src/syncevo/SyncML.cpp \
@@ -150,7 +150,7 @@ src_syncevo_libsyncevolution_include_HEADERS = \
src/syncevo/Cmdline.h \
src/syncevo/ConfigFilter.h \
src/syncevo/GLibSupport.h \
- src/syncevo/ThreadingSupport.h \
+ src/syncevo/ThreadSupport.h \
src/syncevo/TrackingSyncSource.h \
src/syncevo/MapSyncSource.h \
src/syncevo/LogRedirect.h \
diff --git a/src/syncevolution.cpp b/src/syncevolution.cpp
index ab077a15..801a5cf9 100644
--- a/src/syncevolution.cpp
+++ b/src/syncevolution.cpp
@@ -421,7 +421,7 @@ int main( int argc, char **argv )
// stdout is printed normally. Deconstructing it when
// leaving main() does one final processing of pending
// output.
- LogRedirect redirect(false);
+ PushLogger<LogRedirect> redirect(new LogRedirect(LogRedirect::STDERR));
setvbuf(stderr, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
diff --git a/test/client-test-main.cpp b/test/client-test-main.cpp
index b68317c7..6685aa02 100644
--- a/test/client-test-main.cpp
+++ b/test/client-test-main.cpp
@@ -123,9 +123,7 @@ public:
}
~ClientListener() {
- if (&Logger::instance() == m_logger.get()) {
- Logger::popLogger();
- }
+ m_logger.reset();
}
void addAllowedFailures(string allowedFailures) {
@@ -138,9 +136,8 @@ public:
if (!getenv("SYNCEVOLUTION_DEBUG")) {
string logfile = m_currentTest + ".log";
simplifyFilename(logfile);
- m_logger.reset(new LogRedirect(true, logfile.c_str()));
+ m_logger.reset(new LogRedirect(LogRedirect::STDERR_AND_STDOUT, logfile.c_str()));
m_logger->setLevel(Logger::DEBUG);
- Logger::pushLogger(m_logger.get());
}
SE_LOG_DEBUG(NULL, "*** starting %s ***", m_currentTest.c_str());
m_failures.reset();
@@ -192,9 +189,6 @@ public:
if (!failure.empty()) {
SE_LOG_ERROR(NULL, "%s", failure.c_str());
}
- if (&Logger::instance() == m_logger.get()) {
- Logger::popLogger();
- }
m_logger.reset();
string logfile = m_currentTest + ".log";
@@ -245,7 +239,7 @@ private:
bool m_failed, m_testFailed;
string m_currentTest;
int m_alarmSeconds;
- auto_ptr<Logger> m_logger;
+ PushLogger<Logger> m_logger;
CppUnit::TestResultCollector m_failures;
static void alarmTriggered(int signal) {