Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(logger): Implement a custom logger with configurable log levels #3

Merged
merged 1 commit into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# This will strip `Log.v`, `Log.d`, and `Log.i` statements and will leave `Log.w` and `Log.e` statements intact.
#-assumenosideeffects class android.util.Log {
# public static boolean isLoggable(java.lang.String, int);
# public static int v(...);
# public static int d(...);
# public static int i(...);
#}

# JavascriptInterface
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keepclassmembers class * {
Expand Down
21 changes: 14 additions & 7 deletions app/src/main/java/com/ding1ding/jsbridge/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.ding1ding.jsbridge.app

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.webkit.WebView
Expand All @@ -11,6 +10,7 @@ import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import com.ding1ding.jsbridge.Callback
import com.ding1ding.jsbridge.ConsolePipe
import com.ding1ding.jsbridge.Logger
import com.ding1ding.jsbridge.MessageHandler
import com.ding1ding.jsbridge.WebViewJavascriptBridge
import com.ding1ding.jsbridge.model.Person
Expand All @@ -27,6 +27,13 @@ class MainActivity :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

if (BuildConfig.DEBUG) {
WebViewJavascriptBridge.setLogLevel(Logger.LogLevel.DEBUG)
} else {
WebViewJavascriptBridge.setLogLevel(Logger.LogLevel.ERROR)
}

setupWebView()
setupClickListeners()
}
Expand Down Expand Up @@ -56,7 +63,7 @@ class MainActivity :

override fun onDestroy() {
releaseWebView()
Log.d(TAG, "onDestroy")
Logger.d(TAG) { "onDestroy" }
super.onDestroy()
}

Expand Down Expand Up @@ -93,7 +100,7 @@ class MainActivity :
bridge = WebViewJavascriptBridge.create(this, webView, lifecycle).apply {
consolePipe = object : ConsolePipe {
override fun post(message: String) {
Log.d("[console.log]", message)
Logger.d("[console.log]") { message }
}
}

Expand All @@ -110,17 +117,17 @@ class MainActivity :

private fun createWebViewClient() = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: android.graphics.Bitmap?) {
Log.d(TAG, "onPageStarted")
Logger.d(TAG) { "onPageStarted" }
}

override fun onPageFinished(view: WebView?, url: String?) {
Log.d(TAG, "onPageFinished")
Logger.d(TAG) { "onPageFinished" }
}
}

private fun createDeviceLoadHandler() = object : MessageHandler<Map<String, String>, Any> {
override fun handle(parameter: Map<String, String>): Any {
Log.d(TAG, "DeviceLoadJavascriptSuccess, $parameter")
Logger.d(TAG) { "DeviceLoadJavascriptSuccess, $parameter" }
return mapOf("result" to "Android")
}
}
Expand Down Expand Up @@ -151,7 +158,7 @@ class MainActivity :
Person("Wukong", 23),
object : Callback<Any> {
override fun onResult(result: Any) {
Log.d(TAG, "$handlerName, $result")
Logger.d(TAG) { "$handlerName, $result" }
}
},
)
Expand Down
8 changes: 0 additions & 8 deletions library/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -1,10 +1,2 @@
# This will strip `Log.v`, `Log.d`, and `Log.i` statements and will leave `Log.w` and `Log.e` statements intact.
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int d(...);
public static int i(...);
}

-keep class java.lang.reflect.** { *; }
-keep class kotlin.reflect.** { *; }
5 changes: 2 additions & 3 deletions library/src/main/java/com/ding1ding/jsbridge/JsonUtils.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.ding1ding.jsbridge

import android.util.Log
import java.math.BigInteger
import java.text.SimpleDateFormat
import java.util.Date
Expand Down Expand Up @@ -31,7 +30,7 @@ object JsonUtils {
field.name to field.get(any)
}.toJsonObject().toString()
} catch (e: Exception) {
Log.e("[JsBridge]", "Failed to serialize object of type ${any::class.java.simpleName}", e)
Logger.e(e) { "Failed to serialize object of type ${any::class.java.simpleName}" }
JSONObject.quote(any.toString())
}
}
Expand Down Expand Up @@ -68,7 +67,7 @@ object JsonUtils {
else -> parseNumber(json)
}
} catch (e: Exception) {
Log.e("[JsBridge]", "Error parsing JSON: $json", e)
Logger.e(e) { "Error parsing JSON: $json" }
json // Return the original string if parsing fails
}

