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