-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a rudimentary yet very easy to use logger; can log to one or mo…
…re targets at once, and honours level filtering
- Loading branch information
skeezix
committed
Nov 12, 2009
1 parent
1aa0f59
commit 7cb5257
Showing
4 changed files
with
270 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); | ||
} |