# Log系统
## log系统中类图
在cts的log包中。
![](https://box.kancloud.cn/2016-01-09_56911ddd5cd21.jpg)
## 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重定向设备,代码如下:
![](https://box.kancloud.cn/2016-01-09_56911ddd8355d.jpg)
在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方法中调用这个方法
![](https://box.kancloud.cn/2016-01-09_56911dddcc2b4.jpg)
如果你看过我之前的关于cts框架分析的文章,你应该会了解这个invoke在何时被调用,在每个任务启动的时候,会创建一个单独的线程来执行这个任务,这个时候invoke会被调用。那们我们也就明白为什么会用线程所在的线程组来当做Map的关键字啦。这样以后我们就可以通过调用者所在线程组得到器log输出设备。这样就区别了每个线程的log。
- 前言
- (1)-windows下cts配置
- (2)-cts调试环境的搭建
- (3)-基础库tradefederation配置
- (4)-任务的添加
- (5)-9大组件配置
- (6)-任务的执行
- (7)-任务执行的调度室
- (8)-IBuildProvider
- (9)-IDeviceRecovery
- (10)-TestDeviceOptions
- (11)-ICommandOptions
- (12)-ITargetPreparer
- (13)-任务执行过程
- (14)-任务执行过程
- (15)-任务执行完
- (16)-logcat信息收集系统
- (17)-fastboot状态监听器
- (18)-设备恢复
- (19)-设备状态的分类以及恢复模式的分类
- (20)-cts自身log系统
- (21)-测试结果收集系统
- (22)-自动检测设备
- (23)-设备分类
- (24)-case的组织