/** * 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.cassandra.tools; import java.io.*; import java.util.*; import org.apache.commons.cli.*; import org.apache.cassandra.config.ConfigurationException; import org.apache.cassandra.db.commitlog.CommitLogHeader; import org.apache.cassandra.db.commitlog.CommitLogSegment; import org.apache.cassandra.utils.Pair; public class LogTool { private static ToolOptions options = null; static { options = new ToolOptions(); } public enum LogCommand { DIRTY } /** Prints a list of the dirty cf's for the specified log file or all log file in the directory. */ private void printDirty(String logFilePath, PrintStream outs) { File logFile = new File(logFilePath); final List<File> headerFiles = new ArrayList<File>(); if (logFile.isFile()) { if (!CommitLogHeader.possibleCommitLogHeaderFile(logFile.getName()) && CommitLogSegment.possibleCommitLogFile(logFile.getName())) logFile = new File(CommitLogHeader.getHeaderPathFromSegmentPath(logFile.getPath())); headerFiles.add(logFile); } else { final File[] files = logFile.listFiles(new FilenameFilter(){ public boolean accept(File dir, String name) { return CommitLogHeader.possibleCommitLogHeaderFile(name); } }); if (files != null) headerFiles.addAll(Arrays.asList(files)); } if (headerFiles.size() == 0) err(null, String.format("No header files found for path %s", logFilePath), true); outs.println("\nNot connected to a server, Keyspace and Column Family names are not available.\n"); for (File headerFile : headerFiles) { CommitLogHeader clh; try { clh = CommitLogHeader.readCommitLogHeader(headerFile.getPath()); } catch (IOException e) { err(e, String.format("Error opening header file %s", headerFile.getPath()), false); continue; } outs.println(headerFile.getPath()); // keyspace : [(cf, offset),] Map<String, List<Pair<String, Integer>>> dirtyKs = new HashMap<String, List<Pair<String, Integer>>>(); for (Map.Entry<Integer, Integer> entry : clh.dirtyCFs().entrySet()) { Integer cfId = entry.getKey(); Integer offset = entry.getValue(); Pair<String, String> cfName = new Pair<String, String>("Unknown", "Cf id " + cfId); List<Pair<String, Integer>> dirtyCfs = dirtyKs.get(cfName.left); if (dirtyCfs == null) { dirtyCfs = new ArrayList<Pair<String, Integer>>(); dirtyKs.put(cfName.left, dirtyCfs); } dirtyCfs.add(new Pair<String, Integer>(cfName.right, offset)); } if (dirtyKs.isEmpty()) { outs.println("No dirty CF's found."); continue; } for (Map.Entry<String, List<Pair<String, Integer>>> entry : dirtyKs.entrySet()) { String keyspace = entry.getKey(); List<Pair<String, Integer>> dirtyCfs = entry.getValue(); outs.println(String.format("Keyspace %s:", keyspace)); for (Pair<String, Integer> dirtyCf : dirtyCfs) outs.println(String.format("\t%s: %s", dirtyCf.left, dirtyCf.right)); } outs.println(clh.dirtyString()); outs.println("\n"); } } /** * Prints usage information to stdout. */ private static void printUsage() { HelpFormatter hf = new HelpFormatter(); StringBuilder header = new StringBuilder(); header.append("\nAvailable commands:\n"); // One arg addCmdHelp(header, "dirty logfilepath", "Show the dirty CF's in the specified log file, or all log files in a directory."); String usage = String.format("java %s <command>%n", LogTool.class.getName()); hf.printHelp(usage, "", options, ""); System.out.println(header.toString()); } private static void addCmdHelp(StringBuilder sb, String cmd, String description) { sb.append(" ").append(cmd); // Ghetto indentation (trying, but not too hard, to not look too bad) if (cmd.length() <= 20) for (int i = cmd.length(); i < 22; ++i) sb.append(" "); sb.append(" - ").append(description).append("\n"); } public static void main(String[] args) throws IOException, InterruptedException, ConfigurationException, ParseException { CommandLineParser parser = new PosixParser(); ToolCommandLine cmd = null; try { cmd = new ToolCommandLine(parser.parse(options, args)); } catch (ParseException p) { badUse(p.getMessage()); } LogCommand command = null; try { command = cmd.getCommand(); } catch (IllegalArgumentException e) { badUse(e.getMessage()); } LogTool logTool = new LogTool(); // Execute the requested command. String[] arguments = cmd.getCommandArguments(); switch (command) { case DIRTY : if (arguments.length != 1) { badUse("dirty requires logfilepath"); } logTool.printDirty(arguments[0], System.out); break; default : throw new RuntimeException("Unreachable code."); } System.exit(0); } private static void badUse(String useStr) { System.err.println(useStr); printUsage(); System.exit(1); } private static void err(Exception e, String errStr, boolean exit) { System.err.println(errStr); if (e != null) e.printStackTrace(); if (exit) System.exit(3); } private static class ToolCommandLine { private final CommandLine commandLine; public ToolCommandLine(CommandLine commands) { commandLine = commands; } public Option[] getOptions() { return commandLine.getOptions(); } public boolean hasOption(String opt) { return commandLine.hasOption(opt); } public String getOptionValue(String opt) { return commandLine.getOptionValue(opt); } public LogCommand getCommand() { if (commandLine.getArgs().length == 0) throw new IllegalArgumentException("Command was not specified."); String command = commandLine.getArgs()[0]; try { return LogCommand.valueOf(command.toUpperCase()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unrecognized command: " + command); } } public String[] getCommandArguments() { List params = commandLine.getArgList(); if (params.size() < 2) // command parameters are empty return new String[0]; String[] toReturn = new String[params.size() - 1]; for (int i = 1; i < params.size(); i++) toReturn[i - 1] = (String) params.get(i); return toReturn; } } private static class ToolOptions extends Options { public void addOption(Pair<String, String> opts, boolean hasArgument, String description) { addOption(opts, hasArgument, description, false); } public void addOption(Pair<String, String> opts, boolean hasArgument, String description, boolean required) { addOption(opts.left, opts.right, hasArgument, description, required); } public void addOption(String opt, String longOpt, boolean hasArgument, String description, boolean required) { Option option = new Option(opt, longOpt, hasArgument, description); option.setRequired(required); addOption(option); } } }