Syslog and journald support

Add new CLI options to support logging to Syslog and journald.
journald support is also behind a new CMake flag, SUPPORT_JOURNALD,
since it adds a dependency on libsystemd-journal.
Add --quiet, which will disable output to STDOUT/STDERR. Useful
when logging directly to the above mentioned loggers.
Add --log-level, to allow for mix and match log levels, for example
only warning and critical can be enabled together.

New usage output:

VERsatile MONitoring Tool - VERMONT
 MANDATORY OPTIONS:
 -f, --config-file FILE     Use configuration file

 OTHER OPTIONS:
 -h, --help                 Display this help and exit
 -d, --debug                Log verbosity: -d NOTICE, -dd INFO,
                                -ddd DEBUG
 -l, --log-level LEVEL      Log level. Can be specified multiple
                                times and mix-matched.
                                In increasing order:

                                    debug
                                    info
                                    notice
                                    warning
                                    error
                                    critical

                                Default: critical, warning, error
 -q, --quiet                Do not write output to console
 -b, --daemon               Run in daemon mode (implies -q)
 -p, --pid-file FILE        Set process id filename (use with -d)
 -u, --user USER            Change user to USER (use with -d)
 -g, --group GROUP          Change group to GROUP (use with -d)
 -s, --syslog               Log to syslog
 -j, --journald             Log to journald
master
Luca Boccassi 2015-07-07 13:16:02 +01:00
parent 2fc0a05d5b
commit 0c492767bf
7 changed files with 319 additions and 67 deletions

View File

