blob: 2d6e1341c7c1982d5d79f384b90bd8af9573675d [file] [log] [blame]
/*
* 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.util;
import static java.util.stream.Collectors.toList;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Logger;
import org.apache.geode.logging.internal.log4j.api.LogService;
import org.apache.geode.management.internal.configuration.utils.ZipUtils;
/**
* this LogExporter only finds the .log and .gfs files under in the same directory of the base files
* it doesn't use the base file's filename patterns to find the related logs/stats yet.
*/
public class LogExporter {
private static final Logger LOGGER = LogService.getLogger();
private final LogFilter logFilter;
private final File baseLogFile;
private final File baseStatsFile;
/**
* @param logFilter the filter that's used to check if we need to accept the file or the logLine
* @param baseLogFile if not null, we will export the logs in that directory
* @param baseStatsFile if not null, we will export stats in that directory
*/
public LogExporter(LogFilter logFilter, File baseLogFile, File baseStatsFile) {
assert logFilter != null;
this.logFilter = logFilter;
this.baseLogFile = baseLogFile;
this.baseStatsFile = baseStatsFile;
}
/**
*
* @return Path to the zip file that has all the filtered files, null if no files are selected to
* export.
*/
public Path export() throws IOException {
Path tempDirectory = Files.createTempDirectory("exportLogs");
if (baseLogFile != null) {
for (Path logFile : findLogFiles(baseLogFile.toPath().getParent())) {
Path filteredLogFile = tempDirectory.resolve(logFile.getFileName());
writeFilteredLogFile(logFile, filteredLogFile);
}
}
if (baseStatsFile != null) {
for (Path statFile : findStatFiles(baseStatsFile.toPath().getParent())) {
Files.copy(statFile, tempDirectory.resolve(statFile.getFileName()));
}
}
Path zipFile = null;
if (tempDirectory.toFile().listFiles().length > 0) {
zipFile = Files.createTempFile("logExport", ".zip");
ZipUtils.zipDirectory(tempDirectory, zipFile);
LOGGER.info("Zipped files to: " + zipFile);
}
FileUtils.deleteDirectory(tempDirectory.toFile());
return zipFile;
}
protected void writeFilteredLogFile(Path originalLogFile, Path filteredLogFile)
throws IOException {
this.logFilter.startNewFile();
try (BufferedReader reader = new BufferedReader(new FileReader(originalLogFile.toFile()))) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filteredLogFile.toFile()))) {
String line;
while ((line = reader.readLine()) != null) {
LogFilter.LineFilterResult result = this.logFilter.acceptsLine(line);
if (result == LogFilter.LineFilterResult.REMAINDER_OF_FILE_REJECTED) {
break;
}
if (result == LogFilter.LineFilterResult.LINE_ACCEPTED) {
writeLine(line, writer);
}
}
}
}
}
private void writeLine(String line, BufferedWriter writer) {
try {
writer.write(line);
writer.newLine();
} catch (IOException e) {
throw new RuntimeException("Unable to write to log file", e);
}
}
/**
* @return combined size of stat archives and filtered log files in bytes
*/
public long estimateFilteredSize() throws IOException {
long filteredSize = 0;
if (baseLogFile != null) {
for (Path logFile : findLogFiles(baseLogFile.toPath().getParent())) {
filteredSize += filterAndSize(logFile);
}
}
if (baseStatsFile != null) {
for (Path statFile : findStatFiles(baseStatsFile.toPath().getParent())) {
filteredSize += statFile.toFile().length();
}
}
return filteredSize;
}
/**
* @return size of file in bytes
*/
private long filterAndSize(Path originalLogFile) throws IOException {
long size = 0;
this.logFilter.startNewFile();
try (BufferedReader reader = new BufferedReader(new FileReader(originalLogFile.toFile()))) {
String line;
while ((line = reader.readLine()) != null) {
LogFilter.LineFilterResult result = this.logFilter.acceptsLine(line);
if (result == LogFilter.LineFilterResult.REMAINDER_OF_FILE_REJECTED) {
break;
}
if (result == LogFilter.LineFilterResult.LINE_ACCEPTED) {
size += line.length() + System.lineSeparator().length();
}
}
}
return size;
}
List<Path> findLogFiles(Path workingDir) throws IOException {
Predicate<Path> logFileSelector = (Path file) -> file.toString().toLowerCase().contains(".log");
return findFiles(workingDir, logFileSelector);
}
List<Path> findStatFiles(Path workingDir) throws IOException {
Predicate<Path> statFileSelector =
(Path file) -> file.toString().toLowerCase().endsWith(".gfs");
return findFiles(workingDir, statFileSelector);
}
private List<Path> findFiles(Path workingDir, Predicate<Path> fileSelector) throws IOException {
Stream<Path> selectedFiles/* = null */;
if (!workingDir.toFile().isDirectory()) {
return Collections.emptyList();
}
selectedFiles = Files.list(workingDir).filter(fileSelector).filter(this.logFilter::acceptsFile);
return selectedFiles.collect(toList());
}
}