Skip to content

Commit f7f82ed

Browse files
authored
Merge pull request #2311 from OneSignal/feat/add-log-listener
Feat: add public log listener methods
2 parents d1b274a + 24ed613 commit f7f82ed

File tree

6 files changed

+225
-36
lines changed

6 files changed

+225
-36
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/IDebugManager.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,29 @@ package com.onesignal.debug
33
/**
44
* Access to debug the SDK in the event additional information is required to diagnose any
55
* SDK-related issues.
6-
*
7-
* WARNING: This should not be used in a production setting.
86
*/
97
interface IDebugManager {
108
/**
119
* The log level the OneSignal SDK should be writing to the Android log. Defaults to [LogLevel.WARN].
10+
* WARNING: This should not be set higher than LogLevel.WARN in a production setting.
1211
*/
1312
var logLevel: LogLevel
1413

1514
/**
1615
* The log level the OneSignal SDK should be showing as a modal. Defaults to [LogLevel.NONE].
16+
* WARNING: This should not be used in a production setting.
1717
*/
1818
var alertLevel: LogLevel
19+
20+
/**
21+
* Add a listener to receive all logging messages the SDK produces.
22+
* Useful to capture and send logs to your server.
23+
* NOTE: All log messages are always passed, logLevel has no effect on this.
24+
*/
25+
fun addLogListener(listener: ILogListener)
26+
27+
/**
28+
* Removes a listener added by addLogListener
29+
*/
30+
fun removeLogListener(listener: ILogListener)
1931
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.onesignal.debug
2+
3+
fun interface ILogListener {
4+
fun onLogEvent(event: OneSignalLogEvent)
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.onesignal.debug
2+
3+
data class OneSignalLogEvent(
4+
val level: LogLevel,
5+
val entry: String,
6+
)

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/DebugManager.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.onesignal.debug.internal
22

33
import com.onesignal.debug.IDebugManager
4+
import com.onesignal.debug.ILogListener
45
import com.onesignal.debug.LogLevel
56
import com.onesignal.debug.internal.logging.Logging
67

@@ -21,4 +22,12 @@ internal class DebugManager() : IDebugManager {
2122
logLevel = LogLevel.WARN
2223
alertLevel = LogLevel.NONE
2324
}
25+
26+
override fun addLogListener(listener: ILogListener) {
27+
Logging.addListener(listener)
28+
}
29+
30+
override fun removeLogListener(listener: ILogListener) {
31+
Logging.removeListener(listener)
32+
}
2433
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/Logging.kt

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@ package com.onesignal.debug.internal.logging
33
import android.app.AlertDialog
44
import com.onesignal.common.threading.suspendifyOnMain
55
import com.onesignal.core.internal.application.IApplicationService
6+
import com.onesignal.debug.ILogListener
67
import com.onesignal.debug.LogLevel
8+
import com.onesignal.debug.OneSignalLogEvent
79
import java.io.PrintWriter
810
import java.io.StringWriter
11+
import java.util.concurrent.CopyOnWriteArraySet
912

1013
object Logging {
1114
private const val TAG = "OneSignal"
1215

1316
var applicationService: IApplicationService? = null
1417

18+
private val logListeners = CopyOnWriteArraySet<ILogListener>()
19+
1520
@JvmStatic
1621
var logLevel = LogLevel.WARN
1722

1823
@JvmStatic
1924
var visualLogLevel = LogLevel.NONE
2025

2126
@JvmStatic
22-
fun atLogLevel(level: LogLevel): Boolean {
23-
return level.compareTo(visualLogLevel) < 1 || level.compareTo(logLevel) < 1
24-
}
27+
fun atLogLevel(level: LogLevel): Boolean = level.compareTo(visualLogLevel) < 1 || level.compareTo(logLevel) < 1
2528

2629
@JvmStatic
2730
fun verbose(
@@ -86,41 +89,82 @@ object Logging {
8689
throwable: Throwable?,
8790
) {
8891
val fullMessage = "[${Thread.currentThread().name}] $message"
89-
if (level.compareTo(logLevel) < 1) {
90-
when (level) {
91-
LogLevel.VERBOSE -> android.util.Log.v(TAG, fullMessage, throwable)
92-
LogLevel.DEBUG -> android.util.Log.d(TAG, fullMessage, throwable)
93-
LogLevel.INFO -> android.util.Log.i(TAG, fullMessage, throwable)
94-
LogLevel.WARN -> android.util.Log.w(TAG, fullMessage, throwable)
95-
LogLevel.ERROR, LogLevel.FATAL -> android.util.Log.e(TAG, message, throwable)
96-
else -> {}
97-
}
92+
93+
logToLogcat(level, fullMessage, throwable)
94+
showVisualLogging(level, fullMessage, throwable)
95+
callLogListeners(level, fullMessage, throwable)
96+
}
97+
98+
private fun logToLogcat(
99+
level: LogLevel,
100+
message: String,
101+
throwable: Throwable?,
102+
) {
103+
if (level.compareTo(logLevel) >= 1) return
104+
when (level) {
105+
LogLevel.VERBOSE -> android.util.Log.v(TAG, message, throwable)
106+
LogLevel.DEBUG -> android.util.Log.d(TAG, message, throwable)
107+
LogLevel.INFO -> android.util.Log.i(TAG, message, throwable)
108+
LogLevel.WARN -> android.util.Log.w(TAG, message, throwable)
109+
LogLevel.ERROR, LogLevel.FATAL -> android.util.Log.e(TAG, message, throwable)
110+
else -> {}
98111
}
112+
}
99113

100-
if (level.compareTo(visualLogLevel) < 1 && applicationService?.current != null) {
101-
try {
102-
var fullMessage: String? = "$message\n".trimIndent()
103-
if (throwable != null) {
104-
fullMessage += throwable.message
105-
val sw = StringWriter()
106-
val pw = PrintWriter(sw)
107-
throwable.printStackTrace(pw)
108-
fullMessage += sw.toString()
109-
}
110-
val finalFullMessage = fullMessage
111-
112-
suspendifyOnMain {
113-
val currentActivity = applicationService?.current
114-
if (currentActivity != null) {
115-
AlertDialog.Builder(currentActivity)
116-
.setTitle(level.toString())
117-
.setMessage(finalFullMessage)
118-
.show()
119-
}
114+
private fun showVisualLogging(
115+
level: LogLevel,
116+
message: String,
117+
throwable: Throwable?,
118+
) {
119+
if (level.compareTo(visualLogLevel) >= 1) return
120+
121+
try {
122+
var fullMessage: String? = "$message\n".trimIndent()
123+
if (throwable != null) {
124+
fullMessage += throwable.message
125+
val sw = StringWriter()
126+
val pw = PrintWriter(sw)
127+
throwable.printStackTrace(pw)
128+
fullMessage += sw.toString()
129+
}
130+
val finalFullMessage = fullMessage
131+
132+
suspendifyOnMain {
133+
val currentActivity = applicationService?.current
134+
if (currentActivity != null) {
135+
AlertDialog
136+
.Builder(currentActivity)
137+
.setTitle(level.toString())
138+
.setMessage(finalFullMessage)
139+
.show()
120140
}
121-
} catch (t: Throwable) {
122-
android.util.Log.e(TAG, "Error showing logging message.", t)
123141
}
142+
} catch (t: Throwable) {
143+
android.util.Log.e(TAG, "Error showing logging message.", t)
124144
}
125145
}
146+
147+
private fun callLogListeners(
148+
level: LogLevel,
149+
message: String,
150+
throwable: Throwable?,
151+
) {
152+
if (logListeners.isEmpty()) return
153+
154+
var logEntry = message
155+
if (throwable != null) {
156+
logEntry += "\n" + android.util.Log.getStackTraceString(throwable)
157+
}
158+
for (listener in logListeners) {
159+
listener.onLogEvent(OneSignalLogEvent(level, logEntry))
160+
}
161+
}
162+
163+
fun addListener(listener: ILogListener) {
164+
logListeners.add(listener)
165+
}
166+
167+
fun removeListener(listener: ILogListener) {
168+
logListeners.remove(listener)
169+
}
126170
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.onesignal.debug.internal
2+
3+
import com.onesignal.debug.ILogListener
4+
import com.onesignal.debug.LogLevel
5+
import com.onesignal.debug.OneSignalLogEvent
6+
import com.onesignal.debug.internal.logging.Logging
7+
import io.kotest.core.spec.style.FunSpec
8+
import io.kotest.matchers.shouldBe
9+
import io.kotest.matchers.string.shouldEndWith
10+
11+
class TestLogLister : ILogListener {
12+
val calls = ArrayList<String>()
13+
14+
override fun onLogEvent(event: OneSignalLogEvent) {
15+
calls += event.entry
16+
}
17+
}
18+
19+
infix fun <T : Collection<String>> T.shouldHaveEachItemEndWith(expected: Array<String>): T {
20+
this.forEachIndexed { index, it -> it shouldEndWith expected[index] }
21+
return this
22+
}
23+
24+
class LoggingTests : FunSpec({
25+
beforeAny {
26+
Logging.logLevel = LogLevel.NONE
27+
}
28+
29+
test("addListener") {
30+
// Given
31+
val listener = TestLogLister()
32+
Logging.addListener(listener)
33+
34+
// When
35+
Logging.debug("test")
36+
37+
// Then
38+
listener.calls shouldHaveEachItemEndWith arrayOf("test")
39+
}
40+
41+
test("addListener twice") {
42+
// Given
43+
val listener = TestLogLister()
44+
Logging.addListener(listener)
45+
Logging.addListener(listener)
46+
47+
// When
48+
Logging.debug("test")
49+
50+
// Then
51+
listener.calls shouldHaveEachItemEndWith arrayOf("test")
52+
}
53+
54+
test("removeListener") {
55+
// Given
56+
val listener = TestLogLister()
57+
Logging.addListener(listener)
58+
Logging.removeListener(listener)
59+
60+
// When
61+
Logging.debug("test")
62+
63+
// Then
64+
listener.calls shouldBe arrayOf<String>()
65+
}
66+
67+
test("removeListener twice") {
68+
// Given
69+
val listener = TestLogLister()
70+
Logging.addListener(listener)
71+
Logging.removeListener(listener)
72+
Logging.removeListener(listener)
73+
74+
// When
75+
Logging.debug("test")
76+
77+
// Then
78+
listener.calls shouldBe arrayOf<String>()
79+
}
80+
81+
test("addListener nested") {
82+
// Given
83+
val nestedListener = TestLogLister()
84+
Logging.addListener { Logging.addListener(nestedListener) }
85+
86+
// When
87+
Logging.debug("test")
88+
Logging.debug("test2")
89+
Logging.debug("test3")
90+
91+
// Then
92+
nestedListener.calls shouldHaveEachItemEndWith arrayOf("test2", "test3")
93+
}
94+
95+
test("removeListener nested") {
96+
// Given
97+
val calls = ArrayList<String>()
98+
var listener: ILogListener? = null
99+
listener =
100+
ILogListener {
101+
calls += it.entry
102+
Logging.removeListener(listener!!)
103+
}
104+
Logging.addListener(listener!!)
105+
106+
// When
107+
Logging.debug("test")
108+
Logging.debug("test2")
109+
110+
// Then
111+
calls shouldHaveEachItemEndWith arrayOf("test")
112+
}
113+
})

0 commit comments

Comments
 (0)