commit 71138c20cb9d40b5c0d1cdce7430616d7088148b Author: František Dvořák Date: Sun Mar 24 18:15:37 2013 +0100 New socket appender (SF patch #2881285). diff --git a/configure.in b/configure.in index a4b49dd..0e2993a 100644 --- a/configure.in +++ b/configure.in @@ -93,11 +93,14 @@ AC_CHECK_HEADER(pthread.h,[ AC_CHECK_LIB(pthread,pthread_mutex_init,[ LIBS="$LIBS -lpthread" AC_DEFINE([WITH_ROLLINGFILE], [], [Define if we found pthread.h libpthread]) - AC_MSG_NOTICE([Compile with rollingfile code]) - with_rollingfile=true],[AC_MSG_NOTICE([No pthread--not compiling rollingfile code])] + AC_DEFINE([WITH_SOCKET], [], [Define if we found pthread.h libpthread]) + AC_MSG_NOTICE([Compile with additional appenders]) + with_rollingfile=true + with_socket=true],[AC_MSG_NOTICE([No pthread--not compiling rollingfile code])] ) ]) AM_CONDITIONAL(WITH_ROLLINGFILE, test "x$with_rollingfile" = "xtrue") +AM_CONDITIONAL(WITH_SOCKET, test "x$with_socket" = "xtrue") ##################################### # Enable test compilation if required diff --git a/src/log4c/appender_type_socket.c b/src/log4c/appender_type_socket.c new file mode 100644 index 0000000..9ce215e --- /dev/null +++ b/src/log4c/appender_type_socket.c @@ -0,0 +1,783 @@ +static const char version[] = "$Id$"; + +/* + * appender_socket.c + * + * Copyright 2009, H.A.J ten Brugge All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#define _XOPEN_SOURCE 500 /* PTHREAD_PRIO_INHERIT */ + +#ifdef __linux__ +#define _GNU_SOURCE /* syscall */ +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_PORTNR (4560) /* chainsaw port number */ +#define DEFAULT_SIZE (10000) + +#define SOCK_PACK(b,p,v) { (b)[(p) + 0] = (unsigned char) ((v) >> 24); \ + (b)[(p) + 1] = (unsigned char) ((v) >> 16); \ + (b)[(p) + 2] = (unsigned char) ((v) >> 8); \ + (b)[(p) + 3] = (unsigned char) ((v) >> 0); } +#define SOCK_UNPACK(b,p) ((((b)[(p) + 0] & 0xff) << 24) | \ + (((b)[(p) + 1] & 0xff) << 16) | \ + (((b)[(p) + 2] & 0xff) << 8) | \ + (((b)[(p) + 3] & 0xff) << 0)) + +/* Local type for starting stopping service */ +typedef enum +{ + DOWN, /** initial state and when server finished */ + STARTUP, /** set when the server is started */ + RUNNING, /** set by server when initialized and running */ + STOP /** set when the server must be stopped */ +} +SERVER_STATE; + +struct socket_udata { + int socket; + int port; + char *hostname; + SERVER_STATE state; + unsigned int rd; + unsigned int wr; + unsigned int size; + unsigned char *buffer; + unsigned char *tmp; + pthread_mutex_t mtx; + pthread_cond_t cond; + sem_t sem; +}; + +/* xxx would be nice to run-time check the type here */ +#define socket_get_udata(this) \ + (socket_udata_t)log4c_appender_get_udata(this) + +static void uint2asc(unsigned int n, char *str); +static void * socket_thread (void *data); +static int socket_init (log4c_appender_t* this); +static socket_udata_t socket_get_or_make_udata (log4c_appender_t* this); +static void socket_free_udata (socket_udata_t sud); +static int socket_append (log4c_appender_t* this, + const log4c_logging_event_t* a_event); +static int socket_close (log4c_appender_t* this); + +/*******************************************************************************/ + +static void uint2asc(unsigned int n, char *str) +{ + char *end = str; + char *begin = str; + char tmp; + + do { + *end++ = "0123456789"[n % 10]; + n /= 10; + } while (n); + *end-- = 0; + while (end > begin) { + tmp = *end; + *end-- = *begin, + *begin++ = tmp; + } +} + +/*******************************************************************************/ + +static void * socket_thread (void *data) +{ + static const char header[] = { '\xAC', '\xED', /* STREAM_MAGIC */ + '\x00', '\x05' }; /* STREAM_VERSION */ + socket_udata_t sud = (socket_udata_t ) data; + unsigned char *buf; + unsigned int len; + unsigned int count; + int r; + fd_set fdset; + struct timeval timeout; + struct addrinfo hints; + struct addrinfo *result, *rp; + char port[20]; + + uint2asc ((unsigned int) sud->port, port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = AI_NUMERICSERV; /* Numeric portnr */ + hints.ai_protocol = 0; /* Any protocol */ + if (getaddrinfo(sud->hostname, port, &hints, &result) == 0) { + sud->state = RUNNING; + sem_post (&sud->sem); + pthread_mutex_lock (&sud->mtx); + while (sud->state == RUNNING) { + if (sud->socket < 0) { + for (rp = result; rp != NULL; rp = rp->ai_next) { + sud->socket = socket (rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (sud->socket >= 0) { + if (connect (sud->socket, rp->ai_addr, + rp->ai_addrlen) != -1) { + break; + } + close (sud->socket); + sud->socket = -1; + } + } + if (sud->socket >= 0) { + buf = (unsigned char *) header; + len = sizeof (header); + count = 10; + while (sud->state == RUNNING && len) { + r = write (sud->socket,buf,len); + if (r < 0 && + (errno == EWOULDBLOCK || errno == EAGAIN || + errno == EINTR)) { + count--; + if (count == 0) { + close (sud->socket); + sud->socket = -1; + len = 0; + } + } + else { + len -= r; + buf += r; + } + } + } + if (sud->socket < 0) { + pthread_mutex_unlock (&sud->mtx); + sleep (1); + pthread_mutex_lock (&sud->mtx); + } + } + else if (sud->rd != sud->wr) { + len = SOCK_UNPACK (sud->buffer, sud->rd); + if (len == 0) { + sud->rd = 0; + } + else { + memcpy (sud->tmp, sud->buffer + sud->rd + 4, len); + sud->rd += len + 4; + if (sud->rd == sud->wr) { + sud->rd = sud->wr = 0; + } + buf = sud->tmp; + pthread_mutex_unlock (&sud->mtx); + while (sud->state == RUNNING && len) { + r = write (sud->socket, buf, len); + if (r <= 0) { + if (r < 0 && + (errno == EWOULDBLOCK || errno == EAGAIN || + errno == EINTR)) { + FD_ZERO (&fdset); + FD_SET (sud->socket,&fdset); + timeout.tv_sec = 0; + timeout.tv_usec = 1000000; + r = select (sud->socket + 1, NULL, &fdset, NULL, + &timeout); + if (r < 0 && errno != EWOULDBLOCK && + errno != EAGAIN && errno != EINTR) { + close (sud->socket); + sud->socket = -1; + len = 0; + } + } + else { + close (sud->socket); + sud->socket = -1; + len = 0; + } + } + else { + len -= r; + buf += r; + } + } + pthread_mutex_lock (&sud->mtx); + } + } + if (sud->state == RUNNING && sud->socket >= 0 && sud->rd == sud->wr) { + pthread_cond_wait (&sud->cond, &sud->mtx); + } + } + pthread_mutex_unlock (&sud->mtx); + freeaddrinfo (result); + } + sud->state = DOWN; + sem_post (&sud->sem); + return NULL; +} + + +/*******************************************************************************/ +static int socket_init(log4c_appender_t* this){ + + socket_udata_t sud = socket_make_udata(); + + log4c_appender_set_udata (this, sud); + + return(0); +} + +/*******************************************************************************/ +static int socket_open(log4c_appender_t* this) +{ + socket_udata_t sud; + pthread_attr_t attr; + pthread_t id; + int rc = -1; + + if (!this) { + return rc; + } + sud = socket_get_or_make_udata (this); + if (!sud) { + return rc; + } + + sud->state = STARTUP; + + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED); + if (pthread_create (&id, &attr, socket_thread, sud) == 0) { + while (sud->state == STARTUP) { + while (sem_wait (&sud->sem) < 0 && errno == EINTR); + } + } + else { + sud->state = DOWN; + } + pthread_attr_destroy (&attr); + + log4c_appender_set_udata (this, sud); + + return 0; +} + +/*******************************************************************************/ + +static socket_udata_t socket_get_or_make_udata(log4c_appender_t* this){ + socket_udata_t sud; + int rc = 0; + + sud = (socket_udata_t) log4c_appender_get_udata (this); + + if ( !sud) { + rc = socket_init (this); + } + + return (socket_get_udata (this)); +} + +/*******************************************************************************/ + +LOG4C_API socket_udata_t socket_make_udata(){ + + socket_udata_t sud = + (socket_udata_t) sd_calloc(1, sizeof (struct socket_udata)); + char *hostname = sd_strdup ("localhost"); + unsigned char *buffer = (unsigned char *) sd_malloc (DEFAULT_SIZE); + unsigned char *tmp = (unsigned char *) sd_malloc (DEFAULT_SIZE); + pthread_mutexattr_t mattr; + pthread_condattr_t cattr; + + if (sud && hostname && buffer && tmp) { + sud->socket = -1; + sud->size = DEFAULT_SIZE; + sud->hostname = hostname; + sud->port = DEFAULT_PORTNR; + sud->state = DOWN; + sud->rd = 0; + sud->wr = 0; + sud->buffer = buffer; + sud->tmp = tmp; + pthread_mutexattr_init (&mattr); +#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT != -1 + pthread_mutexattr_setprotocol (&mattr, PTHREAD_PRIO_INHERIT); +#endif + pthread_mutex_init (&sud->mtx, &mattr); + pthread_mutexattr_destroy (&mattr); + pthread_condattr_init (&cattr); + pthread_cond_init (&sud->cond, &cattr); + pthread_condattr_destroy (&cattr); + sem_init (&sud->sem, 1, 0); + } + else { + free (sud); + free (hostname); + free (buffer); + free (tmp); + sud = NULL; + } + return(sud); +} + +/*******************************************************************************/ + +LOG4C_API int socket_udata_set_host (socket_udata_t sud, char *value) +{ + char *hostname; + int rc = -1; + + if (sud && value) { + hostname = sd_strdup (value); + if (hostname) { + free (sud->hostname); + sud->hostname = hostname; + rc = 0; + } + } + return rc; +} + +/*******************************************************************************/ + +LOG4C_API int socket_udata_set_port (socket_udata_t sud, int value) +{ + int rc = -1; + + if (sud) { + sud->port = value; + } + return rc; +} + +/*******************************************************************************/ + +LOG4C_API int socket_udata_set_size (socket_udata_t sud, int value) +{ + unsigned char *buffer; + unsigned char *tmp; + int rc = -1; + + if (!sud) return -1; + if (sud->size == value) return 0; + + buffer = (unsigned char *) malloc (value); + tmp = (unsigned char *) malloc (value); + if (buffer && tmp) { + pthread_mutex_lock (&sud->mtx); + free (sud->buffer); + free (sud->tmp); + sud->size = value; + sud->buffer = buffer; + sud->tmp = tmp; + sud->rd = 0; + sud->wr = 0; + pthread_mutex_unlock (&sud->mtx); + rc = 0; + } else { + free (buffer); + free (tmp); + } + + return rc; +} + +/*******************************************************************************/ + +static void socket_free_udata(socket_udata_t sud) +{ + pthread_mutex_destroy (&sud->mtx); + pthread_cond_destroy (&sud->cond); + sem_destroy (&sud->sem); + free (sud->buffer); + free (sud->tmp); + free (sud->hostname); + free (sud); +} + +/*******************************************************************************/ +static int socket_append(log4c_appender_t* this, + const log4c_logging_event_t* a_event) +{ +/* See: http://java.sun.com/javase/6/docs/platform/serialization/spec/protocol.html */ + static const char event[] = { + '\x79', /* TC_RESET */ + '\x73', /* TC_OBJECT */ + '\x72', /* TC_CLASSDESC */ + '\x00', '\x21', /* "org.apache.log4j.spi.LoggingEvent".length() */ + 'o','r','g','.','a','p','a','c','h','e','.','l','o','g','4','j','.', + 's','p','i','.','L','o','g','g','i','n','g','E','v','e','n','t', + /* LoggingEvent.serialVersionUID */ + '\xf3','\xf2','\xb9','\x23','\x74','\x0b','\xb5','\x3f', + '\x03', /* classDescFlags: SC_WRITE_METHOD | SC_SERIALIZABLE */ + '\x00','\x0a', /* fields.length() */ + 'Z', /* typecode for boolean 'Z' */ + '\x00','\x15', /* "mdcCopyLookupRequired".length() */ + 'm','d','c','C','o','p','y','L','o','o','k','u','p', + 'R','e','q','u','i','r','e','d', + 'Z', /* boolean field 'Z' */ + '\x00','\x11', /* "ndcLookupRequired".length() */ + 'n','d','c','L','o','o','k','u','p','R','e','q','u','i','r','e','d', + 'J', /* typecode for long 'J' */ + '\x00','\x09', /* "timeStamp".length() */ + 't','i','m','e','S','t','a','m','p', + 'L', /* typecode 'L' */ + '\x00','\x0C', /* "categoryName".length() */ + 'c','a','t','e','g','o','r','y','N','a','m','e', + '\x74', /* TC_STRING */ + '\x00','\x12', /* "Ljava/lang/String;".length() */ + 'L','j','a','v','a','/','l','a','n','g','/','S','t','r','i','n','g',';', + 'L', /* typecode 'L' */ + '\x00','\x0C', /* "locationInfo".length() */ + 'l','o','c','a','t','i','o','n','I','n','f','o', + '\x74', /* TC_STRING */ + '\x00','\x23', /* "Lorg/apache/log4j/spi/LocationInfo;".length() */ + 'L','o','r','g','/','a','p','a','c','h','e','/','l','o','g','4','j', + '/','s','p','i','/','L','o','c','a','t','i','o','n','I','n','f','o',';', + 'L', /* typecode 'L' */ + '\x00','\x07', /* "mdcCopy".length() */ + 'm','d','c','C','o','p','y', + '\x74', /* TC_STRING */ + '\x00','\x15', /* "Ljava/lang/Hashtable;".length() */ + 'L','j','a','v','a','/','l','a','n','g','/', + 'H','a','s','h','t','a','b','l','e',';', + 'L', /* typecode 'L' */ + '\x00','\x03', /* "ndc".length() */ + 'n','d','c', + '\x71', /* TC_REFERENCE */ + '\x00','\x7E','\x00','\x01', /* handle to second serialized field */ + 'L', /* typecode 'L' */ + '\x00','\x0F', /* "renderedMessage".length() */ + 'r','e','n','d','e','r','e','d','M','e','s','s','a','g','e', + '\x71', /* TC_REFERENCE */ + '\x00','\x7E','\x00','\x01', /* handle to second serialized field */ + 'L', /* typecode 'L' */ + '\x00','\x0a', /* "threadName".length() */ + 't','h','r','e','a','d','N','a','m','e', + '\x71', /* TC_REFERENCE */ + '\x00','\x7E','\x00','\x01', /* handle to second serialized field */ + 'L', /* typecode 'L' */ + '\x00','\x0D', /* "throwableInfo".length() */ + 't','h','r','o','w','a','b','l','e','I','n','f','o', + '\x74', /* TC_STRING */ + '\x00','\x2B', /* "Lorg/apache/log4j/spi/ThrowableInformation;".length() */ + 'L','o','r','g','/','a','p','a','c','h','e','/','l','o','g','4','j', + '/','s','p','i','/','T','h','r','o','w','a','b','l','e','I','n','f', + 'o','r','m','a','t','i','o','n',';', + '\x78', /* TC_ENDBLOCKDATA */ + '\x70' }; /* TC_NULL */ + + char data1[10]; +#if 0 + static const char datax[] = { + '\x00', /* mdcCopyLookupRequired */ + '\x00', /* ndcLookupRequired */ + /* timeStamp in ms */ + '\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00' }; +#endif + char data2[3]; +#if 0 + static const char datax[] = { + '\x74', /* categoryName: TC_STRING */ + '\x00','\x04', /* "root".length() */ + 'r','o','o','t' }; +#endif + static const char location[] = { + '\x73', /* TC_OBJECT */ + '\x72', /* TC_CLASSDESC */ + '\x00','\x21', /* "org.apache.log4j.spi.LocationInfo".length() */ + 'o','r','g','.','a','p','a','c','h','e','.','l','o','g','4','j','.', + 's','p','i','.','L','o','c','a','t','i','o','n','I','n','f','o', + /* LocationInfo.serialVersionUID */ + '\xed','\x99','\xbb','\xe1','\x4a','\x91','\xa5','\x7c', + '\x02', /* classDescFlags of SC_SERIALIZABLE */ + '\x00','\x01', /* fields.length() */ + 'L', /* typecode 'L' */ + '\x00','\x08', /* "fullInfo".length() */ + 'f','u','l','l','I','n','f','o', + '\x71', /* TC_REFERENCE */ + '\x00','\x7E','\x00','\x01', /* handle to second serialized field */ + '\x78', /* TC_ENDBLOCKDATA */ + '\x70', /* TC_NULL */ + '\x74' }; /* LocationInfo: TC_STRING */ + char data3[2]; +#if 0 +static const char datax[] = { + '\x00','\x14', /* "tst.main(tst.java:8)".length() */ + 't','s','t','.','m','a','i','n','(','t','s','t','.', + 'j','a','v','a',':','8',')' }; +#endif + char data4[6]; + char data5[3]; +#if 0 + static const char datax[] = { + '\x70', /* LocationInfo:: TC_NULL */ + '\x70', /* mdcCopy: TC_NULL */ + '\x70', /* ndc: TC_NULL */ + '\x74', /* renderedMessage: TC_STRING */ + '\x00','\x11', /* "Here is some INFO".length() */ + 'H','e','r','e',' ','i','s',' ','s','o','m','e',' ','I','N','F','O', + '\x74', /* threadName: TC_STRING */ + '\x00','\x04', /* "main".length() */ + 'm','a','i','n' }; +#endif + char data6[9]; +#if 0 + static const char datax[] = { + '\x70', /* throwableInfo: TC_NULL */ + '\x77', /* BLOCKDATA */ + '\x04', /* size */ + '\x00','\x00','\x4e','\x20', /* Level.INFO */ + '\x70', /* throwableInfo: TC_NULL */ + '\x78' }; /* TC_ENDBLOCKDATA */ +#endif + + socket_udata_t sud = (socket_udata_t) log4c_appender_get_udata(this); + char threadname[100]; + char location_string[1000]; + unsigned int len1; + unsigned int len2 = 0; + unsigned int len3; + unsigned int len4; + unsigned int level; + XP_UINT64 t; + unsigned int rdlen; + unsigned int totallen; + + if ( !sud ) { + return(-1); + } + + t = (XP_UINT64) a_event->evt_timestamp.tv_sec * 1000 + + (XP_UINT64) a_event->evt_timestamp.tv_usec / 1000; + + data1[0] = 0x00; /* mdcCopyLookupRequired */ + data1[1] = 0x00; /* ndcLookupRequired */ + data1[2] = t >> 56; /* timeStamp in ms */ + data1[3] = t >> 48; + data1[4] = t >> 40; + data1[5] = t >> 32; + data1[6] = t >> 24; + data1[7] = t >> 16; + data1[8] = t >> 8; + data1[9] = t >> 0; + + len1 = strlen (a_event->evt_category); + data2[0] = 0x74; /* categoryName: TC_STRING */ + data2[1] = len1 >> 8; /* categoryName:.length() */ + data2[2] = len1 >> 0; + + if (a_event->evt_loc) { + len3 = a_event->evt_loc->loc_function ? + strlen (a_event->evt_loc->loc_function) : 0; + len4 = a_event->evt_loc->loc_file ? + strlen (a_event->evt_loc->loc_file) : 0; + if ((len3 + len4 + 20) < sizeof (location_string)) { + location_string[0] = '\0'; + if (a_event->evt_loc->loc_function) { + strcpy (&location_string[len2], + a_event->evt_loc->loc_function); + len2 += len3; + } + if (a_event->evt_loc->loc_file || a_event->evt_loc->loc_line) { + strcpy (&location_string[len2], "("); + len2++; + } + if (a_event->evt_loc->loc_file) { + strcpy (&location_string[len2], + a_event->evt_loc->loc_file); + len2 += len4; + } + if (a_event->evt_loc->loc_file || a_event->evt_loc->loc_line) { + strcpy (&location_string[len2], ":"); + len2++; + } + if (a_event->evt_loc->loc_line) { + uint2asc ((unsigned int) a_event->evt_loc->loc_line, + &location_string[len2]); + len2 += strlen (&location_string[len2]); + } + if (a_event->evt_loc->loc_file || a_event->evt_loc->loc_line) { + strcpy (&location_string[len2], ")"); + len2++; + } + data3[0] = len2 >> 8; + data3[1] = len2 >> 0; + } + } + + len3 = strlen (a_event->evt_msg); + data4[0] = 0x70; /* LocationInfo:: TC_NULL */ + data4[1] = 0x70; /* mdcCopy: TC_NULL */ + data4[2] = 0x70; /* ndc: TC_NULL */ + data4[3] = 0x74; /* renderedMessage: TC_STRING */ + data4[4] = len3 >> 8; /* renderedMessage.length() */ + data4[5] = len3 >> 0; + +#ifdef __linux__ + uint2asc ((unsigned int) syscall (SYS_gettid), threadname); +#else + uint2asc ((unsigned int) pthread_self (), threadname); +#endif + len4 = strlen (threadname); + data5[0] = 0x74; /* threadName: TC_STRING */ + data5[1] = len4 >> 8; /* threadName.length() */ + data5[2] = len4 >> 0; + + level = a_event->evt_priority; + switch (level) { + case LOG4C_PRIORITY_FATAL: level = 50000; break; + case LOG4C_PRIORITY_ALERT: level = 50000; break; + case LOG4C_PRIORITY_CRIT: level = 50000; break; + case LOG4C_PRIORITY_ERROR: level = 40000; break; + case LOG4C_PRIORITY_WARN: level = 30000; break; + case LOG4C_PRIORITY_NOTICE: level = 20000; break; + case LOG4C_PRIORITY_INFO: level = 20000; break; + case LOG4C_PRIORITY_DEBUG: level = 10000; break; + case LOG4C_PRIORITY_TRACE: level = 10000; break; + case LOG4C_PRIORITY_NOTSET: level = 2147483647; break; + case LOG4C_PRIORITY_UNKNOWN: level = 2147483647; break; + } + data6[0] = 0x70; /* throwableInfo: TC_NULL */ + data6[1] = 0x77; /* BLOCKDATA */ + data6[2] = 0x04; /* size */ + data6[3] = level >> 24; /* Level */ + data6[4] = level >> 16; + data6[5] = level >> 8; + data6[6] = level >> 0; + data6[7] = 0x70; /* throwableInfo: TC_NULL */ + data6[8] = 0x78; /* TC_ENDBLOCKDATA */ + + totallen = sizeof(event) + sizeof(data1) + sizeof(data2) + len1 + + sizeof(data4) + len3 + sizeof(data5) + len4 + sizeof(data6); + if (len2) { + totallen += sizeof(location) + sizeof(data3) + len2 - 1; + } + pthread_mutex_lock (&sud->mtx); + for (;;) { + if (sud->wr < sud->rd) { + if ((totallen + 4) < (sud->rd - sud->wr)) { + break; + } + rdlen = SOCK_UNPACK (sud->buffer, sud->rd); + if (rdlen == 0) { + sud->rd = 0; + } + else { + sud->rd += rdlen + 4; + } + } + else { + if ((totallen + 8) <= (sud->size - sud->wr)) { + break; + } + if (sud->wr && sud->rd == 0) { + rdlen = SOCK_UNPACK (sud->buffer, sud->rd); + sud->rd += rdlen + 4; + } + else if (sud->wr == 0) { + totallen = 0; + break; + } + SOCK_PACK (sud->buffer, sud->wr, 0); + sud->wr = 0; + } + } + if (totallen) { + SOCK_PACK (sud->buffer, sud->wr, totallen); + sud->wr += 4; + memcpy (sud->buffer + sud->wr, event, sizeof(event)); + sud->wr += sizeof(event); + memcpy (sud->buffer + sud->wr, data1, sizeof(data1)); + sud->wr += sizeof(data1); + memcpy (sud->buffer + sud->wr, data2, sizeof(data2)); + sud->wr += sizeof(data2); + memcpy (sud->buffer + sud->wr, a_event->evt_category, len1); + sud->wr += len1; + if (len2) { + memcpy (sud->buffer + sud->wr, location, sizeof(location)); + sud->wr += sizeof(location); + memcpy (sud->buffer + sud->wr, data3, sizeof(data3)); + sud->wr += sizeof(data3); + memcpy (sud->buffer + sud->wr, location_string, len2); + sud->wr += len2; + memcpy (sud->buffer + sud->wr, data4 + 1, sizeof(data4) - 1); + sud->wr += sizeof(data4) - 1; + } + else { + memcpy (sud->buffer + sud->wr, data4, sizeof(data4)); + sud->wr += sizeof(data4); + } + memcpy (sud->buffer + sud->wr, a_event->evt_msg, len3); + sud->wr += len3; + memcpy (sud->buffer + sud->wr, data5, sizeof(data5)); + sud->wr += sizeof(data5); + memcpy (sud->buffer + sud->wr, threadname, len4); + sud->wr += len4; + memcpy (sud->buffer + sud->wr, data6, sizeof(data6)); + sud->wr += sizeof(data6); + pthread_cond_broadcast (&sud->cond); + } + pthread_mutex_unlock (&sud->mtx); + return 0; +} + +/*******************************************************************************/ +static int socket_close(log4c_appender_t* this) +{ + socket_udata_t sud = (socket_udata_t) log4c_appender_get_udata (this); + int rc = -1; + + if ( !this){ + return rc; + } + sud = socket_get_udata (this); + if ( !sud){ + return(rc); + } + + if (sud->state == RUNNING) { + if (sud->socket >= 0 && sud->rd != sud->wr) { + sleep (1); + } + sud->state = STOP; + pthread_cond_broadcast (&sud->cond); + while (sud->state == STOP) { + while (sem_wait (&sud->sem) < 0 && errno == EINTR); + } + } + + if (sud->socket >= 0) { + rc = close (sud->socket); + } else { + rc = 0; + } + + /* Free up and reset any data associated with this socket appender */ + socket_free_udata (sud); + log4c_appender_set_udata (this, NULL); + + return (rc); +} + +/*******************************************************************************/ +const log4c_appender_type_t log4c_appender_type_socket = { + "socket", + socket_open, + socket_append, + socket_close, +}; diff --git a/src/log4c/appender_type_socket.h b/src/log4c/appender_type_socket.h new file mode 100644 index 0000000..ac8ee00 --- /dev/null +++ b/src/log4c/appender_type_socket.h @@ -0,0 +1,81 @@ +/* $Id$ + * + * appender_type_socket.h + * + * Copyright 2009, H.A.J ten Brugge All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef log4c_appender_type_socket_h +#define log4c_appender_type_socket_h + +/** + * @file appender_type_socket.h + * + * @brief Log4c socket appender interface. + * + * The socket appender uses a socket connection for logging. The socket + * interface is the same as the log4j SocketReceiver. The default settings + * will connect to the chainsaw interface for log4j. + * + * There are some extra parameters in the xml file to setup the connection. + * The host (default localhost) and port (default 4560) are used to connect + * to a remote chainsaw server. The size option (default 10000) is used + * to buffer size data bytes until the connection to chainsaw is made. + * If no connection can be made the oldest event is deleted and overwritten + * the latest event. + * + **/ + +#include +#include + +__LOG4C_BEGIN_DECLS + +typedef struct socket_udata *socket_udata_t; + +/** + * Stream appender type definition. + * + * This should be used as a parameter to the log4c_appender_set_type() + * routine to set the type of the appender. + * + **/ +LOG4C_API const log4c_appender_type_t log4c_appender_type_socket; + + +/** + * Get a new socket udata + * @return a new socket udata, otherwise NULL. + */ +LOG4C_API socket_udata_t socket_make_udata(); + +/** + * Set the host name for the socket interface. + * @param sup the socket udata configuration object. + * @param value the host name. + * @return zero if successful, non-zero otherwise. + */ +LOG4C_API int socket_udata_set_host (socket_udata_t sup, char *value); + +/** + * Set the port number for the socket interface. + * @param sup the socket udata configuration object. + * @param value the post number. + * @return zero if successful, non-zero otherwise. + */ +LOG4C_API int socket_udata_set_port (socket_udata_t sup, int value); + +/** + * Set the buffer size for the socket interface. + * @param sup the socket udata configuration object. + * @param value the size to use for the buffering of data. + * @return zero if successful, non-zero otherwise. + */ +LOG4C_API int socket_udata_set_size (socket_udata_t sup, int value); + + +__LOG4C_END_DECLS + +#endif diff --git a/src/log4c/init.c b/src/log4c/init.c index 4fa8577..d99dbd7 100644 --- a/src/log4c/init.c +++ b/src/log4c/init.c @@ -25,6 +25,7 @@ static const char version[] = "$Id$"; #include #include +#include #include #include #include @@ -59,7 +60,10 @@ static const log4c_appender_type_t * const appender_types[] = { &log4c_appender_type_syslog, #endif #ifdef WITH_ROLLINGFILE - &log4c_appender_type_rollingfile + &log4c_appender_type_rollingfile, +#endif +#ifdef WITH_SOCKET + &log4c_appender_type_socket, #endif }; static size_t nappender_types = sizeof(appender_types) / sizeof(appender_types[0]); diff --git a/src/log4c/rc.c b/src/log4c/rc.c index a9bb46e..fe6cbb1 100644 --- a/src/log4c/rc.c +++ b/src/log4c/rc.c @@ -19,6 +19,7 @@ static const char version[] = "$Id$"; #include #include #include +#include #include #include #include @@ -218,6 +219,28 @@ static int appender_load(log4c_rc_t* this, sd_domnode_t* anode) } } #endif +#ifdef WITH_SOCKET + if ( !strcasecmp(type->value, "socket")) { + socket_udata_t sud = NULL; + sd_domnode_t* host = sd_domnode_attrs_get(anode, + "host"); + sd_domnode_t* port = sd_domnode_attrs_get(anode, + "port"); + sd_domnode_t* size = sd_domnode_attrs_get(anode, + "size"); + sud = socket_make_udata(); + if (host && host->value) { + socket_udata_set_host(sud, (char *)host->value); + } + if (port && port->value) { + socket_udata_set_port(sud, atoi ((char *)port->value)); + } + if (size && size->value) { + socket_udata_set_size(sud, parse_byte_size ((char *)size->value)); + } + log4c_appender_set_udata(app, sud); + } +#endif } if (layout) diff --git a/tests/log4c/Makefile.am b/tests/log4c/Makefile.am index 9e0be30..84dff86 100644 --- a/tests/log4c/Makefile.am +++ b/tests/log4c/Makefile.am @@ -9,6 +9,10 @@ if WITH_ROLLINGFILE noinst_PROGRAMS += test_rollingfile_appender test_rollingfile_appender_mt endif +if WITH_SOCKET +noinst_PROGRAMS += test_socket_appender +endif + cpp_compile_test_SOURCES = cpp_compile_test.cpp test_category_SOURCES = test_category.c @@ -40,6 +44,11 @@ test_rollingfile_appender_mt_LDADD = $(top_builddir)/src/log4c/liblog4c.la \ -lpthread endif +if WITH_SOCKET +test_socket_appender_SOURCES = test_socket_appender.c +test_socket_appender_LDADD = $(top_builddir)/src/log4c/liblog4c.la +endif + test_big_SOURCES = test_big.c test_big_LDADD = $(top_builddir)/src/log4c/liblog4c.la diff --git a/tests/log4c/test_socket_appender.c b/tests/log4c/test_socket_appender.c new file mode 100644 index 0000000..db9afe4 --- /dev/null +++ b/tests/log4c/test_socket_appender.c @@ -0,0 +1,140 @@ + +/* + * test_socket_appender.c + * + * This file demonstrates how to programatically use the socket + * appender. See also the API documentation for the + * appender_type_socket.h file. + * + * It is possible to be more terse here if you accept the default values, + * but we spell it out explicitly. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +/*********************** Parameters ********************************** + * + * params could be taken from the command line to facillitate testing + * +*/ + +/* + * socket specific params +*/ +char *param_host = "localhost"; +//int param_port = 4445; +int param_port = 4560; +long param_size = 10000; + +/* + * Other Logging params + * xxx Problem with dated layout on windows: assert failure + * in gmtime call. +*/ +char *param_layout_to_use = "dated"; /* could also be "basic" */ + +/******************************************************************* + * + * Globals + * + */ +log4c_category_t* root = NULL; +log4c_appender_t* socket_appender = NULL; +/******************************************************************************/ + +/* + * Init log4c and configure a socket appender + * +*/ +static void init_log4c_with_socket_appender(){ + int rc = 2; + socket_udata_t sud = NULL; + + printf("using the socket appender " + "to send log events\n" + "remote host is '%s', remote port is %d, buffer size is %ld\n", + param_host, param_port, param_size); + + if ( (rc = log4c_init()) == 0 ) { + printf("log4c init success\n"); + } else { + printf("log4c init failed--error %d\n", rc); + return; + } + + /* + * Get a reference to the root category + */ + root = log4c_category_get("root"); + log4c_category_set_priority(root, + LOG4C_PRIORITY_WARN); + + /* + * Get an appender and set the type to socket + */ + socket_appender = log4c_appender_get("aname"); + log4c_appender_set_type(socket_appender, + log4c_appender_type_get("socket")); + + /* + * Make a rolling file udata object and set the basic parameters + */ + sud = socket_make_udata(); + if (param_host) socket_udata_set_host(sud, param_host); + if (param_port) socket_udata_set_port(sud, param_port); + if (param_size) socket_udata_set_size(sud, param_size); + + log4c_appender_set_udata(socket_appender, sud); + + /* + * Configure a layout for the rolling file appender + */ + log4c_appender_set_layout(socket_appender, + log4c_layout_get(param_layout_to_use) ); + + /* + * Configure the root category with our socket appender... + * and we can then start logging to it. + * + */ + log4c_category_set_appender(root, socket_appender); + + log4c_dump_all_instances(stderr); +} + +static int test0(int argc, char* argv[]) +{ + int i = 0; + init_log4c_with_socket_appender(); + + for (i = 0; i<200; i++){ + + log4c_category_fatal(root, + "%d%d%d%d%d%d%d%d%d%d%d%d%d\n",i,i,i,i,i,i,i,i,i,i,i,i,i); + + } + + /* Explicitly call the log4c cleanup routine */ + if ( log4c_fini()){ + printf("log4c_fini() failed"); + } + + return 1; +} + +/******************************************************************************/ +int main(int argc, char* argv[]) +{ + + test0(argc,argv); + + return(0); +} diff --git a/src/log4c/Makefile.am b/src/log4c/Makefile.am index f9f5882..c14dac9 100644 --- a/src/log4c/Makefile.am +++ b/src/log4c/Makefile.am @@ -33,6 +33,10 @@ rollingpolicy.c rollingpolicy_type_sizewin.c endif +if WITH_SOCKET +liblog4c_la_SOURCES += appender_type_socket.c +endif + if WITH_MMAP liblog4c_la_SOURCES += appender_type_mmap.c endif @@ -58,6 +62,7 @@ layout_type_dated_r.h \ layout_type_dated_local_r.h \ layout.h \ + appender_type_socket.h \ appender_type_stream.h \ appender_type_stream2.h \ appender_type_syslog.h \