Skip to content

Commit f95e836

Browse files
committed
DataConnectLogging.kt major refactor'
1 parent 264bb9b commit f95e836

File tree

6 files changed

+111
-177
lines changed

6 files changed

+111
-177
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
version=16.0.0-beta04
1+
version=16.0.0-beta99
22
latestReleasedVersion=16.0.0-beta03

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/DataConnectLogging.kt

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.Flow
3838
* The [DataConnectLogging] interface is _not_ stable for inheritance in third-party libraries, as
3939
* new methods might be added to this interface or contracts of the existing methods can be changed.
4040
*/
41-
public interface DataConnectLogging {
41+
public interface DataConnectLogging<out StateT : DataConnectLogging.State> {
4242

4343
/**
4444
* The log level use by all [FirebaseDataConnect] instances.
@@ -49,63 +49,76 @@ public interface DataConnectLogging {
4949
*/
5050
public var level: LogLevel
5151

52-
/** A [Flow] that can be used to observe the changes to [level]. */
53-
public val flow: Flow<LogLevel>
52+
/** A [Flow] that can be used to observe changes to [state]. */
53+
public val flow: Flow<StateT>
5454

5555
/**
56-
* Sets the log level to the given level, as if by setting [level], and returns an object that,
57-
* when closed, will restore the log level to the value it was at the time that this method was
58-
* invoked.
56+
* The state of the logging facilities.
5957
*
60-
* This method can be particularly useful to enable debug logging only for a small section of
61-
* code, without having to manually keep track of the original state. Unit tests and small chunks
62-
* of code that require debugging are the intended use cases for this function.
58+
* A caller may wish to save the value of this property into a local variable, make some changes
59+
* to the public properties of [DataConnectLogging] (for example, change [level]), run some code,
60+
* then restore the original state by calling [DataConnectLogging.State.restore].
6361
*
6462
* For an example in Firebase Data Connect's own Android test suite, see https://goo.gle/3ZuRug5.
65-
*
66-
* Note that when a log level is "popped" it does not interact with any other pushed log levels.
67-
* In particular, if there are nested calls to [push] then they are expected to be popped in
68-
* reverse order; otherwise, the "original" log level may not be properly restored.
6963
*/
70-
public fun push(level: LogLevel): LogLevelStackFrame
64+
public val state: StateT
7165

7266
/**
73-
* A "stack frame" that saves the state of the logging facilities in response to a call to [push].
67+
* A snapshot of the state of the logging facilities, that can be later restored.
7468
*
7569
* ### Safe for Concurrent Use
7670
*
77-
* All methods and properties of [LogLevelStackFrame] are thread-safe and may be safely called
78-
* and/or accessed concurrently from multiple threads and/or coroutines.
71+
* All methods and properties of [State] are thread-safe and may be safely called and/or accessed
72+
* concurrently from multiple threads and/or coroutines.
7973
*
8074
* ### Not Stable for Inheritance
8175
*
82-
* The [LogLevelStackFrame] interface is _not_ stable for inheritance in third-party libraries, as
83-
* new methods might be added to this interface or contracts of the existing methods can be
84-
* changed.
76+
* The [State] interface is _not_ stable for inheritance in third-party libraries, as new methods
77+
* might be added to this interface or contracts of the existing methods can be changed.
78+
*
79+
* @see state
8580
*/
86-
public interface LogLevelStackFrame : AutoCloseable {
81+
public interface State {
8782

88-
/** The log level that was in place when this frame was pushed. */
89-
public val originalLevel: LogLevel
83+
/** The log level. */
84+
public val level: LogLevel
9085

91-
/** The log level that was set when this frame was pushed. */
92-
public val newLevel: LogLevel
86+
/** Restores the state from this object into the [DataConnectLogging] that created it. */
87+
public fun restore()
9388

9489
/**
95-
* Pops this frame off of the log level stack, restoring the log level to the state it was in at
96-
* the time that this frame was pushed onto the stack.
90+
* Compares this object with another object for equality.
9791
*
98-
* This method may be safely called multiple times. Subsequent invocations will do nothing and
99-
* return as if successful. Subsequent invocations will _block_ until the state is restored by
100-
* the thread that is actually doing the restoring.
92+
* @param other The object to compare to this for equality.
93+
* @return true if, and only if, the other object is an instance of the same implementation of
94+
* [State] whose public properties compare equal using the `==` operator to the corresponding
95+
* properties of this object.
10196
*/
102-
override fun close()
97+
override fun equals(other: Any?): Boolean
10398

10499
/**
105-
* Does the exact same thing as [close], except that it _suspends_ rather than _blocks_ if
106-
* another thread is in the process of restoring the original log level.
100+
* Calculates and returns the hash code for this object.
101+
*
102+
* The hash code is _not_ guaranteed to be stable across application restarts.
103+
*
104+
* @return the hash code for this object, that incorporates the values of this object's public
105+
* properties.
107106
*/
108-
public suspend fun suspendingClose()
107+
override fun hashCode(): Int
108+
109+
/**
110+
* Returns a string representation of this object, useful for debugging.
111+
*
112+
* The string representation is _not_ guaranteed to be stable and may change without notice at
113+
* any time. Therefore, the only recommended usage of the returned string is debugging and/or
114+
* logging. Namely, parsing the returned string or storing the returned string in non-volatile
115+
* storage should generally be avoided in order to be robust in case that the string
116+
* representation changes.
117+
*
118+
* @return a string representation of this object, which includes the class name and the values
119+
* of all public properties.
120+
*/
121+
override fun toString(): String
109122
}
110123
}
111124

@@ -126,5 +139,12 @@ public interface DataConnectLogging {
126139
* inline.
127140
* @return whatever the given block returns.
128141
*/
129-
public inline fun <T> DataConnectLogging.withLevel(level: LogLevel, block: () -> T): T =
130-
push(level).use { block() }
142+
public inline fun <T> DataConnectLogging<*>.withLevel(level: LogLevel, block: () -> T): T {
143+
val savedState = state
144+
this.level = level
145+
return try {
146+
block()
147+
} finally {
148+
savedState.restore()
149+
}
150+
}

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/FirebaseDataConnect.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,5 +396,5 @@ public var FirebaseDataConnect.Companion.logLevel: LogLevel
396396
}
397397

398398
/** The logcat logging facilities used by all [FirebaseDataConnect] instances. */
399-
public val FirebaseDataConnect.Companion.logging: DataConnectLogging
399+
public val FirebaseDataConnect.Companion.logging: DataConnectLogging<*>
400400
get() = DataConnectLoggingImpl

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectLoggingImpl.kt

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,52 +19,39 @@ package com.google.firebase.dataconnect.core
1919
import android.util.Log
2020
import com.google.firebase.dataconnect.BuildConfig
2121
import com.google.firebase.dataconnect.DataConnectLogging
22-
import com.google.firebase.dataconnect.DataConnectLogging.LogLevelStackFrame
2322
import com.google.firebase.dataconnect.LogLevel
2423
import com.google.firebase.dataconnect.core.LoggerGlobals.LOG_TAG
2524
import com.google.firebase.dataconnect.core.LoggerGlobals.Logger
2625
import com.google.firebase.util.nextAlphanumericString
2726
import kotlin.random.Random
2827
import kotlinx.coroutines.flow.MutableStateFlow
2928
import kotlinx.coroutines.flow.asSharedFlow
30-
import kotlinx.coroutines.runBlocking
31-
import kotlinx.coroutines.sync.Mutex
32-
import kotlinx.coroutines.sync.withLock
3329

34-
internal object DataConnectLoggingImpl : DataConnectLogging {
30+
internal object DataConnectLoggingImpl : DataConnectLogging<DataConnectLoggingImpl.StateImpl> {
3531

36-
private val _level = MutableStateFlow(LogLevel.WARN)
32+
private val _state = MutableStateFlow(StateImpl(level = LogLevel.WARN))
3733

38-
override val flow = _level.asSharedFlow()
34+
override val flow = _state.asSharedFlow()
3935

40-
override var level by _level::value
36+
override val state
37+
get() = _state.value
4138

42-
override fun push(level: LogLevel): LogLevelStackFrame {
43-
while (true) {
44-
val originalLevel = _level.value
45-
if (_level.compareAndSet(originalLevel, level)) {
46-
return LogLevelStackFrameImpl(originalLevel, level)
39+
override var level: LogLevel
40+
get() = _state.value.level
41+
set(newLevel) {
42+
while (true) {
43+
val oldState = _state.value
44+
val newState = oldState.copy(level = newLevel)
45+
if (_state.compareAndSet(oldState, newState)) {
46+
break
47+
}
4748
}
4849
}
49-
}
50-
51-
private data class LogLevelStackFrameImpl(
52-
override val originalLevel: LogLevel,
53-
override val newLevel: LogLevel,
54-
) : LogLevelStackFrame {
55-
56-
private val closedMutex = Mutex()
57-
private var closed = false
5850

59-
override fun close() = runBlocking { suspendingClose() }
60-
61-
override suspend fun suspendingClose() =
62-
closedMutex.withLock {
63-
if (!closed) {
64-
level = originalLevel
65-
closed = true
66-
}
67-
}
51+
data class StateImpl(override val level: LogLevel) : DataConnectLogging.State {
52+
override fun restore() {
53+
_state.value = this
54+
}
6855
}
6956
}
7057

0 commit comments

Comments
 (0)