diff --git a/src/main.c b/src/main.c index 6b48e00..d05ce36 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,19 @@ +#include #include +#define LOG_LEVEL LOG_LEVEL_DEBUG + +void setup(void) +{ + log_init(); + DEBUG("starting gemstone..."); + + DEBUG("finished starting up gemstone..."); +} + int main() { + setup(); + yyparse(); return 0; } \ No newline at end of file diff --git a/src/sys/log.c b/src/sys/log.c new file mode 100644 index 0000000..bbf2c37 --- /dev/null +++ b/src/sys/log.c @@ -0,0 +1,110 @@ + +#include +#include +#include +#include + +static struct Logger_t { + FILE** streams; + size_t stream_count; +} GlobalLogger; + +void log_init(void) +{ + log_register_stream(LOG_DEFAULT_STREAM); +} + +void log_register_stream(FILE* restrict stream) +{ + if (GlobalLogger.stream_count == 0) + { + GlobalLogger.streams = (FILE**) malloc(sizeof(FILE*)); + GlobalLogger.stream_count = 1; + + if (GlobalLogger.streams == NULL) + { + PANIC("failed to allocate stream buffer"); + } + } + else + { + GlobalLogger.stream_count++; + size_t bytes = GlobalLogger.stream_count * sizeof(FILE*); + GlobalLogger.streams = (FILE**) realloc(GlobalLogger.streams, bytes); + + if (GlobalLogger.streams == NULL) + { + PANIC("failed to reallocate stream buffer"); + } + } + + GlobalLogger.streams[GlobalLogger.stream_count - 1] = stream; +} + +static void vflogf( + FILE* restrict stream, + const char* restrict level, + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + va_list args) +{ + fprintf(stream, "%s in %s() at %s:%lu: ", level, func, file, line); + vfprintf(stream, format, args); +} + +void __logf( + const char* restrict level, + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...) +{ + va_list args; + va_start(args, format); + + for (size_t i = 0; i < GlobalLogger.stream_count; i++) + { + FILE* stream = GlobalLogger.streams[i]; + + vflogf(stream, level, file, line, func, format, args); + } + + va_end(args); +} + +void __panicf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...) +{ + va_list args; + va_start(args, format); + + vflogf(stderr, LOG_STRING_PANIC, file, line, func, format, args); + + va_end(args); + + exit(EXIT_FAILURE); +} + +void __fatalf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...) +{ + va_list args; + va_start(args, format); + + vflogf(stderr, LOG_STRING_FATAL, file, line, func, format, args); + + va_end(args); + + abort(); +} diff --git a/src/sys/log.h b/src/sys/log.h new file mode 100644 index 0000000..49e31ce --- /dev/null +++ b/src/sys/log.h @@ -0,0 +1,81 @@ +#ifndef _SYS_ERR_H_ +#define _SYS_ERR_H_ + +#include +#include + +#define LOG_DEFAULT_STREAM stderr + +#define LOG_LEVEL_ERROR 3 +#define LOG_LEVEL_WARNING 2 +#define LOG_LEVEL_INFORMATION 1 +#define LOG_LEVEL_DEBUG 0 + +#define LOG_LEVEL LOG_LEVEL_DEBUG + +#define LOG_MAX_BACKTRACE_FRAMES 64 + +#define LOG_STRING_PANIC "Critical" +#define LOG_STRING_FATAL "Fatal" +#define LOG_STRING_ERROR "Error" +#define LOG_STRING_WARNING "Warning" +#define LOG_STRING_INFORMATION "Information" +#define LOG_STRING_DEBUG "Debug" + +/** + * @brief Panic is used in cases where the process is in an unrecoverable state. + * This macro will print debug information to stderr and call abort() to + * performa a ungracefull exit. No clean up possible. + */ +#define PANIC(format, ...) __panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) + +/** + * @brief Panic is used in cases where the process is in an invalid or undefined state. + * This macro will print debug information to stderr and call exit() to + * initiate a gracefull exit, giving the process the opportunity to clean up. + */ +#define FATAL(format, ...) __fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) + +/* +Standard log macros. These will not terminate the application. +Can be turned off by setting LOG_LEVEL. All logs which have smaller log numbers +will not print. +*/ +#define ERROR(format, ...) __LOG(LOG_STRING_ERROR, LOG_LEVEL_ERROR, format"\n", ##__VA_ARGS__) +#define WARN(format, ...) __LOG(LOG_STRING_WARNING, LOG_LEVEL_WARNING, format"\n", ##__VA_ARGS__) +#define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__) +#define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__) + +#define __LOG(level, priority, format, ...) \ + do { \ + if (LOG_LEVEL <= priority) \ + __logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ + } while(0) + +void __logf( + const char* restrict level, + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...); + +void __panicf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...); + +void __fatalf( + const char* restrict file, + const unsigned long line, + const char* restrict func, + const char* restrict format, + ...); + +void log_init(void); + +void log_register_stream(FILE* restrict stream); + +#endif /* _SYS_ERR_H_ */