Expand Down
77 changes: 77 additions & 0 deletions library/src/main/java/com/ding1ding/jsbridge/Logger.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.ding1ding.jsbridge

import android.util.Log
import kotlin.math.min

object Logger {
private const val MAX_LOG_LENGTH = 4000
const val DEFAULT_TAG = "WebViewJsBridge"

@Volatile
var logLevel = LogLevel.INFO

enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }

@JvmStatic
inline fun v(tag: String = DEFAULT_TAG, message: () -> String) =
log(LogLevel.VERBOSE, tag, message)

@JvmStatic
inline fun d(tag: String = DEFAULT_TAG, message: () -> String) = log(LogLevel.DEBUG, tag, message)

@JvmStatic
inline fun i(tag: String = DEFAULT_TAG, message: () -> String) = log(LogLevel.INFO, tag, message)

@JvmStatic
inline fun w(tag: String = DEFAULT_TAG, message: () -> String) = log(LogLevel.WARN, tag, message)

@JvmStatic
inline fun e(tag: String = DEFAULT_TAG, message: () -> String) = log(LogLevel.ERROR, tag, message)

@JvmStatic
inline fun e(throwable: Throwable, tag: String = DEFAULT_TAG, message: () -> String = { "" }) {
if (logLevel <= LogLevel.ERROR) {
val fullMessage = buildString {
append(message())
if (isNotEmpty() && message().isNotEmpty()) append(": ")
append(Log.getStackTraceString(throwable))
}
logInternal(Log.ERROR, tag, fullMessage)
}
}

@JvmStatic
inline fun log(level: LogLevel, tag: String = DEFAULT_TAG, message: () -> String) {
if (logLevel <= level) logInternal(level.toAndroidLogLevel(), tag, message())
}

fun logInternal(priority: Int, tag: String, message: String) {
if (message.length < MAX_LOG_LENGTH) {
Log.println(priority, tag, message)
return
}

var i = 0
val length = message.length
while (i < length) {
var newline = message.indexOf('\n', i)
newline = if (newline != -1) newline else length
do {
val end = min(newline, i + MAX_LOG_LENGTH)
val part = message.substring(i, end)
Log.println(priority, tag, part)
i = end
} while (i < newline)
i++
}
}

fun LogLevel.toAndroidLogLevel() = when (this) {
LogLevel.VERBOSE -> Log.VERBOSE
LogLevel.DEBUG -> Log.DEBUG
LogLevel.INFO -> Log.INFO
LogLevel.WARN -> Log.WARN
LogLevel.ERROR -> Log.ERROR
LogLevel.NONE -> Log.ASSERT
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.ding1ding.jsbridge

interface MessageHandler<in InputType, out OutputType> {
fun handle(parameter: InputType): OutputType
interface MessageHandler<in Input, out Output> {
fun handle(parameter: Input): Output
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.ding1ding.jsbridge

import android.util.Log
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import org.json.JSONObject
Expand Down Expand Up @@ -95,7 +94,7 @@ object MessageSerializer {
)
}
} catch (e: Exception) {
Log.e("[JsBridge]", "Error deserializing message: ${e.message}")
Logger.e(e) { "Error deserializing message: ${e.message}" }
ResponseMessage(null, null, null, null, null)
}

Expand Down Expand Up @@ -131,7 +130,7 @@ object MessageSerializer {
constructor.isAccessible = true
constructor.newInstance(*data.values.toTypedArray())
} catch (e: Exception) {
Log.e("[JsBridge]", "Error creating instance of ${clazz.simpleName}: ${e.message}")
Logger.e(e) { "Error creating instance of ${clazz.simpleName}: ${e.message}" }
data
}
}
Expand Down
Loading
Loading