/* * 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.INFO; @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); } } }