Skip to content

Commit

Permalink
minor refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
praveenag authored and klaren committed Jan 8, 2018
1 parent 49e567a commit 4823680
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 138 deletions.
Expand Up @@ -22,11 +22,9 @@
import org.jutils.jprocesses.JProcesses; import org.jutils.jprocesses.JProcesses;
import org.jutils.jprocesses.model.ProcessInfo; import org.jutils.jprocesses.model.ProcessInfo;


import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
Expand All @@ -46,17 +44,15 @@
import org.neo4j.commandline.arguments.OptionalNamedArg; import org.neo4j.commandline.arguments.OptionalNamedArg;
import org.neo4j.commandline.arguments.PositionalArgument; import org.neo4j.commandline.arguments.PositionalArgument;
import org.neo4j.commandline.arguments.common.OptionalCanonicalPath; import org.neo4j.commandline.arguments.common.OptionalCanonicalPath;
import org.neo4j.dbms.diagnostics.jmx.JMXDumper;
import org.neo4j.dbms.diagnostics.jmx.JmxDump; import org.neo4j.dbms.diagnostics.jmx.JmxDump;
import org.neo4j.dbms.diagnostics.jmx.LocalVirtualMachine;
import org.neo4j.diagnostics.DiagnosticsOfflineReportProvider;
import org.neo4j.diagnostics.DiagnosticsReportSource; import org.neo4j.diagnostics.DiagnosticsReportSource;
import org.neo4j.diagnostics.DiagnosticsReportSources; import org.neo4j.diagnostics.DiagnosticsReportSources;
import org.neo4j.diagnostics.DiagnosticsReporter; import org.neo4j.diagnostics.DiagnosticsReporter;
import org.neo4j.diagnostics.DiagnosticsReporterProgressCallback; import org.neo4j.diagnostics.DiagnosticsReporterProgressCallback;
import org.neo4j.diagnostics.InteractiveProgress; import org.neo4j.diagnostics.InteractiveProgress;
import org.neo4j.diagnostics.NonInteractiveProgress; import org.neo4j.diagnostics.NonInteractiveProgress;
import org.neo4j.helpers.Args; import org.neo4j.helpers.Args;
import org.neo4j.helpers.Service;
import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Config;


Expand All @@ -77,8 +73,9 @@ public class DiagnosticsReportCommand implements AdminCommand
private final Path homeDir; private final Path homeDir;
private final Path configDir; private final Path configDir;
static final String[] DEFAULT_CLASSIFIERS = new String[]{"logs", "config", "plugins", "tree", "metrics", "threads", "env", "sysprop", "ps"}; static final String[] DEFAULT_CLASSIFIERS = new String[]{"logs", "config", "plugins", "tree", "metrics", "threads", "env", "sysprop", "ps"};

private final JMXDumper jmxDumper;
private boolean verbose; private boolean verbose;
private final PrintStream err;
private final PrintStream out; private final PrintStream out;
private final FileSystemAbstraction fs; private final FileSystemAbstraction fs;


Expand All @@ -88,7 +85,7 @@ public class DiagnosticsReportCommand implements AdminCommand
this.configDir = configDir; this.configDir = configDir;
this.fs = outsideWorld.fileSystem(); this.fs = outsideWorld.fileSystem();
this.out = outsideWorld.outStream(); this.out = outsideWorld.outStream();
this.err = outsideWorld.errorStream(); this.jmxDumper = new JMXDumper( homeDir, fs, out, outsideWorld.errorStream(), verbose );
} }


public static Arguments allArguments() public static Arguments allArguments()
Expand All @@ -102,106 +99,135 @@ public void execute( String[] stringArgs ) throws IncorrectUsage, CommandFailed
Args args = Args.withFlags( "list", "to", "verbose" ).parse( stringArgs ); Args args = Args.withFlags( "list", "to", "verbose" ).parse( stringArgs );
verbose = args.has( "verbose" ); verbose = args.has( "verbose" );


// Make sure we can access the configuration file DiagnosticsReporter reporter = createAndRegisterSources();
File configFile = configDir.resolve( Config.DEFAULT_CONFIG_FILE_NAME ).toFile();
if ( !fs.fileExists( configFile ) ) Optional<Set<String>> classifiers = parseAndValidateArguments( args, reporter );
if ( !classifiers.isPresent() )
{ {
throw new CommandFailed( "Unable to find config file, tried: " + configFile.getAbsolutePath() ); return;
}

DiagnosticsReporterProgressCallback progress = buildProgress();

// Start dumping
Path destinationDir = new File( destinationArgument.parse( args ) ).toPath();
try
{
SimpleDateFormat dumpFormat = new SimpleDateFormat( "yyyy-MM-dd_HHmmss" );
Path reportFile = destinationDir.resolve( dumpFormat.format( new Date() ) + ".zip" );
out.println( "Writing report to " + reportFile.toAbsolutePath().toString() );
reporter.dump( classifiers.get(), reportFile, progress );
}
catch ( IOException e )
{
throw new CommandFailed( "Creating archive failed", e );
}
}

