Last active
August 31, 2015 22:24
-
-
Save NicolasT/19279bc0282596c48816 to your computer and use it in GitHub Desktop.
Expressive logging with meta-data in C
This file contains hidden or 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
main | |
*.o |
This file contains hidden or 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
#ifndef __SCALITY_COMPILER_H__ | |
#define __SCALITY_COMPILER_H__ | |
/* TODO Wrap with compiler version checks yada yada */ | |
#define INLINE inline | |
#define GNUC_FORMAT(type, fmt, idx) \ | |
__attribute__((format(type, fmt, idx))) | |
#define GNUC_ALWAYS_INLINE \ | |
__attribute__((always_inline)) | |
#define GNUC_CONST \ | |
__attribute__((const)) | |
#define GNUC_UNUSED \ | |
__attribute__((unused)) | |
#endif |
This file contains hidden or 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
#include <stdio.h> | |
#include <stdarg.h> | |
#include <assert.h> | |
#include "logging.h" | |
#define MAX_ARGS 4 | |
struct arg { | |
const char *name; | |
scality_metadata_type type; | |
union { | |
const char *s; | |
int i; | |
} value; | |
}; | |
void scality_log(const char *format, ...) { | |
unsigned int cnt = 0, i = 0; | |
va_list args; | |
struct arg values[MAX_ARGS] = { 0 }; | |
va_start(args, format); | |
for(cnt = 0; cnt < sizeof(values); cnt++) { | |
struct arg *a = &values[cnt]; | |
a->name = va_arg(args, char *); | |
if(a->name == NULL) { | |
break; | |
} | |
a->type = va_arg(args, scality_metadata_type); | |
switch(a->type) { | |
case TYPE_INT: | |
a->value.i = va_arg(args, int); | |
break; | |
case TYPE_STRING: | |
a->value.s = va_arg(args, char *); | |
break; | |
default: | |
assert(!"Invalid type tag"); | |
return; | |
} | |
} | |
va_end(args); | |
switch(cnt) { | |
case 0: | |
fprintf(stderr, format); | |
break; | |
case 1: | |
fprintf(stderr, format, values[0].value); | |
break; | |
case 2: | |
fprintf(stderr, format, values[0].value, values[1].value); | |
break; | |
case 3: | |
fprintf(stderr, format, values[0].value, values[1].value, values[2].value); | |
break; | |
default: | |
assert(!"Impossible code path"); | |
return; | |
} | |
for(i = 0; i < cnt; i++) { | |
struct arg *a = &values[i]; | |
char *type = NULL, | |
buf[255] = { 0 }; | |
switch(a->type) { | |
case TYPE_INT: | |
type = "int"; | |
snprintf(buf, sizeof(buf), "%d", a->value.i); | |
break; | |
case TYPE_STRING: | |
type = "string"; | |
snprintf(buf, sizeof(buf), "%s", a->value.s); | |
break; | |
default: | |
assert(!"Invalid type tag"); | |
return; | |
} | |
fprintf(stderr, "\t* %s [%s] = %s\n", a->name, type, buf); | |
} | |
} |
This file contains hidden or 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
#ifndef __SCALITY_LOGGING_H__ | |
#define __SCALITY_LOGGING_H__ | |
#include "macro.h" | |
#include "compiler.h" | |
SCALITY_BEGIN_DECLS | |
INLINE void | |
GNUC_FORMAT(printf, 1, 2) | |
GNUC_ALWAYS_INLINE | |
GNUC_CONST | |
scality_logging_stub(const char *format GNUC_UNUSED, ...) { | |
/* This function is only used to validate arguments passed to LOG */ | |
} | |
void GNUC_FORMAT(printf, 1, 0) scality_log(const char *format, ...); | |
#define LOG_SELECT_ARGS(...) VFUNC(LOG_SELECT_ARGS, __VA_ARGS__) | |
#define LOG_SELECT_ARGS1() NULL | |
#define LOG_SELECT_ARGS3(a, b, c) c | |
#define LOG_SELECT_ARGS6(a, b, c, d, e, f) LOG_SELECT_ARGS3(a, b, c), f | |
#define LOG_SELECT_ARGS9(a, b, c, d, e, f, g, h, i) LOG_SELECT_ARGS6(a, b, c, d, e, f), i | |
#define LOG(msg, args...) \ | |
do { \ | |
scality_logging_stub(msg, LOG_SELECT_ARGS(args)); \ | |
scality_log(msg "\n", ##args, NULL); \ | |
} while(0) | |
typedef enum { | |
TYPE_INT, | |
TYPE_STRING, | |
} scality_metadata_type; | |
#define STR(name, value) #name, TYPE_STRING, value | |
#define INT(name, value) #name, TYPE_INT, value | |
SCALITY_END_DECLS | |
#endif |
This file contains hidden or 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
#ifndef __SCALITY_MACRO_H__ | |
#define __SCALITY_MACRO_H__ | |
#ifdef __cplusplus | |
# define SCALITY_BEGIN_DECLS \ | |
extern "C" { | |
#define SCALITY_END_DECLS \ | |
} | |
#else | |
# define SCALITY_BEGIN_DECLS | |
# define SCALITY_END_DECLS | |
#endif | |
/* From | |
* http://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments | |
*/ | |
#define __NARG__(...) __NARG_I_(__VA_ARGS__,__RSEQ_N()) | |
#define __NARG_I_(...) __ARG_N(__VA_ARGS__) | |
#define __ARG_N( \ | |
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ | |
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ | |
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ | |
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ | |
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ | |
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ | |
_61,_62,_63,N,...) N | |
#define __RSEQ_N() \ | |
63,62,61,60, \ | |
59,58,57,56,55,54,53,52,51,50, \ | |
49,48,47,46,45,44,43,42,41,40, \ | |
39,38,37,36,35,34,33,32,31,30, \ | |
29,28,27,26,25,24,23,22,21,20, \ | |
19,18,17,16,15,14,13,12,11,10, \ | |
9,8,7,6,5,4,3,2,1,0 | |
#define _VFUNC_(name, n) name##n | |
#define _VFUNC(name, n) _VFUNC_(name, n) | |
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__) | |
#endif |
This file contains hidden or 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
#include <unistd.h> | |
#include "logging.h" | |
int main(int argc, char **argv) { | |
LOG("Hello, world!"); | |
LOG("Application %s invoked with %d arguments", | |
STR(application, argv[0]), | |
INT(argcount, argc), | |
INT(pid, getpid())); | |
return 0; | |
} |
This file contains hidden or 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
BIN := main | |
SRC := $(wildcard *.c) \ | |
$(wildcard scality/*.c) | |
OBJ = $(patsubst %.c,%.o,$(SRC)) | |
CFLAGS := -I. -Wall -Wextra -Werror -Wno-format-extra-args -fsanitize=address -fsanitize=leak -fsanitize=undefined | |
LDFLAGS := -lasan -lubsan | |
$(BIN): $(OBJ) | |
$(CC) $(LDFLAGS) -o $@ $^ | |
clean: | |
@rm -f $(OBJ) | |
@rm -f $(BIN) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment