1 /* 2 * Copyright (C) 2019 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.tradefed.log; 17 18 import com.android.tradefed.config.Option; 19 import com.android.tradefed.config.Option.Importance; 20 import com.android.tradefed.log.Log.LogLevel; 21 import com.android.tradefed.util.StreamUtil; 22 23 import java.io.IOException; 24 import java.io.OutputStream; 25 26 /** A {@link ILeveledLogOutput} that directs log messages to an output stream and to stdout. */ 27 public abstract class BaseStreamLogger<OS extends OutputStream> extends BaseLeveledLogOutput { 28 29 @Option(name = "log-level", description = "the minimum log level to log.") 30 private LogLevel mLogLevel = LogLevel.DEBUG; 31 32 @Option( 33 name = "log-level-display", 34 shortName = 'l', 35 description = "the minimum log level to display on stdout.", 36 importance = Importance.ALWAYS 37 ) 38 private LogLevel mLogLevelDisplay = LogLevel.ERROR; 39 40 // output stream to print logs to, exposed to subclasses 41 protected OS mOutputStream; 42 43 @Override getLogLevel()44 public LogLevel getLogLevel() { 45 return mLogLevel; 46 } 47 48 @Override setLogLevel(LogLevel logLevel)49 public void setLogLevel(LogLevel logLevel) { 50 mLogLevel = logLevel; 51 } 52 53 /** Sets the minimum {@link LogLevel} to display on stdout. */ setLogLevelDisplay(LogLevel logLevel)54 public void setLogLevelDisplay(LogLevel logLevel) { 55 mLogLevelDisplay = logLevel; 56 } 57 58 /** For compatibility */ setLogLevelDisplay(com.android.ddmlib.Log.LogLevel logLevel)59 public void setLogLevelDisplay(com.android.ddmlib.Log.LogLevel logLevel) { 60 mLogLevelDisplay = LogLevel.convertFromDdmlib(logLevel); 61 } 62 63 /** @return current minimum {@link LogLevel} to display on stdout. */ getLogLevelDisplay()64 public LogLevel getLogLevelDisplay() { 65 return mLogLevelDisplay; 66 } 67 68 @Override closeLog()69 public void closeLog() { 70 StreamUtil.flushAndCloseStream(mOutputStream); 71 mOutputStream = null; 72 } 73 74 @Override printAndPromptLog(LogLevel logLevel, String tag, String message)75 public void printAndPromptLog(LogLevel logLevel, String tag, String message) { 76 internalPrintLog(logLevel, tag, message, true /* force print to stdout */); 77 } 78 79 @Override printLog(LogLevel logLevel, String tag, String message)80 public void printLog(LogLevel logLevel, String tag, String message) { 81 internalPrintLog(logLevel, tag, message, false /* don't force stdout */); 82 } 83 84 /** 85 * A version of printLog(...) which can be forced to print to stdout, even if the log level 86 * isn't above the urgency threshold. 87 */ internalPrintLog( LogLevel logLevel, String tag, String message, boolean forceStdout)88 private void internalPrintLog( 89 LogLevel logLevel, String tag, String message, boolean forceStdout) { 90 String outMessage = Log.getLogFormatString(logLevel, tag, message); 91 if (shouldDisplay(forceStdout, mLogLevelDisplay, logLevel, tag)) { 92 System.out.print(outMessage); 93 } 94 if (shouldWrite(tag, logLevel, mLogLevel)) { 95 try { 96 writeToLog(outMessage); 97 } catch (IOException e) { 98 e.printStackTrace(); 99 } 100 } 101 } 102 103 // Determines whether a message should be written to the output stream. shouldWrite(String tag, LogLevel messageLogLevel, LogLevel invocationLogLevel)104 private boolean shouldWrite(String tag, LogLevel messageLogLevel, LogLevel invocationLogLevel) { 105 LogLevel forcedLevel = getForcedVerbosityMap().get(tag); 106 if (forcedLevel == null || !shouldForceVerbosity()) { 107 return true; 108 } 109 // Use the highest level of our forced and invocation to decide if we should log the 110 // particular tag. 111 int minWriteLevel = Math.max(forcedLevel.getPriority(), invocationLogLevel.getPriority()); 112 return messageLogLevel.getPriority() >= minWriteLevel; 113 } 114 115 /** 116 * Writes a message to the output stream. 117 * 118 * @param message the entry to write to log 119 * @throws IOException if an I/O error occurs 120 */ writeToLog(String message)121 protected void writeToLog(String message) throws IOException { 122 if (mOutputStream != null) { 123 mOutputStream.write(message.getBytes()); 124 } 125 } 126 } 127