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;
     19 import com.android.ddmlib.Log.LogLevel;
     20 import com.android.tradefed.result.InputStreamSource;
     21 import com.android.tradefed.util.FileUtil;
     22 
     23 import java.io.File;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.text.SimpleDateFormat;
     27 import java.util.Collection;
     28 import java.util.Date;
     29 import java.util.Hashtable;
     30 import java.util.Iterator;
     31 import java.util.Map;
     32 
     33 /**
     34  * A {@link ILogRegistry} implementation that multiplexes and manages different loggers,
     35  * using the appropriate one based on the {@link ThreadGroup} of the thread making the call.
     36  * <p/>
     37  * Note that the registry hashes on the ThreadGroup in which a thread belongs. If a thread is
     38  * spawned with its own explicitly-supplied ThreadGroup, it will not inherit the parent thread's
     39  * logger, and thus will need to register its own logger with the LogRegistry if it wants to log
     40  * output.
     41  */
     42 public class LogRegistry implements ILogRegistry {
     43     private static final String LOG_TAG = "LogRegistry";
     44     private static final String GLOBAL_LOG_PREFIX = "tradefed_global_log_";
     45     private static final String HISTORY_LOG_PREFIX = "tradefed_history_log_";
     46     private static LogRegistry mLogRegistry = null;
     47     private Map<ThreadGroup, ILeveledLogOutput> mLogTable = new Hashtable<>();
     48     private FileLogger mGlobalLogger;
     49     private HistoryLogger mHistoryLogger;
     50 
     51     /**
     52      * Package-private constructor; callers should use {@link #getLogRegistry} to get an instance of
     53      * the {@link LogRegistry}.
     54      */
     55     LogRegistry() {
     56         try {
     57             mGlobalLogger = new FileLogger();
     58             mGlobalLogger.init();
     59         } catch (IOException e) {
     60             System.err.println("Failed to create global logger");
     61             throw new IllegalStateException(e);
     62         }
     63         try {
     64             mHistoryLogger = new HistoryLogger();
     65             mHistoryLogger.init();
     66         } catch (IOException e) {
     67             System.err.println("Failed to create history logger");
     68             throw new IllegalStateException(e);
     69         }
     70     }
     71 
     72     /**
     73      * Get the {@link LogRegistry} instance
     74      * <p/>
     75      *
     76      * @return a {@link LogRegistry} that can be used to register, get, write to, and close logs
     77      */
     78     public static ILogRegistry getLogRegistry() {
     79         if (mLogRegistry == null) {
     80             mLogRegistry = new LogRegistry();
     81         }
     82 
     83         return mLogRegistry;
     84     }
     85 
     86     /**
     87      * {@inheritDoc}
     88      */
     89     @Override
     90     public void setGlobalLogDisplayLevel(LogLevel logLevel) {
     91         mGlobalLogger.setLogLevelDisplay(logLevel);
     92         mHistoryLogger.setLogLevel(logLevel);
     93     }
     94 
     95     /**
     96      * {@inheritDoc}
     97      */
     98     @Override
     99     public void setGlobalLogTagDisplay(Collection<String> logTagsDisplay) {
    100         mGlobalLogger.addLogTagsDisplay(logTagsDisplay);
    101         mHistoryLogger.addLogTagsDisplay(logTagsDisplay);
    102     }
    103 
    104     /**
    105      * {@inheritDoc}
    106      */
    107     @Override
    108     public LogLevel getGlobalLogDisplayLevel() {
    109         return mGlobalLogger.getLogLevelDisplay();
    110     }
    111 
    112     /**
    113      * {@inheritDoc}
    114      */
    115     @Override
    116     public void registerLogger(ILeveledLogOutput log) {
    117         synchronized (mLogTable) {
    118             ILeveledLogOutput oldValue = mLogTable.put(getCurrentThreadGroup(), log);
    119             if (oldValue != null) {
    120                 Log.e(LOG_TAG, "Registering a new logger when one already exists for this thread!");
    121                 oldValue.closeLog();
    122             }
    123         }
    124     }
    125 
    126     /**
    127      * {@inheritDoc}
    128      */
    129     @Override
    130     public void unregisterLogger() {
    131         ThreadGroup currentThreadGroup = getCurrentThreadGroup();
    132         if (currentThreadGroup != null) {
    133             synchronized (mLogTable) {
    134                 mLogTable.remove(currentThreadGroup);
    135             }
    136         } else {
    137             printLog(LogLevel.ERROR, LOG_TAG, "Unregistering when thread has no logger "
    138                     + "registered.");
    139         }
    140     }
    141 
    142     /**
    143      * {@inheritDoc}
    144      */
    145     @Override
    146     public void dumpToGlobalLog(ILeveledLogOutput log) {
    147         try (InputStreamSource source = log.getLog();
    148                 InputStream stream = source.createInputStream()) {
    149             mGlobalLogger.dumpToLog(stream);
    150         } catch (IOException e) {
    151             System.err.println("Failed to dump log");
    152             e.printStackTrace();
    153         }
    154     }
    155 
    156     /**
    157      * Gets the current thread Group.
    158      * <p/>
    159      * Exposed so unit tests can mock
    160      *
    161      * @return the ThreadGroup that the current thread belongs to
    162      */
    163     ThreadGroup getCurrentThreadGroup() {
    164         return Thread.currentThread().getThreadGroup();
    165     }
    166 
    167     /**
    168      * {@inheritDoc}
    169      */
    170     @Override
    171     public void printLog(LogLevel logLevel, String tag, String message) {
    172         ILeveledLogOutput log = getLogger();
    173         LogLevel currentLogLevel = log.getLogLevel();
    174         if (logLevel.getPriority() >= currentLogLevel.getPriority()) {
    175             log.printLog(logLevel, tag, message);
    176         }
    177     }
    178 
    179     /**
    180      * {@inheritDoc}
    181      */
    182     @Override
    183     public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
    184         getLogger().printAndPromptLog(logLevel, tag, message);
    185     }
    186 
    187     /**
    188      * Gets the underlying logger associated with this thread.
    189      *
    190      * @return the logger for this thread, or null if one has not been registered.
    191      */
    192     ILeveledLogOutput getLogger() {
    193         synchronized (mLogTable) {
    194             ILeveledLogOutput log = mLogTable.get(getCurrentThreadGroup());
    195             if (log == null) {
    196                 // If there's no logger set for this thread, use global logger
    197                 log = mGlobalLogger;
    198             }
    199             return log;
    200         }
    201     }
    202 
    203     /**
    204      * {@inheritDoc}
    205      */
    206     @Override
    207     public void closeAndRemoveAllLogs() {
    208         synchronized (mLogTable) {
    209             Collection<ILeveledLogOutput> allLogs = mLogTable.values();
    210             Iterator<ILeveledLogOutput> iter = allLogs.iterator();
    211             while (iter.hasNext()) {
    212                 ILeveledLogOutput log = iter.next();
    213                 log.closeLog();
    214                 iter.remove();
    215             }
    216         }
    217         saveGlobalLog();
    218         mGlobalLogger.closeLog();
    219         // TODO: enable saving the history log when TF close
    220         // saveHistoryToDirLog();
    221         mHistoryLogger.closeLog();
    222     }
    223 
    224     /**
    225      * {@inheritDoc}
    226      */
    227     @Override
    228     public void saveGlobalLog() {
    229         saveGlobalLogToDir(null);
    230     }
    231 
    232     /** {@inheritDoc} */
    233     @Override
    234     public void logEvent(LogLevel logLevel, EventType event, Map<String, String> args) {
    235         // We always add the time of the event
    236         SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
    237         args.put("time", sdfDate.format(new Date()));
    238         mHistoryLogger.logEvent(logLevel, event, args);
    239     }
    240 
    241     /**
    242      * Save the global log data to a file in the specified directory.
    243      *
    244      * @param dir directory to save file, can be null, file will be saved in tmp directory.
    245      */
    246     private void saveGlobalLogToDir(File dir) {
    247         try (InputStreamSource globalLog = mGlobalLogger.getLog()) {
    248             saveLog(GLOBAL_LOG_PREFIX, globalLog, dir);
    249         }
    250     }
    251 
    252     /**
    253      * Save the history log data to a file in the specified directory.
    254      *
    255      * @param dir directory to save file, can be null, file will be saved in tmp directory.
    256      */
    257     private void saveHistoryLogToDir(File dir) {
    258         try (InputStreamSource globalLog = mHistoryLogger.getLog()) {
    259             saveLog(HISTORY_LOG_PREFIX, globalLog, dir);
    260         }
    261     }
    262 
    263     /**
    264      * Save log data to a temporary file
    265      *
    266      * @param filePrefix the file name prefix
    267      * @param logData the textual log data
    268      */
    269     private void saveLog(String filePrefix, InputStreamSource logData, File parentdir) {
    270         try {
    271             File tradefedLog = FileUtil.createTempFile(filePrefix, ".txt", parentdir);
    272             FileUtil.writeToFile(logData.createInputStream(), tradefedLog);
    273             System.out.println(String.format("Saved log to %s", tradefedLog.getAbsolutePath()));
    274         } catch (IOException e) {
    275             // ignore
    276         }
    277     }
    278 
    279     /**
    280      * {@inheritDoc}
    281      */
    282     @Override
    283     public void dumpLogs() {
    284         dumpLogsToDir(null);
    285     }
    286 
    287     /**
    288      * Save the log data to files in the specified directory.
    289      *
    290      * @param dir directory to save file, can be null, file will be saved in tmp directory.
    291      */
    292     public void dumpLogsToDir(File dir) {
    293         synchronized (mLogTable) {
    294             for (Map.Entry<ThreadGroup, ILeveledLogOutput> logEntry : mLogTable.entrySet()) {
    295                 // use thread group name as file name - assume its descriptive
    296                 String filePrefix = String.format("%s_log_", logEntry.getKey().getName());
    297                 try (InputStreamSource logSource = logEntry.getValue().getLog()) {
    298                     saveLog(filePrefix, logSource, dir);
    299                 }
    300             }
    301         }
    302         // save history log
    303         saveHistoryLogToDir(dir);
    304         // save global log last
    305         saveGlobalLogToDir(dir);
    306     }
    307 }
    308