private DiagnosticsReporterProgressCallback buildProgress()
{
DiagnosticsReporterProgressCallback progress;
if ( System.console() != null )
{
progress = new InteractiveProgress( out, verbose );
} }
Config config = Config.fromFile( configFile ).withHome( homeDir ).withConnectorsDisabled().build(); else
{
progress = new NonInteractiveProgress( out, verbose );
}
return progress;
}


DiagnosticsReporter reporter = createAndRegisterSources( config, configFile ); private Optional<Set<String>> parseAndValidateArguments( Args args, DiagnosticsReporter reporter ) throws IncorrectUsage
{
Set<String> availableClassifiers = reporter.getAvailableClassifiers(); Set<String> availableClassifiers = reporter.getAvailableClassifiers();


// Passing '--list' should print list and end execution // Passing '--list' should print list and end execution
if ( args.has( "list" ) ) if ( args.has( "list" ) )
{ {
out.println( "All available classifiers:" ); listClassifiers( availableClassifiers );
for ( String classifier : availableClassifiers ) return Optional.empty();
{
out.printf( " %-10s %s\n", classifier, describeClassifier( classifier ) );
}
return;
} }


// Make sure 'all' is the only classifier if specified // Make sure 'all' is the only classifier if specified
Set<String> orphans = new TreeSet<>( args.orphans() ); Set<String> classifiers = new TreeSet<>( args.orphans() );
if ( orphans.contains( "all" ) ) if ( classifiers.contains( "all" ) )
{ {
if ( orphans.size() != 1 ) if ( classifiers.size() != 1 )
{ {
orphans.remove( "all" ); classifiers.remove( "all" );
throw new IncorrectUsage( "If you specify 'all' this has to be the only classifier. Found ['" + throw new IncorrectUsage( "If you specify 'all' this has to be the only classifier. Found ['" +
orphans.stream().collect( Collectors.joining( "','" ) ) + "'] as well." ); classifiers.stream().collect( Collectors.joining( "','" ) ) + "'] as well." );
} }
} }
else else
{ {
// Add default classifiers that are available // Add default classifiers that are available
if ( orphans.isEmpty() ) if ( classifiers.isEmpty() )
{ {
for ( String classifier : DEFAULT_CLASSIFIERS ) addDefaultClassifiers( availableClassifiers, classifiers );
{
if ( availableClassifiers.contains( classifier ) )
{
orphans.add( classifier );
}
}
} }


// Validate classifiers validateClassifiers( availableClassifiers, classifiers );
for ( String classifier : orphans )
{
if ( !availableClassifiers.contains( classifier ) )
{
throw new IncorrectUsage( "Unknown classifier: " + classifier );
}
}
} }
return Optional.of( classifiers );
}


DiagnosticsReporterProgressCallback progress; private void validateClassifiers( Set<String> availableClassifiers, Set<String> orphans ) throws IncorrectUsage
if ( System.console() != null ) {
{ for ( String classifier : orphans )
progress = new InteractiveProgress( out, verbose );
}
else
{ {
progress = new NonInteractiveProgress( out, verbose ); if ( !availableClassifiers.contains( classifier ) )
{
throw new IncorrectUsage( "Unknown classifier: " + classifier );
}
} }
}


// Start dumping private void addDefaultClassifiers( Set<String> availableClassifiers, Set<String> orphans )
Path destinationDir = new File( destinationArgument.parse( args ) ).toPath(); {
try for ( String classifier : DEFAULT_CLASSIFIERS )
{ {
SimpleDateFormat dumpFormat = new SimpleDateFormat( "yyyy-MM-dd_HHmmss" ); if ( availableClassifiers.contains( classifier ) )
Path reportFile = destinationDir.resolve( dumpFormat.format( new Date() ) + ".zip" ); {
out.println( "Writing report to " + reportFile.toAbsolutePath().toString() ); orphans.add( classifier );
reporter.dump( orphans, reportFile, progress ); }
} }
catch ( IOException e ) }

private void listClassifiers( Set<String> availableClassifiers )
{
out.println( "All available classifiers:" );
for ( String classifier : availableClassifiers )
{ {
throw new CommandFailed( "Creating archive failed", e ); out.printf( " %-10s %s\n", classifier, describeClassifier( classifier ) );
} }
} }


