Skip to content

Commit

Permalink
feat: Resolve Debug to redirect to UI tool on web (#183)
Browse files Browse the repository at this point in the history
* feat: Resolve Debug to redirect to UI tool on web
  • Loading branch information
nickybondarenko authored Nov 19, 2024
1 parent 64c77ac commit ae05eef
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 15 deletions.
31 changes: 22 additions & 9 deletions Confidence/src/main/java/com/spotify/confidence/Confidence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit

Expand Down Expand Up @@ -106,14 +109,24 @@ class Confidence internal constructor(
fun <T> getFlag(
key: String,
default: T
): Evaluation<T> = cache.get().getEvaluation(
key,
default,
getContext()
) { flagName, resolveToken ->
// this lambda will be invoked inside the evaluation process
// and only if the resolve reason is not targeting key error.
apply(flagName, resolveToken)
): Evaluation<T> {
val evaluationContext = getContext()
val eval = cache.get().getEvaluation(
key,
default,
evaluationContext
) { flagName, resolveToken ->
// this lambda will be invoked inside the evaluation process
// and only if the resolve reason is not targeting key error.
apply(flagName, resolveToken)
}
// we are using a custom serializer so that the Json is serialized correctly in the logs
val newMap: Map<String, @Serializable(NetworkConfidenceValueSerializer::class) ConfidenceValue> =
evaluationContext
val contextJson = Json.encodeToJsonElement(newMap)
val flag = key.splitToSequence(".").first()
debugLogger?.logResolve(flag, contextJson)
return eval
}

@Synchronized
Expand Down Expand Up @@ -308,7 +321,7 @@ object ConfidenceFactory {
val debugLogger: DebugLogger? = if (loggingLevel == LoggingLevel.NONE) {
null
} else {
DebugLoggerImpl(loggingLevel)
DebugLoggerImpl(loggingLevel, clientSecret)
}
val engine = EventSenderEngineImpl.instance(
context,
Expand Down
17 changes: 16 additions & 1 deletion Confidence/src/main/java/com/spotify/confidence/DebugLogger.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.spotify.confidence

import android.util.Log
import kotlinx.serialization.json.JsonElement
import java.net.URLEncoder

private const val TAG = "Confidence"

Expand All @@ -9,9 +11,14 @@ internal interface DebugLogger {
fun logMessage(message: String, isWarning: Boolean = false, throwable: Throwable? = null)
fun logFlag(action: String, flag: String? = null)
fun logContext(action: String, context: Map<String, ConfidenceValue>)
fun logResolve(flag: String, context: JsonElement)
}

internal class DebugLoggerImpl(private val filterLevel: LoggingLevel) : DebugLogger {
internal class DebugLoggerImpl(private val filterLevel: LoggingLevel, private val clientKey: String) : DebugLogger {
private val JsonElement.urlEncoded
get() = URLEncoder.encode(this.toString(), "UTF-8")
private val String.urlEncoded
get() = URLEncoder.encode(this, "UTF-8")

override fun logEvent(action: String, event: EngineEvent) {
debug("[$action] $event")
Expand All @@ -35,6 +42,14 @@ internal class DebugLoggerImpl(private val filterLevel: LoggingLevel) : DebugLog
verbose("[$action] $context")
}

override fun logResolve(flag: String, context: JsonElement) {
debug(
"[Resolve Debug] " +
"https://app.confidence.spotify.com/flags/resolver-test?client-key=$clientKey&flag=flags/" +
"${flag.urlEncoded}&context=${context.urlEncoded}"
)
}

private fun verbose(message: String) = log(LoggingLevel.VERBOSE, message)
private fun debug(message: String) = log(LoggingLevel.DEBUG, message)
private fun warn(message: String, throwable: Throwable?) =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.spotify.confidence

import kotlinx.serialization.json.JsonElement

internal open class DebugLoggerFake : DebugLogger {
val messagesLogged = mutableListOf<Msg>()

Expand All @@ -19,5 +21,9 @@ internal open class DebugLoggerFake : DebugLogger {
// not important enough to test right now
}

override fun logResolve(flag: String, context: JsonElement) {
// not important enough to test right now
}

data class Msg(val message: String, val isWarning: Boolean, val throwable: Throwable?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DebugLoggerImplTest {
every { Log.v(any(), any()) } returns 0
every { Log.w(any(), any<String>()) } returns 0
every { Log.e(any(), any()) } returns 0
verboseLogger = DebugLoggerImpl(LoggingLevel.VERBOSE)
verboseLogger = DebugLoggerImpl(LoggingLevel.VERBOSE, "key")
}

@After
Expand Down Expand Up @@ -77,7 +77,7 @@ class DebugLoggerImplTest {

@Test
fun filterOnDebugLevel() {
val debugLogger = DebugLoggerImpl(LoggingLevel.DEBUG)
val debugLogger = DebugLoggerImpl(LoggingLevel.DEBUG, clientKey = "key")
debugLogger.logFlag("Resolve")
verify(inverse = true) { Log.v("Confidence", "[Resolve] null") }
debugLogger.logMessage("Normal Debug")
Expand All @@ -86,7 +86,7 @@ class DebugLoggerImplTest {

@Test
fun filterOnWarnLevel() {
val warnLogger = DebugLoggerImpl(LoggingLevel.WARN)
val warnLogger = DebugLoggerImpl(LoggingLevel.WARN, clientKey = "key")
warnLogger.logFlag("Resolve")
verify(inverse = true) { Log.v("Confidence", "[Resolve] null") }
warnLogger.logMessage("Normal Debug")
Expand All @@ -98,7 +98,7 @@ class DebugLoggerImplTest {

@Test
fun filterOnErrorLevel() {
val errorLogger = DebugLoggerImpl(LoggingLevel.ERROR)
val errorLogger = DebugLoggerImpl(LoggingLevel.ERROR, clientKey = "key")
errorLogger.logFlag("Resolve")
verify(inverse = true) { Log.v("Confidence", "[Resolve] null") }
errorLogger.logMessage("Normal Debug")
Expand All @@ -113,7 +113,7 @@ class DebugLoggerImplTest {

@Test
fun filterNoneLevel() {
val noneLogger = DebugLoggerImpl(LoggingLevel.NONE)
val noneLogger = DebugLoggerImpl(LoggingLevel.NONE, clientKey = "key")
noneLogger.logFlag("Resolve")
verify(inverse = true) { Log.v("Confidence", "[Resolve] null") }
noneLogger.logMessage("Normal Debug")
Expand Down

0 comments on commit ae05eef

Please sign in to comment.