1 /* 2 * Copyright (C) 2010 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.ddmlib.Log.LogLevel; 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.config.Option.Importance; 21 import com.android.tradefed.config.OptionClass; 22 import com.android.tradefed.config.OptionCopier; 23 import com.android.tradefed.result.ByteArrayInputStreamSource; 24 import com.android.tradefed.result.InputStreamSource; 25 import com.android.tradefed.result.SnapshotInputStreamSource; 26 import com.android.tradefed.util.SizeLimitedOutputStream; 27 import com.android.tradefed.util.StreamUtil; 28 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.Collection; 32 import java.util.HashSet; 33 34 /** 35 * A {@link ILeveledLogOutput} that directs log messages to a file and to stdout. 36 */ 37 @OptionClass(alias = "file") 38 public class FileLogger implements ILeveledLogOutput { 39 private static final String TEMP_FILE_PREFIX = "tradefed_log_"; 40 private static final String TEMP_FILE_SUFFIX = ".txt"; 41 42 @Option(name = "log-level", description = "the minimum log level to log.") 43 private LogLevel mLogLevel = LogLevel.DEBUG; 44 45 @Option(name = "log-level-display", shortName = 'l', 46 description = "the minimum log level to display on stdout.", 47 importance = Importance.ALWAYS) 48 private LogLevel mLogLevelDisplay = LogLevel.ERROR; 49 50 @Option(name = "log-tag-display", description = "Always display given tags logs on stdout") 51 private Collection<String> mLogTagsDisplay = new HashSet<String>(); 52 53 @Option(name = "max-log-size", description = "maximum allowable size of tmp log data in mB.") 54 private long mMaxLogSizeMbytes = 20; 55 56 private SizeLimitedOutputStream mLogStream; 57 58 /** 59 * Adds tags to the log-tag-display list 60 * 61 * @param tags collection of tags to add 62 */ addLogTagsDisplay(Collection<String> tags)63 void addLogTagsDisplay(Collection<String> tags) { 64 mLogTagsDisplay.addAll(tags); 65 } 66 67 /** Returns the collection of tags to always display on stdout. */ getLogTagsDisplay()68 Collection<String> getLogTagsDisplay() { 69 return mLogTagsDisplay; 70 } 71 FileLogger()72 public FileLogger() { 73 } 74 75 /** 76 * {@inheritDoc} 77 */ 78 @Override init()79 public void init() throws IOException { 80 init(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX); 81 } 82 83 /** 84 * Alternative to {@link #init()} where we can specify the file name and suffix. 85 * 86 * @param logPrefix the file name where to log without extension. 87 * @param fileSuffix the extension of the file where to log. 88 */ init(String logPrefix, String fileSuffix)89 protected void init(String logPrefix, String fileSuffix) { 90 mLogStream = 91 new SizeLimitedOutputStream(mMaxLogSizeMbytes * 1024 * 1024, logPrefix, fileSuffix); 92 } 93 94 /** 95 * Creates a new {@link FileLogger} with the same log level settings as the current object. 96 * <p/> 97 * Does not copy underlying log file content (ie the clone's log data will be written to a new 98 * file.) 99 */ 100 @Override clone()101 public ILeveledLogOutput clone() { 102 FileLogger logger = new FileLogger(); 103 OptionCopier.copyOptionsNoThrow(this, logger); 104 return logger; 105 } 106 107 /** 108 * {@inheritDoc} 109 */ 110 @Override printAndPromptLog(LogLevel logLevel, String tag, String message)111 public void printAndPromptLog(LogLevel logLevel, String tag, String message) { 112 internalPrintLog(logLevel, tag, message, true /* force print to stdout */); 113 } 114 115 /** 116 * {@inheritDoc} 117 */ 118 @Override printLog(LogLevel logLevel, String tag, String message)119 public void printLog(LogLevel logLevel, String tag, String message) { 120 internalPrintLog(logLevel, tag, message, false /* don't force stdout */); 121 } 122 123 /** 124 * A version of printLog(...) which can be forced to print to stdout, even if the log level 125 * isn't above the urgency threshold. 126 */ internalPrintLog(LogLevel logLevel, String tag, String message, boolean forceStdout)127 private void internalPrintLog(LogLevel logLevel, String tag, String message, 128 boolean forceStdout) { 129 String outMessage = LogUtil.getLogFormatString(logLevel, tag, message); 130 if (forceStdout 131 || logLevel.getPriority() >= mLogLevelDisplay.getPriority() 132 || mLogTagsDisplay.contains(tag)) { 133 System.out.print(outMessage); 134 } 135 try { 136 writeToLog(outMessage); 137 } catch (IOException e) { 138 e.printStackTrace(); 139 } 140 } 141 142 /** 143 * Writes given message to log. 144 * <p/> 145 * Exposed for unit testing. 146 * 147 * @param outMessage the entry to write to log 148 * @throws IOException 149 */ writeToLog(String outMessage)150 void writeToLog(String outMessage) throws IOException { 151 if (mLogStream != null) { 152 mLogStream.write(outMessage.getBytes()); 153 } 154 } 155 156 /** 157 * {@inheritDoc} 158 */ 159 @Override getLogLevel()160 public LogLevel getLogLevel() { 161 return mLogLevel; 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override setLogLevel(LogLevel logLevel)168 public void setLogLevel(LogLevel logLevel) { 169 mLogLevel = logLevel; 170 } 171 172 /** 173 * Sets the log level filtering for stdout. 174 * 175 * @param logLevel the minimum {@link LogLevel} to display 176 */ setLogLevelDisplay(LogLevel logLevel)177 void setLogLevelDisplay(LogLevel logLevel) { 178 mLogLevelDisplay = logLevel; 179 } 180 181 /** 182 * Gets the log level filtering for stdout. 183 * 184 * @return the current {@link LogLevel} 185 */ getLogLevelDisplay()186 LogLevel getLogLevelDisplay() { 187 return mLogLevelDisplay; 188 } 189 190 /** Returns the max log size of the log in MBytes. */ getMaxLogSizeMbytes()191 public long getMaxLogSizeMbytes() { 192 return mMaxLogSizeMbytes; 193 } 194 195 /** 196 * {@inheritDoc} 197 */ 198 @Override getLog()199 public InputStreamSource getLog() { 200 if (mLogStream != null) { 201 try { 202 // create a InputStream from log file 203 mLogStream.flush(); 204 return new SnapshotInputStreamSource("FileLogger", mLogStream.getData()); 205 } catch (IOException e) { 206 System.err.println("Failed to get log"); 207 e.printStackTrace(); 208 } 209 } 210 return new ByteArrayInputStreamSource(new byte[0]); 211 } 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override closeLog()217 public void closeLog() { 218 doCloseLog(); 219 } 220 221 /** 222 * Flushes stream and closes log file. 223 * <p/> 224 * Exposed for unit testing. 225 */ doCloseLog()226 void doCloseLog() { 227 SizeLimitedOutputStream stream = mLogStream; 228 mLogStream = null; 229 StreamUtil.flushAndCloseStream(stream); 230 if (stream != null) { 231 stream.delete(); 232 } 233 } 234 235 /** 236 * Dump the contents of the input stream to this log 237 * 238 * @param inputStream 239 * @throws IOException 240 */ dumpToLog(InputStream inputStream)241 void dumpToLog(InputStream inputStream) throws IOException { 242 if (mLogStream != null) { 243 StreamUtil.copyStreams(inputStream, mLogStream); 244 } 245 } 246 } 247