blob: 12d3e24ffbe4190a34f437ee425b9947b92e579f [file] [log] [blame]
/*
* 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 com.android.tradefed.result.SnapshotInputStreamSource;
import com.android.tradefed.util.SizeLimitedOutputStream;
import com.android.tradefed.util.StreamUtil;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
/**
* A {@link ILeveledLogOutput} that directs log messages to a file and to stdout.
*/
@OptionClass(alias = "file")
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);
}
/** Returns the collection of tags to always display on stdout. */
Collection<String> getLogTagsDisplay() {
return mLogTagsDisplay;
}
public FileLogger() {
}
/**
* {@inheritDoc}
*/
@Override
public void init() throws IOException {
init(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX);
}
/**
* Alternative to {@link #init()} where we can specify the file name and suffix.
*
* @param logPrefix the file name where to log without extension.
* @param fileSuffix the extension of the file where to log.
*/
protected void init(String logPrefix, String fileSuffix) {
mLogStream =
new SizeLimitedOutputStream(mMaxLogSizeMbytes * 1024 * 1024, logPrefix, fileSuffix);
}
/**
* 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() {
doCloseLog();
}
/**
* Flushes stream and closes log file.
* <p/>
* Exposed for unit testing.
*/
void doCloseLog() {
SizeLimitedOutputStream stream = mLogStream;
mLogStream = null;
StreamUtil.flushAndCloseStream(stream);
if (stream != null) {
stream.delete();
}
}
/**
* Dump the contents of the input stream to this log
*
* @param inputStream
* @throws IOException
*/
void dumpToLog(InputStream inputStream) throws IOException {
if (mLogStream != null) {
StreamUtil.copyStreams(inputStream, mLogStream);
}
}
}