I've created a simple logging library in C which I can use in my other projects. I wanted to get started in software development using the C language, and this is my first attempt at writing something which comes close to an actual software. Looking for a code review/feedback for this library, especially about reusability, optimization, code flow, and testing. Here are the code files:
logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <stdlib.h>
#include <stdbool.h>
#define DEFAULT_LOG_PATH "/var/log/logger.log"
#define RESET 0
typedef enum {
OFF,
DEBUG,
INFO,
WARNING,
ERROR
} LOGGING_LEVELS;
/**
* @brief Function to initialize the logger.
*
* @params[in] level Log level.
* @params[in] filename Path to the log file.
* @params[in] to_file Boolean variable to allow writing to file or to stderr stream.
*
* @retval None.
*/
void init_logger(LOGGING_LEVELS, const char *, bool);
/**
* @brief Function to write the message to the log file.
*
* @params[in] level Log level.
* @params[in] frmt Format message.
* @params[in] ... Variable arguments.
*
* @retval None.
*/
void log_func(LOGGING_LEVELS level, const char *filename, const size_t line, const char *frmt, ...);
#define log_debug(...) log_func(DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_func(INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) log_func(WARNING, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_func(ERROR, __FILE__, __LINE__, __VA_ARGS__)
#endif
logger.c
#include "logger.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
//Global variables to keep track of log files.
static int LOG_LEVEL = OFF;
static const char* log_filename = DEFAULT_LOG_PATH;
static bool write_to_file = false;
static const char* log_level_strings[] = {
"OFF",
"[DEBUG]",
"[INFO]",
"[WARNING]",
"[ERROR]",
};
const char* colors[] = {
"\x1B[0m",
"\x1B[34m",
"\x1B[32m",
"\x1B[33m",
"\x1B[31m",
};
/**
* @brief Function to get the current date and time and write to log file.
*
* @params[in] fptr File pointer to the log file.
*
* @retval None.
*/
static void get_datetime(FILE *fptr) {
time_t t = time(NULL);
struct tm *tm = localtime(&t);
const char *d_time = asctime(tm);
const char *datetime = strchr(d_time, '\n');
int length = datetime - d_time;
char *modified_date = malloc(length + 1);
strncpy(modified_date, d_time, length);
modified_date[length] = '\0';
fprintf(fptr, "%s ", modified_date);
free(modified_date);
}
/**
* @brief Function to write the log level to the log file.
*
* @params[in] level Log level.
* @params[in] fptr File pointer to the log file.
*
* @retval None
*/
static void log_msg(LOGGING_LEVELS level, FILE *fptr) {
get_datetime(fptr);
if(fptr == stderr) {
fprintf(fptr, "%s%-10s%s", colors[level], log_level_strings[level], colors[RESET]);
}
else {
fprintf(fptr, "%-10s", log_level_strings[level]);
}
}
void log_func(LOGGING_LEVELS level, const char *file, const size_t line, const char *frmt, ...) {
if(LOG_LEVEL > level) {
return;
}
FILE *fptr = stderr;
if(write_to_file) {
fptr = fopen(log_filename, "a");
if(NULL == fptr) {
fprintf(stderr, "%sError:%s File not existing or Permission denied\nExiting the program with status 1.\n", colors[ERROR], colors[RESET]);
exit(1);
}
}
log_msg(level, fptr);
fprintf(fptr, "%s: %lu: ", file, line);
char *format = strdup(frmt);
strcat(format, "\n");
va_list argp;
va_start(argp, frmt);
vfprintf(fptr, format, argp);
va_end(argp);
if(fptr != stderr) {
fclose(fptr);
}
free(format);
}
void init_logger(LOGGING_LEVELS level, const char* filename, bool to_file) {
if(LOG_LEVEL > OFF) {
fprintf(stderr, "%sError:%s Logger already initialised in the project\nExiting the program with status 1.\n", colors[ERROR], colors[RESET]);
exit(1);
}
LOG_LEVEL = level;
if(NULL != filename) {
log_filename = filename;
}
write_to_file = to_file;
}
Test files:
test.c
/**
* Test to check that init_logger and log_debug work properly.
*/
#include "../src/logger.h"
#include <stdio.h>
void test_init(void) {
init_logger(INFO, "log.log", true); //Will write to log.log file
// init_logger(DEBUG, NULL, true); //Will write to the default log path. Needs to be run with sudo.
// init_logger(DEBUG, NULL, false); //Will output to stderr console.
}
int main() {
test_init();
log_debug("Print: %d", DEBUG); //Will not print anything.
log_info("Print: %d", INFO);
log_warn("Print: %d", WARNING);
log_error("Print: %d", ERROR);
return 0;
}