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:
parent
44a2a67417
commit
51afed2fe0
@ -16,63 +16,60 @@
|
|||||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#import <Availability.h>
|
#define MP_LIBS_BEGIN \
|
||||||
#import "Pearl-Prefix.pch"
|
_Pragma("clang diagnostic push") \
|
||||||
|
_Pragma("clang diagnostic ignored \"-Weverything\"")
|
||||||
|
#define MP_LIBS_END \
|
||||||
|
_Pragma("clang diagnostic pop")
|
||||||
|
|
||||||
|
MP_LIBS_BEGIN
|
||||||
|
#include <Availability.h>
|
||||||
|
#include <os/log.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
MP_LIBS_END
|
||||||
|
|
||||||
|
#define mpw_log_os(level, file, line, function, format, ...) \
|
||||||
|
do { \
|
||||||
|
if (mpw_verbosity < level) { \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
switch (level) { \
|
||||||
|
case LogLevelTrace: \
|
||||||
|
os_log_debug( OS_LOG_DEFAULT, "%30s:%-3ld TRC | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
||||||
|
break; \
|
||||||
|
case LogLevelDebug: \
|
||||||
|
os_log_debug( OS_LOG_DEFAULT, "%30s:%-3ld DBG | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
||||||
|
break; \
|
||||||
|
case LogLevelInfo: \
|
||||||
|
os_log_info( OS_LOG_DEFAULT, "%30s:%-3ld INF | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
||||||
|
break; \
|
||||||
|
case LogLevelWarning: \
|
||||||
|
os_log( OS_LOG_DEFAULT, "%30s:%-3ld WRN | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
||||||
|
break; \
|
||||||
|
case LogLevelError: \
|
||||||
|
os_log_error( OS_LOG_DEFAULT, "%30s:%-3ld ERR | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
||||||
|
break; \
|
||||||
|
case LogLevelFatal: \
|
||||||
|
os_log_fault( OS_LOG_DEFAULT, "%30s:%-3ld FTL | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
mpw_log_sink( level, file, line, function, format, ##__VA_ARGS__ ); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define MPW_LOG mpw_log_os
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#ifdef __OBJC__
|
#ifdef __OBJC__
|
||||||
|
|
||||||
|
#import "Pearl-Prefix.pch"
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#elif TARGET_OS_OSX
|
#elif TARGET_OS_OSX
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#ifdef CRASHLYTICS
|
|
||||||
#import <Crashlytics/Crashlytics.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
|
||||||
#import "MPTypes.h"
|
|
||||||
#import "MPiOSConfig.h"
|
|
||||||
#elif TARGET_OS_OSX
|
|
||||||
#import "MPTypes.h"
|
|
||||||
#import "MPMacConfig.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#import <libgen.h>
|
|
||||||
#import <CoreFoundation/CFString.h>
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
#import <objc/message.h>
|
|
||||||
#import <objc/NSObjCRuntime.h>
|
|
||||||
#import <stdlib.h>
|
|
||||||
|
|
||||||
#define mpw_log(level, format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
if (_msg) { \
|
|
||||||
CFStringRef fileStr = CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ); \
|
|
||||||
CFStringRef funcStr = CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ); \
|
|
||||||
CFStringRef msgStr = CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ); \
|
|
||||||
id (*_getLogger)(id, SEL) = (void *)objc_msgSend; \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( _getLogger( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), fileStr, __LINE__, funcStr, level, msgStr ); \
|
|
||||||
if (fileStr) { CFRelease( fileStr ); } \
|
|
||||||
if (funcStr) { CFRelease( funcStr ); } \
|
|
||||||
if (msgStr) { CFRelease( msgStr ); } \
|
|
||||||
free(_msg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define trc(format, ...) mpw_log( 0, format, ##__VA_ARGS__ );
|
|
||||||
#define dbg(format, ...) mpw_log( 1, format, ##__VA_ARGS__ );
|
|
||||||
#define inf(format, ...) mpw_log( 2, format, ##__VA_ARGS__ );
|
|
||||||
#define wrn(format, ...) mpw_log( 3, format, ##__VA_ARGS__ );
|
|
||||||
#define err(format, ...) mpw_log( 4, format, ##__VA_ARGS__ );
|
|
||||||
#define ftl(format, ...) mpw_log( 5, format, ##__VA_ARGS__ );
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,6 +22,7 @@ MP_LIBS_BEGIN
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#if MPW_CPERCIVA
|
#if MPW_CPERCIVA
|
||||||
#include <scrypt/crypto_scrypt.h>
|
#include <scrypt/crypto_scrypt.h>
|
||||||
@ -34,54 +35,113 @@ MP_LIBS_BEGIN
|
|||||||
#include "aes.h"
|
#include "aes.h"
|
||||||
MP_LIBS_END
|
MP_LIBS_END
|
||||||
|
|
||||||
int mpw_verbosity = LogLevelInfo;
|
LogLevel mpw_verbosity = LogLevelInfo;
|
||||||
FILE *mpw_log_cli_file;
|
FILE *mpw_log_sink_file_target = NULL;
|
||||||
|
|
||||||
void mpw_log_cli(LogLevel level, const char *format, ...) {
|
static MPLogSink **sinks;
|
||||||
va_list args;
|
static size_t sinks_count;
|
||||||
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;
|
|
||||||
|
|
||||||
if (mpw_verbosity >= level) {
|
bool mpw_log_sink_register(MPLogSink *sink) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vfprintf( mpw_log_cli_file, format, args );
|
if (!mpw_realloc( &sinks, NULL, sizeof( MPLogSink * ) * ++sinks_count )) {
|
||||||
fprintf( mpw_log_cli_file, "\n" );
|
--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();
|
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]) {
|
void mpw_uint16(const uint16_t number, uint8_t buf[2]) {
|
||||||
|
|
||||||
buf[0] = (uint8_t)((number >> 8L) & UINT8_MAX);
|
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;
|
int cmp = 0;
|
||||||
for (; !cmp && max-- > 0 && s1 && s2; ++s1, ++s2)
|
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;
|
return cmp;
|
||||||
}
|
}
|
||||||
|
@ -27,42 +27,57 @@ MP_LIBS_BEGIN
|
|||||||
MP_LIBS_END
|
MP_LIBS_END
|
||||||
|
|
||||||
//// Logging.
|
//// Logging.
|
||||||
typedef mpw_enum(int, LogLevel) {
|
typedef mpw_enum( int, LogLevel ) {
|
||||||
/** Logging internal state. */
|
/** Logging internal state. */
|
||||||
LogLevelTrace = 3,
|
LogLevelTrace = 3,
|
||||||
/** Logging state and events interesting when investigating issues. */
|
/** Logging state and events interesting when investigating issues. */
|
||||||
LogLevelDebug = 2,
|
LogLevelDebug = 2,
|
||||||
/** User messages. */
|
/** User messages. */
|
||||||
LogLevelInfo = 1,
|
LogLevelInfo = 1,
|
||||||
/** Recoverable issues and user suggestions. */
|
/** Recoverable issues and user suggestions. */
|
||||||
LogLevelWarning = 0,
|
LogLevelWarning = 0,
|
||||||
/** Unrecoverable issues. */
|
/** Unrecoverable issues. */
|
||||||
LogLevelError = -1,
|
LogLevelError = -1,
|
||||||
/** Issues that lead to abortion. */
|
/** Issues that lead to abortion. */
|
||||||
LogLevelFatal = -2,
|
LogLevelFatal = -2,
|
||||||
};
|
};
|
||||||
extern LogLevel mpw_verbosity;
|
extern LogLevel mpw_verbosity;
|
||||||
|
|
||||||
/** mpw_log_cli is a sink that writes log messages to the mpw_log_cli_file, which defaults to stderr. */
|
typedef struct {
|
||||||
extern FILE *mpw_log_cli_file;
|
time_t occurrence;
|
||||||
void mpw_log_cli(LogLevel level, const char *format, ...);
|
LogLevel level;
|
||||||
void mpw_vlog_cli(LogLevel level, const char *format, va_list args);
|
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. */
|
/** A log sink describes a function that can receive log events when registered. */
|
||||||
void mpw_log_app(LogLevel level, const char *format, ...);
|
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
|
#ifndef MPW_LOG
|
||||||
#define MPW_LOG mpw_log_cli
|
#define MPW_LOG mpw_log_sink
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef trc
|
#ifndef trc
|
||||||
#define trc(format, ...) MPW_LOG( LogLevelTrace, format, ##__VA_ARGS__ )
|
#define trc(format, ...) MPW_LOG( LogLevelTrace, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
|
||||||
#define dbg(format, ...) MPW_LOG( LogLevelDebug, format, ##__VA_ARGS__ )
|
#define dbg(format, ...) MPW_LOG( LogLevelDebug, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
|
||||||
#define inf(format, ...) MPW_LOG( LogLevelInfo, format, ##__VA_ARGS__ )
|
#define inf(format, ...) MPW_LOG( LogLevelInfo, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
|
||||||
#define wrn(format, ...) MPW_LOG( LogLevelWarning, format, ##__VA_ARGS__ )
|
#define wrn(format, ...) MPW_LOG( LogLevelWarning, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
|
||||||
#define err(format, ...) MPW_LOG( LogLevelError, format, ##__VA_ARGS__ )
|
#define err(format, ...) MPW_LOG( LogLevelError, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
|
||||||
#define ftl(format, ...) MPW_LOG( LogLevelFatal, format, ##__VA_ARGS__ )
|
#define ftl(format, ...) MPW_LOG( LogLevelFatal, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef min
|
#ifndef min
|
||||||
|
Loading…
Reference in New Issue
Block a user