More advanced mpw-internal logging mechanism.
Logging now happens at the mpw-core level, by default using sinks that can be registered. For iOS we forward log messages to os_log for unified logging. We also keep a record of log messages for future retrieval in a log view. This obsoletes and removes Pearl's logger entirely.
This commit is contained in:
		@@ -22,6 +22,7 @@ MP_LIBS_BEGIN
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
 | 
			
		||||
#if MPW_CPERCIVA
 | 
			
		||||
#include <scrypt/crypto_scrypt.h>
 | 
			
		||||
@@ -34,54 +35,113 @@ MP_LIBS_BEGIN
 | 
			
		||||
#include "aes.h"
 | 
			
		||||
MP_LIBS_END
 | 
			
		||||
 | 
			
		||||
int mpw_verbosity = LogLevelInfo;
 | 
			
		||||
FILE *mpw_log_cli_file;
 | 
			
		||||
LogLevel mpw_verbosity = LogLevelInfo;
 | 
			
		||||
FILE *mpw_log_sink_file_target = NULL;
 | 
			
		||||
 | 
			
		||||
void mpw_log_cli(LogLevel level, const char *format, ...) {
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start( args, format );
 | 
			
		||||
    mpw_vlog_cli( level, format, args );
 | 
			
		||||
    va_end( args );
 | 
			
		||||
}
 | 
			
		||||
