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