diff --git a/platform-independent/c/cli/src/mpw-cli.c b/platform-independent/c/cli/src/mpw-cli.c index 4f1a946e..34daca53 100644 --- a/platform-independent/c/cli/src/mpw-cli.c +++ b/platform-independent/c/cli/src/mpw-cli.c @@ -714,7 +714,7 @@ void cli_mpw(Arguments *args, Operation *operation) { if (!operation->site) abort(); - if (mpw_verbosity >= inf_level) + if (mpw_verbosity >= LogLevelInfo) fprintf( stderr, "%s's %s for %s:\n[ %s ]: ", operation->file->user->fullName, operation->purposeResult, operation->site->siteName, operation->identicon ); diff --git a/platform-independent/c/core/build.gradle b/platform-independent/c/core/build.gradle index 00c72dfb..99dc6ed5 100644 --- a/platform-independent/c/core/build.gradle +++ b/platform-independent/c/core/build.gradle @@ -31,12 +31,12 @@ library { toolChains { withType( VisualCpp ) { eachPlatform { - cppCompiler.withArguments { addAll( ['/TC', '/MT', '/Ox', '/DMPW_SODIUM=1', '/DSODIUM_STATIC', '/DSODIUM_EXPORT='] ) } + cppCompiler.withArguments { addAll( ['/TC', '/MT', '/Ox', '/DMPW_SODIUM=1', '/DSODIUM_STATIC', '/DSODIUM_EXPORT=', '/DMPW_LOG=mpw_log_app'] ) } } } withType( GccCompatibleToolChain ) { eachPlatform { - cppCompiler.withArguments { addAll( ['-x', 'c', '-O3', '-Werror', '-DMPW_SODIUM=1'] ) } + cppCompiler.withArguments { addAll( ['-x', 'c', '-O3', '-Werror', '-DMPW_SODIUM=1', '-DMPW_LOG=mpw_log_app'] ) } } } } diff --git a/platform-independent/c/core/src/mpw-jni.c b/platform-independent/c/core/src/mpw-jni.c index 05774d44..be812f20 100644 --- a/platform-independent/c/core/src/mpw-jni.c +++ b/platform-independent/c/core/src/mpw-jni.c @@ -7,11 +7,67 @@ // TODO: We may need to zero the jbytes safely. +static jobject logger; +static JNIEnv *env; + +void mpw_log_app(LogLevel level, const char *format, ...) { + jmethodID method = NULL; + jclass class = (*env)->GetObjectClass( env, logger ); + if (level >= LogLevelTrace) + method = (*env)->GetMethodID( env, class, "trace", "(Ljava/lang/String;)V" ); + else if (level == LogLevelDebug) + method = (*env)->GetMethodID( env, class, "debug", "(Ljava/lang/String;)V" ); + else if (level == LogLevelInfo) + method = (*env)->GetMethodID( env, class, "info", "(Ljava/lang/String;)V" ); + else if (level == LogLevelWarning) + method = (*env)->GetMethodID( env, class, "warn", "(Ljava/lang/String;)V" ); + else if (level <= LogLevelError) + method = (*env)->GetMethodID( env, class, "error", "(Ljava/lang/String;)V" ); + + va_list args; + va_start( args, format ); + + if (class && method && logger) { + char *message = NULL; + int length = vasprintf(&message, format, args); + if (length > 0) + (*env)->CallVoidMethod(env, logger, method, (*env)->NewStringUTF( env, message )); + if (message) + mpw_free(&message, (size_t) length); + } + else + // Can't log via slf4j, fall back to cli logger. + mpw_vlog_cli( level, format, args ); + + va_end( args ); +} + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { - JNIEnv* env; if ((*vm)->GetEnv( vm, (void **)&env, JNI_VERSION_1_6 ) != JNI_OK) return -1; + jclass class = (*env)->FindClass( env, "org/slf4j/LoggerFactory" ); + jmethodID method = (*env)->GetStaticMethodID( env, class, "getLogger", "(Ljava/lang/String;)Lorg/slf4j/Logger;" ); + jstring name = (*env)->NewStringUTF( env, "com.lyndir.masterpassword.algorithm" ); + if (class && method && name) + logger = (*env)->CallStaticObjectMethod( env, class, method, name ); + else + wrn( "Couldn't initialize JNI logger." ); + + class = (*env)->GetObjectClass( env, logger ); + if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, class, "isTraceEnabled", "()Z" ) )) + mpw_verbosity = LogLevelTrace; + else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, class, "isDebugEnabled", "()Z" ) )) + mpw_verbosity = LogLevelDebug; + else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, class, "isInfoEnabled", "()Z" ) )) + mpw_verbosity = LogLevelInfo; + else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, class, "isWarnEnabled", "()Z" ) )) + mpw_verbosity = LogLevelWarning; + else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, class, "isErrorEnabled", "()Z" ) )) + mpw_verbosity = LogLevelError; + else + mpw_verbosity = LogLevelFatal; + return JNI_VERSION_1_6; } diff --git a/platform-independent/c/core/src/mpw-util.c b/platform-independent/c/core/src/mpw-util.c index f653838b..e63558e6 100644 --- a/platform-independent/c/core/src/mpw-util.c +++ b/platform-independent/c/core/src/mpw-util.c @@ -34,9 +34,53 @@ MP_LIBS_BEGIN #include "aes.h" MP_LIBS_END -#ifdef inf_level -int mpw_verbosity = inf_level; -#endif +int mpw_verbosity = LogLevelInfo; +FILE *mpw_log_cli_file; + +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; + + 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; + } + } + + vfprintf( mpw_log_cli_file, format, args ); + fprintf( mpw_log_cli_file, "\n" ); + } + + if (level <= LogLevelFatal) + abort(); +} void mpw_uint16(const uint16_t number, uint8_t buf[2]) { diff --git a/platform-independent/c/core/src/mpw-util.h b/platform-independent/c/core/src/mpw-util.h index ad3e0896..8d0e0487 100644 --- a/platform-independent/c/core/src/mpw-util.h +++ b/platform-independent/c/core/src/mpw-util.h @@ -27,48 +27,42 @@ MP_LIBS_BEGIN MP_LIBS_END //// Logging. +typedef mpw_enum(int, LogLevel) { + /** Logging internal state. */ + LogLevelTrace = 3, + /** Logging state and events interesting when investigating issues. */ + LogLevelDebug = 2, + /** User messages. */ + LogLevelInfo = 1, + /** Recoverable issues and user suggestions. */ + LogLevelWarning = 0, + /** Unrecoverable issues. */ + LogLevelError = -1, + /** Issues that lead to abortion. */ + LogLevelFatal = -2, +}; +LogLevel mpw_verbosity; + +/** mpw_log_cli is a sink that writes log messages to the mpw_log_cli_file, which defaults to stderr. */ +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); + +/** 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, ...); + +/** The sink you want to channel the log messages into, defaults to mpw_log_cli. */ +#ifndef MPW_LOG +#define MPW_LOG mpw_log_cli +#endif + #ifndef trc -extern int mpw_verbosity; - -#ifndef mpw_log_do -#define mpw_log_do(level, format, ...) \ - fprintf( stderr, format "\n", ##__VA_ARGS__ ) -#endif - -#ifndef mpw_log -#define mpw_log(level, format, ...) do { \ - if (mpw_verbosity >= level) { \ - mpw_log_do( level, format, ##__VA_ARGS__ ); \ - } \ - if (level <= -2) { \ - abort(); \ - } \ - } while (0) -#endif - -/** Logging internal state. */ -#define trc_level 3 -#define trc(format, ...) mpw_log( trc_level, format, ##__VA_ARGS__ ) - -/** Logging state and events interesting when investigating issues. */ -#define dbg_level 2 -#define dbg(format, ...) mpw_log( dbg_level, format, ##__VA_ARGS__ ) - -/** User messages. */ -#define inf_level 1 -#define inf(format, ...) mpw_log( inf_level, format, ##__VA_ARGS__ ) - -/** Recoverable issues and user suggestions. */ -#define wrn_level 0 -#define wrn(format, ...) mpw_log( wrn_level, format, ##__VA_ARGS__ ) - -/** Unrecoverable issues. */ -#define err_level -1 -#define err(format, ...) mpw_log( err_level, format, ##__VA_ARGS__ ) - -/** Issues that lead to abortion. */ -#define ftl_level -2 -#define ftl(format, ...) mpw_log( ftl_level, format, ##__VA_ARGS__ ) +#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__ ) #endif #ifndef min