/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.master.journal;
import alluxio.AlluxioURI;
import alluxio.Configuration;
import alluxio.PropertyKey;
import alluxio.RuntimeConstants;
import alluxio.master.journal.options.JournalReaderOptions;
import alluxio.proto.journal.Journal.JournalEntry;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import javax.annotation.concurrent.NotThreadSafe;
/**
* Tool for reading the journal entries given a range of sequence numbers. It reads binary journal
* entries and prints human-readable ones to standard out. Example usage below.
*
* <pre>
* java -cp \
* assembly/server/target/alluxio-assembly-server-<ALLUXIO-VERSION>-jar-with-dependencies.jar \
* alluxio.master.journal.JournalTool -master FileSystemMaster -start 0x100 -end 0x109
* java -cp \
* assembly/server/target/alluxio-assembly-server-<ALLUXIO-VERSION>-jar-with-dependencies.jar \
* alluxio.master.journal.JournalTool -journalFile YourJournalFilePath
* </pre>
*/
@NotThreadSafe
public final class JournalTool {
private static final Logger LOG = LoggerFactory.getLogger(JournalTool.class);
/** Separator to place at the end of each journal entry. */
private static final String ENTRY_SEPARATOR = StringUtils.repeat('-', 80);
private static final int EXIT_FAILED = -1;
private static final int EXIT_SUCCEEDED = 0;
private static final Options OPTIONS =
new Options()
.addOption("help", false, "Show help for this test.")
.addOption("master", true, "The name of the master (e.g. FileSystemMaster, BlockMaster). "
+ "Set to FileSystemMaster by default.")
.addOption("start", true,
"The start log sequence number (inclusive). Set to 0 by default.")
.addOption("end", true,
"The end log sequence number (exclusive). Set to +inf by default.")
.addOption("journalFile", true,
"If set, only read journal from this file. -master is ignored when -journalFile is "
+ "set.");
private static boolean sHelp;
private static String sMaster;
private static long sStart;
private static long sEnd;
private static String sJournalFile;
private JournalTool() {} // prevent instantiation
/**
* Reads a journal via
* {@code java -cp \
* assembly/server/target/alluxio-assembly-server-<ALLUXIO-VERSION>-jar-with-dependencies.jar \
* alluxio.master.journal.JournalTool -master BlockMaster -start 0x100 -end 0x109}.
*
* @param args arguments passed to the tool
*/
public static void main(String[] args) {
if (!parseInputArgs(args)) {
usage();
System.exit(EXIT_FAILED);
}
if (sHelp) {
usage();
System.exit(EXIT_SUCCEEDED);
}
if (sJournalFile != null && !sJournalFile.isEmpty()) {
parseJournalFile();
} else {
readFromJournal();
}
}
private static void parseJournalFile() {
URI location;
try {
location = new URI(sJournalFile);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
try (JournalFileParser parser = JournalFileParser.Factory.create(location)) {
JournalEntry entry;
while ((entry = parser.next()) != null) {
if (entry.getSequenceNumber() < sStart) {
continue;
}
if (entry.getSequenceNumber() >= sEnd) {
break;
}
System.out.println(ENTRY_SEPARATOR);
System.out.print(entry);
}
} catch (Exception e) {
LOG.error("Failed to get next journal entry.", e);
}
}
private static void readFromJournal() {
JournalFactory factory = new Journal.Factory(getJournalLocation());
Journal journal = factory.create(sMaster);
JournalReaderOptions options =
JournalReaderOptions.defaults().setPrimary(true).setNextSequenceNumber(sStart);
try (JournalReader reader = journal.getReader(options)) {
JournalEntry entry;
while ((entry = reader.read()) != null) {
if (entry.getSequenceNumber() >= sEnd) {
break;
}
System.out.println(ENTRY_SEPARATOR);
System.out.print(entry);
}
} catch (Exception e) {
LOG.error("Failed to read next journal entry.", e);
}
}
/**
* @return the journal location
*/
private static URI getJournalLocation() {
String journalDirectory = Configuration.get(PropertyKey.MASTER_JOURNAL_FOLDER);
if (!journalDirectory.endsWith(AlluxioURI.SEPARATOR)) {
journalDirectory += AlluxioURI.SEPARATOR;
}
try {
return new URI(journalDirectory);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
/**
* Parses the input args with a command line format, using
* {@link org.apache.commons.cli.CommandLineParser}.
*
* @param args the input args
* @return true if parsing succeeded
*/
private static boolean parseInputArgs(String[] args) {
CommandLineParser parser = new DefaultParser();
CommandLine cmd;
try {
cmd = parser.parse(OPTIONS, args);
} catch (ParseException e) {
System.out.println("Failed to parse input args: " + e);
return false;
}
sHelp = cmd.hasOption("help");
sMaster = cmd.getOptionValue("master", "FileSystemMaster");
sStart = Long.parseLong(cmd.getOptionValue("start", "0"));
sEnd = Long.parseLong(cmd.getOptionValue("end", Long.valueOf(Long.MAX_VALUE).toString()));
sJournalFile = cmd.getOptionValue("journalFile", "");
return true;
}
/**
* Prints the usage.
*/
private static void usage() {
new HelpFormatter().printHelp("java -cp alluxio-" + RuntimeConstants.VERSION
+ "-jar-with-dependencies.jar alluxio.master.journal.JournalTool",
"Read an Alluxio journal and write it to stdout in a human-readable format.", OPTIONS, "",
true);
}
}