aboutsummaryrefslogtreecommitdiffstats
path: root/src/world.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-11-26 22:14:39 +0100
committerDavid Robillard <d@drobilla.net>2019-12-19 20:57:58 -0500
commitf65a77adf806025f84dce23880a518ca1bd70f61 (patch)
tree43245e22116b61073efb7c3c929fd5b721277779 /src/world.c
parent74a4425ca220379f058f410f4cf5e92d99f0f79e (diff)
downloadserd-f65a77adf806025f84dce23880a518ca1bd70f61.tar.gz
serd-f65a77adf806025f84dce23880a518ca1bd70f61.tar.bz2
serd-f65a77adf806025f84dce23880a518ca1bd70f61.zip
Add extensible logging API
Diffstat (limited to 'src/world.c')
-rw-r--r--src/world.c146
1 files changed, 123 insertions, 23 deletions
diff --git a/src/world.c b/src/world.c
index f950e948..1c090f8d 100644
--- a/src/world.c
+++ b/src/world.c
@@ -34,14 +34,21 @@
#define BLANK_CHARS 11
+static const char* const log_level_strings[] = { "emergengy", "alert",
+ "critical", "error",
+ "warning", "note",
+ "info", "debug" };
+
FILE*
serd_world_fopen(SerdWorld* world, const char* path, const char* mode)
{
FILE* fd = fopen(path, mode);
if (!fd) {
- serd_world_errorf(world, SERD_ERR_INTERNAL,
- "failed to open file %s (%s)\n",
- path, strerror(errno));
+ char errno_str[24];
+ snprintf(errno_str, sizeof(errno_str), "%d", errno);
+ const SerdLogField fields[] = { { "ERRNO", errno_str } };
+ serd_world_logf(world, "serd", SERD_LOG_LEVEL_ERR, 1, fields,
+ "failed to open file %s (%s)\n", path, strerror(errno));
return NULL;
}
#if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO)
@@ -50,32 +57,127 @@ serd_world_fopen(SerdWorld* world, const char* path, const char* mode)
return fd;
}
+static const char*
+get_log_field(const SerdLogField* const fields,
+ const size_t n_fields,
+ const char* const key)
+{
+ for (size_t i = 0; i < n_fields; ++i) {
+ if (!strcmp(fields[i].key, key)) {
+ return fields[i].value;
+ }
+ }
+
+ return NULL;
+}
+
+const char*
+serd_log_entry_get_field(const SerdLogEntry* const entry, const char* const key)
+{
+ return get_log_field(entry->fields, entry->n_fields, key);
+}
+
SerdStatus
-serd_world_error(const SerdWorld* world, const SerdError* e)
+serd_world_vlogf(const SerdWorld* world,
+ const char* domain,
+ SerdLogLevel level,
+ const unsigned n_fields,
+ const SerdLogField* fields,
+ const char* fmt,
+ va_list args)
{
- if (world->error_sink) {
- world->error_sink(world->error_handle, e);
+ // Copy args (which may be an array) to portably get a pointer
+ va_list ap;
+ va_copy(ap, args);
+
+ const SerdLogEntry e = {domain, level, n_fields, fields, fmt, &ap};
+ SerdStatus st = SERD_SUCCESS;
+
+ if (world->log_func) {
+ st = world->log_func(world->log_handle, &e);
} else {
- fprintf(stderr, "error: ");
- if (e->cursor) {
- fprintf(stderr,
- "%s:%u:%u: ",
- serd_node_get_string(e->cursor->file),
- e->cursor->line,
- e->cursor->col);
+ // Print GCC-style level prefix (error, warning, etc)
+ fprintf(stderr, "%s: ", log_level_strings[level]);
+
+ // Add input file and position to prefix if available
+ const char* const file = serd_log_entry_get_field(&e, "SERD_FILE");
+ const char* const line = serd_log_entry_get_field(&e, "SERD_LINE");
+ const char* const col = serd_log_entry_get_field(&e, "SERD_COL");
+ if (file && line && col) {
+ fprintf(stderr, "%s:%s:%s: ", file, line, col);
}
- vfprintf(stderr, e->fmt, *e->args);
+
+ // Using a copy isn't necessary here, but it avoids a clang-tidy bug
+ vfprintf(stderr, fmt, ap);
}
- return e->status;
+
+ va_end(ap);
+ return st;
+}
+
+SerdStatus
+serd_world_logf(const SerdWorld* world,
+ const char* domain,
+ SerdLogLevel level,
+ const unsigned n_fields,
+ const SerdLogField* fields,
+ const char* fmt,
+ ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ const SerdStatus st =
+ serd_world_vlogf(world, domain, level, n_fields, fields, fmt, args);
+
+ va_end(args);
+ return st;
+}
+
+SerdStatus
+serd_world_vlogf_internal(const SerdWorld* world,
+ SerdStatus st,
+ SerdLogLevel level,
+ const SerdCursor* cursor,
+ const char* fmt,
+ va_list args)
+{
+ char st_str[8];
+ snprintf(st_str, sizeof(st_str), "%d", st);
+ if (cursor) {
+ const char* file = serd_node_get_string(serd_cursor_get_name(cursor));
+
+ char line[24];
+ snprintf(line, sizeof(line), "%u", serd_cursor_get_line(cursor));
+
+ char col[24];
+ snprintf(col, sizeof(col), "%u", serd_cursor_get_column(cursor));
+
+ const SerdLogField fields[] = { { "SERD_STATUS", st_str },
+ { "SERD_FILE", file },
+ { "SERD_LINE", line },
+ { "SERD_COL", col } };
+
+ serd_world_vlogf(world, "serd", level, 4, fields, fmt, args);
+ } else {
+ const SerdLogField fields[] = { { "SERD_STATUS", st_str } };
+ serd_world_vlogf(world, "serd", level, 1, fields, fmt, args);
+ }
+
+ return st;
}
SerdStatus
-serd_world_errorf(const SerdWorld* world, SerdStatus st, const char* fmt, ...)
+serd_world_logf_internal(const SerdWorld* world,
+ SerdStatus st,
+ SerdLogLevel level,
+ const SerdCursor* cursor,
+ const char* fmt,
+ ...)
{
va_list args;
va_start(args, fmt);
- const SerdError e = { st, NULL, fmt, &args };
- serd_world_error(world, &e);
+ serd_world_vlogf_internal(world, st, level, cursor, fmt, args);
va_end(args);
return st;
}
@@ -125,10 +227,8 @@ serd_world_get_blank(SerdWorld* world)
}
void
-serd_world_set_error_sink(SerdWorld* world,
- SerdErrorSink error_sink,
- void* handle)
+serd_world_set_log_func(SerdWorld* world, SerdLogFunc log_func, void* handle)
{
- world->error_sink = error_sink;
- world->error_handle = handle;
+ world->log_func = log_func;
+ world->log_handle = handle;
}