From e2009a3e33a58bab37f433845f6d105cb7821da1 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sun, 31 Dec 2017 15:07:05 -0500 Subject: Enhancement/logger (#120) * Rewrite logger to use lazy loading * Clean up and add to changelog --- .../kotlin/ca/allanwang/kau/email/EmailBuilder.kt | 2 +- .../kotlin/ca/allanwang/kau/kotlin/Debouncer.kt | 8 +- .../main/kotlin/ca/allanwang/kau/kpref/KPref.kt | 2 +- .../kotlin/ca/allanwang/kau/logging/KauLogger.kt | 127 ++++++++++----------- .../allanwang/kau/permissions/PermissionManager.kt | 12 +- .../ca/allanwang/kau/swipe/SwipeBackHelper.kt | 4 +- .../ca/allanwang/kau/swipe/SwipeBackLayout.kt | 10 +- .../kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt | 2 +- .../kotlin/ca/allanwang/kau/utils/ContextUtils.kt | 4 +- 9 files changed, 86 insertions(+), 85 deletions(-) (limited to 'core/src/main') diff --git a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt index 8c6acff..94a79ab 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt @@ -60,7 +60,7 @@ class EmailBuilder(val email: String, val subject: String) { .append("\nApp Version Name: ").append(appInfo.versionName) .append("\nApp Version Code: ").append(appInfo.versionCode).append("\n") } catch (e: PackageManager.NameNotFoundException) { - KL.e("EmailBuilder packageInfo not found") + KL.e{"EmailBuilder packageInfo not found"} } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt index 4fba2c8..4c27e19 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt @@ -30,7 +30,7 @@ open class Debouncer(var interval: Long) { synchronized(this) { task?.invalidate() val newTask = DebounceTask(callback) - KL.v("Debouncer task created: $newTask in $this") + KL.v { "Debouncer task created: $newTask in $this" } sched.schedule(newTask, interval, TimeUnit.MILLISECONDS) task = newTask } @@ -47,7 +47,7 @@ open class Debouncer(var interval: Long) { */ fun cancel() { synchronized(this) { - if (task != null) KL.v("Debouncer cancelled for $task in $this") + if (task != null) KL.v { "Debouncer cancelled for $task in $this" } task?.invalidate() task = null } @@ -74,11 +74,11 @@ private class DebounceTask(inline val callback: () -> Unit) : Runnable { override fun run() { if (!valid) return valid = false - KL.v("Debouncer task executed $this") + KL.v { "Debouncer task executed $this" } try { callback() } catch (e: Exception) { - KL.e(e, "DebouncerTask exception") + KL.e(e) { "DebouncerTask exception" } } } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt index fbb8c7d..12f9606 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt @@ -29,7 +29,7 @@ open class KPref { fun initialize(c: Context, preferenceName: String) { PREFERENCE_NAME = preferenceName sp = c.applicationContext.getSharedPreferences(preferenceName, Context.MODE_PRIVATE) - KL.d("Shared Preference $preferenceName has been initialized") + KL.d { "Shared Preference $preferenceName has been initialized" } val toDelete = deleteKeys() if (toDelete.isNotEmpty()) { val edit = sp.edit() diff --git a/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt b/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt index 61f0708..da1c05b 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt @@ -5,13 +5,22 @@ package ca.allanwang.kau.logging import android.os.Looper import android.util.Log - /** * Created by Allan Wang on 2017-05-28. * * Base logger class with a predefined tag * This may be extended by an object to effectively replace [Log] - * Almost everything is opened to make everything customizable + * Only direct lazy logging is supported, as for best results, + * applications should extend this and use const/final flags to decide whether logging occurs + * That way, it will be stripped away by proguard + * + * Generally speaking, verbose log may contain private information, + * as it should be stripped away from production build + * + * Debug and info logs may contain sensitive info, and may be differentiated by creating a method such as + * inline fun _d(message: () -> Any?) { + * if (BuildConfig.DEBUG) d(message) + * } */ open class KauLogger(val tag: String) { @@ -20,83 +29,71 @@ open class KauLogger(val tag: String) { */ open var enabled = true - /** - * Global toggle to show private text - */ - open var showPrivateText = false + inline fun v(message: () -> Any?) = log(Log.VERBOSE, message) - /** - * If both msg and priv msg are accepted, output the combined output - */ - open var messageJoiner: (msg: String, privMsg: String) -> String = { msg, privMsg -> "$msg: $privMsg" } + inline fun i(message: () -> Any?) = log(Log.INFO, message) - /** - * Open hook to change the output of the logger (for instance, output to stdout rather than Android log files - * Does not use reference notation to avoid constructor leaks - */ - open var logFun: (priority: Int, message: String?, privateMessage: String?, t: Throwable?) -> Unit = { p, m, pm, t -> - logImpl(p, m, pm, t) - } + inline fun d(message: () -> Any?) = log(Log.DEBUG, message) - /** - * Filter pass-through to decide what we wish to log - * By default, we will ignore verbose and debug logs - * @returns {@code true} to log the message, {@code false} to ignore - */ - open var filter: (Int) -> Boolean = { it != Log.VERBOSE && it != Log.DEBUG } + inline fun e(t: Throwable? = null, message: () -> Any?) = log(Log.ERROR, message, t) - open fun disable(disable: Boolean = true): KauLogger { - enabled = !disable - return this + inline fun eThrow(message: Any) = with(message.toString()) { + log(Log.ERROR, { this }, Throwable(this)) } - open fun debug(enable: Boolean) { - filter = if (enable) { _ -> true } else { i -> i != Log.VERBOSE && i != Log.DEBUG } - showPrivateText = enable + inline fun log(priority: Int, message: () -> Any?, t: Throwable? = null) { + if (enabled) + logImpl(priority, message()?.toString() ?: "null", t) } - private fun log(priority: Int, message: String?, privateMessage: String?, t: Throwable? = null) { - if (!shouldLog(priority, message, privateMessage, t)) return - logImpl(priority, message, privateMessage, t) + open fun logImpl(priority: Int, message: String, t: Throwable?) { + if (t != null) + Log.e(tag, message, t) + else + Log.println(priority, tag, message) } /** - * Condition to pass to allow the input to be logged - */ - protected open fun shouldLog(priority: Int, message: String?, privateMessage: String?, t: Throwable?): Boolean - = enabled && filter(priority) - - /** - * Base implementation of the Android logger + * Log the looper */ - protected open fun logImpl(priority: Int, message: String?, privateMessage: String?, t: Throwable?) { - val text = if (showPrivateText) { - if (message == null) privateMessage - else if (privateMessage == null) message - else messageJoiner(message, privateMessage) - } else message - - if (t != null) Log.e(tag, text ?: "Error", t) - else if (!text.isNullOrBlank()) Log.println(priority, tag, text) + inline fun checkThread(id: Int) { + d { + val name = Thread.currentThread().name + val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" + "$id $status in the main thread - thread name: $name" + } } - open fun v(text: String?, privateText: String? = null) = log(Log.VERBOSE, text, privateText) - open fun d(text: String?, privateText: String? = null) = log(Log.DEBUG, text, privateText) - open fun i(text: String?, privateText: String? = null) = log(Log.INFO, text, privateText) - open fun e(text: String?, privateText: String? = null) = log(Log.ERROR, text, privateText) - open fun a(text: String?, privateText: String? = null) = log(Log.ASSERT, text, privateText) - open fun e(t: Throwable?, text: String?, privateText: String? = null) = log(Log.ERROR, text, privateText, t) - open fun eThrow(text: String?) { - if (text != null) - e(Throwable(text), text) + fun extend(tag: String) = KauLoggerExtension(tag, this) +} + +/** + * Tag extender for [KauLogger] + * Will prepend [tag] to any expected log output by [logger] + * Note that if the parent logger is disabled, the extension logger will not output anything either + */ +class KauLoggerExtension(val tag: String, val logger: KauLogger) { + + inline fun v(message: () -> Any?) = log(Log.VERBOSE, message) + + inline fun i(message: () -> Any?) = log(Log.INFO, message) + + inline fun d(message: () -> Any?) = log(Log.DEBUG, message) + + inline fun e(t: Throwable? = null, message: () -> Any?) = log(Log.ERROR, message, t) + + inline fun eThrow(message: Any) = with(message.toString()) { + log(Log.ERROR, { this }, Throwable(this)) } - /** - * Log the looper - */ - open fun checkThread(id: Int) { - val name = Thread.currentThread().name - val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" - d("$id $status in the main thread - thread name: $name") + inline fun log(priority: Int, message: () -> Any?, t: Throwable? = null) = + logger.log(priority, { "$tag: ${message()?.toString() ?: "null"}" }, t) + + inline fun checkThread(id: Int) { + d { + val name = Thread.currentThread().name + val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" + "$id $status in the main thread - thread name: $name" + } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt index 18f3e41..8b639ad 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt @@ -33,7 +33,7 @@ internal object PermissionManager { } operator fun invoke(context: Context, permissions: Array, callback: (granted: Boolean, deniedPerm: String?) -> Unit) { - KL.d("Permission manager for: ${permissions.contentToString()}") + KL.d { "Permission manager for: ${permissions.contentToString()}" } if (!buildIsMarshmallowAndUp) return callback(true, null) val missingPermissions = permissions.filter { !context.hasPermission(it) } if (missingPermissions.isEmpty()) return callback(true, null) @@ -41,24 +41,24 @@ internal object PermissionManager { if (!requestInProgress) { requestInProgress = true requestPermissions(context, missingPermissions.toTypedArray()) - } else KL.d("Request is postponed since another one is still in progress; did you remember to override onRequestPermissionsResult?") + } else KL.d { "Request is postponed since another one is still in progress; did you remember to override onRequestPermissionsResult?" } } @Synchronized internal fun requestPermissions(context: Context, permissions: Array) { permissions.forEach { if (!manifestPermission(context).contains(it)) { - KL.e("Requested permission $it is not stated in the manifest") + KL.e { "Requested permission $it is not stated in the manifest" } context.toast("$it is not in the manifest") //we'll let the request pass through so it can be denied and so the callback can be triggered } } val activity = (context as? Activity) ?: throw KauException("Context is not an instance of an activity; cannot request permissions") - KL.i("Requesting permissions ${permissions.contentToString()}") + KL.i { "Requesting permissions ${permissions.contentToString()}" } ActivityCompat.requestPermissions(activity, permissions, 1) } fun onRequestPermissionsResult(context: Context, permissions: Array, grantResults: IntArray) { - KL.i("On permission result: pending ${pendingResults.size}") + KL.i { "On permission result: pending ${pendingResults.size}" } val count = Math.min(permissions.size, grantResults.size) val iter = pendingResults.iterator() while (iter.hasNext()) { @@ -77,7 +77,7 @@ internal object PermissionManager { } requestPermissions(context, action.permissions.toTypedArray()) } - KL.i("Post on permission result: pending ${pendingResults.size}") + KL.i { "Post on permission result: pending ${pendingResults.size}" } } } \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt index bc909be..4bc216d 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt @@ -29,14 +29,14 @@ internal object SwipeBackHelper { } activity.overridePendingTransition(startAnimation, 0) page.onPostCreate() - KL.v("KauSwipe onCreate ${activity.localClassName}") + KL.v { "KauSwipe onCreate ${activity.localClassName}" } } fun onDestroy(activity: Activity) { val page: SwipeBackPage? = this[activity] pageStack.kauRemoveIf { it.activityRef.get() == null || it === page } page?.activityRef?.clear() - KL.v("KauSwipe onDestroy ${activity.localClassName}") + KL.v { "KauSwipe onDestroy ${activity.localClassName}" } } fun finish(activity: Activity) = this[activity]?.scrollToFinishActivity() diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt index a8e8ec3..ae09c8a 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt @@ -205,7 +205,8 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs * Scroll out contentView and finish the activity */ override fun scrollToFinishActivity() { - val contentView = contentViewRef.get() ?: return KL.e("KauSwipe cannot scroll to finish as contentView is null. Is onPostCreate called?") + val contentView = contentViewRef.get() + ?: return KL.e { "KauSwipe cannot scroll to finish as contentView is null. Is onPostCreate called?" } val childWidth = contentView.width val top = 0 val left = childWidth + OVERSCROLL_DISTANCE @@ -222,6 +223,7 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs } } + @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent): Boolean { if (!swipeEnabled) return false try { @@ -233,7 +235,8 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - val contentView = contentViewRef.get() ?: return KL.e("KauSwipe cannot change layout as contentView is null. Is onPostCreate called?") + val contentView = contentViewRef.get() + ?: return KL.e { "KauSwipe cannot change layout as contentView is null. Is onPostCreate called?" } inLayout = true val xOffset: Int val yOffset: Int @@ -335,7 +338,8 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) { super.onViewPositionChanged(changedView, left, top, dx, dy) - val contentView = contentViewRef.get() ?: return KL.e("KauSwipe cannot change view position as contentView is null; is onPostCreate called?") + val contentView = contentViewRef.get() + ?: return KL.e { "KauSwipe cannot change view position as contentView is null; is onPostCreate called?" } //make sure that we are using the proper axis scrollPercent = Math.abs( if (horizontal) left.toFloat() / contentView.width diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt index 3132f8c..7b6654d 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt @@ -40,7 +40,7 @@ internal class SwipeBackPage(activity: Activity) : SwipeBackContractInternal by } private fun handleLayout() { - val activity = activityRef.get() ?: return KL.v("KauSwipe activity ref gone during handleLayout") + val activity = activityRef.get() ?: return KL.v { "KauSwipe activity ref gone during handleLayout" } if (swipeEnabled) swipeBackLayout.attachToActivity(activity) else swipeBackLayout.removeFromActivity(activity) } diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt index 06b9601..1b8d58c 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -80,7 +80,7 @@ inline fun View.toast(text: String, duration: Int = Toast.LENGTH_LONG, log: Bool inline fun Context.toast(text: String, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) { Toast.makeText(this, text, duration).show() - if (log) KL.i("Toast: $text") + if (log) KL.i { "Toast: $text" } } //Resource retrievers @@ -142,7 +142,7 @@ inline fun Context.materialDialog(action: MaterialDialog.Builder.() -> Unit): Ma val builder = MaterialDialog.Builder(this) builder.action() if (isFinishing) { - KL.d("Material Dialog triggered from finishing context; did not show") + KL.d { "Material Dialog triggered from finishing context; did not show" } return builder.build() } return builder.show() -- cgit v1.2.3