Cts框架解析(20)-cts自身log系统

Log系统


log系统中类图


在cts的log包中。


Cts框架解析(20)-cts自身log系统_第1张图片


log系统的入口


入口类为CLog。采用的是代理模式,被代理的类是DDM内部的Log类。


CLog


public static class CLog {
        /**
         * The shim version of {@link Log#v(String, String)}.
         *
         * @param message The {@code String} to log
         */
        public static void v(String message) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.v(getClassName(2), message);
        }

        /**
         * The shim version of {@link Log#v(String, String)}.  Also calls String.format for
         * convenience.
         *
         * @param format A format string for the message to log
         * @param args The format string arguments
         */
        public static void v(String format, Object... args) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.v(getClassName(2), String.format(format, args));
        }

        /**
         * The shim version of {@link Log#d(String, String)}.
         *
         * @param message The {@code String} to log
         */
        public static void d(String message) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.d(getClassName(2), message);
        }

        /**
         * The shim version of {@link Log#d(String, String)}.  Also calls String.format for
         * convenience.
         *
         * @param format A format string for the message to log
         * @param args The format string arguments
         */
        public static void d(String format, Object... args) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.d(getClassName(2), String.format(format, args));
        }

        /**
         * The shim version of {@link Log#i(String, String)}.
         *
         * @param message The {@code String} to log
         */
        public static void i(String message) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.i(getClassName(2), message);
        }

        /**
         * The shim version of {@link Log#i(String, String)}.  Also calls String.format for
         * convenience.
         *
         * @param format A format string for the message to log
         * @param args The format string arguments
         */
        public static void i(String format, Object... args) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.i(getClassName(2), String.format(format, args));
        }

        /**
         * The shim version of {@link Log#w(String, String)}.
         *
         * @param message The {@code String} to log
         */
        public static void w(String message) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.w(getClassName(2), message);
        }

        /**
         * The shim version of {@link Log#w(String, String)}.  Also calls String.format for
         * convenience.
         *
         * @param format A format string for the message to log
         * @param args The format string arguments
         */
        public static void w(String format, Object... args) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.w(getClassName(2), String.format(format, args));
        }

        /**
         * The shim version of {@link Log#e(String, String)}.
         *
         * @param message The {@code String} to log
         */
        public static void e(String message) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.e(getClassName(2), message);
        }

        /**
         * The shim version of {@link Log#e(String, String)}.  Also calls String.format for
         * convenience.
         *
         * @param format A format string for the message to log
         * @param args The format string arguments
         */
        public static void e(String format, Object... args) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.e(getClassName(2), String.format(format, args));
        }

        /**
         * The shim version of {@link Log#e(String, Throwable)}.
         *
         * @param t the {@link Throwable} to output.
         */
        public static void e(Throwable t) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.e(getClassName(2), t);
        }

        /**
         * The shim version of {@link Log#logAndDisplay(LogLevel, String, String)}.
         *
         * @param logLevel the {@link LogLevel}
         * @param format A format string for the message to log
         * @param args The format string arguments
         */
        public static void logAndDisplay(LogLevel logLevel, String format, Object... args) {
            // frame 2: skip frames 0 (#getClassName) and 1 (this method)
            Log.logAndDisplay(logLevel, getClassName(2), String.format(format, args));
        }

        /**
         * Return the simple classname from the {@code frame}th stack frame in the call path.
         * Note: this method does <emph>not</emph> check array bounds for the stack trace length.
         *
         * @param frame The index of the stack trace frame to inspect for the class name
         * @return The simple class name (or full-qualified if an error occurs getting a ref to the
         *         class) for the given element of the stack trace.
         */
        public static String getClassName(int frame) {
            StackTraceElement[] frames = (new Throwable()).getStackTrace();
            String fullName = frames[frame].getClassName();
            @SuppressWarnings("rawtypes")
            Class klass = null;
            try {
                klass = Class.forName(fullName);
            } catch (ClassNotFoundException e) {
                // oops; not much we can do.
                // Intentionally fall through so we hit the null check below
            }

            if (klass == null) {
                return fullName;
            } else {
                return klass.getSimpleName();
            }
        }
    }

