/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.management.internal.cli.functions;
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.apache.geode.GemFireIOException;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.internal.InternalEntity;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.management.internal.MBeanJMXAdapter;
import org.apache.geode.management.internal.cli.GfshParser;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.result.ResultBuilder;
import org.apache.geode.management.internal.cli.util.ReadWriteFile;
// TODO:LOG:CONVERT: supports log-file only -- update to support Log4J 2 as well?
/**
*
* @since GemFire 7.0
*/
public class LogFileFunction implements Function, InternalEntity {
private static final Logger logger = LogService.getLogger();
public static final String ID = LogFileFunction.class.getName();
private static final long serialVersionUID = 1L;
@Override
public void execute(final FunctionContext context) {
Thread waiting = new Thread(new Runnable() {
public void run() {
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
Cache cache = CacheFactory.getAnyInstance();
if (isDebugEnabled) {
logger.debug("Exporting logs LogFileFunction");
}
Object[] args = (Object[]) context.getArguments();
String targetDirName = ((String) args[0]);
String logLevel = ((String) args[1]);
String onlyLogLevel = ((String) args[2]);
int numOfLogFilesForTesting = ((Number) args[5]).intValue();
InternalDistributedSystem ds = (InternalDistributedSystem) cache.getDistributedSystem();
if (ds != null && ds.isConnected()) {
// write all the file in the same directory with extension .log
String filterString = ds.getConfig().getLogFile().getName();
final String filterStr = filterString.substring(0, filterString.lastIndexOf(".") > 0
? filterString.lastIndexOf(".") : filterString.length() - 1);
File dir = ds.getConfig().getLogFile(); // get log file object
if (dir == null) {
context.getResultSender()
.lastResult(CliStrings.format(
CliStrings.EXPORT_LOGS__MSG__FAILED_TO_EXPORT_LOG_FILES_FOR_MEMBER_0,
ds.getMemberId()));
return;
}
try {
dir = dir.getAbsoluteFile(); // get absolute log file
} catch (SecurityException se) {
context.getResultSender().lastResult(se.getMessage());
return;
}
String logFileDir = dir.getParent(); // get log file directory
if (logFileDir == null)
logFileDir = "/"; // this works in Windows too
if (isDebugEnabled) {
logger.debug(
"For member={}: Exporting logs LogFileFunction logFileDir={}, filterStr={}",
ds.getMemberId(), logFileDir, filterStr);
}
dir = new File(logFileDir); // get log file directory object
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
// get those files which start with the name of the log file
return name.startsWith(filterStr) && name.endsWith(".log");
}
};
// create target directory if does not exists
if (isDebugEnabled) {
logger.debug("For member={}: Exporting logs LogFileFunction targetDirName={}",
ds.getMemberId(), targetDirName);
}
File targetDir = new File(targetDirName);
if (targetDir.exists() == false) {
if (targetDir.mkdirs() == true) {
String logsWritten = processLogs(dir, logFileDir, targetDirName, cache, logLevel,
onlyLogLevel, filter, ((Number) args[3]).toString(),
((Number) args[4]).toString(), numOfLogFilesForTesting);
if (isDebugEnabled) {
logger.debug(
"For member={}: Done with Exporting logs LogFileFunction targetDirName={}",
ds.getMemberId(), targetDirName);
}
context.getResultSender().lastResult(logsWritten);
} else {
if (isDebugEnabled) {
logger.debug("For member={}{} {}", ds.getMemberId(),
CliStrings.EXPORT_LOGS__MSG__TARGET_DIR_CANNOT_BE_CREATED, targetDirName);
}
context.getResultSender().lastResult(ResultBuilder.createInfoResult(CliStrings
.format(CliStrings.EXPORT_LOGS__MSG__TARGET_DIR_CANNOT_BE_CREATED, targetDir)));
}
} else {
String logsWritten = processLogs(dir, logFileDir, targetDirName, cache, logLevel,
onlyLogLevel, filter, ((Number) args[3]).toString(),
((Number) args[4]).toString(), numOfLogFilesForTesting);
if (isDebugEnabled) {
logger.debug(
"For member={}: Done with Exporting logs LogFileFunction targetDirName={} logsWritten={}",
ds.getMemberId(), targetDirName, logsWritten);
}
context.getResultSender().lastResult(logsWritten);
}
} else {
context.getResultSender().lastResult(
LocalizedStrings.InternalDistributedSystem_THIS_CONNECTION_TO_A_DISTRIBUTED_SYSTEM_HAS_BEEN_DISCONNECTED
.toLocalizedString());
}
} catch (Exception e) {
context.getResultSender().lastResult(e.getMessage());
}
}
});
try {
final boolean isDebugEnabled = logger.isDebugEnabled();
if (isDebugEnabled) {
logger.debug("For member={}: started copying log files",
((InternalDistributedSystem) CacheFactory.getAnyInstance().getDistributedSystem())
.getMemberId());
}
waiting.start();
waiting.join();
if (isDebugEnabled) {
logger.debug("For member={}: done with Exporting all log files",
((InternalDistributedSystem) CacheFactory.getAnyInstance().getDistributedSystem())
.getMemberId());
}
} catch (Exception e) {
context.getResultSender().lastResult(e.getMessage());
} finally {
if (waiting.isAlive()) {
waiting.interrupt();
}
}
}
public String processLogs(File dir, String logFileDir, String targetDirName, Cache cache,
String logLevel, String onlyLogLevel, FilenameFilter filter, String startTime, String endTime,
int numOfLogFilesForTesting) {
try {
final boolean isDebugEnabled = logger.isDebugEnabled();
String[] logsInDir = dir.list(filter);
if (isDebugEnabled) {
logger.debug("LogFileFunction processLogs logsInDir={} sample={}", logsInDir.length,
logsInDir[0]);
}
StringBuilder logsWritten = new StringBuilder();
if (logsInDir.length > 0) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-ms");
// numOfLogFilesForTesting is used to limit the files that get copied while running entire
// dunit suite
// from user perspective numOfLogFilesForTesting is of no use
if (isDebugEnabled) {
logger.debug("LogFileFunction before copy numOfLogFilesForTesting={}",
numOfLogFilesForTesting);
}
for (int i = 0, j = 0; i < logsInDir.length
&& (j < numOfLogFilesForTesting || numOfLogFilesForTesting == 0); i++, j++) {
String fileName = new String(logFileDir + File.separator + logsInDir[i]);
String logToBeWritten = targetDirName + File.separator
+ MBeanJMXAdapter
.getMemberNameOrId(cache.getDistributedSystem().getDistributedMember())
+ "_" + logsInDir[i].substring(0, logsInDir[i].length() - 4) + "_"
+ sdf.format(new java.util.Date())
+ logsInDir[i].substring(logsInDir[i].length() - 4, logsInDir[i].length());
// create a new process for log read and write
if (isDebugEnabled) {
logger.debug("LogFileFunction processLogs fileName={} logToBeWritten={}", fileName,
logToBeWritten);
}
List<String> commandList = new ArrayList<String>();
commandList.add(System.getProperty("java.home") + File.separatorChar + "bin"
+ File.separatorChar + "java");
commandList.add("-classpath");
commandList.add(System.getProperty("java.class.path", "."));
commandList.add(ReadWriteFile.class.getName());
commandList.add(fileName);
commandList.add(logToBeWritten);
commandList.add(logLevel);
commandList.add(onlyLogLevel);
commandList.add(startTime);
commandList.add(endTime);
ProcessBuilder procBuilder = new ProcessBuilder(commandList);
StringBuilder output = new StringBuilder();
String errorString = new String(), resultString = new String();
try {
Process copyLogProcess = procBuilder.redirectErrorStream(true).start();
if (isDebugEnabled) {
logger.debug("LogFileFunction processLogs fileName before process waitfor");
}
int compacterProcessStatus = copyLogProcess.waitFor();
if (isDebugEnabled) {
logger.debug(
"LogFileFunction processLogs fileName after process waitfor destroy compacterProcessStatus={}",
compacterProcessStatus);
}
InputStream inputStream = copyLogProcess.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = br.readLine()) != null) {
output.append(line).append(GfshParser.LINE_SEPARATOR);
}
copyLogProcess.destroy();
if (isDebugEnabled) {
logger.debug(
"LogFileFunction processLogs fileName after process waitfor after destroy compacterProcessStatus={}",
compacterProcessStatus);
}
} catch (IOException e) {
errorString =
(new GemFireIOException(" Exception in LogFileFunction is " + e, e)).toString();
} finally {
if (errorString != null) {
output.append(errorString).append(GfshParser.LINE_SEPARATOR);
}
resultString = output.toString();
}
// merge files which are written successfully
if (resultString.contains("Sucessfully written file")) {
if (isDebugEnabled) {
logger.debug("LogFileFunction wrote file logToBeWritten={}", logToBeWritten);
}
logsWritten.append(logToBeWritten + ";");
} else {
if (isDebugEnabled) {
logger.debug("LogFileFunction wrote file logToBeWritten={} resultString={}",
logToBeWritten, resultString);
}
}
}
} else {
if (isDebugEnabled) {
logger
.debug(CliStrings.format("No file was found for exporting in export logs function"));
}
}
if (isDebugEnabled) {
logger.debug(CliStrings.format("logsWritten=" + logsWritten));
}
return logsWritten.toString();
} catch (Exception e) {
return ("Exception in LogFileFunction processLogs " + e.getMessage());
}
}
@Override
public String getId() {
return LogFileFunction.ID;
}
@Override
public boolean hasResult() {
return true;
}
@Override
public boolean optimizeForWrite() {
// no need of optimization since read-only.
return false;
}
@Override
public boolean isHA() {
return false;
}
}