Skip to content

Commit

Permalink
Merge pull request #48 from sysprog21/logging
Browse files Browse the repository at this point in the history
Consolidate logging with a unified interface
  • Loading branch information
jserv authored Sep 14, 2024
2 parents 6b95d08 + fbd18ab commit e350d40
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 11 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ libtwin.a_includes-y := \
src

# Features
libtwin.a_files-$(CONFIG_LOGGING) += src/log.c
libtwin.a_files-$(CONFIG_CURSOR) += src/cursor.c

# Image loaders
Expand Down
6 changes: 3 additions & 3 deletions backend/sdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ twin_context_t *twin_sdl_init(int width, int height)
}

if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("error : %s\n", SDL_GetError());
log_error("%s", SDL_GetError());
goto bail;
}

Expand All @@ -154,7 +154,7 @@ twin_context_t *twin_sdl_init(int width, int height)
SDL_WINDOWPOS_UNDEFINED, width, height,
SDL_WINDOW_SHOWN);
if (!tx->win) {
printf("error : %s\n", SDL_GetError());
log_error("%s", SDL_GetError());
goto bail;
}

Expand All @@ -163,7 +163,7 @@ twin_context_t *twin_sdl_init(int width, int height)

tx->render = SDL_CreateRenderer(tx->win, -1, SDL_RENDERER_ACCELERATED);
if (!tx->render) {
printf("error : %s\n", SDL_GetError());
log_error("%s", SDL_GetError());
goto bail_pixels;
}
SDL_SetRenderDrawColor(tx->render, 255, 255, 255, 255);
Expand Down
17 changes: 17 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ endchoice

menu "Features"

config LOGGING
bool "Enable logging"
default y

config LOGGING_COLOR
bool "Use colors for logging"
default y
depends on LOGGING

config LOGGING_CALLBACK
bool "Enable callback functions"
default y
depends on LOGGING

comment "Logging is disabled"
depends on !LOGGING

config CURSOR
bool "Manipulate cursor"
default n
Expand Down
5 changes: 5 additions & 0 deletions include/twin_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

#include "twin.h"

/* FIXME: Both twin_private.h and log.h are private header files. They should
* be moved to src/ directory.
*/
#include "../src/log.h"

/* Boilerplate for compiler compatibility */
#ifndef __has_attribute
#define __has_attribute(x) 0
Expand Down
10 changes: 5 additions & 5 deletions src/image-gif.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ static twin_gif_t *gif_open(const char *fname)
/* Header */
read(fd, sigver, 3);
if (memcmp(sigver, "GIF", 3) != 0) {
fprintf(stderr, "invalid signature\n");
log_error("Invalid signature");
goto fail;
}
/* Version */
read(fd, sigver, 3);
if (memcmp(sigver, "89a", 3) != 0) {
fprintf(stderr, "invalid version\n");
log_error("Invalid version");
goto fail;
}
/* Width x Height */
Expand All @@ -101,7 +101,7 @@ static twin_gif_t *gif_open(const char *fname)
read(fd, &fdsz, 1);
/* Presence of GCT */
if (!(fdsz & 0x80)) {
fprintf(stderr, "no global color table\n");
log_error("No global color table");
goto fail;
}
/* Color Space's Depth */
Expand Down Expand Up @@ -480,7 +480,7 @@ static void read_ext(twin_gif_t *gif)
read_application_ext(gif);
break;
default:
fprintf(stderr, "unknown extension: %02X\n", label);
log_error("Unknown extension: %02X", label);
}
}

Expand Down Expand Up @@ -617,7 +617,7 @@ twin_pixmap_t *_twin_gif_to_pixmap(const char *filepath, twin_format_t fmt)
return NULL;
FILE *infile = fopen(filepath, "rb");
if (!infile) {
fprintf(stderr, "Failed to open %s\n", filepath);
log_error("Failed to open %s", filepath);
return NULL;
}
twin_animation_t *gif = _twin_animation_from_gif_file(filepath);
Expand Down
4 changes: 2 additions & 2 deletions src/image-jpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ twin_pixmap_t *_twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt)

FILE *infile = fopen(filepath, "rb");
if (!infile) {
fprintf(stderr, "Failed to open %s\n", filepath);
log_error("Failed to open %s", filepath);
return NULL;
}

