Home | History | Annotate | Download | only in log
      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