Skip to content

Commit 6f2d0dc

Browse files
committed
Remove Timber from dependencies and implement logging internally
1 parent 0163556 commit 6f2d0dc

21 files changed

Lines changed: 276 additions & 66 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"client-sdk-android": patch
3+
---
4+
5+
Remove Timber from dependencies

NOTICE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ limitations under the License.
3939

4040
The following modifications follow MIT License from https://github.com/ggarber/sdpparser
4141

42+
https://github.com/livekit/client-sdk-android/commit/60ebdd5da1e81db2b1f7ad543105c9cdc058d471
43+
4244
MIT License
4345

4446
Copyright (c) 2023 Gustavo Garcia
@@ -96,3 +98,25 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
9698
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
9799
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
98100
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
101+
102+
###################################################################################
103+
104+
Parts of this source code come from JakeWharton/timber, following Apache License 2.0.
105+
106+
https://github.com/JakeWharton/timber
107+
108+
Apache License 2.0
109+
110+
Copyright 2013 Jake Wharton
111+
112+
Licensed under the Apache License, Version 2.0 (the "License");
113+
you may not use this file except in compliance with the License.
114+
You may obtain a copy of the License at
115+
116+
http://www.apache.org/licenses/LICENSE-2.0
117+
118+
Unless required by applicable law or agreed to in writing, software
119+
distributed under the License is distributed on an "AS IS" BASIS,
120+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121+
See the License for the specific language governing permissions and
122+
limitations under the License.

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ subprojects {
6464
"src/*/java/**/ReentrantMutex.kt", // Different license
6565
"src/*/java/**/TextureViewRenderer.kt", // Different license
6666
"src/*/java/**/FlowDelegate.kt", // Different license
67+
"src/*/java/**/JainSdpUtils.kt", // Different license
6768
)
6869
ktlint("0.50.0")
6970
.setEditorConfigPath("$rootDir/.editorconfig")