Expand All @@ -61,7 +61,7 @@ twin_pixmap_t *_twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt)
struct twin_jpeg_err_mgr jerr = {.mgr.error_exit = twin_jpeg_error_exit};
cinfo.err = jpeg_std_error(&jerr.mgr);
if (setjmp(jerr.jbuf)) {
fprintf(stderr, "Failed to decode %s\n", filepath);
log_error("Failed to decode %s", filepath);
if (pix)
twin_pixmap_destroy(pix);
jpeg_destroy_decompress(&cinfo);
Expand Down
2 changes: 1 addition & 1 deletion src/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ static twin_image_format_t image_type_detect(const char *path)
twin_image_format_t type = IMAGE_TYPE_unknown;
FILE *file = fopen(path, "rb");
if (!file) {
fprintf(stderr, "Failed to open %s\n", path);
log_error("Failed to open %s", path);
return IMAGE_TYPE_unknown;
}

Expand Down
170 changes: 170 additions & 0 deletions src/log.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Copyright (c) 2020 rxi
*
* 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 included in
* all copies or substantial 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.
*/

#include <stdio.h>

#include "log.h"

#define MAX_CALLBACKS 32

typedef struct {
log_func_t fn;
void *udata;
int level;
} callback_t;

static struct {
void *udata;
log_lock_func_t lock;
int level;
bool quiet;
callback_t callbacks[MAX_CALLBACKS];
} L;

static const char *level_strings[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL",
};

#if defined(CONFIG_LOGGING_COLOR)
static const char *level_colors[] = {
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m",
};
#endif

static void stdout_callback(log_event_t *ev)
{
char buf[16];
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
#if defined(CONFIG_LOGGING_COLOR)
fprintf(ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf,
level_colors[ev->level], level_strings[ev->level], ev->file,
ev->line);
#else
fprintf(ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level],
ev->file, ev->line);
#endif
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}

static void file_callback(log_event_t *ev)
{
char buf[64];
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
fprintf(ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level],
ev->file, ev->line);
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}

static void lock(void)
{
if (L.lock)
L.lock(true, L.udata);
}

static void unlock(void)
{
if (L.lock)
L.lock(false, L.udata);
}

const char *log_level_string(int level)
{
return level_strings[level];
}

void log_set_lock(log_lock_func_t fn, void *udata)
{
L.lock = fn;
L.udata = udata;
}

void log_set_level(int level)
{
L.level = level;
}

void log_set_quiet(bool enable)
{
L.quiet = enable;
}

int log_add_callback(log_func_t fn, void *udata, int level)
{
for (int i = 0; i < MAX_CALLBACKS; i++) {
if (!L.callbacks[i].fn) {
L.callbacks[i] = (callback_t){fn, udata, level};
return 0;
}
}
return -1;
}

int log_add_fp(FILE *fp, int level)
{
return log_add_callback(file_callback, fp, level);
}

static void init_event(log_event_t *ev, void *udata)
{
if (!ev->time) {
time_t t = time(NULL);
ev->time = localtime(&t);
}
ev->udata = udata;
}

void log_impl(int level, const char *file, int line, const char *fmt, ...)
{
log_event_t ev = {
.fmt = fmt,
.file = file,
.line = line,
.level = level,
};

lock();

if (!L.quiet && level >= L.level) {
init_event(&ev, stderr);
va_start(ev.ap, fmt);
stdout_callback(&ev);
va_end(ev.ap);
}

#ifdef CONFIG_LOGGING_CALLBACK
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
callback_t *cb = &L.callbacks[i];
if (level >= cb->level) {
init_event(&ev, cb->udata);
va_start(ev.ap, fmt);
cb->fn(&ev);
va_end(ev.ap);
}
}
#endif

unlock();
}
82 changes: 82 additions & 0 deletions src/log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright (c) 2020 rxi
*
* 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 included in
* all copies or substantial 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.
*/

#ifndef _TWIN_LOGGING_H_
#define _TWIN_LOGGING_H_

#include <stdarg.h>
#include <stdbool.h>
#include <time.h>

typedef struct {
va_list ap;
const char *fmt;
const char *file;
struct tm *time;
void *udata;
int line;
int level;
} log_event_t;

typedef void (*log_func_t)(log_event_t *ev);
typedef void (*log_lock_func_t)(bool lock, void *udata);

enum { LOGC_TRACE, LOGC_DEBUG, LOGC_INFO, LOGC_WARN, LOGC_ERROR, LOGC_FATAL };

#if defined(CONFIG_LOGGING)
#define log_trace(...) log_impl(LOGC_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) log_impl(LOGC_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_impl(LOGC_INFO, __FILE__, __LINE__, __VA_ARGS__)

#else /* !CONFIG_LOGGING */
#define log_trace(...) \
do { \
} while (0)
#define log_debug(...) \
do { \
} while (0)
#define log_info(...) \
do { \
} while (0)
#endif /* CONFIG_LOGGING */

#define log_warn(...) log_impl(LOGC_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_impl(LOGC_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define log_fatal(...) log_impl(LOGC_FATAL, __FILE__, __LINE__, __VA_ARGS__)

const char *log_level_string(int level);
void log_set_lock(log_lock_func_t fn, void *udata);
void log_set_level(int level);
void log_set_quiet(bool enable);
int log_add_callback(log_func_t fn, void *udata, int level);
int log_add_fp(FILE *fp, int level);

#if defined(CONFIG_LOGGING)
void log_impl(int level, const char *file, int line, const char *fmt, ...);
#else
#define log_impl(...) \
do { \
/* dummy implementation */ \
} while (0)
#endif

#endif /* _TWIN_LOGGING_H_ */

0 comments on commit e350d40

Please sign in to comment.