From: skeezix Date: Thu, 12 Nov 2009 18:56:55 +0000 (-0500) Subject: Added a rudimentary yet very easy to use logger; can log to one or more targets at... X-Git-Tag: Release-2010-05/1~151^2~5 X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7cb525790dc97d65abd64e4156e4aefc3ba578b7;p=pandora-libraries.git Added a rudimentary yet very easy to use logger; can log to one or more targets at once, and honours level filtering --- diff --git a/Makefile b/Makefile index 4bfd9cf..77cbf2b 100644 --- a/Makefile +++ b/Makefile @@ -21,12 +21,12 @@ LIB = libpnd.a SOLIB = libpnd.so.1 # canonicle name SOLIB1 = libpnd.so.1.0.1 # versioned name XMLOBJ = lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o -ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o +ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o pnd_logger.o -all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator +all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest clean: - ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o + ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o ${RM} -rf deployment/media find . -name "*~*" -exec rm {} \; -print @@ -98,3 +98,6 @@ locatetest: locatetest.o ${SOLIB1} rawpxmltest: rawpxmltest.o ${LIB} ${CC} -lstdc++ -o bin/rawpxmltest rawpxmltest.o ${LIB} + +loggertest: loggertest.o ${LIB} + ${CC} -lstdc++ -o bin/loggertest loggertest.o libpnd.a diff --git a/include/pnd_logger.h b/include/pnd_logger.h new file mode 100644 index 0000000..dd9fe0d --- /dev/null +++ b/include/pnd_logger.h @@ -0,0 +1,44 @@ + +#ifndef h_pnd_logger_h +#define h_pnd_logger_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* rudimentary logger; note that this is meant to be a no-brainer to use, so no setup is needed. + */ + +// defaults will have no filtering, so any message will be emitted to all targets +// default target is nothing, however, so logger is silent unless activated +unsigned char pnd_log ( unsigned char level, char *format, ... ); // returns true if emitted; \n is implied! +unsigned char pnd_log_to_stdout ( void ); // same as pnd_log_to_stream ( stdout ); +unsigned char pnd_log_to_stderr ( void ); // same as pnd_log_to_stream ( stderr ); + +/* the below is all optional, for when you need more control + */ + +// logging is additive; you can log to multiple targets at once. Returns 'true' if accepted, false if could not set up. +void pnd_log_to_nil ( void ); // stop logging to anywhere; does not close streams/etc +unsigned char pnd_log_to_stream ( FILE * ); // 'stdout', 'stderr', or your own FILE* are good values +unsigned char pnd_log_to_syslog ( char *facility ); // NYI +typedef void (*pnd_log_callback_f)( char *text, void *userdata ); +unsigned char pnd_log_to_callback ( pnd_log_callback_f f, void *userdata ); // NYI + +// pass NULL to free any pre-text, otherwise it'll be kept. Passed in string is duplicated, so you may free yours if you like. +void pnd_log_set_pretext ( char * ); // example: your app-name, or app+function-names, say. + +// set a 'filter level'; any log message of higher-or-equal level than current filter-level will be emitted. Thus, to remove filters +// just set to level 0. Returns existing setting. +unsigned char pnd_log_set_filter ( unsigned char newlevel ); // ex: app-specific enum/#defines for your levels + +// how many targets can be opened, entirely? this is a compile time limit, for sanity. +unsigned char pnd_log_max_targets ( void ); + +#ifdef __cplusplus +} /* "C" */ +#endif + +#endif diff --git a/lib/pnd_logger.c b/lib/pnd_logger.c new file mode 100644 index 0000000..c3bcc43 --- /dev/null +++ b/lib/pnd_logger.c @@ -0,0 +1,181 @@ + +#include // va-args +#include // malloc/free +#include // strdup +#include "pnd_logger.h" + +char *log_pretext = NULL; +unsigned char log_filterlevel = 0; + +typedef enum { + pndl_nil = 0, + pndl_stream, + pndl_syslog, + pndl_callback +} pnd_logger_e; + +typedef struct { + pnd_logger_e type; + union { + FILE *stream; + pnd_log_callback_f callback; + }; +} pnd_log_target_t; + +#define PND_LOG_MAX 5 +static pnd_log_target_t log_targets [ PND_LOG_MAX ]; // implicitly nil + +static int pnd_log_empty_slot ( void ) { + unsigned char i; + + for ( i = 0; i < PND_LOG_MAX; i++ ) { + if ( log_targets [ i ].type == pndl_nil ) { + return ( i ); + } + } + + return ( -1 ); +} + +unsigned char pnd_log_set_filter ( unsigned char newlevel ) { + unsigned char foo = log_filterlevel; + log_filterlevel = newlevel; + return ( foo ); +} + +void pnd_log_set_pretext ( char *pre ) { + + if ( log_pretext ) { + free ( log_pretext ); + log_pretext = NULL; + } + + if ( pre ) { + log_pretext = strdup ( pre ); + } + + return; +} + +void pnd_log_to_nil ( void ) { + memset ( log_targets, '\0', sizeof(pnd_log_target_t) * PND_LOG_MAX ); + return; +} + +unsigned char pnd_log_to_stream ( FILE *f ) { + int i = pnd_log_empty_slot(); + + if ( i < 0 ) { + return ( 0 ); // fail! + } + + log_targets [ i ].type = pndl_stream; + log_targets [ i ].stream = f; + + return ( 1 ); +} + +unsigned char pnd_log_to_syslog ( char *facility ) { + return ( 0 ); // NYI +} + +unsigned char pnd_log_to_callback ( pnd_log_callback_f f, void *userdata ) { + return ( 0 ); // NYI +} + +unsigned char pnd_log_to_stdout ( void ) { + return ( pnd_log_to_stream ( stdout ) ); +} + +unsigned char pnd_log_to_stderr ( void ) { + return ( pnd_log_to_stream ( stderr ) ); +} + +unsigned char pnd_log_max_targets ( void ) { + return ( PND_LOG_MAX ); +} + +void pnd_log_emit ( char *message ) { + unsigned char i; + + // iterate across targets and attempt to emit + for ( i = 0; i < PND_LOG_MAX; i++ ) { + + switch ( log_targets [ i ].type ) { + + case pndl_nil: + // nop + break; + + case pndl_stream: + if ( log_pretext ) { + fprintf ( log_targets [ i ].stream, "%s\t", log_pretext ); + } + if ( message ) { + fprintf ( log_targets [ i ].stream, "%s\n", message ); + } + break; + + case pndl_syslog: + // NYI + break; + + case pndl_callback: + // NYI + break; + + } // switch + + } // for + + return; +} + +unsigned char pnd_log ( unsigned char level, char *fmt, ... ) { + + if ( level < log_filterlevel ) { + return ( 0 ); // too low level + } + + // format the actual log string + int n, size = 100; + char *p, *np; + va_list ap; + + if ( ( p = malloc ( size ) ) == NULL ) { + return ( 0 ); // fail! + } + + while ( 1 ) { + + /* Try to print in the allocated space. */ + va_start ( ap, fmt ); + n = vsnprintf ( p, size, fmt, ap ); + va_end ( ap ); + + /* If that worked, return the string. */ + if ( n > -1 && n < size ) { + pnd_log_emit ( p ); + break; + } + + /* Else try again with more space. */ + if ( n > -1 ) /* glibc 2.1 */ + size = n + 1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + if ( ( np = realloc ( p, size ) ) == NULL ) { + free(p); + return ( 0 ); // fail! + } else { + p = np; + } + + } // while + + if ( p ) { + free ( p ); + } + + return ( 1 ); +} diff --git a/test/loggertest.c b/test/loggertest.c new file mode 100644 index 0000000..f9255cf --- /dev/null +++ b/test/loggertest.c @@ -0,0 +1,39 @@ + +#include "pnd_logger.h" + +// arbitrary warning level +#define PLOG_LOW 0 +#define PLOG_MEDIUM 1 +#define PLOG_HIGH 2 + +int main ( void ) { + + pnd_log ( PLOG_LOW, "low message, should be ignored" ); + + /* normal operation -------------- */ + pnd_log_to_stdout(); + pnd_log ( PLOG_LOW, "low message, should go stdout once" ); + /* ------------------------------- */ + + /* extra testing vvvvvvvvvvvvvvvvv + */ + + pnd_log_to_stdout(); + pnd_log ( PLOG_LOW, "low message, should go stdout twice" ); + + pnd_log_to_nil(); + pnd_log_to_stdout(); + + pnd_log_set_pretext ( "loggertest" ); + pnd_log ( PLOG_LOW, "low message, emit once, with pretext" ); + + pnd_log_set_filter ( PLOG_MEDIUM ); + pnd_log ( PLOG_LOW, "low message, emit once, filter is medium+" ); + pnd_log ( PLOG_MEDIUM, "medium message, emit once, filter is medium+" ); + pnd_log ( PLOG_HIGH, "high message, emit once, filter is medium+" ); + + pnd_log_set_filter ( PLOG_LOW ); + pnd_log ( PLOG_LOW, "low message, emit once, filter is low+ again" ); + + return ( 0 ); +}