telodendria/src/Log.c

337 lines
6.5 KiB
C
Raw Normal View History

/*
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
2022-07-28 16:00:52 +00:00
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
2022-07-23 00:19:12 +00:00
#include <Log.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <stdarg.h>
2022-07-29 19:36:21 +00:00
#include <pthread.h>
2022-07-23 00:19:12 +00:00
#define LOG_TSBUFFER 64
2022-07-25 19:18:35 +00:00
struct LogConfig
{
2022-07-23 00:19:12 +00:00
LogLevel level;
size_t indent;
FILE *out;
int flags;
char *tsFmt;
2022-07-29 19:36:21 +00:00
pthread_mutex_t lock;
};
2022-07-23 00:19:12 +00:00
LogConfig *
LogConfigCreate(void)
{
LogConfig *config;
config = calloc(1, sizeof(LogConfig));
if (!config)
{
return NULL;
}
LogConfigLevelSet(config, LOG_MESSAGE);
LogConfigIndentSet(config, 0);
2022-07-25 19:18:35 +00:00
LogConfigOutputSet(config, NULL); /* Will set to stdout */
2022-07-23 00:19:12 +00:00
LogConfigFlagSet(config, LOG_FLAG_COLOR);
LogConfigTimeStampFormatSet(config, "%y-%m-%d %H:%M:%S");
return config;
}
void
2022-07-25 19:18:35 +00:00
LogConfigFlagClear(LogConfig * config, int flags)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return;
}
config->flags &= ~flags;
}
2022-07-29 19:36:21 +00:00
static int
2022-07-25 19:18:35 +00:00
LogConfigFlagGet(LogConfig * config, int flags)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return 0;
}
return config->flags & flags;
}
void
2022-07-25 19:18:35 +00:00
LogConfigFlagSet(LogConfig * config, int flags)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return;
}
config->flags |= flags;
}
void
2022-07-25 19:18:35 +00:00
LogConfigFree(LogConfig * config)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return;
}
fclose(config->out);
2022-07-23 00:19:12 +00:00
free(config);
}
void
2022-07-25 19:18:35 +00:00
LogConfigIndent(LogConfig * config)
2022-07-23 00:19:12 +00:00
{
if (config)
{
config->indent += 2;
}
}
void
2022-07-25 19:18:35 +00:00
LogConfigIndentSet(LogConfig * config, size_t indent)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return;
}
config->indent = indent;
}
LogLevel
2022-07-25 19:18:35 +00:00
LogConfigLevelGet(LogConfig * config)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return -1;
}
return config->level;
}
void
2022-07-25 19:18:35 +00:00
LogConfigLevelSet(LogConfig * config, LogLevel level)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return;
}
switch (level)
{
case LOG_ERROR:
case LOG_WARNING:
case LOG_MESSAGE:
case LOG_DEBUG:
config->level = level;
default:
break;
}
}
void
2022-07-25 19:18:35 +00:00
LogConfigOutputSet(LogConfig * config, FILE * out)
2022-07-23 00:19:12 +00:00
{
if (!config)
{
return;
}
if (out)
{
config->out = out;
}
else
{
config->out = stdout;
}
}
void
2022-07-25 19:18:35 +00:00
LogConfigTimeStampFormatSet(LogConfig * config, char *tsFmt)
2022-07-23 00:19:12 +00:00
{
if (config)
{
config->tsFmt = tsFmt;
}
}
void
2022-07-25 19:18:35 +00:00
LogConfigUnindent(LogConfig * config)
2022-07-23 00:19:12 +00:00
{
if (config && config->indent >= 2)
{
config->indent -= 2;
}
}
2022-07-29 19:36:21 +00:00
void
Log(LogConfig * config, LogLevel level, const char *msg,...)
{
size_t i;
int doColor;
char indicator;
va_list argp;
/*
* Only proceed if we have a config and its log level is set to a
* value that permits us to log. This is as close as we can get
* to a no-op function if we aren't logging anything, without doing
* some crazy macro magic.
*/
if (!config || level > config->level)
{
return;
}
/* Misconfiguration */
if (!config->out)
{
return;
}
pthread_mutex_lock(&config->lock);
doColor = LogConfigFlagGet(config, LOG_FLAG_COLOR)
&& isatty(fileno(config->out));
if (doColor)
{
char *ansi;
switch (level)
{
case LOG_ERROR:
/* Bold Red */
ansi = "\033[1;31m";
break;
case LOG_WARNING:
/* Bold Yellow */
ansi = "\033[1;33m";
break;
case LOG_TASK:
/* Bold Magenta */
ansi = "\033[1;35m";
break;
case LOG_MESSAGE:
/* Bold Green */
ansi = "\033[1;32m";
break;
case LOG_DEBUG:
/* Bold Blue */
ansi = "\033[1;34m";
break;
default:
ansi = "";
break;
}
fputs(ansi, config->out);
}
fputc('[', config->out);
if (config->tsFmt)
{
time_t timer = time(NULL);
struct tm *timeInfo = localtime(&timer);
char tsBuffer[LOG_TSBUFFER];
int tsLength = strftime(tsBuffer, LOG_TSBUFFER, config->tsFmt,
timeInfo);
if (tsLength)
{
fputs(tsBuffer, config->out);
if (!isspace(tsBuffer[tsLength - 1]))
{
fputc(' ', config->out);
}
}
}
switch (level)
{
case LOG_ERROR:
indicator = 'x';
break;
case LOG_WARNING:
indicator = '!';
break;
case LOG_TASK:
indicator = '~';
break;
case LOG_MESSAGE:
indicator = '>';
break;
case LOG_DEBUG:
indicator = '*';
break;
default:
indicator = '?';
break;
}
fprintf(config->out, "%c]", indicator);
if (doColor)
{
/* ANSI Reset */
fputs("\033[0m", config->out);
}
fputc(' ', config->out);
for (i = 0; i < config->indent; i++)
{
fputc(' ', config->out);
}
2022-07-29 19:36:21 +00:00
va_start(argp, msg);
vfprintf(config->out, msg, argp);
fputc('\n', config->out);
va_end(argp);
/* If we are debugging, there might be something that's going to
* segfault the program coming up, so flush the output stream
* immediately. */
if (config->level == LOG_DEBUG)
{
fflush(config->out);
}
pthread_mutex_unlock(&config->lock);
}