private DiagnosticsReporter createAndRegisterSources( Config config, File configFile ) private DiagnosticsReporter createAndRegisterSources() throws CommandFailed
{ {
DiagnosticsReporter reporter = new DiagnosticsReporter(); DiagnosticsReporter reporter = new DiagnosticsReporter();
File configFile = configDir.resolve( Config.DEFAULT_CONFIG_FILE_NAME ).toFile();
Config config = getConfig( configFile );

File storeDirectory = config.get( database_path ); File storeDirectory = config.get( database_path );


// Find all offline providers and register them reporter.registerAllOfflineProviders( config, storeDirectory, this.fs );
for ( DiagnosticsOfflineReportProvider provider : Service.load( DiagnosticsOfflineReportProvider.class ) )
{
provider.init( fs, config, storeDirectory );
reporter.registerOfflineProvider( provider );
}


// Register sources provided by this tool // Register sources provided by this tool
reporter.registerSource( "config", DiagnosticsReportSources.newDiagnosticsFile( "neo4j.conf", fs, configFile ) ); reporter.registerSource( "config",
DiagnosticsReportSources.newDiagnosticsFile( "neo4j.conf", fs, configFile ) );

reporter.registerSource( "ps", runningProcesses() ); reporter.registerSource( "ps", runningProcesses() );


// Online connection // Online connection
Optional<JmxDump> jmxDump = connectToNeo4jInstance(); registerJMXSources( reporter );
return reporter;
}

private void registerJMXSources( DiagnosticsReporter reporter )
{
Optional<JmxDump> jmxDump = jmxDumper.getJMXDump();
if ( jmxDump.isPresent() ) if ( jmxDump.isPresent() )
{ {
JmxDump jmx = jmxDump.get(); JmxDump jmx = jmxDump.get();
Expand All @@ -211,83 +237,15 @@ private DiagnosticsReporter createAndRegisterSources( Config config, File config
reporter.registerSource( "env", jmx.environmentVariables() ); reporter.registerSource( "env", jmx.environmentVariables() );
reporter.registerSource( "activetxs", jmx.listTransactions() ); reporter.registerSource( "activetxs", jmx.listTransactions() );
} }
return reporter;
} }


private Optional<JmxDump> connectToNeo4jInstance() private Config getConfig( File configFile ) throws CommandFailed
{ {
out.println( "Trying to find running instance of neo4j" ); if ( !fs.fileExists( configFile ) )

Optional<Long> pid = getPid();
if ( pid.isPresent() )
{
try
{
LocalVirtualMachine vm = LocalVirtualMachine.from( pid.get() );
out.println( "Attached to running process with process id " + pid.get() );
try
{
JmxDump jmxDump = JmxDump.connectTo( vm.getJmxAddress() );
jmxDump.attachSystemProperties( vm.getSystemProperties() );
out.println( "Connected to JMX endpoint" );
return Optional.of( jmxDump );
}
catch ( IOException e )
{
printError( "Unable to communicate with JMX endpoint. Reason: " + e.getMessage(), e );
}
}
catch ( IOException e )
{
printError( "Unable to connect to process. Reason: " + e.getMessage(), e );
}
}
else
{
out.println( "No running instance of neo4j was found. Online reports will be omitted." );
}

return Optional.empty();
}

private Optional<Long> getPid()
{
Path pidFile = homeDir.resolve( "run/neo4j.pid" );
if ( fs.fileExists( pidFile.toFile() ) )
{
try ( BufferedReader reader = new BufferedReader( fs.openAsReader( pidFile.toFile(), Charset.defaultCharset() ) ) )
{
String pidFileContent = reader.readLine();
try
{
return Optional.of( Long.parseLong( pidFileContent ) );
}

catch ( NumberFormatException e )
{
printError( pidFile.toString() + " does not contain a valid id. Found: " + pidFileContent );
}
}
catch ( IOException e )
{
printError( "Error reading the .pid file. Reason: " + e.getMessage(), e );
}
}
return Optional.empty();
}

private void printError( String message )
{
printError( message, null );
}

private void printError( String message, Throwable e )
{
err.println( message );
if ( verbose && e != null )
{ {
e.printStackTrace( err ); throw new CommandFailed( "Unable to find config file, tried: " + configFile.getAbsolutePath() );
} }
return Config.fromFile( configFile ).withHome( homeDir ).withConnectorsDisabled().build();
} }


static String describeClassifier( String classifier ) static String describeClassifier( String classifier )
Expand Down

0 comments on commit 4823680

Please sign in to comment.