diff --git a/c/color.h b/c/color.h new file mode 100644 index 0000000..f30d6e4 --- /dev/null +++ b/c/color.h @@ -0,0 +1,33 @@ +/** Various helper functions. + * + * @author Steffen Vogel + * @copyright 2014, Institute for Automation of Complex Power Systems, EONERC + * @file + */ + +#ifndef _COLOR_H_ +#define _COLOR_H_ + +#include +#include +#include +#include + +/* Some color escape codes for pretty log messages */ +#define GRY(str) "\e[30m" str "\e[0m" /**< Print str in gray */ +#define RED(str) "\e[31m" str "\e[0m" /**< Print str in red */ +#define GRN(str) "\e[32m" str "\e[0m" /**< Print str in green */ +#define YEL(str) "\e[33m" str "\e[0m" /**< Print str in yellow */ +#define BLU(str) "\e[34m" str "\e[0m" /**< Print str in blue */ +#define MAG(str) "\e[35m" str "\e[0m" /**< Print str in magenta */ +#define CYN(str) "\e[36m" str "\e[0m" /**< Print str in cyan */ +#define WHT(str) "\e[37m" str "\e[0m" /**< Print str in white */ +#define BLD(str) "\e[1m" str "\e[0m" /**< Print str in bold */ + +#define GFX(chr) "\e(0" chr "\e(B" +#define UP(n) "\e[" ## n ## "A" +#define DOWN(n) "\e[" ## n ## "B" +#define RIGHT(n) "\e[" ## n ## "C" +#define LEFT(n) "\e[" ## n ## "D" + +#endif /* _COLOR_H_ */ diff --git a/c/logger.c b/c/logger.c new file mode 100644 index 0000000..daf83e1 --- /dev/null +++ b/c/logger.c @@ -0,0 +1,70 @@ +#include + +#include "color.h" +#include "logger.h" + +/* This global variable contains the debug level for debug() and assert() macros */ +int _debug = V; +int _indent = 0; + +static struct timeval epoch; + +void _outdent(int *old) +{ + _indent = *old; +} + +void log_reset() +{ + gettimeofday(&epoch, NULL); +} + +void log_level(int lvl) +{ + _debug = lvl; +} + +void print(enum log_level lvl, const char *fmt, ...) +{ + struct timeval tv; + + va_list ap; + va_start(ap, fmt); + + /* Timestamp */ + gettimeofday(&tv, NULL); + + fprintf(stderr, "%8.3f ", timespec_delta(&epoch, &tv)); + + switch (lvl) { + case DEBUG: fprintf(stderr, BLD("%-5s "), GRY("Debug")); break; + case INFO: fprintf(stderr, BLD("%-5s "), WHT(" Info")); break; + case WARN: fprintf(stderr, BLD("%-5s "), YEL(" Warn")); break; + case ERROR: fprintf(stderr, BLD("%-5s "), RED("Error")); break; + } + + if (_indent) { + for (int i = 0; i < _indent-1; i++) + fprintf(stderr, GFX("\x78") " "); + + fprintf(stderr, GFX("\x74") " "); + } + + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + + va_end(ap); +} + +double timespec_delta(struct timeval *start, struct timeval *end) +{ + double sec = end->tv_sec - start->tv_sec; + double usec = end->tv_usec - start->tv_usec; + + if (usec < 0) { + sec -= 1; + usec += 1e6; + } + + return sec + usec * 1e-6; +} \ No newline at end of file diff --git a/c/logger.h b/c/logger.h new file mode 100644 index 0000000..b9426a4 --- /dev/null +++ b/c/logger.h @@ -0,0 +1,85 @@ +#include +#include + +#ifndef V + #define V 5 +#endif + +#ifdef __GNUC__ + #define EXPECT(x, v) __builtin_expect(x, v) + #define INDENT int __attribute__ ((__cleanup__(_outdent), unused)) _old_indent = _indent++; +#else + #define EXPECT(x, v) (x) + #define INDENT ; +#endif + +/* These global variables allow changing the output style and verbosity */ +extern int _debug; +extern int _indent; + +void outdent(int *old); + +/** The log level which is passed as first argument to print() */ +enum log_level { DEBUG, INFO, WARN, ERROR }; + +/** Reset the timer for log outputs to zero. */ +void log_reset(); + +/** Set the current logging level */ +void log_level(int lvl); + +/** Get delta between two timespec structs */ +double timespec_delta(struct timeval *start, struct timeval *end); + +/** Logs variadic messages to stdout. + * + * @param lvl The log level + * @param fmt The format string (printf alike) + */ +void print(enum log_level lvl, const char *fmt, ...); + +/** Check assertion and exit if failed. */ +#define assert(exp) do { \ + if (EXPECT(!exp, 0)) { \ + print(ERROR, "Assertion failed: '%s' in %s, %s:%d", \ + #exp, __FUNCTION__, __BASE_FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } } while (0) + +/** Printf alike debug message with level. */ +#define debug(lvl, msg, ...) do { \ + if (lvl <= _debug) \ + print(DEBUG, msg, ##__VA_ARGS__); \ + } while (0) + +/** Printf alike info message. */ +#define info(msg, ...) do { \ + print(INFO, msg, ##__VA_ARGS__); \ + } while (0) + +/** Printf alike warning message. */ +#define warn(msg, ...) do { \ + print(WARN, msg, ##__VA_ARGS__); \ + } while (0) + +/** Print error and exit. */ +#define error(msg, ...) do { \ + print(ERROR, msg, ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) + +/** Print error and strerror(errno). */ +#define serror(msg, ...) do { \ + print(ERROR, msg ": %s", ##__VA_ARGS__, \ + strerror(errno)); \ + exit(EXIT_FAILURE); \ + } while (0) + +/** Print configuration error and exit. */ +#define cerror(c, msg, ...) do { \ + print(ERROR, msg " in %s:%u", ##__VA_ARGS__, \ + (config_setting_source_file(c)) ? \ + config_setting_source_file(c) : "(stdio)", \ + config_setting_source_line(c)); \ + exit(EXIT_FAILURE); \ + } while (0) \ No newline at end of file diff --git a/c/logger_test.c b/c/logger_test.c new file mode 100644 index 0000000..9f4b23d --- /dev/null +++ b/c/logger_test.c @@ -0,0 +1,48 @@ +#include + +#include "logger.h" + +void recursive(int i) +{ INDENT + info("We are inside the recursive function. Current level = %d", i); + + if (i) { + usleep(rand() * 3e6 / RAND_MAX); + recursive(i-1); + } +} + +void goodbye() +{ + warn("There was an error message. Program terminating..."); +} + +int main(int argc, char *argv[]) +{ + /* Reset log timer to zero */ + epoch_reset(); + + /* Register exit() handler */ + atexit(goodbye); + + info("Welcome, this is a little program to demo the logger"); + + recursive(5); + + /* Some other types of messages */ + info("This is a info message"); + warn("This is a warning message"); + + /* Debug messages are only printed if the level (1st arg) is higher than the current level */ + debug(3, "This is a debug message"); + + /* INDENT's are valid per code block */ + { INDENT + info("This is an indented message"); + } + + /* Error messages will cause program termination */ + error("This is an error message"); + + return 0; +} \ No newline at end of file