所以CLog里的方法也是直接调用Log相应的方法。


仔细看看Log类中相应的log方法,比如Log.i()、Log.e()。他们都不约而同的转向了println方法。


private static void println(LogLevel logLevel, String tag, String message) {
        if (logLevel.getPriority() >= mLevel.getPriority()) {
            if (sLogOutput != null) {
                sLogOutput.printLog(logLevel, tag, message);
            } else {
                printLog(logLevel, tag, message);
            }
        }
    }

其中sLogOutput为Log类放出的接口对象,接口定义如下所示。一般情况下如果你不设置该sLogOutput值的话,它为null。Log信息的打印会直接转到System.out.println,直接在控制台输出信息。但是如果我们实现该接口,将该属性附上值,我们就可重定向log的输出,转而到我们自己定义的输出设备中。所以Cts的log系统就是利用这一点做的。


ILogOutput


public interface ILogOutput {
        /**
         * Sent when a log message needs to be printed.
         * @param logLevel The {@link LogLevel} enum representing the priority of the message.
         * @param tag The tag associated with the message.
         * @param message The message to display.
         */
        public void printLog(LogLevel logLevel, String tag, String message);

        /**
         * Sent when a log message needs to be printed, and, if possible, displayed to the user
         * in a dialog box.
         * @param logLevel The {@link LogLevel} enum representing the priority of the message.
         * @param tag The tag associated with the message.
         * @param message The message to display.
         */
        public void printAndPromptLog(LogLevel logLevel, String tag, String message);
    }


    private static ILogOutput sLogOutput;

上面的ILogOutput接口只有2个方法,Cts扩展了该接口,重新定义了一个接口,继承ILogOutput接口,取名为ILeveledLogOutput。该接口主要定义输出设备类要使用的方法。


ILeveledLogOutput


public interface ILeveledLogOutput extends ILogOutput {

    /**
     * Initialize the log, creating any required IO resources.
     */
    public void init() throws IOException;

    /**
     * Gets the minimum log level to display.
     *
     * @return the current {@link LogLevel}
     */
    public LogLevel getLogLevel();

    /**
     * Sets the minimum log level to display.
     *
     * @param logLevel the {@link LogLevel} to display
     */
    public void setLogLevel(LogLevel logLevel);

    /**
     * Grabs a snapshot stream of the log data.
     * <p/>
     * Must not be called after {@link ILeveledLogOutput#closeLog()}.
     * <p/>
     * The returned stream is not guaranteed to have optimal performance. Callers may wish to
     * wrap result in a {@link BufferedInputStream}.
     *
     * @return a {@link InputStreamSource} of the log data
     * @throws IllegalStateException if called when log has been closed.
     */
    public InputStreamSource getLog();

    /**
     * Closes the log and performs any cleanup before closing, as necessary.
     */
    public void closeLog();

    /**
     * @return a {@link ILeveledLogOutput}
     */
    public ILeveledLogOutput clone();
}

ILeveledLogOutput拥有2个实现类


FileLogger


public class FileLogger implements ILeveledLogOutput {
    private static final String TEMP_FILE_PREFIX = "tradefed_log_";
    private static final String TEMP_FILE_SUFFIX = ".txt";

    @Option(name = "log-level", description = "the minimum log level to log.")
    private LogLevel mLogLevel = LogLevel.DEBUG;

    @Option(name = "log-level-display", shortName = 'l',
            description = "the minimum log level to display on stdout.",
            importance = Importance.ALWAYS)
    private LogLevel mLogLevelDisplay = LogLevel.ERROR;

    @Option(name = "log-tag-display", description = "Always display given tags logs on stdout")
    private Collection<String> mLogTagsDisplay = new HashSet<String>();

