summaryrefslogtreecommitdiff
path: root/src/syncevo/SyncContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/syncevo/SyncContext.cpp')
-rw-r--r--src/syncevo/SyncContext.cpp240
1 files changed, 146 insertions, 94 deletions
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() {