examples/screenshare-audio/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ dependencies {
6060
implementation libs.androidx.ui.graphics
6161
implementation libs.androidx.ui.tooling.preview
6262
implementation libs.androidx.material3
63-
implementation libs.timber
6463
testImplementation libs.junit
6564
androidTestImplementation libs.androidx.test.junit
6665
androidTestImplementation libs.espresso

gradle/libs.versions.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ auto-service-annotations = { module = "com.google.auto.service:auto-service-anno
7171
coroutines-lib = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
7272
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
7373
compose-bom = "androidx.compose:compose-bom:2023.08.00"
74-
timber = { module = "com.github.ajalt:timberkt", version = "1.5.1" }
7574

7675
# Lint
7776
lint-lib = { module = "com.android.tools.lint:lint", version.ref = "lint" }

livekit-android-camerax/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ dokkaHtml {
7272
dependencies {
7373

7474
api(project(":livekit-android-sdk"))
75-
implementation libs.timber
7675
implementation libs.coroutines.lib
7776
implementation libs.androidx.annotation
7877

livekit-android-sdk/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ dependencies {
159159
implementation libs.dagger.lib
160160
kapt libs.dagger.compiler
161161

162-
implementation libs.timber
163162
api libs.semver4j
164163

165164
lintChecks project(':livekit-lint')

livekit-android-sdk/src/main/java/io/livekit/android/LiveKit.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ package io.livekit.android
1818

1919
import android.app.Application
2020
import android.content.Context
21+
import io.livekit.android.LiveKit.loggingLevel
2122
import io.livekit.android.dagger.DaggerLiveKitComponent
2223
import io.livekit.android.dagger.RTCModule
2324
import io.livekit.android.dagger.create
2425
import io.livekit.android.room.Room
2526
import io.livekit.android.util.LKLog
2627
import io.livekit.android.util.LoggingLevel
27-
import timber.log.Timber
2828

2929
/**
3030
* The main entry point into using LiveKit.
@@ -42,16 +42,18 @@ object LiveKit {
4242
get() = LKLog.loggingLevel
4343
set(value) {
4444
LKLog.loggingLevel = value
45+
}
4546

46-
// Plant debug tree if needed.
47-
if (value != LoggingLevel.OFF) {
48-
val forest = Timber.forest()
49-
val needsPlanting = forest.none { it is Timber.DebugTree }
50-
51-
if (needsPlanting) {
52-
Timber.plant(Timber.DebugTree())
53-
}
54-
}
47+
/**
48+
* The [LKLog.Logger] to use for Livekit logs.
49+
*
50+
* Default implementation prints to logcat.
51+
*/
52+
@JvmStatic
53+
var logger: LKLog.Logger?
54+
get() = LKLog.logger
55+
set(value) {
56+
LKLog.logger = value
5557
}
5658

5759
/**

livekit-android-sdk/src/main/java/io/livekit/android/dagger/RTCModule.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2025 LiveKit, Inc.
2+
* Copyright 2023-2026 LiveKit, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -58,7 +58,6 @@ import livekit.org.webrtc.VideoDecoderFactory
5858
import livekit.org.webrtc.VideoEncoderFactory
5959
import livekit.org.webrtc.audio.AudioDeviceModule
6060
import livekit.org.webrtc.audio.JavaAudioDeviceModule
61-
import timber.log.Timber
6261
import javax.inject.Named
6362
import javax.inject.Singleton
6463

@@ -123,9 +122,7 @@ internal object RTCModule {
123122
else -> LoggingLevel.OFF
124123
}
125124

126-
LKLog.log(loggingLevel) {
127-
Timber.log(loggingLevel.toAndroidLogPriority(), "$s2: $s")
128-
}
125+
LKLog.log(loggingLevel, null) { "$s2: $s" }
129126
},
130127
Logging.Severity.LS_VERBOSE,
131128
)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright 2026 LiveKit, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "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+
* http://www.apache.org/licenses/LICENSE-2.0
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+
17+
package io.livekit.android.util
18+
19+
import android.os.Build
20+
import android.util.Log
21+
import java.io.PrintWriter
22+
import java.io.StringWriter
23+
import java.util.regex.Pattern
24+
25+
/**
26+
* This is a modified version of DebugTree from JakeWharton/timber for LiveKit.
27+
* https://github.com/JakeWharton/timber
28+
*/
29+
30+
/**
31+
* A tree for debug builds. Automatically infers the tag from the calling class.
32+
* @suppress
33+
* */
34+
open class LKDebugTree {
35+
36+
private val fqcnIgnore =
37+
listOf(
38+
LKDebugTree::class.java.name,
39+
LKLog.defaultLogger::class.java.name,
40+
)
41+
42+
val tag: String?
43+
get() =
44+
Throwable()
45+
.stackTrace
46+
.first { it.className !in fqcnIgnore }
47+
.let(::createStackElementTag)
48+
49+
fun prepareLog(priority: Int, t: Throwable?, message: String?) {
50+
// Consume tag even when message is not loggable so that next message is correctly tagged.
51+
val tag = tag
52+
53+
var message = message
54+
if (message.isNullOrEmpty()) {
55+
if (t == null) {
56+
return // Swallow message if it's null and there's no throwable.
57+
}
58+
message = getStackTraceString(t)
59+
} else {
60+
if (t != null) {
61+
message += "\n" + getStackTraceString(t)
62+
}
63+
}
64+
65+
log(priority, tag, message, t)
66+
}
67+
68+
private fun getStackTraceString(t: Throwable): String {
69+
// Don't replace this with Log.getStackTraceString() - it hides
70+
// UnknownHostException, which is not what we want.
71+
val sw = StringWriter(256)
72+
val pw = PrintWriter(sw, false)
73+
t.printStackTrace(pw)
74+
pw.flush()
75+
return sw.toString()
76+
}
77+
78+
/**
79+
* Extract the tag which should be used for the message from the `element`. By default this will
80+
* use the class name without any anonymous class suffixes (e.g., `Foo$1` becomes `Foo`).
81+
*
82+
* Note: This will not be called if a [manual tag][.tag] was specified.
83+
*/
84+
private fun createStackElementTag(element: StackTraceElement): String? {
85+
var tag = element.className.substringAfterLast('.')
86+
val m = ANONYMOUS_CLASS.matcher(tag)
87+
if (m.find()) {
88+
tag = m.replaceAll("")
89+
}
90+
// Tag length limit was removed in API 26.
91+
return if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= 26) {
92+
tag
93+
} else {
94+
tag.take(MAX_TAG_LENGTH)
95+
}
96+
}
97+
98+
/**
99+
* Break up `message` into maximum-length chunks (if needed) and send to either
100+
* [Log.println()][Log.println] or [Log.wtf()][Log.wtf] for logging.
101+
*
102+
* {@inheritDoc}
103+
*/
104+
open fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
105+
if (message.length < MAX_LOG_LENGTH) {
106+
if (priority == Log.ASSERT) {
107+
Log.wtf(tag, message)
108+
} else {
109+
Log.println(priority, tag, message)
110+
}
111+
return
112+
}
113+
114+
// Split by line, then ensure each line can fit into Log's maximum length.
115+
var i = 0
116+
val length = message.length
117+
while (i < length) {
118+
var newline = message.indexOf('\n', i)
119+
newline = if (newline != -1) newline else length
120+
do {
121+
val end = newline.coerceAtMost(i + MAX_LOG_LENGTH)
122+
val part = message.substring(i, end)
123+
if (priority == Log.ASSERT) {
124+
Log.wtf(tag, part)
125+
} else {
126+
Log.println(priority, tag, part)
127+
}
128+
i = end
129+
} while (i < newline)
130+
i++
131+
}
132+
}
133+
134+
companion object {
135+
private const val MAX_LOG_LENGTH = 4000
136+
private const val MAX_TAG_LENGTH = 23
137+
private val ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$")
138+
}
139+
}

0 commit comments

Comments
 (0)