1 /*
2  * Copyright (C) 2016 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 
17 package com.android.tradefed.result;
18 
19 import com.android.ddmlib.Log.LogLevel;
20 import com.android.tradefed.invoker.IInvocationContext;
21 import com.android.tradefed.invoker.TestInvocation;
22 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
23 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
24 import com.android.tradefed.log.LogRegistry;
25 import com.android.tradefed.log.LogUtil.CLog;
26 import com.android.tradefed.log.StdoutLogger;
27 import com.android.tradefed.util.StreamUtil;
28 import com.android.tradefed.util.SystemUtil;
29 
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.util.List;
33 
34 /** A {@link ResultForwarder} for saving logs with the global file saver. */
35 public class LogSaverResultForwarder extends ResultForwarder implements ILogSaverListener {
36 
37     ILogSaver mLogSaver;
38 
LogSaverResultForwarder(ILogSaver logSaver, List<ITestInvocationListener> listeners)39     public LogSaverResultForwarder(ILogSaver logSaver,
40             List<ITestInvocationListener> listeners) {
41         super(listeners);
42         mLogSaver = logSaver;
43         for (ITestInvocationListener listener : listeners) {
44             if (listener instanceof ILogSaverListener) {
45                 ((ILogSaverListener) listener).setLogSaver(mLogSaver);
46             }
47         }
48     }
49 
50     /**
51      * {@inheritDoc}
52      */
53     @Override
invocationStarted(IInvocationContext context)54     public void invocationStarted(IInvocationContext context) {
55         // Intentionally call invocationStarted for the log saver first.
56         try {
57             mLogSaver.invocationStarted(context);
58         } catch (RuntimeException e) {
59             CLog.e("Caught runtime exception from log saver: %s", mLogSaver.getClass().getName());
60             CLog.e(e);
61         }
62         InvocationSummaryHelper.reportInvocationStarted(getListeners(), context);
63     }
64 
65     /**
66      * {@inheritDoc}
67      */
68     @Override
invocationEnded(long elapsedTime)69     public void invocationEnded(long elapsedTime) {
70         InvocationSummaryHelper.reportInvocationEnded(getListeners(), elapsedTime);
71         // Intentionally call invocationEnded for the log saver last.
72         try {
73             mLogSaver.invocationEnded(elapsedTime);
74         } catch (RuntimeException e) {
75             CLog.e("Caught runtime exception from log saver: %s", mLogSaver.getClass().getName());
76             CLog.e(e);
77         }
78         reportEndHostLog(getListeners(), mLogSaver, TestInvocation.TRADEFED_END_HOST_LOG);
79     }
80 
81     /** Log a final file before completion */
logFile( List<ITestInvocationListener> listeners, ILogSaver saver, InputStreamSource source, String name, LogDataType type)82     public static void logFile(
83             List<ITestInvocationListener> listeners,
84             ILogSaver saver,
85             InputStreamSource source,
86             String name,
87             LogDataType type) {
88         try (InputStream stream = source.createInputStream()) {
89             LogFile logFile = saver.saveLogData(name, type, stream);
90 
91             for (ITestInvocationListener listener : listeners) {
92                 try {
93                     if (listener instanceof ILogSaverListener) {
94                         ((ILogSaverListener) listener).testLogSaved(name, type, source, logFile);
95                         ((ILogSaverListener) listener).logAssociation(name, logFile);
96                     }
97                 } catch (Exception e) {
98                     CLog.logAndDisplay(LogLevel.ERROR, e.getMessage());
99                     CLog.e(e);
100                 }
101             }
102         } catch (IOException e) {
103             CLog.e(e);
104         }
105     }
106 
107     /** Reports host_log from session in progress. */
reportEndHostLog( List<ITestInvocationListener> listeners, ILogSaver saver, String name)108     public static void reportEndHostLog(
109             List<ITestInvocationListener> listeners, ILogSaver saver, String name) {
110         LogRegistry registry = (LogRegistry) LogRegistry.getLogRegistry();
111         try (InputStreamSource source = registry.getLogger().getLog()) {
112             if (source == null) {
113                 if (!(registry.getLogger() instanceof StdoutLogger)) {
114                     CLog.e("%s stream was null, skip saving it.", name);
115                 }
116                 return;
117             }
118             logFile(listeners, saver, source, name, LogDataType.HOST_LOG);
119             if (SystemUtil.isRemoteEnvironment()) {
120                 try (InputStream stream = source.createInputStream()) {
121                     // In remote environment, dump to the stdout so we can get the logs in the
122                     // console.
123                     System.out.println(
124                             String.format(
125                                     "===== Result Reporters =====\n%s",
126                                     StreamUtil.getStringFromStream(stream)));
127                 }
128             }
129         } catch (IOException e) {
130             CLog.e(e);
131         }
132     }
133 
134     /**
135      * {@inheritDoc}
136      * <p/>
137      * Also, save the log file with the global {@link ILogSaver} and call
138      * {@link ILogSaverListener#testLogSaved(String, LogDataType, InputStreamSource, LogFile)}
139      * for those listeners implementing the {@link ILogSaverListener} interface.
140      */
141     @Override
testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)142     public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
143         testLogForward(dataName, dataType, dataStream);
144         try {
145             if (dataStream == null) {
146                 CLog.w("Skip forwarding of '%s', data stream is null.", dataName);
147                 return;
148             }
149             long startTime = System.currentTimeMillis();
150             LogFile logFile = null;
151             try {
152                 // If it's a file, copy it directly as it's faster
153                 if (dataStream instanceof FileInputStreamSource) {
154                     logFile =
155                             mLogSaver.saveLogFile(
156                                     dataName,
157                                     dataType,
158                                     ((FileInputStreamSource) dataStream).getFile());
159                 } else {
160                     logFile =
161                             mLogSaver.saveLogData(
162                                     dataName, dataType, dataStream.createInputStream());
163                 }
164             } finally {
165                 InvocationMetricLogger.addInvocationMetrics(
166                         InvocationMetricKey.LOG_SAVING_TIME,
167                         System.currentTimeMillis() - startTime);
168                 InvocationMetricLogger.addInvocationMetrics(
169                         InvocationMetricKey.LOG_SAVING_COUNT, 1);
170             }
171             for (ITestInvocationListener listener : getListeners()) {
172                 if (listener instanceof ILogSaverListener) {
173                     ((ILogSaverListener) listener).testLogSaved(dataName, dataType,
174                             dataStream, logFile);
175                     ((ILogSaverListener) listener).logAssociation(dataName, logFile);
176                 }
177             }
178         } catch (RuntimeException | IOException e) {
179             CLog.e("Failed to save log data");
180             CLog.e(e);
181         }
182     }
183 
184     /** Only forward the testLog instead of saving the log first. */
testLogForward( String dataName, LogDataType dataType, InputStreamSource dataStream)185     public void testLogForward(
186             String dataName, LogDataType dataType, InputStreamSource dataStream) {
187         super.testLog(dataName, dataType, dataStream);
188     }
189 
190     /**
191      * {@inheritDoc}
192      *
193      * <p>If {@link LogSaverResultForwarder} is wrap in another one, ensure we forward the
194      * testLogSaved callback to the listeners under it.
195      */
196     @Override
testLogSaved( String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile)197     public void testLogSaved(
198             String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile) {
199         try {
200             for (ITestInvocationListener listener : getListeners()) {
201                 if (listener instanceof ILogSaverListener) {
202                     ((ILogSaverListener) listener)
203                             .testLogSaved(dataName, dataType, dataStream, logFile);
204                 }
205             }
206         } catch (RuntimeException e) {
207             CLog.e("Failed to save log data");
208             CLog.e(e);
209         }
210     }
211 
212     /** {@inheritDoc} */
213     @Override
logAssociation(String dataName, LogFile logFile)214     public void logAssociation(String dataName, LogFile logFile) {
215         for (ITestInvocationListener listener : getListeners()) {
216             try {
217                 // Forward the logAssociation call
218                 if (listener instanceof ILogSaverListener) {
219                     ((ILogSaverListener) listener).logAssociation(dataName, logFile);
220                 }
221             } catch (RuntimeException e) {
222                 CLog.e("Failed to provide the log association");
223                 CLog.e(e);
224             }
225         }
226     }
227 }
228