Over the past couple of weeks I have been working on a general purpose logging library for C in my free time. At the moment I have stabilized the API and most of the features I wanted to implement have been implemented. I am looking for general suggestions (note I haven't done much API documentation yet) on code quality and correctness (especially the synchronization techniques, IO methods, and message building functions).
You can find the full repository here.
And below I have included the sample usage that appears in the repo:
#include <stdio.h>
#include <stdlib.h>
#include "scribe.h"
int main(void)
{
if (SCRB_Success != scrb_init()) {
return EXIT_FAILURE;
}
// We open a new file called `example.log` in write mode without synchronization that only
// listens to info level log messages.
struct scrb_stream * const logstream = scrb_open_stream("example.log", "w", false, LVL_INFO);
// We define a log format to tell scribe how to format our messages.
// This format prints out the file, method, and line locations followed by the message itself.
struct scrb_format const * const fmt = scrb_create_format("[%F:%L:%M] %m", NULL);
// Now we're ready to go.
scrb_logln(logstream, fmt, LVL_INFO, "Hello from the example program!");
// We can do formatted output as well.
scrb_flogln(logstream, fmt, LVL_INFO, "Hello for the %dnd time!", 2);
// If we want to write directly to the file without formatting or location/time info
// (or paying attention to log levels) we can use the `scrb_putstrln` method to write directly to the file.
scrb_putstrln(logstream, "Now we're done.");
scrb_format_release(&fmt);
scrb_close_stream(&logstream);
return EXIT_SUCCESS;
}
I have written a synchronized version similar to the above wherein 4 separate threads all log to the same file, which you may find the the examples
folder.
Here is the scribe.h
header file (as per request):
#ifndef SCRIBE_H
#define SCRIBE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "scribe_conf.h"
#include "scribe_debug.h"
#include "scribe_format.h"
#include "scribe_io.h"
#include "scribe_loglevel.h"
#include "scribe_metainfo.h"
#include "scribe_return_types.h"
#include "scribe_stream.h"
#include "scribe_utils.h"
#include "spinlock.h"
#if defined(SCRIBE_WINDOWS)
#include "Windows.h"
#endif
#define LVL_TO_DEBUG (LVL_DEBUG)
#define LVL_TO_TRACE (LVL_DEBUG | LVL_TRACE)
#define LVL_TO_INFO (LVL_DEBUG | LVL_TRACE | LVL_INFO)
#define LVL_TO_NOTICE (LVL_DEBUG | LVL_TRACE | LVL_INFO | LVL_NOTICE)
#define LVL_TO_WARN (LVL_DEBUG | LVL_TRACE | LVL_INFO | LVL_NOTICE | LVL_WARN)
#define LVL_TO_ALERT (LVL_DEBUG | LVL_TRACE | LVL_INFO | LVL_NOTICE | LVL_WARN | LVL_ALERT)
#define LVL_TO_ERROR (LVL_DEBUG | LVL_TRACE | LVL_INFO | LVL_NOTICE | LVL_WARN | LVL_ALERT | LVL_ERROR)
#define LVL_TO_EMERG (LVL_DEBUG | LVL_TRACE | LVL_INFO | LVL_NOTICE | LVL_WARN | LVL_ALERT | LVL_ERROR | LVL_EMERG)
#define LVL_ALL (LVL_DEBUG | LVL_TRACE | LVL_INFO | LVL_NOTICE | LVL_WARN | LVL_ALERT | LVL_ERROR | LVL_EMERG)
static
struct scrb_stream * const scrb_stdout = &stream_out_default;
static
struct scrb_stream * const scrb_stdin = &stream_in_default;
static
struct scrb_stream * const scrb_stderr = &stream_err_default;
#if SCRIBE_DEBUG
static
struct scrb_stream const * const scrb_debug = &scrb_dbg_default;
#endif
// `scrb_init`
// doc...
static inline
int scrb_init(void)
{
#if defined(SCRIBE_WINDOWS)
static volatile LONG initialized = 0;
if (0 == InterlockedCompareExchange(&initialized, 1, 0)) {
#else
static volatile int initialized = 0;
if (__sync_bool_compare_and_swap(&initialized, 0, 1)) {
#endif
struct scrb_stream outstream = (struct scrb_stream) {
.name = "stdout",
.synchronize = true,
.severity = LVL_ALL,
.stream = {
.mode = NULL,
.filestream = stdout,
},
.rwlock = spinlock_init(SCRIBE_RWLOCK_DELAY)
};
struct scrb_stream instream = (struct scrb_stream) {
.name = "stdin",
.synchronize = true,
.severity = LVL_ALL,
.stream = {
.mode = NULL,
.filestream = stdin,
},
.rwlock = spinlock_init(SCRIBE_RWLOCK_DELAY)
};
struct scrb_stream errstream = (struct scrb_stream) {
.name = "stderr",
.synchronize = true,
.severity = LVL_ALL,
.stream = {
.mode = NULL,
.filestream = stderr,
},
.rwlock = spinlock_init(SCRIBE_RWLOCK_DELAY)
};
#if SCRIBE_DEBUG
FILE * const dbg_filestream = fopen("scribedebug.log", "a");
struct scrb_stream dbgstream = (struct scrb_stream) {
.name = "debug",
.synchronize = true,
.severity = LVL_ALL,
.stream = {
.mode = NULL,
.filestream = dbg_filestream,
},
.rwlock = spinlock_init(SCRIBE_RWLOCK_DELAY)
};
if (NULL == dbgstream.stream.filestream) {
return (SCRB_Failure);
}
scrb_init_defaults(&outstream, &instream, &errstream, &dbgstream);
fprintf(dbgstream.stream.filestream, "----- scribe init :: version (%s) :: compile date (%s %s) :: compiled as "SCRB_LANGUAGE" by "SCRB_CC" ver. " SCRB_CC_VER " :: pid (%d) -----\n",
SCRIBE_VERSION, __DATE__, __TIME__, (int) scrb_getpid());
#else
scrb_init_defaults(&outstream, &instream, &errstream);
#endif
}
return (SCRB_Success);
}
// `scrb_create_format`
// doc...
static inline
struct scrb_format * scrb_create_format(char const * const fmtstr,
void (*timehook)(char ** buff,
size_t * len,
SCRIBE_TIME_T ts))
{
return scrb_create_format__internal(fmtstr, timehook);
}
//
//
static inline
void (scrb_format_release)(struct scrb_format ** fmtptr)
{
scrb_format_release__internal(fmtptr);
}
#define scrb_format_release(fmtptr) (scrb_format_release)((struct scrb_format **) (fmtptr))
// `scrb_stream_name`
// doc...
static inline
char const * scrb_stream_name(struct scrb_stream const * const st)
{
return scrb_stream_name__internal(st);
}
// `scrb_stream_severity`
// doc...
static inline
uint16_t scrb_stream_severity(struct scrb_stream const * const st)
{
return scrb_stream_severity__internal(st);
}
// `scribe_open_file`
// doc...
static inline
struct scrb_stream * scrb_open_stream(char const * const path,
char const * const mode,
bool const synchronize,
uint16_t const severity)
{
return scrb_open_stream__internal(path, mode, synchronize, severity);
}
// `scribe_close`
// doc...
static inline
void (scrb_close_stream)(struct scrb_stream ** streamptr)
{
scrb_close_stream__internal(streamptr);
}
#define scrb_close_stream(streamptr) (scrb_close_stream)((struct scrb_stream **)(streamptr))
// `scribe_flush_stream`
// doc...
static inline
void scrb_flush_stream(struct scrb_stream * const st)
{
scrb_flush_stream__internal(st);
}
// `scribe_write`
// doc...
static inline
int (scrb_log)(struct scrb_meta_info const mi,
struct scrb_stream * const st,
struct scrb_format const * const fmt,
uint16_t const severity,
char const * const msg)
{
if (severity & st->severity) {
return scrb_log__internal(mi, st, fmt, severity, msg, false);
} else {
return SCRB_Success;
}
}
#define scrb_log(st, fmt, sv, msg) (scrb_log)(get_meta_info((st)->name), (st), (fmt), (sv), (msg))
// `scribe_writeln`
// doc...
static inline
int (scrb_logln)(struct scrb_meta_info const mi,
struct scrb_stream * const st,
struct scrb_format const * const fmt,
uint16_t const severity,
char const * const msg)
{
if (severity & st->severity) {
return scrb_log__internal(mi, st, fmt, severity, msg, true);
} else {
return SCRB_Success;
}
}
#define scrb_logln(st, fmt, sv, msg) (scrb_logln)(get_meta_info((st)->name), (st), (fmt), (sv), (msg))
// `scribe_writestr`
// doc...
static inline
int scrb_putstr(struct scrb_stream * const st, char const * const msg)
{
return scrb_putstr__internal(st, msg, false);
}
// `scribe_writestrln`
// doc...
static inline
int scrb_putstrln(struct scrb_stream * const st, char const * const msg)
{
return scrb_putstr__internal(st, msg, true);
}
// `fscribe_write`
// doc...
static inline
int (scrb_flog)(struct scrb_meta_info const mi,
struct scrb_stream * const st,
struct scrb_format const * const fmt,
uint16_t const severity,
char const * const msgfmt, ...)
{
if (severity & st->severity) {
va_list ap;
va_start (ap, msgfmt);
int const rval = scrb_flog__internal(mi, st, fmt, severity, msgfmt, false, ap);
va_end (ap);
return rval;
} else {
return SCRB_Success;
}
}
#define scrb_flog(st, fmt, sv, msg, ...) (scrb_flog)(get_meta_info((st)->name), (st), (fmt), (sv), (msg), ##__VA_ARGS__)
// `fscribe_writeln`
// doc...
static inline
int (scrb_flogln)(struct scrb_meta_info const mi,
struct scrb_stream * const st,
struct scrb_format const * const fmt,
uint16_t const severity,
char const * const msgfmt, ...)
{
if (severity & st->severity) {
va_list ap;
va_start (ap, msgfmt);
int const rval = scrb_flog__internal(mi, st, fmt, severity, msgfmt, true, ap);
va_end (ap);
return rval;
} else {
return SCRB_Success;
}
}
#define scrb_flogln(st, fmt, sv, msg, ...) (scrb_flogln)(get_meta_info((st)->name), (st), (fmt), (sv), (msg), ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif
struct scrb_format * scrb_create_format("[%F:%L:%M] %m", NULL);
compile? It looks like a function declaration until you scan the arguments. Maybe you're missing the namefmt
:struct scrb_format *fmt = scrb_create_format("[%F:%L:%M] %m", NULL);
? Not including thescribe.h
header in the question is probably not a good move; I don't particularly want to have to go to some other site to see the code to review. – Jonathan Leffler Dec 17 '14 at 21:07scribe.h
header file to the post. – Dalton Dec 17 '14 at 21:15