log: enhance syslog logging using CEE format

This patch enables to use syslog as log target in addition to the
default.  The logs are sent in CEE format (http://cee.mitre.org/).
This logging can be disabled using compile time option by

./configure --disable-syslog
(or)
rpmbuild glusterfs.tar.gz --without syslog

The framework provides two api

void gf_openlog (const char *ident, int option, int facility);
void gf_syslog (int error_code, int facility_priority, char *format, ...);

consumers need to call gf_openlog() prior to gf_syslog() like the way
traditional syslog function calls.  error_code is mandatory when using
gf_syslog().  For example,

gf_openlog (NULL, -1, -1);
gf_syslog (GF_ERR_DEV, LOG_ERR, "error reading configuration file");

Using syslog, admin is free to configure logger to
* reduce repeated log messages
* forward logs to remote logger
* execute a command on certain log pattern
* alert people for certain log pattern by email, snmp etc
* and many more

Change-Id: Ibacbcbbc547192893fc4a46b387496b622e4811f
BUG: 928648
Signed-off-by: Bala.FA <barumuga@redhat.com>
Reviewed-on: http://review.gluster.org/4915
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
This commit is contained in:
Bala.FA 2013-04-30 17:39:30 +05:30 committed by Vijay Bellur
parent f75957ab6b
commit 040319d8bc
3 changed files with 231 additions and 1 deletions

View File

@ -658,6 +658,20 @@ fi
AC_SUBST(CFLAGS)
# end enable debug section
# syslog section
AC_ARG_ENABLE([syslog],
AC_HELP_STRING([--disable-syslog],
[Disable syslog for logging]))
USE_SYSLOG="yes"
if test "x$enable_syslog" != "xno"; then
AC_DEFINE(GF_USE_SYSLOG, 1, [Use syslog for logging])
else
USE_SYSLOG="no"
fi
AM_CONDITIONAL([ENABLE_SYSLOG], [test x$USE_SYSLOG = xyes])
#end syslog section
BUILD_READLINE=no
AC_CHECK_LIB([readline -lcurses],[readline],[RLLIBS="-lreadline -lcurses"])
AC_CHECK_LIB([readline -ltermcap],[readline],[RLLIBS="-lreadline -ltermcap"])
@ -768,4 +782,5 @@ echo "Enable Debug : $BUILD_DEBUG"
echo "systemtap : $BUILD_SYSTEMTAP"
echo "Block Device backend : $BUILD_BD_XLATOR"
echo "glupy : $BUILD_GLUPY"
echo "Use syslog : $USE_SYSLOG"
echo

View File

@ -31,6 +31,10 @@
# rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without ocf
%{?_without_ocf:%global _without_ocf --without-ocf}
# if you wish to build rpms without syslog logging, compile like this
# rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@tar.gz --without syslog
%{?_without_syslog:%global _without_syslog --disable-syslog}
%if ( 0%{?fedora} && 0%{?fedora} > 16 ) || ( 0%{?rhel} && 0%{?rhel} > 6 )
%global _with_systemd true
%endif
@ -323,7 +327,7 @@ This package provides the api include files.
%build
./autogen.sh
%configure %{?_without_rdma} %{?_without_epoll} %{?_without_fusermount} %{?_without_georeplication} %{?_without_ocf}
%configure %{?_without_rdma} %{?_without_epoll} %{?_without_fusermount} %{?_without_georeplication} %{?_without_ocf} %{?_without_syslog}
# fix hardening and remove rpath in shlibs
%if ( 0%{?fedora} && 0%{?fedora} > 17 ) || ( 0%{?rhel} && 0%{?rhel} > 6 )

View File

@ -22,6 +22,16 @@
#include <string.h>
#include <stdlib.h>
#ifdef GF_USE_SYSLOG
#include <libintl.h>
#include <syslog.h>
#include "gf-error-codes.h"
#define GF_JSON_MSG_LENGTH 8192
#define GF_SYSLOG_CEE_FORMAT \
"@cee: {\"msg\": \"%s\", \"gf_code\": \"%u\", \"gf_message\": \"%s\"}"
#endif /* GF_USE_SYSLOG */
#include "xlator.h"
#include "logging.h"
#include "defaults.h"
@ -102,6 +112,207 @@ gf_log_fini (void)
}
#ifdef GF_USE_SYSLOG
/**
* gf_get_error_message -function to get error message for given error code
* @error_code: error code defined by log book
*
* @return: success: string
* failure: NULL
*/
const char *
gf_get_error_message (int error_code) {
return _gf_get_message (error_code);
}
/**
* gf_openlog -function to open syslog specific to gluster based on
* existent of file /var/lib/glusterd/logger.conf
* @ident: optional identification string similar to openlog()
* @option: optional value to option to openlog(). Passing -1 uses
* 'LOG_PID | LOG_NDELAY' as default
* @facility: optional facility code similar to openlog(). Passing -1
* uses LOG_DAEMON as default
*
* @return: void
*/
void
gf_openlog (const char *ident, int option, int facility)
{
int _option = option;
int _facility = facility;
if (-1 == _option) {
_option = LOG_PID | LOG_NDELAY;
}
if (-1 == _facility) {
_facility = LOG_LOCAL1;
}
setlocale(LC_ALL, "");
bindtextdomain("gluster", "/usr/share/locale");
textdomain("gluster");
openlog(ident, _option, _facility);
}
/**
* _json_escape -function to convert string to json encoded string
* @str: input string
* @buf: buffer to store encoded string
* @len: length of @buf
*
* @return: success: last unprocessed character position by pointer in @str
* failure: NULL
*
* Internal function. Heavily inspired by _ul_str_escape() function in
* libumberlog
*
* Sample output:
* [1] str = "devel error"
* buf = "devel error"
* [2] str = "devel error"
* buf = "devel\terror"
* [3] str = "I/O error on "/tmp/foo" file"
* buf = "I/O error on \"/tmp/foo\" file"
* [4] str = "I/O erroron /tmp/bar file"
* buf = "I/O error\u001bon /tmp/bar file"
*
*/
char *
_json_escape(const char *str, char *buf, size_t len)
{
static const unsigned char json_exceptions[UCHAR_MAX + 1] =
{
[0x01] = 1, [0x02] = 1, [0x03] = 1, [0x04] = 1,
[0x05] = 1, [0x06] = 1, [0x07] = 1, [0x08] = 1,
[0x09] = 1, [0x0a] = 1, [0x0b] = 1, [0x0c] = 1,
[0x0d] = 1, [0x0e] = 1, [0x0f] = 1, [0x10] = 1,
[0x11] = 1, [0x12] = 1, [0x13] = 1, [0x14] = 1,
[0x15] = 1, [0x16] = 1, [0x17] = 1, [0x18] = 1,
[0x19] = 1, [0x1a] = 1, [0x1b] = 1, [0x1c] = 1,
[0x1d] = 1, [0x1e] = 1, [0x1f] = 1,
['\\'] = 1, ['"'] = 1
};
static const char json_hex_chars[16] = "0123456789abcdef";
unsigned char *p = NULL;
size_t pos = 0;
if (!str || !buf || len <= 0) {
return NULL;
}
for (p = (unsigned char *)str;
*p && (pos + 1) < len;
p++)
{
if (json_exceptions[*p] == 0) {
buf[pos++] = *p;
continue;
}
if ((pos + 2) >= len) {
break;
}
switch (*p)
{
case '\b':
buf[pos++] = '\\';
buf[pos++] = 'b';
break;
case '\n':
buf[pos++] = '\\';
buf[pos++] = 'n';
break;
case '\r':
buf[pos++] = '\\';
buf[pos++] = 'r';
break;
case '\t':
buf[pos++] = '\\';
buf[pos++] = 't';
break;
case '\\':
buf[pos++] = '\\';
buf[pos++] = '\\';
break;
case '"':
buf[pos++] = '\\';
buf[pos++] = '"';
break;
default:
if ((pos + 6) >= len) {
buf[pos] = '\0';
return (char *)p;
}
buf[pos++] = '\\';
buf[pos++] = 'u';
buf[pos++] = '0';
buf[pos++] = '0';
buf[pos++] = json_hex_chars[(*p) >> 4];
buf[pos++] = json_hex_chars[(*p) & 0xf];
break;
}
}
buf[pos] = '\0';
return (char *)p;
}
/**
* gf_syslog -function to submit message to syslog specific to gluster
* @error_code: error code defined by log book
* @facility_priority: facility_priority of syslog()
* @format: optional format string to syslog()
*
* @return: void
*/
void
gf_syslog (int error_code, int facility_priority, char *format, ...)
{
char *msg = NULL;
char json_msg[GF_JSON_MSG_LENGTH];
char *p = NULL;
const char *error_message = NULL;
char json_error_message[GF_JSON_MSG_LENGTH];
va_list ap;
error_message = gf_get_error_message (error_code);
va_start (ap, format);
if (format) {
vasprintf (&msg, format, ap);
p = _json_escape (msg, json_msg, GF_JSON_MSG_LENGTH);
if (error_message) {
p = _json_escape (error_message, json_error_message,
GF_JSON_MSG_LENGTH);
syslog (facility_priority, GF_SYSLOG_CEE_FORMAT,
json_msg, error_code, json_error_message);
} else {
/* ignore the error code because no error message for it
and use normal syslog */
syslog (facility_priority, "%s", msg);
}
free (msg);
} else {
if (error_message) {
/* no user message: treat error_message as msg */
syslog (facility_priority, GF_SYSLOG_CEE_FORMAT,
json_error_message, error_code,
json_error_message);
} else {
/* cannot produce log as neither error_message nor
msg available */
}
}
va_end (ap);
}
#endif /* GF_USE_SYSLOG */
void
gf_log_globals_init (void *data)
{