syncevolution/src/syncevo/Logging.h
Patrick Ohly eba8a74779 support local sync (BMC #712)
Local sync is configured with a new syncURL = local://<context> where
<context> identifies the set of databases to synchronize with. The
URI of each source in the config identifies the source in that context
to synchronize with.

The databases in that context run a SyncML session as client. The
config itself is for a server. Reversing these roles is possible by
putting the config into the other context.

A sync is started by the server side, via the new LocalTransportAgent.
That agent forks, sets up the client side, then passes messages
back and forth via stream sockets. Stream sockets are useful because
unexpected peer shutdown can be detected.

Running the server side requires a few changes:
- do not send a SAN message, the client will start the
  message exchange based on the config
- wait for that message before doing anything

The client side is more difficult:
- Per-peer config nodes do not exist in the target context.
  They are stored in a hidden .<context> directory inside
  the server config tree. This depends on the new "registering nodes
  in the tree" feature. All nodes are hidden, because users
  are not meant to edit any of them. Their name is intentionally
  chosen like traditional nodes so that removing the config
  also removes the new files.
- All relevant per-peer properties must be copied from the server
  config (log level, printing changes, ...); they cannot be set
  differently.

Because two separate SyncML sessions are used, we end up with
two normal session directories and log files.

The implementation is not complete yet:
- no glib support, so cannot be used in syncevo-dbus-server
- no support for CTRL-C and abort
- no interactive password entry for target sources
- unexpected slow syncs are detected on the client side, but
  not reported properly on the server side
2010-12-01 12:32:43 +01:00

225 lines
8.3 KiB
C++

/*
* Copyright (C) 2009 Patrick Ohly <patrick.ohly@gmx.de>
* Copyright (C) 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef INCL_LOGGING
#define INCL_LOGGING
#include <stdarg.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
/**
* Abstract interface for logging in SyncEvolution. Can be
* implemented by other classes to add information (like a certain
* prefix) before passing the message on to a global instance for the
* actual processing.
*/
class Logger
{
public:
/**
* Which of these levels is the right one for a certain message
* is a somewhat subjective choice. Here is a definition how they
* are supposed to be used:
* - error: severe problem which the user and developer have to
* know about
* - warning: a problem that was handled, but users and developers
* probably will want to know about
* - info: information about a sync session which the user
* will want to read during/after each sync session
* - developer: information about a sync session that is not
* interesting for a user (for example, because it
* is constant and already known) but which should
* be in each log because developers need to know
* it. Messages logged with this calls will be included
* at LOG_LEVEL_INFO, therefore messages should be small and
* not recur so that the log file size remains small.
* - debug: most detailed logging, messages may be arbitrarily large
*
* Here is a decision tree which helps to pick the right level:
* - an error: => ERROR
* - a non-fatal error: => WARNING
* - it changes during each sync or marks important steps
* in the sync: INFO
* - same as before, but without the [INFO] prefix added to each line: => SHOW
* - small, non-recurring message which is important for developers
* who read a log produced at LOG_LEVEL_INFO: DEVELOPER
* - everything else: DEBUG
*/
typedef enum {
/**
* only error messages printed
*/
ERROR,
/**
* error and warning messages printed
*/
WARNING,
/**
* "Normal" stdout output which is meant to be seen by a
* user.
*/
SHOW,
/**
* errors and info messages for users and developers will be
* printed: use this to keep the output consise and small
*/
INFO,
/**
* important messages to developers
*/
DEV,
/**
* all messages will be printed, including detailed debug
* messages
*/
DEBUG
} Level;
static const char *levelToStr(Level level);
/** always returns a valid level, also for NULL, by falling back to DEBUG */
static Level strToLevel(const char *str);
virtual ~Logger() {}
/**
* output a single message
*
* @param level level for current message
* @param prefix inserted at beginning of each line, if non-NULL
* @param file source file where message comes from, if non-NULL
* @param line source line number, if file is non-NULL
* @param function surrounding function name, if non-NULL
* @param format sprintf format
* @param args parameters for sprintf: consumed by this function,
* make copy with va_copy() if necessary!
*/
virtual void messagev(Level level,
const char *prefix,
const char *file,
int line,
const char *function,
const char *format,
va_list args) = 0;
/** default: redirect into messagev() */
virtual void message(Level level,
const char *prefix,
const char *file,
int line,
const char *function,
const char *format,
...)
#ifdef __GNUC__
__attribute__((format(printf, 7, 8)))
#endif
;
};
/**
* Global logging, implemented as a singleton with one instance per
* process.
*
* @TODO avoid global variable
*/
class LoggerBase : public Logger
{
public:
LoggerBase() : m_level(INFO) {}
/**
* Grants access to the singleton which implements logging.
* The implementation of this function and thus the Log
* class itself is platform specific: if no Log instance
* has been set yet, then this call has to create one.
*/
static LoggerBase &instance();
/**
* Overrides the default Logger implementation. The Logger class
* itself will never delete the active logger.
*
* @param logger will be used for all future logging activities
*/
static void pushLogger(LoggerBase *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
*/
static LoggerBase *loggerAt(int index);
virtual void setLevel(Level level) { m_level = level; }
virtual Level getLevel() { return m_level; }
private:
Level m_level;
};
/**
* Vararg macro which passes the message through a specific
* Logger class instance (if non-NULL) and otherwise calls
* the global logger directly. Adds source file and line.
*
* @TODO make source and line info optional for release
* @TODO add function name (GCC extension)
*/
#define SE_LOG(_level, _instance, _prefix, _format, _args...) \
do { \
if (_instance) { \
static_cast<Logger *>(_instance)->message(_level, \
_prefix, \
__FILE__, \
__LINE__, \
0, \
_format, \
##_args); \
} else { \
LoggerBase::instance().message(_level, \
_prefix, \
__FILE__, \
__LINE__, \
0, \
_format, \
##_args); \
} \
} while(false)
#define SE_LOG_SHOW(_instance, _prefix, _format, _args...) SE_LOG(Logger::SHOW, _instance, _prefix, _format, ##_args)
#define SE_LOG_ERROR(_instance, _prefix, _format, _args...) SE_LOG(Logger::ERROR, _instance, _prefix, _format, ##_args)
#define SE_LOG_WARNING(_instance, _prefix, _format, _args...) SE_LOG(Logger::WARNING, _instance, _prefix, _format, ##_args)
#define SE_LOG_INFO(_instance, _prefix, _format, _args...) SE_LOG(Logger::INFO, _instance, _prefix, _format, ##_args)
#define SE_LOG_DEV(_instance, _prefix, _format, _args...) SE_LOG(Logger::DEV, _instance, _prefix, _format, ##_args)
#define SE_LOG_DEBUG(_instance, _prefix, _format, _args...) SE_LOG(Logger::DEBUG, _instance, _prefix, _format, ##_args)
SE_END_CXX
#endif // INCL_LOGGING