1 /*
2  * Copyright (C) 2020 The Android Open Source Project
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 com.android.systemui.log.core
18 
19 import android.icu.text.SimpleDateFormat
20 import java.io.PrintWriter
21 import java.util.Locale
22 
23 /**
24  * Generic data class for storing messages logged to a [LogBuffer]
25  *
26  * Each LogMessage has a few standard fields ([level], [tag], and [timestamp]). The rest are generic
27  * data slots that may or may not be used, depending on the nature of the specific message being
28  * logged.
29  *
30  * When a message is logged, the code doing the logging stores data in one or more of the generic
31  * fields ([str1], [int1], etc). When it comes time to dump the message to logcat/bugreport/etc, the
32  * [messagePrinter] function reads the data stored in the generic fields and converts that to a
33  * human- readable string. Thus, for every log type there must be a specialized initializer function
34  * that stores data specific to that log type and a specialized printer function that prints that
35  * data.
36  *
37  * See [LogBuffer.log] for more information.
38  */
39 interface LogMessage {
40     val level: LogLevel
41     val tag: String
42     val timestamp: Long
43     val messagePrinter: MessagePrinter
44     val exception: Throwable?
45 
46     var str1: String?
47     var str2: String?
48     var str3: String?
49     var int1: Int
50     var int2: Int
51     var long1: Long
52     var long2: Long
53     var double1: Double
54     var bool1: Boolean
55     var bool2: Boolean
56     var bool3: Boolean
57     var bool4: Boolean
58 
59     /** Function that dumps the [LogMessage] to the provided [writer]. */
dumpnull60     fun dump(writer: PrintWriter) {
61         val formattedTimestamp = DATE_FORMAT.format(timestamp)
62         val shortLevel = level.shortString
63         val messageToPrint = messagePrinter(this)
64         printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
65         exception?.printStackTrace(writer)
66     }
67 }
68 
69 /**
70  * A function that will be called immediately to store relevant data on the log message. The value
71  * of `this` will be the LogMessage to be initialized.
72  */
73 typealias MessageInitializer = LogMessage.() -> Unit
74 
75 /**
76  * A function that will be called if and when the message needs to be dumped to logcat or a bug
77  * report. It should read the data stored by the initializer and convert it to a human-readable
78  * string. The value of `this` will be the LogMessage to be printed. **IMPORTANT:** The printer
79  * should ONLY ever reference fields on the LogMessage and NEVER any variables in its enclosing
80  * scope. Otherwise, the runtime will need to allocate a new instance of the printer for each call,
81  * thwarting our attempts at avoiding any sort of allocation.
82  */
83 typealias MessagePrinter = LogMessage.() -> String
84 
printLikeLogcatnull85 private fun printLikeLogcat(
86     pw: PrintWriter,
87     formattedTimestamp: String,
88     shortLogLevel: String,
89     tag: String,
90     message: String
91 ) {
92     pw.print(formattedTimestamp)
93     pw.print(" ")
94     pw.print(shortLogLevel)
95     pw.print(" ")
96     pw.print(tag)
97     pw.print(": ")
98     pw.println(message)
99 }
100 
101 private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
102