Skip to content

Commit 7ea9701

Browse files
committed
Allow passing a custom logger
1 parent ae31b27 commit 7ea9701

File tree

9 files changed

+236
-11
lines changed

9 files changed

+236
-11
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.feeds.android.client.api.logging
17+
18+
import okhttp3.logging.HttpLoggingInterceptor
19+
20+
public enum class HttpLoggingLevel(internal val okhttp: HttpLoggingInterceptor.Level) {
21+
None(HttpLoggingInterceptor.Level.NONE),
22+
Basic(HttpLoggingInterceptor.Level.BASIC),
23+
Headers(HttpLoggingInterceptor.Level.HEADERS),
24+
Body(HttpLoggingInterceptor.Level.BODY),
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.feeds.android.client.api.logging
17+
18+
public interface Logger {
19+
public fun log(level: Level, tag: String, throwable: Throwable? = null, message: () -> String)
20+
21+
public enum class Level {
22+
Verbose,
23+
Debug,
24+
Info,
25+
Warning,
26+
Error,
27+
}
28+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.feeds.android.client.api.logging
17+
18+
/**
19+
* Configuration for logging within the FeedsClient.
20+
*
21+
* @property customLogger An optional custom logger implementation. If not provided, a default
22+
* logger will be used.
23+
* @property httpLoggingLevel What level of HTTP logging to use. Defaults to
24+
* [HttpLoggingLevel.Basic].
25+
*/
26+
public class LoggingConfig(
27+
public val customLogger: Logger? = null,
28+
public val httpLoggingLevel: HttpLoggingLevel = HttpLoggingLevel.Basic,
29+
)

stream-feeds-android-client/src/main/kotlin/io/getstream/feeds/android/client/api/model/FeedsConfig.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.getstream.feeds.android.client.api.model
1717

1818
import io.getstream.feeds.android.client.api.file.FeedUploader
19+
import io.getstream.feeds.android.client.api.logging.LoggingConfig
1920

2021
/**
2122
* Configuration class for the Stream Feeds Android client. This class contains all the
@@ -24,4 +25,7 @@ import io.getstream.feeds.android.client.api.file.FeedUploader
2425
*
2526
* @param customUploader Optional [FeedUploader] implementation for overriding the default CDN.
2627
*/
27-
public class FeedsConfig(public val customUploader: FeedUploader? = null)
28+
public class FeedsConfig(
29+
public val customUploader: FeedUploader? = null,
30+
public val loggingConfig: LoggingConfig = LoggingConfig(),
31+
)

stream-feeds-android-client/src/main/kotlin/io/getstream/feeds/android/client/internal/client/Create.kt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import androidx.lifecycle.ProcessLifecycleOwner
2222
import io.getstream.android.core.api.StreamClient
2323
import io.getstream.android.core.api.authentication.StreamTokenManager
2424
import io.getstream.android.core.api.authentication.StreamTokenProvider
25-
import io.getstream.android.core.api.log.StreamLogger
2625
import io.getstream.android.core.api.log.StreamLoggerProvider
2726
import io.getstream.android.core.api.model.config.StreamClientSerializationConfig
2827
import io.getstream.android.core.api.model.config.StreamHttpConfig
@@ -51,6 +50,8 @@ import io.getstream.feeds.android.client.internal.client.reconnect.DefaultRetryS
5150
import io.getstream.feeds.android.client.internal.client.reconnect.lifecycle.StreamLifecycleObserver
5251
import io.getstream.feeds.android.client.internal.file.StreamFeedUploader
5352
import io.getstream.feeds.android.client.internal.http.FeedsSingleFlightApi
53+
import io.getstream.feeds.android.client.internal.logging.createLoggerProvider
54+
import io.getstream.feeds.android.client.internal.logging.createLoggingInterceptor
5455
import io.getstream.feeds.android.client.internal.repository.ActivitiesRepositoryImpl
5556
import io.getstream.feeds.android.client.internal.repository.AppRepositoryImpl
5657
import io.getstream.feeds.android.client.internal.repository.BookmarksRepositoryImpl
@@ -70,7 +71,6 @@ import kotlinx.coroutines.Dispatchers
7071
import kotlinx.coroutines.SupervisorJob
7172
import kotlinx.coroutines.plus
7273
import okhttp3.OkHttpClient
73-
import okhttp3.logging.HttpLoggingInterceptor
7474
import retrofit2.Retrofit
7575
import retrofit2.converter.moshi.MoshiConverterFactory
7676
import retrofit2.converter.scalars.ScalarsConverterFactory
@@ -156,11 +156,7 @@ internal fun createFeedsClient(
156156
config: FeedsConfig,
157157
): FeedsClient {
158158

159-
val logProvider =
160-
StreamLoggerProvider.Companion.defaultAndroidLogger(
161-
minLevel = StreamLogger.LogLevel.Verbose,
162-
honorAndroidIsLoggable = false,
163-
)
159+
val logProvider = createLoggerProvider(config.loggingConfig.customLogger)
164160
val logger = logProvider.taggedLogger("FeedsClient")
165161

166162
// Setup coroutine scope for the client
@@ -192,7 +188,9 @@ internal fun createFeedsClient(
192188
// HTTP Configuration
193189
val okHttpBuilder =
194190
OkHttpClient.Builder()
195-
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
191+
.addInterceptor(
192+
createLoggingInterceptor(logProvider, config.loggingConfig.httpLoggingLevel)
193+
)
196194

197195
val client =
198196
createStreamCoreClient(
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.feeds.android.client.internal.logging
17+
18+
import io.getstream.android.core.api.log.StreamLogger
19+
import io.getstream.android.core.api.log.StreamLogger.LogLevel as CoreLogLevel
20+
import io.getstream.android.core.api.log.StreamLoggerProvider
21+
import io.getstream.feeds.android.client.api.logging.Logger
22+
23+
internal class CustomLoggerProvider(private val customLogger: Logger) : StreamLoggerProvider {
24+
override fun taggedLogger(tag: String): StreamLogger = CustomTaggedLogger(customLogger, tag)
25+
}
26+
27+
private class CustomTaggedLogger(private val customLogger: Logger, private val tag: String) :
28+
StreamLogger {
29+
override fun log(level: CoreLogLevel, throwable: Throwable?, message: () -> String) =
30+
customLogger.log(level.map(), tag, throwable, message)
31+
}
32+
33+
private fun CoreLogLevel.map(): Logger.Level =
34+
when (this) {
35+
CoreLogLevel.Verbose -> Logger.Level.Verbose
36+
CoreLogLevel.Debug -> Logger.Level.Debug
37+
CoreLogLevel.Info -> Logger.Level.Info
38+
CoreLogLevel.Warning -> Logger.Level.Warning
39+
CoreLogLevel.Error -> Logger.Level.Error
40+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.feeds.android.client.internal.logging
17+
18+
import io.getstream.android.core.api.log.StreamLoggerProvider
19+
import io.getstream.feeds.android.client.api.logging.HttpLoggingLevel
20+
import io.getstream.feeds.android.client.api.logging.Logger
21+
import okhttp3.Interceptor
22+
import okhttp3.logging.HttpLoggingInterceptor
23+
24+
internal fun createLoggerProvider(customLogger: Logger?): StreamLoggerProvider =
25+
customLogger?.let(::CustomLoggerProvider) ?: StreamLoggerProvider.defaultAndroidLogger()
26+
27+
internal fun createLoggingInterceptor(
28+
provider: StreamLoggerProvider,
29+
level: HttpLoggingLevel,
30+
): Interceptor {
31+
val logger = provider.taggedLogger("FeedHTTP")
32+
33+
return HttpLoggingInterceptor(logger = { logger.i { it } }).setLevel(level.okhttp)
34+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.feeds.android.client.internal.logging
17+
18+
import io.getstream.android.core.api.log.StreamLogger
19+
import io.getstream.feeds.android.client.api.logging.Logger
20+
import io.mockk.mockk
21+
import io.mockk.verify
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
import org.junit.runners.Parameterized
25+
26+
@RunWith(Parameterized::class)
27+
internal class CustomLoggerProviderTest(
28+
private val logLevel: StreamLogger.LogLevel,
29+
private val expectedLevel: Logger.Level,
30+
) {
31+
@Test
32+
fun `on taggedLogger, return a logger that forwards the tag`() {
33+
val customLogger = mockk<Logger>(relaxed = true)
34+
val taggedLogger = CustomLoggerProvider(customLogger).taggedLogger("a tag")
35+
val exception = Exception("an exception")
36+
37+
taggedLogger.log(logLevel, exception) { "a message" }
38+
39+
verify {
40+
customLogger.log(
41+
level = expectedLevel,
42+
tag = "a tag",
43+
throwable = exception,
44+
message = match { it() == "a message" },
45+
)
46+
}
47+
}
48+
49+
companion object {
50+
@JvmStatic
51+
@Parameterized.Parameters
52+
fun data(): Collection<Array<Any>> =
53+
listOf(
54+
arrayOf(StreamLogger.LogLevel.Verbose, Logger.Level.Verbose),
55+
arrayOf(StreamLogger.LogLevel.Debug, Logger.Level.Debug),
56+
arrayOf(StreamLogger.LogLevel.Info, Logger.Level.Info),
57+
arrayOf(StreamLogger.LogLevel.Warning, Logger.Level.Warning),
58+
arrayOf(StreamLogger.LogLevel.Error, Logger.Level.Error),
59+
)
60+
}
61+
}

stream-feeds-android-sample/src/main/java/io/getstream/feeds/android/sample/login/LoginManager.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ import io.getstream.android.core.api.model.value.StreamApiKey
2626
import io.getstream.android.core.api.model.value.StreamToken
2727
import io.getstream.android.core.api.model.value.StreamUserId
2828
import io.getstream.feeds.android.client.api.FeedsClient
29+
import io.getstream.feeds.android.client.api.logging.HttpLoggingLevel
30+
import io.getstream.feeds.android.client.api.logging.LoggingConfig
31+
import io.getstream.feeds.android.client.api.model.FeedsConfig
2932
import io.getstream.feeds.android.client.api.model.User
3033
import io.getstream.feeds.android.sample.DemoAppConfig
31-
import javax.inject.Inject
32-
import javax.inject.Singleton
3334
import kotlinx.coroutines.flow.first
3435
import kotlinx.coroutines.sync.Mutex
3536
import kotlinx.coroutines.sync.withLock
37+
import javax.inject.Inject
38+
import javax.inject.Singleton
3639

3740
@Singleton
3841
class LoginManager
@@ -93,6 +96,9 @@ constructor(
9396
return credentials.userToken
9497
}
9598
},
99+
config = FeedsConfig(
100+
loggingConfig = LoggingConfig(httpLoggingLevel = HttpLoggingLevel.Body),
101+
)
96102
)
97103

98104
return client.connect().map { UserState(user = credentials.user, client = client) }

0 commit comments

Comments
 (0)