Skip to content

Commit

Permalink
Added a rudimentary yet very easy to use logger; can log to one or mo…
Browse files Browse the repository at this point in the history
…re targets at once, and honours level filtering
  • Loading branch information
skeezix committed Nov 12, 2009
1 parent 1aa0f59 commit 7cb5257
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 3 deletions.
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
44 changes: 44 additions & 0 deletions include/pnd_logger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

#ifndef h_pnd_logger_h
#define h_pnd_logger_h

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>

/* 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
181 changes: 181 additions & 0 deletions lib/pnd_logger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@

#include <stdarg.h> // va-args
#include <stdlib.h> // malloc/free
#include <string.h> // 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 );
}
39 changes: 39 additions & 0 deletions test/loggertest.c
Original file line number Diff line number Diff line change
@@ -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 );
}

0 comments on commit 7cb5257

Please sign in to comment.