Skip to content

Instantly share code, notes, and snippets.

@rumpelsepp
Last active October 26, 2022 06:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rumpelsepp/3229275bbe9e3f41ba5f33a84643fe73 to your computer and use it in GitHub Desktop.
Save rumpelsepp/3229275bbe9e3f41ba5f33a84643fe73 to your computer and use it in GitHub Desktop.
A printf wrapper, with loglevel support and timestamps
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <libiberty/libiberty.h>
#include "log.h"
#include "config.h"
#define BUFSIZE 1024
#ifdef LOG_TIMESTAMPS
#define LOG_FORMAT "%s [%s]: %s\n" // timestamp loglevel: message\n
static char *gettimestamp() {
int r;
time_t t;
char *res;
struct tm *tmp;
// In order to not waste space, the string is reallocated
// later on. strftime(3) returns how many bytes have been put
// into the buffer. If it does not fit into the buffer, it
// errors out.
res = xcalloc(1, BUFSIZE);
t = time(NULL);
tmp = localtime(&t);
if (tmp == NULL) {
perror("localtime(3)");
exit(EXIT_FAILURE);
}
r = strftime(res, BUFSIZE, LOG_STRFTIME, tmp);
if (r == 0) {
fprintf(stderr, "strftime(3) failed\n");
exit(EXIT_FAILURE);
}
// Reallocate to the actual size of the string.
// It is not likely to fail, since we are freeing
// memory here.
return xrealloc(res, r);
}
static int __log(const char *level, const char *format, va_list args) {
int len;
char *timestamp;
char *newformat;
char *logformat = LOG_FORMAT;
timestamp = gettimestamp();
len = snprintf(NULL, 0, logformat, timestamp, level, format);
len++; // Trailing null byte.
newformat = xmalloc(len);
len = snprintf(newformat, len, logformat, timestamp, level, format);
if (len < 0) {
goto cleanup;
}
len = vprintf(newformat, args);
cleanup:
free(timestamp);
free(newformat);
return len;
}
#else
#define LOG_FORMAT "[%s]: %s\n" // loglevel: message\n
static int __log(const char *level, const char *format, va_list args) {
int len;
char *newformat;
char *logformat = LOG_FORMAT;
len = snprintf(NULL, 0, logformat, level, format);
len++; // Trailing null byte.
newformat = xmalloc(len);
len = snprintf(newformat, len, logformat, level, format);
if (len < 0) {
goto cleanup;
}
len = vprintf(newformat, args);
cleanup:
free(newformat);
return len;
}
#endif
int debug(const char *format, ...) {
int r;
va_list ap;
if (loglevel > DEBUG) {
return -1;
}
va_start(ap, format);
r = __log("DEBUG", format, ap);
va_end(ap);
return r;
}
int info(const char *format, ...) {
int r;
va_list ap;
if (loglevel > INFO) {
return -1;
}
va_start(ap, format);
r = __log("INFO", format, ap);
va_end(ap);
return r;
}
int warning(const char *format, ...) {
int r;
va_list ap;
if (loglevel > WARNING) {
return -1;
}
va_start(ap, format);
r = __log("WARNING", format, ap);
va_end(ap);
return r;
}
int error(const char *format, ...) {
int r;
va_list ap;
if (loglevel > ERROR) {
return -1;
}
va_start(ap, format);
r = __log("ERROR", format, ap);
va_end(ap);
return r;
}
#pragma once
enum loglevel_t { DEBUG, INFO, WARNING, ERROR };
extern enum loglevel_t loglevel;
int debug(const char *format, ...);
int info(const char *format, ...);
int warning(const char *format, ...);
int error(const char *format, ...);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment