1 /*
2  * Copyright (C) 2019 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.LogLevel;
19 import com.android.tradefed.config.Option;
20 import com.android.tradefed.config.Option.Importance;
21 import com.android.tradefed.util.StreamUtil;
22 
23 import java.io.IOException;
24 import java.io.OutputStream;
25 
26 /** A {@link ILeveledLogOutput} that directs log messages to an output stream and to stdout. */
27 public abstract class BaseStreamLogger<OS extends OutputStream> extends BaseLeveledLogOutput {
28 
29     @Option(name = "log-level", description = "the minimum log level to log.")
30     private LogLevel mLogLevel = LogLevel.DEBUG;
31 
32     @Option(
33         name = "log-level-display",
34         shortName = 'l',
35         description = "the minimum log level to display on stdout.",
36         importance = Importance.ALWAYS
37     )
38     private LogLevel mLogLevelDisplay = LogLevel.ERROR;
39 
40     // output stream to print logs to, exposed to subclasses
41     protected OS mOutputStream;
42 
43     @Override
getLogLevel()44     public LogLevel getLogLevel() {
45         return mLogLevel;
46     }
47 
48     @Override
setLogLevel(LogLevel logLevel)49     public void setLogLevel(LogLevel logLevel) {
50         mLogLevel = logLevel;
51     }
52 
53     /** Sets the minimum {@link LogLevel} to display on stdout. */
setLogLevelDisplay(LogLevel logLevel)54     public void setLogLevelDisplay(LogLevel logLevel) {
55         mLogLevelDisplay = logLevel;
56     }
57 
58     /** @return current minimum {@link LogLevel} to display on stdout. */
getLogLevelDisplay()59     LogLevel getLogLevelDisplay() {
60         return mLogLevelDisplay;
61     }
62 
63     @Override
closeLog()64     public void closeLog() {
65         StreamUtil.flushAndCloseStream(mOutputStream);
66         mOutputStream = null;
67     }
68 
69     @Override
printAndPromptLog(LogLevel logLevel, String tag, String message)70     public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
71         internalPrintLog(logLevel, tag, message, true /* force print to stdout */);
72     }
73 
74     @Override
printLog(LogLevel logLevel, String tag, String message)75     public void printLog(LogLevel logLevel, String tag, String message) {
76         internalPrintLog(logLevel, tag, message, false /* don't force stdout */);
77     }
78 
79     /**
80      * A version of printLog(...) which can be forced to print to stdout, even if the log level
81      * isn't above the urgency threshold.
82      */
internalPrintLog( LogLevel logLevel, String tag, String message, boolean forceStdout)83     private void internalPrintLog(
84             LogLevel logLevel, String tag, String message, boolean forceStdout) {
85         String outMessage = LogUtil.getLogFormatString(logLevel, tag, message);
86         if (shouldDisplay(forceStdout, mLogLevelDisplay, logLevel, tag)) {
87             System.out.print(outMessage);
88         }
89         if (shouldWrite(tag, logLevel, mLogLevel)) {
90             try {
91                 writeToLog(outMessage);
92             } catch (IOException e) {
93                 e.printStackTrace();
94             }
95         }
96     }
97 
98     // Determines whether a message should be written to the output stream.
shouldWrite(String tag, LogLevel messageLogLevel, LogLevel invocationLogLevel)99     private boolean shouldWrite(String tag, LogLevel messageLogLevel, LogLevel invocationLogLevel) {
100         LogLevel forcedLevel = getForcedVerbosityMap().get(tag);
101         if (forcedLevel == null || !shouldForceVerbosity()) {
102             return true;
103         }
104         // Use the highest level of our forced and invocation to decide if we should log the
105         // particular tag.
106         int minWriteLevel = Math.max(forcedLevel.getPriority(), invocationLogLevel.getPriority());
107         return messageLogLevel.getPriority() >= minWriteLevel;
108     }
109 
110     /**
111      * Writes a message to the output stream.
112      *
113      * @param message the entry to write to log
114      * @throws IOException if an I/O error occurs
115      */
writeToLog(String message)116     protected void writeToLog(String message) throws IOException {
117         if (mOutputStream != null) {
118             mOutputStream.write(message.getBytes());
119         }
120     }
121 }
122