Merge "Minor refactor that moves the code for dumping LogMessage to an extension method for LogMessage." into tm-qpr-dev

This commit is contained in:
Chandru S
2022-08-08 18:06:15 +00:00
committed by Android (Google) Code Review
3 changed files with 72 additions and 151 deletions

View File

@@ -22,17 +22,11 @@ import com.android.systemui.log.dagger.LogModule
import com.android.systemui.util.collection.RingBuffer import com.android.systemui.util.collection.RingBuffer
import com.google.errorprone.annotations.CompileTimeConstant import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.Arrays.stream
import java.util.Locale
import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue import java.util.concurrent.BlockingQueue
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.math.max import kotlin.math.max
const val UNBOUNDED_STACK_TRACE = -1
const val NESTED_TRACE_DEPTH = 10
/** /**
* A simple ring buffer of recyclable log messages * A simple ring buffer of recyclable log messages
* *
@@ -74,18 +68,12 @@ const val NESTED_TRACE_DEPTH = 10
* @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
* out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
* the maximum, it behaves like a ring buffer. * the maximum, it behaves like a ring buffer.
* @param rootStackTraceDepth The number of stack trace elements to be logged for an exception when
* the logBuffer is dumped. Defaulted to -1 [UNBOUNDED_STACK_TRACE] to print the entire stack trace.
* @param nestedStackTraceDepth The number of stack trace elements to be logged for any nested
* exceptions present in [Throwable.cause] or [Throwable.suppressedExceptions].
*/ */
class LogBuffer @JvmOverloads constructor( class LogBuffer @JvmOverloads constructor(
private val name: String, private val name: String,
private val maxSize: Int, private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker, private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true, private val systrace: Boolean = true,
private val rootStackTraceDepth: Int = UNBOUNDED_STACK_TRACE,
private val nestedStackTraceDepth: Int = NESTED_TRACE_DEPTH,
) { ) {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() } private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
@@ -236,7 +224,7 @@ class LogBuffer @JvmOverloads constructor(
val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) } val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
for (i in iterationStart until buffer.size) { for (i in iterationStart until buffer.size) {
dumpMessage(buffer[i], pw) buffer[i].dump(pw)
} }
} }
@@ -264,76 +252,6 @@ class LogBuffer @JvmOverloads constructor(
} }
} }
private fun dumpMessage(
message: LogMessage,
pw: PrintWriter
) {
val formattedTimestamp = DATE_FORMAT.format(message.timestamp)
val shortLevel = message.level.shortString
val messageToPrint = message.messagePrinter(message)
val tag = message.tag
printLikeLogcat(pw, formattedTimestamp, shortLevel, tag, messageToPrint)
message.exception?.let { ex ->
printException(
pw,
formattedTimestamp,
shortLevel,
ex,
tag,
stackTraceDepth = rootStackTraceDepth)
}
}
private fun printException(
pw: PrintWriter,
timestamp: String,
level: String,
exception: Throwable,
tag: String,
exceptionMessagePrefix: String = "",
stackTraceDepth: Int = UNBOUNDED_STACK_TRACE
) {
val message = "$exceptionMessagePrefix$exception"
printLikeLogcat(pw, timestamp, level, tag, message)
var stacktraceStream = stream(exception.stackTrace)
if (stackTraceDepth != UNBOUNDED_STACK_TRACE) {
stacktraceStream = stacktraceStream.limit(stackTraceDepth.toLong())
}
stacktraceStream.forEach { line ->
printLikeLogcat(pw, timestamp, level, tag, "\tat $line")
}
exception.cause?.let { cause ->
printException(pw, timestamp, level, cause, tag, "Caused by: ", nestedStackTraceDepth)
}
exception.suppressedExceptions.forEach { suppressed ->
printException(
pw,
timestamp,
level,
suppressed,
tag,
"Suppressed: ",
nestedStackTraceDepth
)
}
}
private fun printLikeLogcat(
pw: PrintWriter,
formattedTimestamp: String,
shortLogLevel: String,
tag: String,
message: String
) {
pw.print(formattedTimestamp)
pw.print(" ")
pw.print(shortLogLevel)
pw.print(" ")
pw.print(tag)
pw.print(": ")
pw.println(message)
}
private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) { private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
if (toLogcat || toSystrace) { if (toLogcat || toSystrace) {
val strMessage = message.messagePrinter(message) val strMessage = message.messagePrinter(message)
@@ -370,5 +288,4 @@ class LogBuffer @JvmOverloads constructor(
typealias MessageInitializer = LogMessage.() -> Unit typealias MessageInitializer = LogMessage.() -> Unit
private const val TAG = "LogBuffer" private const val TAG = "LogBuffer"
private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val FROZEN_MESSAGE = LogMessageImpl.create() private val FROZEN_MESSAGE = LogMessageImpl.create()

View File

@@ -16,6 +16,10 @@
package com.android.systemui.log package com.android.systemui.log
import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.Locale
/** /**
* Generic data class for storing messages logged to a [LogBuffer] * Generic data class for storing messages logged to a [LogBuffer]
* *
@@ -50,6 +54,17 @@ interface LogMessage {
var bool2: Boolean var bool2: Boolean
var bool3: Boolean var bool3: Boolean
var bool4: Boolean var bool4: Boolean
/**
* Function that dumps the [LogMessage] to the provided [writer].
*/
fun dump(writer: PrintWriter) {
val formattedTimestamp = DATE_FORMAT.format(timestamp)
val shortLevel = level.shortString
val messageToPrint = messagePrinter(this)
printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
exception?.printStackTrace(writer)
}
} }
/** /**
@@ -61,3 +76,21 @@ interface LogMessage {
* of the printer for each call, thwarting our attempts at avoiding any sort of allocation. * of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
*/ */
typealias MessagePrinter = LogMessage.() -> String typealias MessagePrinter = LogMessage.() -> String
private fun printLikeLogcat(
pw: PrintWriter,
formattedTimestamp: String,
shortLogLevel: String,
tag: String,
message: String
) {
pw.print(formattedTimestamp)
pw.print(" ")
pw.print(shortLogLevel)
pw.print(" ")
pw.print(tag)
pw.print(": ")
pw.println(message)
}
private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)

View File

@@ -24,16 +24,11 @@ class LogBufferTest : SysuiTestCase() {
@Before @Before
fun setup() { fun setup() {
outputWriter = StringWriter() outputWriter = StringWriter()
buffer = createBuffer(UNBOUNDED_STACK_TRACE, NESTED_TRACE_DEPTH) buffer = createBuffer()
} }
private fun createBuffer(rootTraceDepth: Int, nestedTraceDepth: Int): LogBuffer { private fun createBuffer(): LogBuffer {
return LogBuffer("TestBuffer", return LogBuffer("TestBuffer", 1, logcatEchoTracker, false)
1,
logcatEchoTracker,
false,
rootStackTraceDepth = rootTraceDepth,
nestedStackTraceDepth = nestedTraceDepth)
} }
@Test @Test
@@ -56,95 +51,83 @@ class LogBufferTest : SysuiTestCase() {
} }
@Test @Test
fun dump_writesExceptionAndStacktraceLimitedToGivenDepth() { fun dump_writesExceptionAndStacktrace() {
buffer = createBuffer(rootTraceDepth = 2, nestedTraceDepth = -1) buffer = createBuffer()
// stack trace depth of 5 val exception = createTestException("Exception message", "TestClass")
val exception = createTestException("Exception message", "TestClass", 5)
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception) buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer() val dumpedString = dumpBuffer()
// logs are limited to depth 2 assertThat(dumpedString).contains("Extra message")
assertThat(dumpedString).contains("E Tag: Extra message") assertThat(dumpedString).contains("java.lang.RuntimeException: Exception message")
assertThat(dumpedString).contains("E Tag: java.lang.RuntimeException: Exception message") assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)") assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
assertThat(dumpedString)
.doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
} }
@Test @Test
fun dump_writesCauseAndStacktraceLimitedToGivenDepth() { fun dump_writesCauseAndStacktrace() {
buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2) buffer = createBuffer()
val exception = createTestException("Exception message", val exception = createTestException("Exception message",
"TestClass", "TestClass",
1, cause = createTestException("The real cause!", "TestClass"))
cause = createTestException("The real cause!", "TestClass", 5))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception) buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer() val dumpedString = dumpBuffer()
// logs are limited to depth 2
assertThat(dumpedString) assertThat(dumpedString)
.contains("E Tag: Caused by: java.lang.RuntimeException: The real cause!") .contains("Caused by: java.lang.RuntimeException: The real cause!")
assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)") assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)") assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
assertThat(dumpedString)
.doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
} }
@Test @Test
fun dump_writesSuppressedExceptionAndStacktraceLimitedToGivenDepth() { fun dump_writesSuppressedExceptionAndStacktrace() {
buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2) buffer = createBuffer()
val exception = RuntimeException("Root exception message") val exception = RuntimeException("Root exception message")
exception.addSuppressed( exception.addSuppressed(
createTestException( createTestException(
"First suppressed exception", "First suppressed exception",
"FirstClass", "FirstClass",
5, createTestException("Cause of suppressed exp", "ThirdClass")
createTestException("Cause of suppressed exp", "ThirdClass", 5)
)) ))
exception.addSuppressed( exception.addSuppressed(
createTestException("Second suppressed exception", "SecondClass", 5)) createTestException("Second suppressed exception", "SecondClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception) buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedStr = dumpBuffer() val dumpedStr = dumpBuffer()
// logs are limited to depth 2
// first suppressed exception // first suppressed exception
assertThat(dumpedStr) assertThat(dumpedStr)
.contains("E Tag: Suppressed: " + .contains("Suppressed: " +
"java.lang.RuntimeException: First suppressed exception") "java.lang.RuntimeException: First suppressed exception")
assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:1)") assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:1)")
assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:2)") assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:2)")
assertThat(dumpedStr)
.doesNotContain("E Tag: \tat FirstClass.TestMethod(FirstClass.java:3)")
assertThat(dumpedStr) assertThat(dumpedStr)
.contains("E Tag: Caused by: java.lang.RuntimeException: Cause of suppressed exp") .contains("Caused by: java.lang.RuntimeException: Cause of suppressed exp")
assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:1)") assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:1)")
assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:2)") assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:2)")
assertThat(dumpedStr)
.doesNotContain("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:3)")
// second suppressed exception // second suppressed exception
assertThat(dumpedStr) assertThat(dumpedStr)
.contains("E Tag: Suppressed: " + .contains("Suppressed: " +
"java.lang.RuntimeException: Second suppressed exception") "java.lang.RuntimeException: Second suppressed exception")
assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:1)") assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:1)")
assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:2)") assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:2)")
assertThat(dumpedStr)
.doesNotContain("E Tag: \tat SecondClass.TestMethod(SecondClass.java:3)")
} }
private fun createTestException( private fun createTestException(
message: String, message: String,
errorClass: String, errorClass: String,
stackTraceLength: Int, cause: Throwable? = null,
cause: Throwable? = null
): Exception { ): Exception {
val exception = RuntimeException(message, cause) val exception = RuntimeException(message, cause)
exception.stackTrace = createStackTraceElements(errorClass, stackTraceLength) exception.stackTrace = (1..5).map { lineNumber ->
StackTraceElement(errorClass,
"TestMethod",
"$errorClass.java",
lineNumber)
}.toTypedArray()
return exception return exception
} }
@@ -152,16 +135,4 @@ class LogBufferTest : SysuiTestCase() {
buffer.dump(PrintWriter(outputWriter), tailLength = 100) buffer.dump(PrintWriter(outputWriter), tailLength = 100)
return outputWriter.toString() return outputWriter.toString()
} }
private fun createStackTraceElements(
errorClass: String,
stackTraceLength: Int
): Array<StackTraceElement> {
return (1..stackTraceLength).map { lineNumber ->
StackTraceElement(errorClass,
"TestMethod",
"$errorClass.java",
lineNumber)
}.toTypedArray()
}
} }