Loading packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +7 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.DumpController; import com.android.systemui.assist.AssistModule; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.Recents; Loading Loading @@ -59,9 +60,12 @@ import dagger.Provides; * A dagger module for injecting components of System UI that are not overridden by the System UI * implementation. */ @Module(includes = {AssistModule.class, @Module(includes = { AssistModule.class, ConcurrencyModule.class, PeopleHubModule.class}, LogModule.class, PeopleHubModule.class, }, subcomponents = {StatusBarComponent.class, NotificationRowComponent.class}) public abstract class SystemUIModule { Loading packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt 0 → 100644 +213 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 import android.util.Log import com.android.systemui.DumpController import com.android.systemui.Dumpable import com.android.systemui.log.dagger.LogModule import java.text.SimpleDateFormat import java.util.ArrayDeque import java.util.Locale /** * A simple ring buffer of recyclable log messages * * The goal of this class is to enable logging that is both extremely chatty and extremely * lightweight. If done properly, logging a message will not result in any heap allocations or * string generation. Messages are only converted to strings if the log is actually dumped (usually * as the result of taking a bug report). * * You can dump the entire buffer at any time by running: * * ``` * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService \ * dependency DumpController <bufferName> * ``` * * where `bufferName` is the (case-sensitive) [name] passed to the constructor. * * By default, only messages of WARN level or higher are echoed to logcat, but this can be adjusted * locally (usually for debugging purposes). * * To enable logcat echoing for an entire buffer: * * ``` * $ adb shell settings put global systemui/buffer/<bufferName> <level> * ``` * * To enable logcat echoing for a specific tag: * * ``` * $ adb shell settings put global systemui/tag/<tag> <level> * ``` * * In either case, `level` can be any of `verbose`, `debug`, `info`, `warn`, `error`, `assert`, or * the first letter of any of the previous. * * Buffers are provided by [LogModule]. * * @param name The name of this buffer * @param maxLogs The maximum number of messages to keep in memory at any one time, including the * unused pool. * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to * sequential calls to [document] that aren't immediately followed by a matching call to [push]. */ class LogBuffer( private val name: String, private val maxLogs: Int, private val poolSize: Int, private val logcatEchoTracker: LogcatEchoTracker ) { private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque() fun attach(dumpController: DumpController) { dumpController.registerDumpable(name, onDump) } /** * Logs a message to the log buffer * * May also log the message to logcat if echoing is enabled for this buffer or tag. * * 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 [initializer]. 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 [printer] function is called on the message. It reads whatever data the * initializer stored and converts it to a human-readable log message. * * @param tag A string of at most 23 characters, used for grouping logs into categories or * subjects. If this message is echoed to logcat, this will be the tag that is used. * @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 initializer 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. * @param printer 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. */ inline fun log( tag: String, level: LogLevel, initializer: LogMessage.() -> Unit, noinline printer: LogMessage.() -> String ) { val message = obtain(tag, level, printer) initializer(message) push(message) } /** * Same as [log], but doesn't push the message to the buffer. Useful if you need to supply a * "reason" for doing something (the thing you supply the reason to will presumably call [push] * on that message at some point). */ inline fun document( tag: String, level: LogLevel, initializer: LogMessage.() -> Unit, noinline printer: LogMessage.() -> String ): LogMessage { val message = obtain(tag, level, printer) initializer(message) return message } /** * Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been * exhausted, creates a new instance. * * In general, you should call [log] or [document] instead of this method. */ fun obtain( tag: String, level: LogLevel, printer: (LogMessage) -> String ): LogMessageImpl { val message = synchronized(buffer) { if (buffer.size > maxLogs - poolSize) { buffer.removeFirst() } else { LogMessageImpl.create() } } message.reset(tag, level, System.currentTimeMillis(), printer) return message } /** * Pushes a message into buffer, possibly evicting an older message if the buffer is full. */ fun push(message: LogMessage) { synchronized(buffer) { if (buffer.size == maxLogs) { Log.e(TAG, "LogBuffer $name has exceeded its pool size") buffer.removeFirst() } buffer.add(message as LogMessageImpl) if (logcatEchoTracker.isBufferLoggable(name, message.level) || logcatEchoTracker.isTagLoggable(message.tag, message.level)) { echoToLogcat(message) } } } /** Converts the entire buffer to a newline-delimited string */ fun dump(): String { synchronized(buffer) { val sb = StringBuilder() for (message in buffer) { dumpMessage(message, sb) } return sb.toString() } } private fun dumpMessage(message: LogMessage, sb: StringBuilder) { sb.append(DATE_FORMAT.format(message.timestamp)) .append(" ").append(message.level) .append(" ").append(message.tag) .append(" ").append(message.printer(message)) .append("\n") } private fun echoToLogcat(message: LogMessage) { val strMessage = message.printer(message) when (message.level) { LogLevel.VERBOSE -> Log.v(message.tag, strMessage) LogLevel.DEBUG -> Log.d(message.tag, strMessage) LogLevel.INFO -> Log.i(message.tag, strMessage) LogLevel.WARNING -> Log.w(message.tag, strMessage) LogLevel.ERROR -> Log.e(message.tag, strMessage) LogLevel.WTF -> Log.wtf(message.tag, strMessage) } } private val onDump = Dumpable { _, pw, _ -> pw.println(dump()) } } private const val TAG = "LogBuffer" private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US) packages/SystemUI/src/com/android/systemui/log/LogLevel.kt 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 import android.util.Log /** * Enum version of @Log.Level */ enum class LogLevel(@Log.Level val nativeLevel: Int) { VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARNING(Log.WARN), ERROR(Log.ERROR), WTF(Log.ASSERT) } packages/SystemUI/src/com/android/systemui/log/LogMessage.kt 0 → 100644 +47 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 /** * Generic data class for storing messages logged to a [LogBuffer] * * Each LogMessage has a few standard fields ([level], [tag], and [timestamp]). The rest are generic * data slots that may or may not be used, depending on the nature of the specific message being * logged. * * When a message is logged, the code doing the logging stores data in one or more of the generic * fields ([str1], [int1], etc). When it comes time to dump the message to logcat/bugreport/etc, the * [printer] function reads the data stored in the generic fields and converts that to a human- * readable string. Thus, for every log type there must be a specialized initializer function that * stores data specific to that log type and a specialized printer function that prints that data. * * See [LogBuffer.log] for more information. */ interface LogMessage { val level: LogLevel val tag: String val timestamp: Long val printer: LogMessage.() -> String var str1: String? var str2: String? var str3: String? var int1: Int var int2: Int var long1: Long var double1: Double } packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 /** * Recyclable implementation of [LogMessage]. */ data class LogMessageImpl( override var level: LogLevel, override var tag: String, override var timestamp: Long, override var printer: LogMessage.() -> String, override var str1: String?, override var str2: String?, override var str3: String?, override var int1: Int, override var int2: Int, override var long1: Long, override var double1: Double ) : LogMessage { fun reset( tag: String, level: LogLevel, timestamp: Long, renderer: LogMessage.() -> String ) { this.level = level this.tag = tag this.timestamp = timestamp this.printer = renderer str1 = null str2 = null str3 = null int1 = 0 int2 = 0 long1 = 0 double1 = 0.0 } companion object Factory { fun create(): LogMessageImpl { return LogMessageImpl( LogLevel.DEBUG, DEFAULT_TAG, 0, DEFAULT_RENDERER, null, null, null, 0, 0, 0, 0.0) } } } private const val DEFAULT_TAG = "UnknownTag" private val DEFAULT_RENDERER: LogMessage.() -> String = { "Unknown message: $this" } Loading
packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +7 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.DumpController; import com.android.systemui.assist.AssistModule; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.Recents; Loading Loading @@ -59,9 +60,12 @@ import dagger.Provides; * A dagger module for injecting components of System UI that are not overridden by the System UI * implementation. */ @Module(includes = {AssistModule.class, @Module(includes = { AssistModule.class, ConcurrencyModule.class, PeopleHubModule.class}, LogModule.class, PeopleHubModule.class, }, subcomponents = {StatusBarComponent.class, NotificationRowComponent.class}) public abstract class SystemUIModule { Loading
packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt 0 → 100644 +213 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 import android.util.Log import com.android.systemui.DumpController import com.android.systemui.Dumpable import com.android.systemui.log.dagger.LogModule import java.text.SimpleDateFormat import java.util.ArrayDeque import java.util.Locale /** * A simple ring buffer of recyclable log messages * * The goal of this class is to enable logging that is both extremely chatty and extremely * lightweight. If done properly, logging a message will not result in any heap allocations or * string generation. Messages are only converted to strings if the log is actually dumped (usually * as the result of taking a bug report). * * You can dump the entire buffer at any time by running: * * ``` * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService \ * dependency DumpController <bufferName> * ``` * * where `bufferName` is the (case-sensitive) [name] passed to the constructor. * * By default, only messages of WARN level or higher are echoed to logcat, but this can be adjusted * locally (usually for debugging purposes). * * To enable logcat echoing for an entire buffer: * * ``` * $ adb shell settings put global systemui/buffer/<bufferName> <level> * ``` * * To enable logcat echoing for a specific tag: * * ``` * $ adb shell settings put global systemui/tag/<tag> <level> * ``` * * In either case, `level` can be any of `verbose`, `debug`, `info`, `warn`, `error`, `assert`, or * the first letter of any of the previous. * * Buffers are provided by [LogModule]. * * @param name The name of this buffer * @param maxLogs The maximum number of messages to keep in memory at any one time, including the * unused pool. * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to * sequential calls to [document] that aren't immediately followed by a matching call to [push]. */ class LogBuffer( private val name: String, private val maxLogs: Int, private val poolSize: Int, private val logcatEchoTracker: LogcatEchoTracker ) { private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque() fun attach(dumpController: DumpController) { dumpController.registerDumpable(name, onDump) } /** * Logs a message to the log buffer * * May also log the message to logcat if echoing is enabled for this buffer or tag. * * 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 [initializer]. 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 [printer] function is called on the message. It reads whatever data the * initializer stored and converts it to a human-readable log message. * * @param tag A string of at most 23 characters, used for grouping logs into categories or * subjects. If this message is echoed to logcat, this will be the tag that is used. * @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 initializer 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. * @param printer 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. */ inline fun log( tag: String, level: LogLevel, initializer: LogMessage.() -> Unit, noinline printer: LogMessage.() -> String ) { val message = obtain(tag, level, printer) initializer(message) push(message) } /** * Same as [log], but doesn't push the message to the buffer. Useful if you need to supply a * "reason" for doing something (the thing you supply the reason to will presumably call [push] * on that message at some point). */ inline fun document( tag: String, level: LogLevel, initializer: LogMessage.() -> Unit, noinline printer: LogMessage.() -> String ): LogMessage { val message = obtain(tag, level, printer) initializer(message) return message } /** * Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been * exhausted, creates a new instance. * * In general, you should call [log] or [document] instead of this method. */ fun obtain( tag: String, level: LogLevel, printer: (LogMessage) -> String ): LogMessageImpl { val message = synchronized(buffer) { if (buffer.size > maxLogs - poolSize) { buffer.removeFirst() } else { LogMessageImpl.create() } } message.reset(tag, level, System.currentTimeMillis(), printer) return message } /** * Pushes a message into buffer, possibly evicting an older message if the buffer is full. */ fun push(message: LogMessage) { synchronized(buffer) { if (buffer.size == maxLogs) { Log.e(TAG, "LogBuffer $name has exceeded its pool size") buffer.removeFirst() } buffer.add(message as LogMessageImpl) if (logcatEchoTracker.isBufferLoggable(name, message.level) || logcatEchoTracker.isTagLoggable(message.tag, message.level)) { echoToLogcat(message) } } } /** Converts the entire buffer to a newline-delimited string */ fun dump(): String { synchronized(buffer) { val sb = StringBuilder() for (message in buffer) { dumpMessage(message, sb) } return sb.toString() } } private fun dumpMessage(message: LogMessage, sb: StringBuilder) { sb.append(DATE_FORMAT.format(message.timestamp)) .append(" ").append(message.level) .append(" ").append(message.tag) .append(" ").append(message.printer(message)) .append("\n") } private fun echoToLogcat(message: LogMessage) { val strMessage = message.printer(message) when (message.level) { LogLevel.VERBOSE -> Log.v(message.tag, strMessage) LogLevel.DEBUG -> Log.d(message.tag, strMessage) LogLevel.INFO -> Log.i(message.tag, strMessage) LogLevel.WARNING -> Log.w(message.tag, strMessage) LogLevel.ERROR -> Log.e(message.tag, strMessage) LogLevel.WTF -> Log.wtf(message.tag, strMessage) } } private val onDump = Dumpable { _, pw, _ -> pw.println(dump()) } } private const val TAG = "LogBuffer" private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US)
packages/SystemUI/src/com/android/systemui/log/LogLevel.kt 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 import android.util.Log /** * Enum version of @Log.Level */ enum class LogLevel(@Log.Level val nativeLevel: Int) { VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARNING(Log.WARN), ERROR(Log.ERROR), WTF(Log.ASSERT) }
packages/SystemUI/src/com/android/systemui/log/LogMessage.kt 0 → 100644 +47 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 /** * Generic data class for storing messages logged to a [LogBuffer] * * Each LogMessage has a few standard fields ([level], [tag], and [timestamp]). The rest are generic * data slots that may or may not be used, depending on the nature of the specific message being * logged. * * When a message is logged, the code doing the logging stores data in one or more of the generic * fields ([str1], [int1], etc). When it comes time to dump the message to logcat/bugreport/etc, the * [printer] function reads the data stored in the generic fields and converts that to a human- * readable string. Thus, for every log type there must be a specialized initializer function that * stores data specific to that log type and a specialized printer function that prints that data. * * See [LogBuffer.log] for more information. */ interface LogMessage { val level: LogLevel val tag: String val timestamp: Long val printer: LogMessage.() -> String var str1: String? var str2: String? var str3: String? var int1: Int var int2: Int var long1: Long var double1: Double }
packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 /** * Recyclable implementation of [LogMessage]. */ data class LogMessageImpl( override var level: LogLevel, override var tag: String, override var timestamp: Long, override var printer: LogMessage.() -> String, override var str1: String?, override var str2: String?, override var str3: String?, override var int1: Int, override var int2: Int, override var long1: Long, override var double1: Double ) : LogMessage { fun reset( tag: String, level: LogLevel, timestamp: Long, renderer: LogMessage.() -> String ) { this.level = level this.tag = tag this.timestamp = timestamp this.printer = renderer str1 = null str2 = null str3 = null int1 = 0 int2 = 0 long1 = 0 double1 = 0.0 } companion object Factory { fun create(): LogMessageImpl { return LogMessageImpl( LogLevel.DEBUG, DEFAULT_TAG, 0, DEFAULT_RENDERER, null, null, null, 0, 0, 0, 0.0) } } } private const val DEFAULT_TAG = "UnknownTag" private val DEFAULT_RENDERER: LogMessage.() -> String = { "Unknown message: $this" }