    @Option(name = "max-log-size", description = "maximum allowable size of tmp log data in mB.")
    private long mMaxLogSizeMbytes = 20;

    private SizeLimitedOutputStream mLogStream;

    /**
     * Adds tags to the log-tag-display list
     *
     * @param tags collection of tags to add
     */
    void addLogTagsDisplay(Collection<String> tags) {
        mLogTagsDisplay.addAll(tags);
    }

    public FileLogger() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void init() throws IOException {
        mLogStream = new SizeLimitedOutputStream(mMaxLogSizeMbytes * 1024 * 1024,
                TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX);
    }

    /**
     * Creates a new {@link FileLogger} with the same log level settings as the current object.
     * <p/>
     * Does not copy underlying log file content (ie the clone's log data will be written to a new
     * file.)
     */
    @Override
    public ILeveledLogOutput clone()  {
        FileLogger logger = new FileLogger();
        logger.setLogLevelDisplay(mLogLevelDisplay);
        logger.setLogLevel(mLogLevel);
        logger.addLogTagsDisplay(mLogTagsDisplay);
        return logger;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
        internalPrintLog(logLevel, tag, message, true /* force print to stdout */);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printLog(LogLevel logLevel, String tag, String message) {
        internalPrintLog(logLevel, tag, message, false /* don't force stdout */);
    }

    /**
     * A version of printLog(...) which can be forced to print to stdout, even if the log level
     * isn't above the urgency threshold.
     */
    private void internalPrintLog(LogLevel logLevel, String tag, String message,
            boolean forceStdout) {
        String outMessage = LogUtil.getLogFormatString(logLevel, tag, message);
        if (forceStdout
                || logLevel.getPriority() >= mLogLevelDisplay.getPriority()
                || mLogTagsDisplay.contains(tag)) {
            System.out.print(outMessage);
        }
        try {
            writeToLog(outMessage);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Writes given message to log.
     * <p/>
     * Exposed for unit testing.
     *
     * @param outMessage the entry to write to log
     * @throws IOException
     */
    void writeToLog(String outMessage) throws IOException {
        if (mLogStream != null) {
            mLogStream.write(outMessage.getBytes());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public LogLevel getLogLevel() {
        return mLogLevel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setLogLevel(LogLevel logLevel) {
        mLogLevel = logLevel;
    }

    /**
     * Sets the log level filtering for stdout.
     *
     * @param logLevel the minimum {@link LogLevel} to display
     */
    void setLogLevelDisplay(LogLevel logLevel) {
        mLogLevelDisplay = logLevel;
    }

    /**
     * Gets the log level filtering for stdout.
     *
     * @return the current {@link LogLevel}
     */
    LogLevel getLogLevelDisplay() {
        return mLogLevelDisplay;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public InputStreamSource getLog() {
        if (mLogStream != null) {
            try {
                // create a InputStream from log file
                mLogStream.flush();
                return new SnapshotInputStreamSource(mLogStream.getData());
            } catch (IOException e) {
                System.err.println("Failed to get log");
                e.printStackTrace();
            }
        }
        return new ByteArrayInputStreamSource(new byte[0]);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void closeLog() {
        try {
            doCloseLog();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Flushes stream and closes log file.
     * <p/>
     * Exposed for unit testing.
     *
     * @throws IOException
     */
    void doCloseLog() throws IOException {
        SizeLimitedOutputStream stream = mLogStream;
        mLogStream = null;
        if (stream != null) {
            try {
                stream.flush();
                stream.close();
            } finally {
                stream.delete();
            }
        }
    }

    /**
     * Dump the contents of the input stream to this log
     *
     * @param createInputStream
     * @throws IOException
     */
    void dumpToLog(InputStream inputStream) throws IOException {
        if (mLogStream != null) {
            StreamUtil.copyStreams(inputStream, mLogStream);
        }
    }
}

StdoutLogger


/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tradefed.log;

import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.result.ByteArrayInputStreamSource;
import com.android.tradefed.result.InputStreamSource;

import java.io.IOException;

/**
 * A {@link ILeveledLogOutput} that directs log messages to stdout.
 */
@OptionClass(alias = "stdout")
public class StdoutLogger implements ILeveledLogOutput {

    @Option(name="log-level", description="minimum log level to display.",
            importance = Importance.ALWAYS)
    private LogLevel mLogLevel = LogLevel.INFO;
    //StdoutLogger(){}
    /**
     * {@inheritDoc}
     * 
     * 
     */
    @Override
    public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
        printLog(logLevel, tag, message);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printLog(LogLevel logLevel, String tag, String message) {
        LogUtil.printLog(logLevel, tag, message);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setLogLevel(LogLevel logLevel) {
        mLogLevel = logLevel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public LogLevel getLogLevel() {
        return mLogLevel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void closeLog() {
        // ignore
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public InputStreamSource getLog() {
        // not supported - return empty stream
        return new ByteArrayInputStreamSource(new byte[0]);
    }

    @Override
    public ILeveledLogOutput clone()  {
        return new StdoutLogger();
    }

    @Override
    public void init() throws IOException {
        // ignore
    }
}

又定义了一个Log注册器的接口,叫ILogRegistry。该接口也是继承ILogOutput,但是该接口中的方法明显不是一个log输出系统,而是一个注册log所要用到的方法。


ILogRegistry


public interface ILogRegistry extends ILogOutput {

    /**
     * Set the log level display for the global log
     *
     * @param logLevel the {@link LogLevel} to use
     */
    public void setGlobalLogDisplayLevel(LogLevel logLevel);

    /**
     * Set the log tags to display for the global log
     */
    public void setGlobalLogTagDisplay(Collection<String> logTagsDisplay);

    /**
     * Returns current log level display for the global log
     *
     * @return logLevel the {@link LogLevel} to use
     */
    public LogLevel getGlobalLogDisplayLevel();

    /**
     * Registers the logger as the instance to use for the current thread.
     */
    public void registerLogger(ILeveledLogOutput log);

    /**
     * Unregisters the current logger in effect for the current thread.
     */
    public void unregisterLogger();

    /**
     * Dumps the entire contents of a {@link ILeveledLogOutput} logger to the global log.
     * <p/>
     * This is useful in scenarios where you know the logger's output won't be saved permanently,
     * yet you want the contents to be saved somewhere and not lost.
     *
     * @param log
     */
    public void dumpToGlobalLog(ILeveledLogOutput log);

    /**
     * Closes and removes all logs being managed by this LogRegistry.
     */
    public void closeAndRemoveAllLogs();


    /**
     * Diagnosis method to dump all logs to files.
     */
    public void dumpLogs();

}


ILogRegistry实现类,该类采用的是单例模式


LogRegistry


/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tradefed.log;

import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.StreamUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

/**
 * A {@link ILogRegistry} implementation that multiplexes and manages different loggers,
 * using the appropriate one based on the {@link ThreadGroup} of the thread making the call.
 * <p/>
 * Note that the registry hashes on the ThreadGroup in which a thread belongs. If a thread is
 * spawned with its own explicitly-supplied ThreadGroup, it will not inherit the parent thread's
 * logger, and thus will need to register its own logger with the LogRegistry if it wants to log
 * output.
 */
@OptionClass(alias="logreg")
public class LogRegistry implements ILogRegistry {
    private static final String LOG_TAG = "LogRegistry";
    private static LogRegistry mLogRegistry = null;
    private Map<ThreadGroup, ILeveledLogOutput> mLogTable =
            new Hashtable<ThreadGroup, ILeveledLogOutput>();
    private FileLogger mGlobalLogger;
    /**
     * Package-private constructor; callers should use {@link #getLogRegistry} to get an instance of
     * the {@link LogRegistry}.
     */
    LogRegistry() {
        try {
            mGlobalLogger = new FileLogger();
            mGlobalLogger.init();
        } catch (IOException e) {
            System.err.println("Failed to create global logger");
            throw new IllegalStateException(e);
        }

    }

    /**
     * Get the {@link LogRegistry} instance
     * <p/>
     *
     * @return a {@link LogRegistry} that can be used to register, get, write to, and close logs
     */
    public static ILogRegistry getLogRegistry() {
        if (mLogRegistry == null) {
            mLogRegistry = new LogRegistry();
        }

        return mLogRegistry;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setGlobalLogDisplayLevel(LogLevel logLevel) {
        mGlobalLogger.setLogLevelDisplay(logLevel);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setGlobalLogTagDisplay(Collection<String> logTagsDisplay) {
        mGlobalLogger.addLogTagsDisplay(logTagsDisplay);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public LogLevel getGlobalLogDisplayLevel() {
        return mGlobalLogger.getLogLevelDisplay();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerLogger(ILeveledLogOutput log) {
        ILeveledLogOutput oldValue = mLogTable.put(getCurrentThreadGroup(), log);
        if (oldValue != null) {
            Log.e(LOG_TAG, "Registering a new logger when one already exists for this thread!");
            oldValue.closeLog();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void unregisterLogger() {
        ThreadGroup currentThreadGroup = getCurrentThreadGroup();
        if (currentThreadGroup != null) {
            mLogTable.remove(currentThreadGroup);
        }
        else {
          printLog(LogLevel.ERROR, LOG_TAG, "Unregistering when thread has no logger registered.");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void dumpToGlobalLog(ILeveledLogOutput log) {
        InputStreamSource source = log.getLog();
        try {
            InputStream stream = source.createInputStream();
            mGlobalLogger.dumpToLog(stream);
            StreamUtil.close(stream);
        } catch (IOException e) {
            System.err.println("Failed to dump log");
            e.printStackTrace();
        } finally {
            source.cancel();
        }
    }

    /**
     * Gets the current thread Group.
     * <p/>
     * Exposed so unit tests can mock
     *
     * @return the ThreadGroup that the current thread belongs to
     */
    ThreadGroup getCurrentThreadGroup() {
        return Thread.currentThread().getThreadGroup();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printLog(LogLevel logLevel, String tag, String message) {
        ILeveledLogOutput log = getLogger();
        LogLevel currentLogLevel = log.getLogLevel();
        if (logLevel.getPriority() >= currentLogLevel.getPriority()) {
            log.printLog(logLevel, tag, message);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
        getLogger().printAndPromptLog(logLevel, tag, message);
    }

    /**
     * Gets the underlying logger associated with this thread.
     *
     * @return the logger for this thread, or null if one has not been registered.
     */
    ILeveledLogOutput getLogger() {
        ILeveledLogOutput log = mLogTable.get(getCurrentThreadGroup());
        if (log == null) {
            // If there's no logger set for this thread, use global logger
            log = mGlobalLogger;
        }
        return log;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void closeAndRemoveAllLogs() {
        Collection<ILeveledLogOutput> allLogs = mLogTable.values();
        Iterator<ILeveledLogOutput> iter = allLogs.iterator();
        while (iter.hasNext()) {
            ILeveledLogOutput log = iter.next();
            log.closeLog();
            iter.remove();
        }
        saveGlobalLog();
        mGlobalLogger.closeLog();
    }

    /**
     * {@inheritDoc}
     */
    private void saveGlobalLog() {
        InputStreamSource globalLog = mGlobalLogger.getLog();
        saveLog("tradefed_global_log_", globalLog);
        globalLog.cancel();
    }

    /**
     * Save log data to a temporary file
     *
     * @param filePrefix the file name prefix
     * @param logData the textual log data
     */
    private void saveLog(String filePrefix, InputStreamSource logData) {
        try {
            File tradefedLog = FileUtil.createTempFile(filePrefix, ".txt");
            FileUtil.writeToFile(logData.createInputStream(), tradefedLog);
            CLog.logAndDisplay(LogLevel.INFO, String.format("Saved log to %s", tradefedLog.getAbsolutePath()));
        } catch (IOException e) {
            // ignore
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void dumpLogs() {
        for (Map.Entry<ThreadGroup, ILeveledLogOutput> logEntry : mLogTable.entrySet()) {
            // use thread group name as file name - assume its descriptive
            String filePrefix = String.format("%s_log_", logEntry.getKey().getName());
            InputStreamSource logSource = logEntry.getValue().getLog();
            saveLog(filePrefix, logSource);
            logSource.cancel();
        }
        // save global log last
        saveGlobalLog();
    }
}

Log系统的运行过程


当cts系统开始运行的时候,我们会将LogRegistry对象注册为Log类中sLogOutput,将DDM内部的Log输出重定向到CTS自己的Log系统中,该对象就是LogRegistry对象,但是该对象不是直接输出lgo的,它照样采用代理模式或者也可以说是装饰者模式,负责调配、管理所有的Log设备。因为cts中的允许多任务同时运行,所以管理好每一个线程的log信息,很有必要。那具体是怎么做的呢,当Cts启动的时候,将Log注册器设置为DDM内部的log重定向设备,代码如下:


Cts框架解析(20)-cts自身log系统_第2张图片


在initLogging()方法中,调用Log.setLogOutput方法将我们的log注册器设置成sLogOutput属性。


public static void setLogOutput(ILogOutput logOutput) {
        sLogOutput = logOutput;
    }

那么以后所有调用CLog打印的信息的操作都会转到我们的LogRegistry对象中相应的方法中。那么就有必要来看看LogRegistry中对ILogOutput方法的实现


 /**
     * {@inheritDoc}
     */
    @Override
    public void printLog(LogLevel logLevel, String tag, String message) {
        ILeveledLogOutput log = getLogger();
        LogLevel currentLogLevel = log.getLogLevel();
        if (logLevel.getPriority() >= currentLogLevel.getPriority()) {
            log.printLog(logLevel, tag, message);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
        getLogger().printAndPromptLog(logLevel, tag, message);
    }

这两个方法中,首先第一步都是要调用getLogger方法得到具体的输出设备。


ILeveledLogOutput getLogger() {
        ILeveledLogOutput log = mLogTable.get(getCurrentThreadGroup());
        if (log == null) {
            // If there's no logger set for this thread, use global logger
            log = mGlobalLogger;
        }
        return log;
    }

由上面的代码可以看出,如果我们在Map对象中找不到log输出设备,我们就会使用全局log设备。那么就要先看看全局log器是啥,如果能从map中得到log器,那这个log器又是啥。

首先来看全局log器


private FileLogger mGlobalLogger;

它的定义是一个FileLogger对象:该对象是一个文件log系统,它会把CLog传入的信息在打印的同时也会保存到文件中。那么我们可以说这是个log文件。

那么如果Map不为空,我们能得到啥log器。追踪源码可以发现向map放入log器的代码存在于registerLogger方法中。那么该方法为何会将当前线程所在的线程组作为关键字放到map中。那我们就要看看谁调用了registerLogger方法。


 @Override
    public void registerLogger(ILeveledLogOutput log) {
        ILeveledLogOutput oldValue = mLogTable.put(getCurrentThreadGroup(), log);
        if (oldValue != null) {
            Log.e(LOG_TAG, "Registering a new logger when one already exists for this thread!");
            oldValue.closeLog();
        }
    }

原来在TestInvocation类中的invoke方法中调用这个方法


Cts框架解析(20)-cts自身log系统_第3张图片


如果你看过我之前的关于cts框架分析的文章,你应该会了解这个invoke在何时被调用,在每个任务启动的时候,会创建一个单独的线程来执行这个任务,这个时候invoke会被调用。那们我们也就明白为什么会用线程所在的线程组来当做Map的关键字啦。这样以后我们就可以通过调用者所在线程组得到器log输出设备。这样就区别了每个线程的log。


你可能感兴趣的:(Cts框架解析(20)-cts自身log系统)