@ -116,6 +116,31 @@ TARGET_LINK_LIBRARIES(vermont
${CMAKE_THREAD_LIBS_INIT}
)
### journald
OPTION(SUPPORT_JOURNALD "Enable systemd journald support" OFF)
IF (SUPPORT_JOURNALD)
SET(PKG_CONFIG_USE_CMAKE_PREFIX_PATH 1)
FIND_PACKAGE(PkgConfig REQUIRED)
PKG_CHECK_MODULES(JOURNALD libsystemd)
IF (NOT JOURNALD_FOUND)
PKG_CHECK_MODULES(JOURNALD libsystemd-journal)
IF (NOT JOURNALD_FOUND)
MESSAGE(FATAL_ERROR "Could not find journald libraries.")
ENDIF (NOT JOURNALD_FOUND)
ENDIF (NOT JOURNALD_FOUND)
ENDIF (SUPPORT_JOURNALD)
IF (JOURNALD_FOUND)
ADD_DEFINITIONS(-DJOURNALD_SUPPORT_ENABLED)
LINK_DIRECTORIES(${JOURNALD_LIBRARY_DIRS})
INCLUDE_DIRECTORIES(${JOURNALD_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(vermont
${JOURNALD_LIBRARIES}
)
ELSE (JOURNALD_FOUND)
REMOVE_DEFINITIONS(-DJOURNALD_SUPPORT_ENABLED)
ENDIF (JOURNALD_FOUND)
### ZMQ receiver
OPTION(SUPPORT_ZMQ "Enable ZMQ support" OFF)

View File

@ -30,8 +30,10 @@ extern "C" {
/** Maximum length of exception strings */
const int EXCEPTION_MAXLEN = 1024;
static int msg_level=MSG_ERROR;
static const char *MSG_TAB[]={ "FATAL ", "VERMONT", "ERROR ", "INFO ", "DEBUG ", "VDEBUG ", 0};
static int msg_level = LOG_UPTO(MSG_ERROR);
static bool quiet = false;
static bool journald_enabled = false;
static bool syslog_enabled = false;
/*
we need to serialize for msg_stat()
@ -54,6 +56,30 @@ extern "C" {
// mutex for logging function
static pthread_mutex_t msg_mutex;
static const char *level_to_string (int level)
{
switch (level) {
case LOG_EMERG:
return "EMERGENCY";
case LOG_ALERT:
return "ALERT";
case LOG_CRIT:
return "CRITICAL";
case LOG_ERR:
return "ERROR";
case LOG_WARNING:
return "WARNING";
case LOG_NOTICE:
return "NOTICE";
case LOG_INFO:
return "INFO";
case LOG_DEBUG:
return "DEBUG";
default:
return "UNKNOWN";
}
}
/**
* initializes logging system
* must be called at program startup!
@ -91,53 +117,48 @@ extern "C" {
static int nothreads = 0; // how many threads access this function?
#endif
/* nummerically higher value means lower priority */
if (level > msg_level) {
return;
// we must lock via mutex, else logging outputs are mixed when several
// threads log simultaneously
int retval = pthread_mutex_lock(&msg_mutex);
if (retval != 0) printf("!!! msg: pthread_mutex_lock returned error code %d (%s)\n", retval, strerror(retval));
struct timeval tv;
gettimeofday(&tv, 0);
struct tm* tform = localtime(reinterpret_cast<time_t*>(&tv.tv_sec));
#if defined(DEBUG)
// determine thread id
pthread_t pt = pthread_self();
std::map<pthread_t, int>::iterator iter = threadids.find(pt);
int threadid;
if (iter == threadids.end()) {
threadid = nothreads;
threadids[pt] = nothreads++;
} else {
// we must lock via mutex, else logging outputs are mixed when several
// threads log simultaneously
int retval = pthread_mutex_lock(&msg_mutex);
if (retval != 0) printf("!!! msg: pthread_mutex_lock returned error code %d (%s)\n", retval, strerror(retval));
struct timeval tv;
gettimeofday(&tv, 0);
struct tm* tform = localtime(reinterpret_cast<time_t*>(&tv.tv_sec));
#if defined(DEBUG)
// determine thread id
pthread_t pt = pthread_self();
std::map<pthread_t, int>::iterator iter = threadids.find(pt);
int threadid;
if (iter == threadids.end()) {
threadid = nothreads;
threadids[pt] = nothreads++;
} else {
threadid = iter->second;
}
printf("%02d:%02d:%02d.%03ld[%d] %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, threadid, MSG_TAB[level]);
#else
//printf("%02d:%02d:%02d.%03ld %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, MSG_TAB[level]);
// Gerhard: message level is more important than Milliseconds (at least to me)
printf("%02d/%02d %02d:%02d:%02d %6s", tform->tm_mday, tform->tm_mon +1, tform->tm_hour, tform->tm_min, tform->tm_sec, MSG_TAB[level]);
#endif
// need helper variable here because va_list parameter of vprintf is undefined after function call
va_list my_args;
va_copy(my_args, *args);
vprintf(fmt, my_args);
printf("\n");
if (logtext != NULL) {
#if defined(DEBUG)
snprintf(logtext, EXCEPTION_MAXLEN, "%02d:%02d:%02d.%03ld[%d] %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, threadid, MSG_TAB[level]);
#else
snprintf(logtext, EXCEPTION_MAXLEN, "%02d:%02d:%02d.%03ld %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, MSG_TAB[level]);
#endif
vsnprintf(logtext, EXCEPTION_MAXLEN-strlen(logtext), fmt, *args);
}
retval = pthread_mutex_unlock(&msg_mutex);
if (retval != 0) printf("!!! msg: pthread_mutex_unlock returned error code %d\n", retval);
threadid = iter->second;
}
printf("%02d:%02d:%02d.%03ld[%d] %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, threadid, level_to_string(level));
#else
//printf("%02d:%02d:%02d.%03ld %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, level_to_string(level));
// Gerhard: message level is more important than Milliseconds (at least to me)
printf("%02d/%02d %02d:%02d:%02d %6s", tform->tm_mday, tform->tm_mon +1, tform->tm_hour, tform->tm_min, tform->tm_sec, level_to_string(level));
#endif
// need helper variable here because va_list parameter of vprintf is undefined after function call
va_list my_args;
va_copy(my_args, *args);
vprintf(fmt, my_args);
printf("\n");
if (logtext != NULL) {
#if defined(DEBUG)
snprintf(logtext, EXCEPTION_MAXLEN, "%02d:%02d:%02d.%03ld[%d] %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, threadid, level_to_string(level));
#else
snprintf(logtext, EXCEPTION_MAXLEN, "%02d:%02d:%02d.%03ld %6s", tform->tm_hour, tform->tm_min, tform->tm_sec, tv.tv_usec/1000, level_to_string(level));
#endif
vsnprintf(logtext, EXCEPTION_MAXLEN-strlen(logtext), fmt, *args);
}
retval = pthread_mutex_unlock(&msg_mutex);
if (retval != 0) printf("!!! msg: pthread_mutex_unlock returned error code %d\n", retval);
}
/**
@ -197,6 +218,62 @@ extern "C" {
msg_level=level;
}
/**
* gets verbosity level of vermont
*/
int msg_getlevel()
{
return msg_level;
}
/**
* check if log to file, not to stdout/err
*/
void msg_setquiet(bool set_quiet)
{
quiet = set_quiet;
}
/**
* check if log to file, not to stdout/err
*/
bool msg_getquiet()
{
return quiet;
}
/**
* set whether to log to journald
*/
void msg_set_journald(bool set_journald)
{
journald_enabled = set_journald;
}
/**
* return true if logging to journald, false otherwise
*/
bool msg_get_journald()
{
return journald_enabled;
}
/**
* set whether to log to syslog
*/
void msg_set_syslog(bool set_syslog)
{
syslog_enabled = set_syslog;
}
/**
* return true if logging to syslog, false otherwise
*/
bool msg_get_syslog()
{
return syslog_enabled;
}
/**
* used by vermont modules to generate output statistics

View File

@ -9,6 +9,13 @@
#define MSG_H
#include <stdio.h>
#include <stdbool.h>
#include <syslog.h>
#ifdef JOURNALD_SUPPORT_ENABLED
#include <systemd/sd-journal.h>
#else
#define sd_journal_print(lvl, ...)
#endif
#ifdef __cplusplus
extern "C" {
@ -34,13 +41,14 @@ typedef void (*LOGFUNCTION)(void *);
#endif
/* defines for the message system */
#define MSG_BLANK 256
#define MSG_VDEBUG 5 // mostly for ipfix byte-level messages
#define MSG_DEBUG 4 // debugging messages, for example used by DPRINTF
#define MSG_INFO 3 // informational messages, shown without debug-mode but only with verbose logging enabled
#define MSG_ERROR 2 // error or warning messages which are shown during default execution
#define MSG_DIALOG 1 // messages which are shown during default execution
#define MSG_FATAL 0 // fatal messages which are shown every time
#define MSG_VDEBUG LOG_DEBUG // mostly for ipfix byte-level messages
#define MSG_DEBUG LOG_INFO // debugging messages, for example used by DPRINTF
#define MSG_INFO LOG_NOTICE // informational messages, shown without debug-mode but only with verbose logging enabled
#define MSG_ERROR LOG_WARNING // error or warning messages which are shown during default execution
#define MSG_DIALOG LOG_ERR // messages which are shown during default execution
#define MSG_FATAL LOG_CRIT // fatal messages which are shown every time
#define MSG_ALERT LOG_ALERT // not used
#define MSG_EMERG LOG_EMERG // not used
//#define MSG_DEFAULT MSG_ERROR
@ -48,6 +56,13 @@ void msg_init(void);
void msg_shutdown(void);
void msg2(const int, const char*, const char*, const char*, const int, const char *, ...);
void msg_setlevel(int);
int msg_getlevel();
void msg_setquiet(bool);
bool msg_getquiet();
void msg_set_journald(bool);
bool msg_get_journald();
void msg_set_syslog(bool);
bool msg_get_syslog();
int msg_stat(const char *fmt, ...);
int msg_stat_setup(int mode, FILE *f);
void vermont_assert(const char* expr, const char* description, int line, const char* filename, const char* prettyfuncname, const char* funcname);
@ -65,21 +80,56 @@ void vermont_exception(const int, const char*, const char*, const char*, const c
//#endif
// useful defines for logging
#define THROWEXCEPTION(...) vermont_exception(__LINE__, __FILE__, __PRETTY_FUNCTION__, __func__, ##__VA_ARGS__)
#define THROWEXCEPTION(...) \
__extension__ \
({ \
if (msg_getlevel() & LOG_MASK(MSG_FATAL)) { \
if (msg_get_syslog()) { \
syslog(MSG_FATAL, ##__VA_ARGS__); \
} \
if (msg_get_journald()) { \
sd_journal_print(MSG_FATAL, ##__VA_ARGS__); \
} \
} \
vermont_exception(__LINE__, __FILE__, __PRETTY_FUNCTION__, __func__, ##__VA_ARGS__); \
})
#define msg(...) msg2(__LINE__, __FILE__, __PRETTY_FUNCTION__, __func__, ##__VA_ARGS__)
#define msg(lvl, ...) \
__extension__ \
({ \
if (msg_getlevel() & LOG_MASK(lvl)) { \
if (msg_get_syslog()) { \
syslog(lvl, ##__VA_ARGS__); \
} \
if (msg_get_journald()) { \
sd_journal_print(lvl, ##__VA_ARGS__); \
} \
if (!msg_getquiet()) { \
msg2(__LINE__, __FILE__, __PRETTY_FUNCTION__, __func__, lvl, ##__VA_ARGS__); \
} \
} \
})
#ifdef DEBUG
#define DPRINTF(...) msg2(__LINE__, __FILE__, __PRETTY_FUNCTION__, __func__, MSG_DEBUG, ##__VA_ARGS__)
#define DPRINTFL(...) msg2(__LINE__, __FILE__, __PRETTY_FUNCTION__, __func__, ##__VA_ARGS__)
#define DPRINTF(...) msg(MSG_DEBUG, ##__VA_ARGS__)
#define DPRINTFL(lvl, ...) msg(lvl, ##__VA_ARGS__)
#define ASSERT(exp, description) \
{ \
__extension__ \
({ \
if (!(exp)) { \
if (msg_getlevel() & LOG_MASK(MSG_ERROR)) { \
if (msg_get_syslog()) { \
syslog(MSG_ERROR, description); \
} \
if (msg_get_journald()) { \
sd_journal_print(MSG_ERROR, description); \
} \
} \
vermont_assert(#exp, (description), __LINE__, __FILE__, __PRETTY_FUNCTION__, __func__); \
} \
}
})
#else

View File

@ -47,3 +47,10 @@ IF (SUPPORT_DTLS)
TARGET_LINK_LIBRARIES(example_code ${OPENSSL_LIBRARIES})
TARGET_LINK_LIBRARIES(example_code_2 ${OPENSSL_LIBRARIES})
ENDIF (SUPPORT_DTLS)
IF (JOURNALD_FOUND)
TARGET_LINK_LIBRARIES(test_everything ${JOURNALD_LIBRARIES})
TARGET_LINK_LIBRARIES(mtutest ${JOURNALD_LIBRARIES})
TARGET_LINK_LIBRARIES(example_code ${JOURNALD_LIBRARIES})
TARGET_LINK_LIBRARIES(example_code_2 ${JOURNALD_LIBRARIES})
ENDIF (JOURNALD_FOUND)

View File

@ -54,3 +54,9 @@ IF (CONNECTION_FILTER)
${GSL_LIBRARIES}
)
ENDIF (CONNECTION_FILTER)
IF (JOURNALD_FOUND)
TARGET_LINK_LIBRARIES(vermonttest
${JOURNALD_LIBRARIES}
)
ENDIF (JOURNALD_FOUND)

View File

@ -51,7 +51,8 @@ struct parameters {
const char *pid_file;
uid_t uid;
gid_t gid;
int log_level;
int log_mask;
bool quiet;
bool daemon_mode;
};
@ -129,6 +130,26 @@ daemonise (const char *pid_file, uid_t uid, gid_t gid)
}
}
static int
parse_log_level (const char *arg, int mask)
{
if (!strcmp("debug", arg)) {
mask |= LOG_MASK(LOG_DEBUG);
} else if (!strcmp("info", arg)) {
mask |= LOG_MASK(LOG_INFO);
} else if (!strcmp("notice", arg)) {
mask |= LOG_MASK(LOG_NOTICE);
} else if (!strcmp("warning", arg)) {
mask |= LOG_MASK(LOG_WARNING);
} else if (!strcmp("error", arg)) {
mask |= LOG_MASK(LOG_ERR);
} else if (!strcmp("critical", arg)) {
mask |= LOG_MASK(LOG_CRIT);
}
return mask;
}
static void
usage (int status)
{
@ -139,10 +160,25 @@ usage (int status)
" -h, --help Display this help and exit\n"
" -d, --debug Log verbosity: -d NOTICE, -dd INFO,\n"
" -ddd DEBUG\n"
" -l, --log-level LEVEL Log level. Can be specified multiple\n"
" times and mix-matched. \n"
" In increasing order:\n\n"
" debug\n"
" info\n"
" notice\n"
" warning\n"
" error\n"
" critical\n\n"
" Default: critical, warning, error\n"
" -q, --quiet Do not write output to console\n"
" -b, --daemon Run in daemon mode (implies -q)\n"
" -p, --pid-file FILE Set process id filename (use with -d)\n"
" -u, --user USER Change user to USER (use with -d)\n"
" -g, --group GROUP Change group to GROUP (use with -d)\n"
" -s, --syslog Log to syslog\n"
#ifdef JOURNALD_SUPPORT_ENABLED
" -j, --journald Log to journald\n"
#endif
);
exit(status);
@ -151,7 +187,7 @@ usage (int status)
static int
parse_args (int argc, char **argv, struct parameters *params)
{
int opt, ret, option_index;
int opt, ret, option_index, level = 0;
struct passwd *pw;
struct group *gr;
@ -162,15 +198,22 @@ parse_args (int argc, char **argv, struct parameters *params)
{ "pid-file", required_argument, NULL, 'p' },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "quiet", no_argument, NULL, 'q' },
{ "debug", no_argument, NULL, 'd' },
{ "log-level", required_argument, NULL, 'l' },
#ifdef JOURNALD_SUPPORT_ENABLED
{ "journald", no_argument, NULL, 'j' },
#endif
{ "syslog", no_argument, NULL, 's' },
{ NULL, 0, NULL, 0}
};
while ((opt = getopt_long(argc, argv, "hbp:u:g:df:", long_opts,
while ((opt = getopt_long(argc, argv, "hbp:u:g:df:ql:js", long_opts,
&option_index)) != EOF) {
switch (opt) {
case 'b':
params->daemon_mode = true;
params->quiet = true;
break;
case 'f':
@ -206,7 +249,32 @@ parse_args (int argc, char **argv, struct parameters *params)
break;
case 'd':
params->log_level++;
/*
* Default log_level is LOG_WARNING (1 << 4). For each -d,
* bump log_level up to LOG_DEBUG (1 << 7).
*/
if (!(params->log_mask & LOG_MASK(LOG_DEBUG))) {
params->log_mask |= params->log_mask << 1;
}
break;
case 'l':
level = parse_log_level(optarg, level);
params->log_mask = level;
break;
case 'q':
params->quiet = true;
break;
#ifdef JOURNALD_SUPPORT_ENABLED
case 'j':
msg_set_journald(true);
break;
#endif
case 's':
msg_set_syslog(true);
break;
default:
@ -244,7 +312,7 @@ int main(int ac, char **dc)
struct parameters parameters;
memset(&parameters, 0, sizeof(struct parameters));
parameters.log_level = MSG_ERROR;
parameters.log_mask = LOG_UPTO(LOG_WARNING);
/* parse command line */
if (parse_args (ac, dc, &parameters) < 0) {
@ -260,6 +328,15 @@ int main(int ac, char **dc)
daemonise(parameters.pid_file, parameters.uid, parameters.gid);
}
if (parameters.quiet) {
msg_setquiet(true);
}
if (msg_get_syslog()) {
setlogmask(parameters.log_mask);
openlog("vermont", LOG_PID, LOG_DAEMON);
}
msg_init();
/**< Wrapper for the main thread's signal handlers*/
MainSignalHandler main_signal_handler;
@ -273,8 +350,8 @@ int main(int ac, char **dc)
}
/* setup verboseness */
msg(MSG_DIALOG, "message debug level is %d", parameters.log_level);
msg_setlevel(parameters.log_level);
msg(MSG_DIALOG, "message debug level is %d", parameters.log_mask);
msg_setlevel(parameters.log_mask);
manager.parseConfig(string(parameters.config_file));
@ -301,6 +378,10 @@ int main(int ac, char **dc)
}
msg(MSG_FATAL, "got signal - exiting");
manager.shutdown();
if (msg_get_syslog()) {
closelog();
}
}
//static void __cplusplus_really_sucks_andPeopleUsingVeryStrangeNamingConventionsWithLongLongExplicitBlaBlaAndfUnNycasE()

View File

@ -57,3 +57,9 @@ ADD_EXECUTABLE(injectUDPToCollector
TARGET_LINK_LIBRARIES(injectUDPToCollector
${PCAP_LIBRARY}
)
IF (JOURNALD_FOUND)
TARGET_LINK_LIBRARIES(testCollector
${JOURNALD_LIBRARIES}
)
ENDIF (JOURNALD_FOUND)