Skip to content

Instantly share code, notes, and snippets.

@NicolasT
Last active August 31, 2015 22:24
Show Gist options
  • Save NicolasT/19279bc0282596c48816 to your computer and use it in GitHub Desktop.
Save NicolasT/19279bc0282596c48816 to your computer and use it in GitHub Desktop.
Expressive logging with meta-data in C
#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
#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);
}
}
#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
#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
#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;
}
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