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 return this 93 } 94 95 /** Flush all the printers */ flushnull96 fun flush() { 97 printers.forEach { it.flush() } 98 } 99 indentnull100 fun indent() { 101 indentLevel++ 102 } 103 unindentnull104 fun unindent() { 105 if (indentLevel <= 0) { 106 throw IllegalStateException("Unbalanced unindent() call.") 107 } 108 indentLevel-- 109 } 110 withIndentnull111 inline fun <T> withIndent(block: () -> T): T { 112 try { 113 indent() 114 return block() 115 } finally { 116 unindent() 117 } 118 } 119 isEnablednull120 fun isEnabled(level: LogLevel): Boolean { 121 return level.ordinal <= maxLogLevel.ordinal 122 } 123 printlnnull124 private fun println(level: LogLevel, message: String) { 125 printers.forEach { 126 if (it.logLevel.ordinal >= level.ordinal) { 127 it.println(level, indent, message) 128 } 129 } 130 } 131 printlnnull132 private fun println(level: LogLevel, format: String, vararg args: Any?) { 133 if (isEnabled(level)) { 134 println(level, String.format(format, *args)) 135 } 136 } 137 138 /** Log an error. */ enull139 fun e(message: String) { 140 println(LogLevel.Error, message) 141 } 142 143 /** Log an error. */ enull144 fun e(format: String, vararg args: Any?) { 145 println(LogLevel.Error, format, *args) 146 } 147 148 /** Log a warning. */ wnull149 fun w(message: String) { 150 println(LogLevel.Warn, message) 151 } 152 153 /** Log a warning. */ wnull154 fun w(format: String, vararg args: Any?) { 155 println(LogLevel.Warn, format, *args) 156 } 157 158 /** Log an info message. */ inull159 fun i(message: String) { 160 println(LogLevel.Info, message) 161 } 162 163 /** Log an info message. */ inull164 fun i(format: String, vararg args: Any?) { 165 println(LogLevel.Info, format, *args) 166 } 167 168 /** Log a verbose message. */ vnull169 fun v(message: String) { 170 println(LogLevel.Verbose, message) 171 } 172 173 /** Log a verbose message. */ vnull174 fun v(format: String, vararg args: Any?) { 175 println(LogLevel.Verbose, format, *args) 176 } 177 178 /** Log a debug message. */ dnull179 fun d(message: String) { 180 println(LogLevel.Debug, message) 181 } 182 183 /** Log a debug message. */ dnull184 fun d(format: String, vararg args: Any?) { 185 println(LogLevel.Debug, format, *args) 186 } 187 forVerbosenull188 inline fun forVerbose(block: () -> Unit) { 189 if (isEnabled(LogLevel.Verbose)) { 190 block() 191 } 192 } 193 forDebugnull194 inline fun forDebug(block: () -> Unit) { 195 if (isEnabled(LogLevel.Debug)) { 196 block() 197 } 198 } 199 200 /** Return a Writer for a given log level. */ getWriternull201 fun getWriter(level: LogLevel): Writer { 202 return MultiplexingWriter(level) 203 } 204 205 private inner class MultiplexingWriter(val level: LogLevel) : Writer() { forPrintersnull206 private inline fun forPrinters(callback: (LogPrinter) -> Unit) { 207 printers.forEach { 208 if (it.logLevel.ordinal >= level.ordinal) { 209 callback(it) 210 } 211 } 212 } 213 closenull214 override fun close() { 215 flush() 216 } 217 flushnull218 override fun flush() { 219 forPrinters { 220 it.flush() 221 } 222 } 223 writenull224 override fun write(cbuf: CharArray, off: Int, len: Int) { 225 // TODO Apply indent 226 forPrinters { 227 it.write(cbuf, off, len) 228 } 229 } 230 } 231 } 232 233 private interface LogPrinter { 234 val logLevel: LogLevel 235 printlnnull236 fun println(logLevel: LogLevel, indent: String, message: String) 237 238 // TODO: This should be removed once MultiplexingWriter starts applying indent, at which point 239 // println() should be used instead. 240 fun write(cbuf: CharArray, off: Int, len: Int) 241 242 fun flush() 243 } 244 245 private class StreamPrinter( 246 override val logLevel: LogLevel, 247 val out: PrintWriter, 248 ) : LogPrinter { 249 override fun println(logLevel: LogLevel, indent: String, message: String) { 250 out.print(indent) 251 out.println(message) 252 } 253 254 override fun write(cbuf: CharArray, off: Int, len: Int) { 255 out.write(cbuf, off, len) 256 } 257 258 override fun flush() { 259 out.flush() 260 } 261 } 262