summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Darrell Shi <darrellshi@google.com> 2023-06-07 03:17:21 +0000
committer Darrell Shi <darrellshi@google.com> 2023-06-27 21:53:27 +0000
commit8ef1cef2da40a012d1a7dc9c1495897965485be1 (patch)
tree7ed605a7fa20b506e8e04aab2d181e668fdb7341
parent2be74e83fd1a463279730105e3e37ae41b63038c (diff)
Introduce Logger
This change introduces Logger, which is a wrapper class around LogBuffer, providing a list of convenience methods. Directly logging through the LogBuffer is now deprecated in favor of the Logger. Bug: 276475093 Test: atest LogBufferTest LoggerTest Change-Id: I5cdc5fb49657729735d81cf4a70b168e38b3678d
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt9
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt224
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt140
5 files changed, 423 insertions, 11 deletions
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
index 885873824d1c..e0051f59469d 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
@@ -21,6 +21,7 @@ import android.util.Log
import com.android.systemui.common.buffer.RingBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.log.core.MessageInitializer
import com.android.systemui.log.core.MessagePrinter
import com.google.errorprone.annotations.CompileTimeConstant
@@ -77,7 +78,7 @@ constructor(
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
-) {
+) : MessageBuffer {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
private val echoMessageQueue: BlockingQueue<LogMessage>? =
@@ -178,11 +179,11 @@ constructor(
* store any relevant data on the message and then call [commit].
*/
@Synchronized
- fun obtain(
+ override fun obtain(
tag: String,
level: LogLevel,
messagePrinter: MessagePrinter,
- exception: Throwable? = null,
+ exception: Throwable?,
): LogMessage {
if (!mutable) {
return FROZEN_MESSAGE
@@ -199,7 +200,7 @@ constructor(
* have finished filling in its data fields. The message will be echoed to logcat if necessary.
*/
@Synchronized
- fun commit(message: LogMessage) {
+ override fun commit(message: LogMessage) {
if (!mutable) {
return
}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt
new file mode 100644
index 000000000000..5729ab270487
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+import com.google.errorprone.annotations.CompileTimeConstant
+
+/** Logs messages to the [MessageBuffer] with [tag]. */
+open class Logger(val buffer: MessageBuffer, val tag: String) {
+ /**
+ * Logs a message to the buffer.
+ *
+ * The actual string of the log message is not constructed until it is needed. To accomplish
+ * this, logging a message is a two-step process. First, a fresh instance of [LogMessage] is
+ * obtained and is passed to the [messageInitializer]. The initializer stores any relevant data
+ * on the message's fields. The message is then inserted into the buffer where it waits until it
+ * is either pushed out by newer messages or it needs to printed. If and when this latter moment
+ * occurs, the [messagePrinter] function is called on the message. It reads whatever data the
+ * initializer stored and converts it to a human-readable log message.
+ *
+ * @param level Which level to log the message at, both to the buffer and to logcat if it's
+ * echoed. In general, a module should split most of its logs into either INFO or DEBUG level.
+ * INFO level should be reserved for information that other parts of the system might care
+ * about, leaving the specifics of code's day-to-day operations to DEBUG.
+ * @param messagePrinter A function that will be called if and when the message needs to be
+ * dumped to logcat or a bug report. It should read the data stored by the initializer and
+ * convert it to a human-readable string. The value of `this` will be the [LogMessage] to be
+ * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the [LogMessage]
+ * and NEVER any variables in its enclosing scope. Otherwise, the runtime will need to
+ * allocate a new instance of the printer for each call, thwarting our attempts at avoiding
+ * any sort of allocation.
+ * @param exception Provide any exception that need to be logged. This is saved as
+ * [LogMessage.exception]
+ * @param messageInitializer A function that will be called immediately to store relevant data
+ * on the log message. The value of `this` will be the [LogMessage] to be initialized.
+ */
+ @JvmOverloads
+ inline fun log(
+ level: LogLevel,
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) {
+ val message = buffer.obtain(tag, level, messagePrinter, exception)
+ messageInitializer(message)
+ buffer.commit(message)
+ }
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer. Use sparingly.
+ *
+ * This is for simpler use-cases where [message] is a compile time string constant. For
+ * use-cases where the log message is built during runtime, use the [log] overloaded method that
+ * takes in an initializer and a message printer.
+ *
+ * Buffers are limited by the number of entries, so logging more frequently will limit the time
+ * window that the [MessageBuffer] covers in a bug report. Richer logs, on the other hand, make
+ * a bug report more actionable, so using the [log] with a [MessagePrinter] to add more details
+ * to every log may do more to improve overall logging than adding more logs with this method.
+ */
+ @JvmOverloads
+ fun log(
+ level: LogLevel,
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(level, { str1!! }, exception) { str1 = message }
+
+ /**
+ * Logs a message to the buffer at [LogLevel.VERBOSE].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun v(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.VERBOSE, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.VERBOSE]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun v(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.VERBOSE, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.DEBUG].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun d(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.DEBUG, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.DEBUG]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun d(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.DEBUG, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.INFO].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun i(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.INFO, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.INFO]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun i(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.INFO, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.WARNING].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun w(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.WARNING, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.WARNING]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun w(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.WARNING, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.ERROR].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun e(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.ERROR, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.ERROR]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun e(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.ERROR, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.WTF].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun wtf(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.WTF, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.WTF]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun wtf(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.WTF, message, exception)
+}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt
new file mode 100644
index 000000000000..bb91633c4d87
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+/**
+ * [MessageBuffer] is an interface that represents a buffer of log messages, and provides methods to
+ * [obtain] a log message and [commit] it to the buffer.
+ */
+interface MessageBuffer {
+ /**
+ * Obtains the next [LogMessage] from the buffer.
+ *
+ * After calling [obtain], the caller must store any relevant data on the message and then call
+ * [commit].
+ */
+ fun obtain(
+ tag: String,
+ level: LogLevel,
+ messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ ): LogMessage
+
+ /**
+ * After acquiring a log message via [obtain], call this method to signal to the buffer that
+ * data fields have been filled.
+ */
+ fun commit(message: LogMessage)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 6836733f1580..b5eae5b03b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -2,7 +2,7 @@ package com.android.systemui.log
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.Logger
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -33,7 +33,8 @@ class LogBufferTest : SysuiTestCase() {
@Test
fun log_shouldSaveLogToBuffer() {
- buffer.log("Test", LogLevel.INFO, "Some test message")
+ val logger = Logger(buffer, "Test")
+ logger.i("Some test message")
val dumpedString = dumpBuffer()
@@ -42,8 +43,9 @@ class LogBufferTest : SysuiTestCase() {
@Test
fun log_shouldRotateIfLogBufferIsFull() {
- buffer.log("Test", LogLevel.INFO, "This should be rotated")
- buffer.log("Test", LogLevel.INFO, "New test message")
+ val logger = Logger(buffer, "Test")
+ logger.i("This should be rotated")
+ logger.i("New test message")
val dumpedString = dumpBuffer()
@@ -54,7 +56,8 @@ class LogBufferTest : SysuiTestCase() {
fun dump_writesExceptionAndStacktrace() {
buffer = createBuffer()
val exception = createTestException("Exception message", "TestClass")
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedString = dumpBuffer()
@@ -73,7 +76,8 @@ class LogBufferTest : SysuiTestCase() {
"TestClass",
cause = createTestException("The real cause!", "TestClass")
)
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedString = dumpBuffer()
@@ -94,7 +98,8 @@ class LogBufferTest : SysuiTestCase() {
)
)
exception.addSuppressed(createTestException("Second suppressed exception", "SecondClass"))
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedStr = dumpBuffer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
new file mode 100644
index 000000000000..ab19b3aeceb0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
@@ -0,0 +1,140 @@
+package com.android.systemui.log.core
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogMessageImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnitRunner
+
+@SmallTest
+@RunWith(MockitoJUnitRunner::class)
+class LoggerTest : SysuiTestCase() {
+ @Mock private lateinit var buffer: MessageBuffer
+ private lateinit var message: LogMessage
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(buffer.obtain(any(), any(), any(), isNull())).thenAnswer {
+ message = LogMessageImpl.Factory.create()
+ return@thenAnswer message
+ }
+ }
+
+ @Test
+ fun log_shouldCommitLogMessage() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.log(LogLevel.DEBUG, { "count=$int1" }) {
+ int1 = 1
+ str1 = "test"
+ bool1 = true
+ }
+
+ assertThat(message.int1).isEqualTo(1)
+ assertThat(message.str1).isEqualTo("test")
+ assertThat(message.bool1).isEqualTo(true)
+ }
+
+ @Test
+ fun log_shouldUseCorrectLoggerTag() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.log(LogLevel.DEBUG, { "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(eq("LoggerTest"), any(), any(), nullable())
+ }
+
+ @Test
+ fun v_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.v({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.VERBOSE), any(), nullable())
+ }
+
+ @Test
+ fun v_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.v("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.VERBOSE), any(), nullable())
+ }
+
+ @Test
+ fun d_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.d({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.DEBUG), any(), nullable())
+ }
+
+ @Test
+ fun d_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.d("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.DEBUG), any(), nullable())
+ }
+
+ @Test
+ fun i_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.i({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.INFO), any(), nullable())
+ }
+
+ @Test
+ fun i_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.i("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.INFO), any(), nullable())
+ }
+
+ @Test
+ fun w_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.w({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.WARNING), any(), nullable())
+ }
+
+ @Test
+ fun w_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.w("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.WARNING), any(), nullable())
+ }
+
+ @Test
+ fun e_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.e({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.ERROR), any(), nullable())
+ }
+
+ @Test
+ fun e_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.e("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.ERROR), any(), nullable())
+ }
+
+ @Test
+ fun wtf_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.wtf({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.WTF), any(), nullable())
+ }
+
+ @Test
+ fun wtf_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.wtf("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.WTF), any(), nullable())
+ }
+}