Skip to content

Commit c8a1133

Browse files
Ian BirdIanDBird
authored andcommitted
Implement logging support
1 parent ac1fae2 commit c8a1133

File tree

6 files changed

+112
-18
lines changed

6 files changed

+112
-18
lines changed

dev-app/src/main/java/com/uid2/dev/DevApplication.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ class DevApplication : Application() {
1010

1111
// Initialise the UID2Manager class. We will use it's DefaultNetworkSession rather than providing our own
1212
// custom implementation. This can be done to allow wrapping something like OkHttp.
13-
UID2Manager.init(this.applicationContext)
13+
UID2Manager.init(context = this, isLoggingEnabled = true)
1414

1515
// Alternatively, we could initialise the UID2Manager with our own custom NetworkSession...
16-
// UID2Manager.init(this.applicationContext, OkNetworkSession())
16+
// UID2Manager.init(this.applicationContext, OkNetworkSession(), true)
1717

1818
// For the development app, we will enable a strict thread policy to ensure we have suitable visibility of any
1919
// issues within the SDK.

sdk/src/main/java/com/uid2/UID2Client.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.uid2.network.NetworkRequestType
66
import com.uid2.network.NetworkSession
77
import com.uid2.network.RefreshPackage
88
import com.uid2.network.RefreshResponse
9+
import com.uid2.utils.Logger
910
import kotlinx.coroutines.CoroutineDispatcher
1011
import kotlinx.coroutines.Dispatchers
1112
import kotlinx.coroutines.withContext
@@ -21,6 +22,7 @@ import java.net.URL
2122
internal class UID2Client(
2223
private val apiUrl: String,
2324
private val session: NetworkSession,
25+
private val logger: Logger = Logger(),
2426
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
2527
) {
2628
// The refresh endpoint is built from the given API root, along with our known refresh path appended. If the
@@ -48,8 +50,13 @@ internal class UID2Client(
4850
refreshToken: String,
4951
refreshResponseKey: String,
5052
): RefreshPackage = withContext(ioDispatcher) {
53+
logger.i(TAG) { "Refreshing identity" }
54+
5155
// Check to make sure we have a valid endpoint to hit.
52-
val url = apiRefreshUrl ?: throw InvalidApiUrlException()
56+
val url = apiRefreshUrl ?: run {
57+
logger.e(TAG) { "Error determining identity refresh API" }
58+
throw InvalidApiUrlException()
59+
}
5360

5461
// Build the request to refresh the token.
5562
val request = NetworkRequest(
@@ -64,19 +71,27 @@ internal class UID2Client(
6471
// Attempt to make the request via the provided NetworkSession.
6572
val response = session.loadData(url, request)
6673
if (response.code != HttpURLConnection.HTTP_OK) {
74+
logger.e(TAG) { "Client details failure: ${response.code}" }
6775
throw RefreshTokenException(response.code)
6876
}
6977

7078
// The response should be an encrypted payload. Let's attempt to decrypt it using the key we were provided.
71-
val payload = DataEnvelope.decrypt(refreshResponseKey, response.data, true)
72-
?: throw PayloadDecryptException()
79+
val payload = DataEnvelope.decrypt(refreshResponseKey, response.data, true) ?: run {
80+
logger.e(TAG) { "Error decrypting response from client details" }
81+
throw PayloadDecryptException()
82+
}
7383

7484
// The decrypted payload should be JSON which we can parse.
7585
val refreshResponse = RefreshResponse.fromJson(JSONObject(String(payload, Charsets.UTF_8)))
76-
return@withContext refreshResponse?.toRefreshPackage() ?: throw InvalidPayloadException()
86+
return@withContext refreshResponse?.toRefreshPackage() ?: run {
87+
logger.e(TAG) { "Error parsing response from client details" }
88+
throw InvalidPayloadException()
89+
}
7790
}
7891

7992
private companion object {
93+
const val TAG = "UID2Client"
94+
8095
// The relative path of the API's refresh endpoint
8196
const val API_REFRESH_PATH = "/v2/token/refresh"
8297
}

sdk/src/main/java/com/uid2/UID2Manager.kt

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.uid2.extensions.getMetadata
2222
import com.uid2.network.DefaultNetworkSession
2323
import com.uid2.network.NetworkSession
2424
import com.uid2.storage.StorageManager
25+
import com.uid2.utils.Logger
2526
import com.uid2.utils.TimeUtils
2627
import kotlinx.coroutines.CoroutineDispatcher
2728
import kotlinx.coroutines.CoroutineScope
@@ -83,6 +84,7 @@ public class UID2Manager internal constructor(
8384
private val timeUtils: TimeUtils,
8485
defaultDispatcher: CoroutineDispatcher,
8586
initialAutomaticRefreshEnabled: Boolean,
87+
private val logger: Logger,
8688
) {
8789
private val scope = CoroutineScope(defaultDispatcher + SupervisorJob())
8890

@@ -156,6 +158,10 @@ public class UID2Manager internal constructor(
156158
initialized = scope.launch {
157159
// Attempt to load the Identity from storage. If successful, we can notify any observers.
158160
storageManager.loadIdentity().let {
161+
if (it.first != null) {
162+
logger.i(TAG) { "Restoring previously persisted identity" }
163+
}
164+
159165
validateAndSetIdentity(it.first, it.second, false)
160166
}
161167
}
@@ -168,6 +174,7 @@ public class UID2Manager internal constructor(
168174
* This will also be persisted locally, so that when the application re-launches, we reload this Identity.
169175
*/
170176
public fun setIdentity(identity: UID2Identity): Unit = afterInitialized {
177+
logger.i(TAG) { "Setting external identity" }
171178
validateAndSetIdentity(identity, null)
172179
}
173180

@@ -177,6 +184,7 @@ public class UID2Manager internal constructor(
177184
public fun resetIdentity(): Unit = afterInitialized {
178185
currentIdentity ?: return@afterInitialized
179186

187+
logger.i(TAG) { "Resetting identity" }
180188
setIdentityInternal(null, NO_IDENTITY, true)
181189
}
182190

@@ -185,7 +193,10 @@ public class UID2Manager internal constructor(
185193
*/
186194
public fun refreshIdentity(): Unit = afterInitialized {
187195
// If we have a valid Identity, let's refresh it.
188-
currentIdentity?.let { refreshIdentityInternal(it) }
196+
currentIdentity?.let {
197+
logger.i(TAG) { "Refreshing identity" }
198+
refreshIdentityInternal(it)
199+
}
189200
}
190201

191202
/**
@@ -207,6 +218,8 @@ public class UID2Manager internal constructor(
207218
private fun refreshIdentityInternal(identity: UID2Identity) = scope.launch {
208219
try {
209220
refreshToken(identity).retryWhen { _, attempt ->
221+
logger.i(TAG) { "Refreshing (Attempt: $attempt)" }
222+
210223
// The delay between retry attempts is based upon how many attempts we have previously had. After a
211224
// number of sequential failures, we will increase the delay.
212225
val delayMs = if (attempt < REFRESH_TOKEN_FAILURE_RETRY_THRESHOLD) {
@@ -221,10 +234,12 @@ public class UID2Manager internal constructor(
221234
getIdentityPackage(identity, false).valid
222235
}.single().let {
223236
result ->
237+
logger.i(TAG) { "Successfully refreshed identity" }
224238
validateAndSetIdentity(result.identity, result.status)
225239
}
226-
} catch (_: UID2Exception) {
240+
} catch (ex: UID2Exception) {
227241
// This will happen after we decide to no longer try to update the identity, e.g. it's no longer valid.
242+
logger.e(TAG, ex) { "Error when trying to refresh identity" }
228243
}
229244
}
230245

@@ -313,6 +328,8 @@ public class UID2Manager internal constructor(
313328
checkRefreshExpiresJob = scope.launch {
314329
val timeToCheck = timeUtils.diffToNow(it.refreshExpires) + EXPIRATION_CHECK_TOLERANCE_MS
315330
delay(timeToCheck)
331+
332+
logger.i(TAG) { "Detected refresh has expired" }
316333
validateAndSetIdentity(it, null, true)
317334
}
318335
}
@@ -323,6 +340,8 @@ public class UID2Manager internal constructor(
323340
checkIdentityExpiresJob = scope.launch {
324341
val timeToCheck = timeUtils.diffToNow(it.identityExpires) + EXPIRATION_CHECK_TOLERANCE_MS
325342
delay(timeToCheck)
343+
344+
logger.i(TAG) { "Detected identity has expired" }
326345
validateAndSetIdentity(it, null, true)
327346
}
328347
}
@@ -336,12 +355,18 @@ public class UID2Manager internal constructor(
336355
) {
337356
// Process Opt Out.
338357
if (status == OPT_OUT) {
358+
logger.i(TAG) { "User opt-out detected" }
339359
setIdentityInternal(null, OPT_OUT)
340360
return
341361
}
342362

343363
// Check to see the validity of the Identity, updating our internal state.
344364
val validity = getIdentityPackage(identity, currentIdentity == null)
365+
366+
logger.i(TAG) {
367+
"Updating identity (Identity: ${validity.identity != null}, Status: ${validity.status}, " +
368+
"Updating Storage: $updateStorage)"
369+
}
345370
setIdentityInternal(validity.identity, validity.status, updateStorage)
346371
}
347372

@@ -396,6 +421,8 @@ public class UID2Manager internal constructor(
396421
}
397422

398423
public companion object {
424+
private const val TAG = "UID2Manager"
425+
399426
// The default API server.
400427
private const val UID2_API_URL_KEY = "uid2_api_url"
401428
private const val UID2_API_URL_DEFAULT = "https://prod.uidapi.com"
@@ -420,15 +447,10 @@ public class UID2Manager internal constructor(
420447
private var api: String = UID2_API_URL_DEFAULT
421448
private var networkSession: NetworkSession = DefaultNetworkSession()
422449
private var storageManager: StorageManager? = null
450+
private var isLoggingEnabled: Boolean = false
423451

424452
private var instance: UID2Manager? = null
425453

426-
/**
427-
* Initializes the class with the given [Context].
428-
*/
429-
@JvmStatic
430-
public fun init(context: Context): Unit = init(context, DefaultNetworkSession())
431-
432454
/**
433455
* Initializes the class with the given [Context], along with a [NetworkSession] that will be responsible
434456
* for making any required network calls.
@@ -439,8 +461,13 @@ public class UID2Manager internal constructor(
439461
* The default implementation supported by the SDK can be found as [DefaultNetworkSession].
440462
*/
441463
@JvmStatic
464+
@JvmOverloads
442465
@Throws(InitializationException::class)
443-
public fun init(context: Context, networkSession: NetworkSession) {
466+
public fun init(
467+
context: Context,
468+
networkSession: NetworkSession = DefaultNetworkSession(),
469+
isLoggingEnabled: Boolean = false,
470+
) {
444471
if (instance != null) {
445472
throw InitializationException()
446473
}
@@ -449,7 +476,8 @@ public class UID2Manager internal constructor(
449476

450477
this.api = metadata?.getString(UID2_API_URL_KEY, UID2_API_URL_DEFAULT) ?: UID2_API_URL_DEFAULT
451478
this.networkSession = networkSession
452-
this.storageManager = StorageManager.getInstance(context)
479+
this.storageManager = StorageManager.getInstance(context.applicationContext)
480+
this.isLoggingEnabled = isLoggingEnabled
453481
}
454482

455483
/**
@@ -466,16 +494,19 @@ public class UID2Manager internal constructor(
466494
@JvmStatic
467495
public fun getInstance(): UID2Manager {
468496
val storage = storageManager ?: throw InitializationException()
497+
val logger = Logger(isLoggingEnabled)
469498

470499
return instance ?: UID2Manager(
471500
UID2Client(
472501
api,
473502
networkSession,
503+
logger,
474504
),
475505
storage,
476506
TimeUtils(),
477507
Dispatchers.Default,
478508
true,
509+
logger,
479510
).apply {
480511
instance = this
481512
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.uid2.utils
2+
3+
import android.util.Log
4+
5+
/**
6+
* Simple logger class that wraps Android's [Log].
7+
*/
8+
internal class Logger(private val isEnabled: Boolean = false) {
9+
fun v(tag: String, throwable: Throwable? = null, message: () -> String = { "" }) {
10+
if (isEnabled) {
11+
Log.v(tag, message(), throwable)
12+
}
13+
}
14+
15+
fun d(tag: String, throwable: Throwable? = null, message: () -> String = { "" }) {
16+
if (isEnabled) {
17+
Log.d(tag, message(), throwable)
18+
}
19+
}
20+
21+
fun i(tag: String, throwable: Throwable? = null, message: () -> String = { "" }) {
22+
if (isEnabled) {
23+
Log.i(tag, message(), throwable)
24+
}
25+
}
26+
27+
fun e(tag: String, throwable: Throwable? = null, message: () -> String = { "" }) {
28+
Log.e(tag, message(), throwable)
29+
}
30+
}

sdk/src/test/java/com/uid2/UID2ClientTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.uid2.data.UID2Identity
66
import com.uid2.network.NetworkRequest
77
import com.uid2.network.NetworkResponse
88
import com.uid2.network.NetworkSession
9+
import com.uid2.utils.Logger
910
import kotlinx.coroutines.runBlocking
1011
import org.json.JSONObject
1112
import org.junit.Assert.assertEquals
@@ -23,6 +24,7 @@ import org.mockito.kotlin.whenever
2324
@RunWith(MockitoJUnitRunner::class)
2425
class UID2ClientTest {
2526
private val networkSession: NetworkSession = mock()
27+
private val logger: Logger = mock()
2628

2729
private val url = "https://test.dev"
2830
private val refreshToken = "RefreshToken"
@@ -33,6 +35,7 @@ class UID2ClientTest {
3335
val client = UID2Client(
3436
"this is not a url",
3537
networkSession,
38+
logger,
3639
)
3740

3841
// Verify that when we have configured the client with an invalid URL, that it throws the appropriate exception
@@ -47,6 +50,7 @@ class UID2ClientTest {
4750
val client = UID2Client(
4851
url,
4952
networkSession,
53+
logger,
5054
)
5155

5256
// Configure the network session to report a failure.
@@ -63,6 +67,7 @@ class UID2ClientTest {
6367
val client = UID2Client(
6468
url,
6569
networkSession,
70+
logger,
6671
)
6772

6873
whenever(networkSession.loadData(any(), any())).thenReturn(
@@ -80,6 +85,7 @@ class UID2ClientTest {
8085
val client = UID2Client(
8186
url,
8287
networkSession,
88+
logger,
8389
)
8490

8591
whenever(networkSession.loadData(any(), any())).thenReturn(
@@ -97,6 +103,7 @@ class UID2ClientTest {
97103
val client = UID2Client(
98104
url,
99105
networkSession,
106+
logger,
100107
)
101108

102109
// Configure the network session to return a valid (encrypted) payload.
@@ -120,6 +127,7 @@ class UID2ClientTest {
120127
val client = UID2Client(
121128
url,
122129
networkSession,
130+
logger,
123131
)
124132

125133
// Configure the network session to return a valid (encrypted) payload.
@@ -138,6 +146,7 @@ class UID2ClientTest {
138146
val client = UID2Client(
139147
url,
140148
networkSession,
149+
logger,
141150
)
142151

143152
// Configure the network session to return a valid (encrypted) payload and allows us to capture the given

0 commit comments

Comments
 (0)