void mpw_vlog_cli(LogLevel level, const char *format, va_list args) {
 | 
			
		||||
    if (!mpw_log_cli_file)
 | 
			
		||||
        mpw_log_cli_file = stderr;
 | 
			
		||||
static MPLogSink **sinks;
 | 
			
		||||
static size_t sinks_count;
 | 
			
		||||
 | 
			
		||||
    if (mpw_verbosity >= level) {
 | 
			
		||||
        if (mpw_verbosity >= LogLevelDebug) {
 | 
			
		||||
            switch (level) {
 | 
			
		||||
                case LogLevelTrace:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[TRC] " );
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogLevelDebug:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[DBG] " );
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogLevelInfo:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[INF] " );
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogLevelWarning:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[WRN] " );
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogLevelError:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[ERR] " );
 | 
			
		||||
                    break;
 | 
			
		||||
                case LogLevelFatal:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[FTL] " );
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    fprintf( mpw_log_cli_file, "[???] " );
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
bool mpw_log_sink_register(MPLogSink *sink) {
 | 
			
		||||
 | 
			
		||||
        vfprintf( mpw_log_cli_file, format, args );
 | 
			
		||||
        fprintf( mpw_log_cli_file, "\n" );
 | 
			
		||||
    if (!mpw_realloc( &sinks, NULL, sizeof( MPLogSink * ) * ++sinks_count )) {
 | 
			
		||||
        --sinks_count;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (level <= LogLevelFatal)
 | 
			
		||||
    sinks[sinks_count - 1] = sink;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mpw_log_sink_unregister(MPLogSink *sink) {
 | 
			
		||||
 | 
			
		||||
    for (unsigned int r = 0; r < sinks_count; ++r) {
 | 
			
		||||
        if (sinks[r] == sink) {
 | 
			
		||||
            sinks[r] = NULL;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_log_sink(LogLevel level, const char *file, int line, const char *function, const char *format, ...) {
 | 
			
		||||
 | 
			
		||||
    if (mpw_verbosity < level)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start( args, format );
 | 
			
		||||
    mpw_log_vsink( level, file, line, function, format, args );
 | 
			
		||||
    va_end( args );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_log_vsink(LogLevel level, const char *file, int line, const char *function, const char *format, va_list args) {
 | 
			
		||||
 | 
			
		||||
    if (mpw_verbosity < level)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    return mpw_log_ssink( level, file, line, function, mpw_vstr( format, args ) );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_log_ssink(LogLevel level, const char *file, int line, const char *function, const char *message) {
 | 
			
		||||
 | 
			
		||||
    if (mpw_verbosity < level)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    MPLogEvent record = (MPLogEvent){
 | 
			
		||||
            .occurrence = time( NULL ),
 | 
			
		||||
            .level = level,
 | 
			
		||||
            .file = file,
 | 
			
		||||
            .line = line,
 | 
			
		||||
            .function = function,
 | 
			
		||||
            .message = message,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (unsigned int s = 0; s < sinks_count; ++s) {
 | 
			
		||||
        MPLogSink *sink = sinks[s];
 | 
			
		||||
 | 
			
		||||
        if (sink)
 | 
			
		||||
            sink( &record );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (record.level <= LogLevelFatal)
 | 
			
		||||
        abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_log_sink_file(const MPLogEvent *record) {
 | 
			
		||||
 | 
			
		||||
    if (!mpw_log_sink_file_target)
 | 
			
		||||
        mpw_log_sink_file_target = stderr;
 | 
			
		||||
 | 
			
		||||
    if (mpw_verbosity >= LogLevelDebug) {
 | 
			
		||||
        switch (record->level) {
 | 
			
		||||
            case LogLevelTrace:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[TRC] " );
 | 
			
		||||
                break;
 | 
			
		||||
            case LogLevelDebug:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[DBG] " );
 | 
			
		||||
                break;
 | 
			
		||||
            case LogLevelInfo:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[INF] " );
 | 
			
		||||
                break;
 | 
			
		||||
            case LogLevelWarning:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[WRN] " );
 | 
			
		||||
                break;
 | 
			
		||||
            case LogLevelError:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[ERR] " );
 | 
			
		||||
                break;
 | 
			
		||||
            case LogLevelFatal:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[FTL] " );
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                fprintf( mpw_log_sink_file_target, "[???] " );
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fprintf( mpw_log_sink_file_target, "%s\n", record->message );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_uint16(const uint16_t number, uint8_t buf[2]) {
 | 
			
		||||
 | 
			
		||||
    buf[0] = (uint8_t)((number >> 8L) & UINT8_MAX);
 | 
			
		||||
@@ -581,7 +641,7 @@ int mpw_strncasecmp(const char *s1, const char *s2, size_t max) {
 | 
			
		||||
 | 
			
		||||
    int cmp = 0;
 | 
			
		||||
    for (; !cmp && max-- > 0 && s1 && s2; ++s1, ++s2)
 | 
			
		||||
        cmp = tolower( *(unsigned char *)s1 ) - tolower( *(unsigned char *)s2 );
 | 
			
		||||
        cmp = tolower( (unsigned char)*s1 ) - tolower( (unsigned char)*s2 );
 | 
			
		||||
 | 
			
		||||
    return cmp;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,42 +27,57 @@ MP_LIBS_BEGIN
 | 
			
		||||
MP_LIBS_END
 | 
			
		||||
 | 
			
		||||
//// Logging.
 | 
			
		||||
typedef mpw_enum(int, LogLevel) {
 | 
			
		||||
typedef mpw_enum( int, LogLevel ) {
 | 
			
		||||
    /** Logging internal state. */
 | 
			
		||||
    LogLevelTrace = 3,
 | 
			
		||||
            LogLevelTrace = 3,
 | 
			
		||||
    /** Logging state and events interesting when investigating issues. */
 | 
			
		||||
    LogLevelDebug = 2,
 | 
			
		||||
            LogLevelDebug = 2,
 | 
			
		||||
    /** User messages. */
 | 
			
		||||
    LogLevelInfo = 1,
 | 
			
		||||
            LogLevelInfo = 1,
 | 
			
		||||
    /** Recoverable issues and user suggestions. */
 | 
			
		||||
    LogLevelWarning = 0,
 | 
			
		||||
            LogLevelWarning = 0,
 | 
			
		||||
    /** Unrecoverable issues. */
 | 
			
		||||
    LogLevelError = -1,
 | 
			
		||||
            LogLevelError = -1,
 | 
			
		||||
    /** Issues that lead to abortion. */
 | 
			
		||||
    LogLevelFatal = -2,
 | 
			
		||||
            LogLevelFatal = -2,
 | 
			
		||||
};
 | 
			
		||||
extern LogLevel mpw_verbosity;
 | 
			
		||||
 | 
			
		||||
/** mpw_log_cli is a sink that writes log messages to the mpw_log_cli_file, which defaults to stderr. */
 | 
			
		||||
extern FILE *mpw_log_cli_file;
 | 
			
		||||
void mpw_log_cli(LogLevel level, const char *format, ...);
 | 
			
		||||
void mpw_vlog_cli(LogLevel level, const char *format, va_list args);
 | 
			
		||||
typedef struct {
 | 
			
		||||
    time_t occurrence;
 | 
			
		||||
    LogLevel level;
 | 
			
		||||
    const char *file;
 | 
			
		||||
    int line;
 | 
			
		||||
    const char *function;
 | 
			
		||||
    const char *message;
 | 
			
		||||
} MPLogEvent;
 | 
			
		||||
 | 
			
		||||
/** mpw_log_app is a sink placeholder that an application can implement to consume log messages. */
 | 
			
		||||
void mpw_log_app(LogLevel level, const char *format, ...);
 | 
			
		||||
/** A log sink describes a function that can receive log events when registered. */
 | 
			
		||||
typedef void (MPLogSink)(const MPLogEvent *event);
 | 
			
		||||
bool mpw_log_sink_register(MPLogSink *sink);
 | 
			
		||||
bool mpw_log_sink_unregister(MPLogSink *sink);
 | 
			
		||||
 | 
			
		||||
/** The sink you want to channel the log messages into, defaults to mpw_log_cli. */
 | 
			
		||||
/** mpw_log_sink_file is a sink that writes log messages to the mpw_log_cli_file, which defaults to stderr. */
 | 
			
		||||
extern MPLogSink mpw_log_sink_file;
 | 
			
		||||
extern FILE *mpw_log_sink_file_target;
 | 
			
		||||
 | 
			
		||||
/** These functions dispatch log events to the registered sinks. */
 | 
			
		||||
void mpw_log_sink(LogLevel level, const char *file, int line, const char *function, const char *format, ...);
 | 
			
		||||
void mpw_log_vsink(LogLevel level, const char *file, int line, const char *function, const char *format, va_list args);
 | 
			
		||||
void mpw_log_ssink(LogLevel level, const char *file, int line, const char *function, const char *message);
 | 
			
		||||
 | 
			
		||||
/** The log dispatcher you want to channel log messages into; defaults to mpw_log_sink, enabling the log sink mechanism. */
 | 
			
		||||
#ifndef MPW_LOG
 | 
			
		||||
#define MPW_LOG mpw_log_cli
 | 
			
		||||
#define MPW_LOG mpw_log_sink
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef trc
 | 
			
		||||
#define trc(format, ...) MPW_LOG( LogLevelTrace, format, ##__VA_ARGS__ )
 | 
			
		||||
#define dbg(format, ...) MPW_LOG( LogLevelDebug, format, ##__VA_ARGS__ )
 | 
			
		||||
#define inf(format, ...) MPW_LOG( LogLevelInfo, format, ##__VA_ARGS__ )
 | 
			
		||||
#define wrn(format, ...) MPW_LOG( LogLevelWarning, format, ##__VA_ARGS__ )
 | 
			
		||||
#define err(format, ...) MPW_LOG( LogLevelError, format, ##__VA_ARGS__ )
 | 
			
		||||
#define ftl(format, ...) MPW_LOG( LogLevelFatal, format, ##__VA_ARGS__ )
 | 
			
		||||
#define trc(format, ...) MPW_LOG( LogLevelTrace, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
 | 
			
		||||
#define dbg(format, ...) MPW_LOG( LogLevelDebug, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
 | 
			
		||||
#define inf(format, ...) MPW_LOG( LogLevelInfo, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
 | 
			
		||||
#define wrn(format, ...) MPW_LOG( LogLevelWarning, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
 | 
			
		||||
#define err(format, ...) MPW_LOG( LogLevelError, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
 | 
			
		||||
#define ftl(format, ...) MPW_LOG( LogLevelFatal, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef min
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user