xref: /aosp_15_r20/frameworks/base/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2023 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 package com.android.hoststubgen
17 
18 import java.io.BufferedOutputStream
19 import java.io.FileOutputStream
20 import java.io.PrintWriter
21 import java.io.Writer
22 
23 val log: HostStubGenLogger = HostStubGenLogger().setConsoleLogLevel(LogLevel.Info)
24 
25 /** Logging level */
26 enum class LogLevel {
27     None,
28     Error,
29     Warn,
30     Info,
31     Verbose,
32     Debug,
33 }
34 
35 /**
36  * Simple logging class.
37  *
38  * By default, it has no printers set. Use [setConsoleLogLevel] or [addFilePrinter] to actually
39  * write log.
40  */
41 class HostStubGenLogger {
42     private var indentLevel: Int = 0
43         get() = field
44         set(value) {
45             field = value
46             indent = "  ".repeat(value)
47         }
48     private var indent: String = ""
49 
50     private val printers: MutableList<LogPrinter> = mutableListOf()
51 
52     private var consolePrinter: LogPrinter? = null
53 
54     private var maxLogLevel = LogLevel.None
55 
updateMaxLogLevelnull56     private fun updateMaxLogLevel() {
57         maxLogLevel = LogLevel.None
58 
59         printers.forEach {
60             if (maxLogLevel < it.logLevel) {
61                 maxLogLevel = it.logLevel
62             }
63         }
64     }
65 
addPrinternull66     private fun addPrinter(printer: LogPrinter) {
67         printers.add(printer)
68         updateMaxLogLevel()
69     }
70 
removePrinternull71     private fun removePrinter(printer: LogPrinter) {
72         printers.remove(printer)
73         updateMaxLogLevel()
74     }
75 
setConsoleLogLevelnull76     fun setConsoleLogLevel(level: LogLevel): HostStubGenLogger {
77         // If there's already a console log printer set, remove it, and then add a new one
78         consolePrinter?.let {
79             removePrinter(it)
80         }
81         val cp = StreamPrinter(level, PrintWriter(System.out))
82         addPrinter(cp)
83         consolePrinter = cp
84 
85         return this
86     }
87 
addFilePrinternull88     fun addFilePrinter(level: LogLevel, logFilename: String): HostStubGenLogger {
89         addPrinter(StreamPrinter(level, PrintWriter(BufferedOutputStream(
90             FileOutputStream(logFilename)))))
91 
92         log.i("Log file set: $logFilename for $level")
93 
94         return this
95     }
96 
97     /** Flush all the printers */
flushnull98     fun flush() {
99         printers.forEach { it.flush() }
100     }
101 
indentnull102     fun indent() {
103         indentLevel++
104     }
105 
unindentnull106     fun unindent() {
107         if (indentLevel <= 0) {
108             throw IllegalStateException("Unbalanced unindent() call.")
109         }
110         indentLevel--
111     }
112 
withIndentnull113     inline fun <T> withIndent(block: () -> T): T {
114         try {
115             indent()
116             return block()
117         } finally {
118             unindent()
119         }
120     }
121 
isEnablednull122     fun isEnabled(level: LogLevel): Boolean {
123         return level.ordinal <= maxLogLevel.ordinal
124     }
125 
printlnnull126     fun println(level: LogLevel, message: String) {
127         if (message.isEmpty()) {
128             return // Don't print an empty message.
129         }
130         printers.forEach {
131             if (it.logLevel.ordinal >= level.ordinal) {
132                 it.println(level, indent, message)
133             }
134         }
135     }
136 
printlnnull137     fun println(level: LogLevel, format: String, vararg args: Any?) {
138         if (isEnabled(level)) {
139             println(level, String.format(format, *args))
140         }
141     }
142 
143     /** Log an error. */
enull144     fun e(message: String) {
145         println(LogLevel.Error, message)
146     }
147 
148     /** Log an error. */
enull149     fun e(format: String, vararg args: Any?) {
150         println(LogLevel.Error, format, *args)
151     }
152 
153     /** Log a warning. */
wnull154     fun w(message: String) {
155         println(LogLevel.Warn, message)
156     }
157 
158     /** Log a warning. */
wnull159     fun w(format: String, vararg args: Any?) {
160         println(LogLevel.Warn, format, *args)
161     }
162 
163     /** Log an info message. */
inull164     fun i(message: String) {
165         println(LogLevel.Info, message)
166     }
167 
168     /** Log an info message. */
inull169     fun i(format: String, vararg args: Any?) {
170         println(LogLevel.Info, format, *args)
171     }
172 
173     /** Log a verbose message. */
vnull174     fun v(message: String) {
175         println(LogLevel.Verbose, message)
176     }
177 
178     /** Log a verbose message. */
vnull179     fun v(format: String, vararg args: Any?) {
180         println(LogLevel.Verbose, format, *args)
181     }
182 
183     /** Log a debug message. */
dnull184     fun d(message: String) {
185         println(LogLevel.Debug, message)
186     }
187 
188     /** Log a debug message. */
dnull189     fun d(format: String, vararg args: Any?) {
190         println(LogLevel.Debug, format, *args)
191     }
192 
logTimenull193     inline fun <T> logTime(level: LogLevel, message: String, block: () -> T): Double {
194         var ret: Double = -1.0
195         val start = System.currentTimeMillis()
196         try {
197             block()
198         } finally {
199             val end = System.currentTimeMillis()
200             ret = (end - start) / 1000.0
201             if (isEnabled(level)) {
202                 println(level,
203                     String.format("%s: took %.1f second(s).", message, (end - start) / 1000.0))
204             }
205         }
206         return ret
207     }
208 
209     /** Do an "i" log with how long it took. */
iTimenull210     inline fun <T> iTime(message: String, block: () -> T): Double {
211         return logTime(LogLevel.Info, message, block)
212     }
213 
214     /** Do a "v" log with how long it took. */
vTimenull215     inline fun <T> vTime(message: String, block: () -> T): Double {
216         return logTime(LogLevel.Verbose, message, block)
217     }
218 
219     /** Do a "d" log with how long it took. */
dTimenull220     inline fun <T> dTime(message: String, block: () -> T): Double {
221         return logTime(LogLevel.Debug, message, block)
222     }
223 
224     /**
225      * Similar to the other "xTime" methods, but the message is not supposed to be printed.
226      * It's only used to measure the duration with the same interface as other log methods.
227      */
nTimenull228     inline fun <T> nTime(block: () -> T): Double {
229         return logTime(LogLevel.Debug, "", block)
230     }
231 
forVerbosenull232     inline fun forVerbose(block: () -> Unit) {
233         if (isEnabled(LogLevel.Verbose)) {
234             block()
235         }
236     }
237 
forDebugnull238     inline fun forDebug(block: () -> Unit) {
239         if (isEnabled(LogLevel.Debug)) {
240             block()
241         }
242     }
243 
244     /** Return a Writer for a given log level. */
getWriternull245     fun getWriter(level: LogLevel): Writer {
246         return MultiplexingWriter(level)
247     }
248 
249     private inner class MultiplexingWriter(val level: LogLevel) : Writer() {
forPrintersnull250         private inline fun forPrinters(callback: (LogPrinter) -> Unit) {
251             printers.forEach {
252                 if (it.logLevel.ordinal >= level.ordinal) {
253                     callback(it)
254                 }
255             }
256         }
257 
closenull258         override fun close() {
259             flush()
260         }
261 
flushnull262         override fun flush() {
263             forPrinters {
264                 it.flush()
265             }
266         }
267 
writenull268         override fun write(cbuf: CharArray, off: Int, len: Int) {
269             // TODO Apply indent
270             forPrinters {
271                 it.write(cbuf, off, len)
272             }
273         }
274     }
275 
276     /**
277      * Handle log-related command line arguments.
278      */
maybeHandleCommandLineArgnull279     fun maybeHandleCommandLineArg(currentArg: String, nextArgProvider: () -> String): Boolean {
280         when (currentArg) {
281             "-v", "--verbose" -> setConsoleLogLevel(LogLevel.Verbose)
282             "-d", "--debug" -> setConsoleLogLevel(LogLevel.Debug)
283             "-q", "--quiet" -> setConsoleLogLevel(LogLevel.None)
284             "--verbose-log" -> addFilePrinter(LogLevel.Verbose, nextArgProvider())
285             "--debug-log" -> addFilePrinter(LogLevel.Debug, nextArgProvider())
286             else -> return false
287         }
288         return true
289     }
290 }
291 
292 private interface LogPrinter {
293     val logLevel: LogLevel
294 
printlnnull295     fun println(logLevel: LogLevel, indent: String, message: String)
296 
297     // TODO: This should be removed once MultiplexingWriter starts applying indent, at which point
298     // println() should be used instead.
299     fun write(cbuf: CharArray, off: Int, len: Int)
300 
301     fun flush()
302 }
303 
304 private class StreamPrinter(
305     override val logLevel: LogLevel,
306     val out: PrintWriter,
307 ) : LogPrinter {
308     override fun println(logLevel: LogLevel, indent: String, message: String) {
309         out.print(indent)
310         out.println(message)
311     }
312 
313     override fun write(cbuf: CharArray, off: Int, len: Int) {
314         out.write(cbuf, off, len)
315     }
316 
317     override fun flush() {
318         out.flush()
319     }
320 }
321