diff --git a/community/dbms/pom.xml b/community/dbms/pom.xml index 40b011141a6c7..f2994605bf52b 100644 --- a/community/dbms/pom.xml +++ b/community/dbms/pom.xml @@ -164,6 +164,11 @@ ${project.version} + + org.jprocesses + jProcesses + + org.apache.commons commons-compress diff --git a/community/dbms/src/main/java/org/neo4j/commandline/dbms/DiagnosticsReportCommand.java b/community/dbms/src/main/java/org/neo4j/commandline/dbms/DiagnosticsReportCommand.java index 46fbea10655be..d032483afe9a9 100644 --- a/community/dbms/src/main/java/org/neo4j/commandline/dbms/DiagnosticsReportCommand.java +++ b/community/dbms/src/main/java/org/neo4j/commandline/dbms/DiagnosticsReportCommand.java @@ -19,6 +19,9 @@ */ package org.neo4j.commandline.dbms; +import org.jutils.jprocesses.JProcesses; +import org.jutils.jprocesses.model.ProcessInfo; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -30,6 +33,7 @@ import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -43,9 +47,10 @@ import org.neo4j.commandline.arguments.OptionalNamedArg; import org.neo4j.commandline.arguments.PositionalArgument; import org.neo4j.commandline.arguments.common.OptionalCanonicalPath; -import org.neo4j.dbms.report.jmx.JmxDump; -import org.neo4j.dbms.report.jmx.LocalVirtualMachine; +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.DiagnosticsReportSources; import org.neo4j.diagnostics.DiagnosticsReporter; import org.neo4j.helpers.Args; @@ -57,7 +62,7 @@ public class DiagnosticsReportCommand implements AdminCommand { - private static OptionalNamedArg destinationArgument = + private static final OptionalNamedArg destinationArgument = new OptionalCanonicalPath( "to", "/tmp/", "reports/", "Destination directory for reports" ); private static final Arguments arguments = new Arguments() .withArgument( new OptionalListArgument() ) @@ -67,18 +72,16 @@ public class DiagnosticsReportCommand implements AdminCommand private final Path homeDir; private final Path configDir; - private final OutsideWorld outsideWorld; static final String[] DEFAULT_CLASSIFIERS = new String[]{"logs", "config"}; private boolean verbose; - private PrintStream err; - private PrintStream out; - private FileSystemAbstraction fs; + private final PrintStream err; + private final PrintStream out; + private final FileSystemAbstraction fs; DiagnosticsReportCommand( Path homeDir, Path configDir, OutsideWorld outsideWorld ) { this.homeDir = homeDir; this.configDir = configDir; - this.outsideWorld = outsideWorld; this.fs = outsideWorld.fileSystem(); this.out = outsideWorld.outStream(); this.err = outsideWorld.errorStream(); @@ -103,31 +106,7 @@ public void execute( String[] stringArgs ) throws IncorrectUsage, CommandFailed } Config config = Config.fromFile( configFile ).withHome( homeDir ).withConnectorsDisabled().build(); - File storeDirectory = config.get( database_path ); - - DiagnosticsReporter reporter = new DiagnosticsReporter( out ); - - // Find all offline providers and register them - for ( DiagnosticsOfflineReportProvider provider : Service.load( DiagnosticsOfflineReportProvider.class ) ) - { - provider.init( fs, config, storeDirectory ); - reporter.registerOfflineProvider( provider ); - } - - // Register sources provided by this tool - reporter.registerSource( "config", - DiagnosticsReportSources.newDiagnosticsFile( "neo4j.conf", fs, configFile ) ); - - // Online connection - JmxDump jmxDump = connectToNeo4jInstance(); - if ( jmxDump != null ) - { - reporter.registerSource( "threads", jmxDump.threadDump() ); - reporter.registerSource( "heap", jmxDump.heapDump() ); - reporter.registerSource( "sysprop", jmxDump.systemProperties() ); - //reporter.registerSource( "env", null ); - } - + DiagnosticsReporter reporter = createAndRegisterSources( config, configFile ); Set availableClassifiers = reporter.getAvailableClassifiers(); // Passing '--list' should print list and end execution @@ -188,9 +167,73 @@ public void execute( String[] stringArgs ) throws IncorrectUsage, CommandFailed } } - private JmxDump connectToNeo4jInstance() + private DiagnosticsReporter createAndRegisterSources( Config config, File configFile ) + { + DiagnosticsReporter reporter = new DiagnosticsReporter( out ); + File storeDirectory = config.get( database_path ); + + // Find all offline providers and register them + for ( DiagnosticsOfflineReportProvider provider : Service.load( DiagnosticsOfflineReportProvider.class ) ) + { + provider.init( fs, config, storeDirectory ); + reporter.registerOfflineProvider( provider ); + } + + // Register sources provided by this tool + reporter.registerSource( "config", DiagnosticsReportSources.newDiagnosticsFile( "neo4j.conf", fs, configFile ) ); + reporter.registerSource( "ps", runningProcesses() ); + + // Online connection + Optional jmxDump = connectToNeo4jInstance(); + if ( jmxDump.isPresent() ) + { + JmxDump jmx = jmxDump.get(); + reporter.registerSource( "threads", jmx.threadDump() ); + reporter.registerSource( "heap", jmx.heapDump() ); + reporter.registerSource( "sysprop", jmx.systemProperties() ); + //reporter.registerSource( "env", null ); // TODO: + } + return reporter; + } + + private Optional connectToNeo4jInstance() { out.println( "Trying to find running instance of neo4j" ); + + Optional pid = getPid(); + if ( pid.isPresent() ) + { + try + { + LocalVirtualMachine vm = LocalVirtualMachine.from( pid.get() ); + out.println( "Attached to running process with process id " + pid ); + 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 getPid() + { Path pidFile = homeDir.resolve( "run/neo4j.pid" ); if ( fs.fileExists( pidFile.toFile() ) ) { @@ -199,29 +242,9 @@ private JmxDump connectToNeo4jInstance() String pidFileContent = reader.readLine(); try { - long pid = Long.parseLong( pidFileContent ); - - try - { - LocalVirtualMachine vm = LocalVirtualMachine.from( pid ); - out.println( "Attached to running process with process id " + pid ); - try - { - JmxDump jmxDump = JmxDump.connectTo( vm.getJmxAddress() ); - jmxDump.attachSystemProperties( vm.getSystemProperties() ); - out.println( "Connected to JMX endpoint" ); - return 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 ); - } + return Optional.of( Long.parseLong( pidFileContent ) ); } + catch ( NumberFormatException e ) { printError( pidFile.toString() + " does not contain a valid id. Found: " + pidFileContent ); @@ -232,12 +255,7 @@ private JmxDump connectToNeo4jInstance() printError( "Error reading the .pid file. Reason: " + e.getMessage(), e ); } } - else - { - out.println( "No running instance of neo4j was found. Online reports will be omitted." ); - } - - return null; + return Optional.empty(); } private void printError( String message ) @@ -282,11 +300,37 @@ static String describeClassifier( String classifier ) return "include the raft log"; case "queries": return "include the output of dbms.listQueries()"; + case "ps": + return "include a list of running processes"; default: } throw new IllegalArgumentException( "Unknown classifier: " + classifier ); } + private static DiagnosticsReportSource runningProcesses() + { + return DiagnosticsReportSources.newDiagnosticsString( "ps.txt", () -> + { + List processesList = JProcesses.getProcessList(); + + StringBuilder sb = new StringBuilder(); + for (final ProcessInfo processInfo : processesList) { + sb.append( "Process PID: " ).append( processInfo.getPid() ).append( '\n' ) + .append( "Process Name: " ).append( processInfo.getName() ).append( '\n' ) + .append( "Process Time: " ).append( processInfo.getTime() ).append( '\n' ) + .append( "User: " ).append( processInfo.getUser() ).append( '\n' ) + .append( "Virtual Memory: " ).append( processInfo.getVirtualMemory() ).append( '\n' ) + .append( "Physical Memory: " ).append( processInfo.getPhysicalMemory() ).append( '\n' ) + .append( "CPU usage: " ).append( processInfo.getCpuUsage() ).append( '\n' ) + .append( "Start Time: " ).append( processInfo.getStartTime() ).append( '\n' ) + .append( "Priority: " ).append( processInfo.getPriority() ).append( '\n' ) + .append( "Full command: " ).append( processInfo.getCommand() ).append( '\n' ) + .append("------------------").append( '\n' ); + } + return sb.toString(); + }); + } + /** * Helper class to format output of {@link Usage}. Parsing is done manually in this command module. */ diff --git a/community/dbms/src/main/java/org/neo4j/dbms/report/jmx/JmxDump.java b/community/dbms/src/main/java/org/neo4j/dbms/diagnostics/jmx/JmxDump.java similarity index 97% rename from community/dbms/src/main/java/org/neo4j/dbms/report/jmx/JmxDump.java rename to community/dbms/src/main/java/org/neo4j/dbms/diagnostics/jmx/JmxDump.java index b715e1721d832..ed9bb97b4b2e8 100644 --- a/community/dbms/src/main/java/org/neo4j/dbms/report/jmx/JmxDump.java +++ b/community/dbms/src/main/java/org/neo4j/dbms/diagnostics/jmx/JmxDump.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.dbms.report.jmx; +package org.neo4j.dbms.diagnostics.jmx; import com.sun.management.HotSpotDiagnosticMXBean; @@ -193,8 +193,10 @@ public void addToArchive( Path archiveDestination, DiagnosticsReporterProgressCa monitor.info( "archiving..." ); long size = Files.size( tempFile ); InputStream in = Files.newInputStream( tempFile ); - ProgressAwareInputStream inStream = new ProgressAwareInputStream( in, size, monitor::percentChanged ); - Files.copy( inStream, archiveDestination ); + try ( ProgressAwareInputStream inStream = new ProgressAwareInputStream( in, size, monitor::percentChanged ) ) + { + Files.copy( inStream, archiveDestination ); + } Files.delete( tempFile ); } diff --git a/community/dbms/src/main/java/org/neo4j/dbms/report/jmx/LocalVirtualMachine.java b/community/dbms/src/main/java/org/neo4j/dbms/diagnostics/jmx/LocalVirtualMachine.java similarity index 98% rename from community/dbms/src/main/java/org/neo4j/dbms/report/jmx/LocalVirtualMachine.java rename to community/dbms/src/main/java/org/neo4j/dbms/diagnostics/jmx/LocalVirtualMachine.java index 1f3a0c2cf6e2e..0469f9624b5d1 100644 --- a/community/dbms/src/main/java/org/neo4j/dbms/report/jmx/LocalVirtualMachine.java +++ b/community/dbms/src/main/java/org/neo4j/dbms/diagnostics/jmx/LocalVirtualMachine.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.dbms.report.jmx; +package org.neo4j.dbms.diagnostics.jmx; import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.VirtualMachine; diff --git a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java b/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java index 099f483091912..cffc2a03356c3 100644 --- a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java +++ b/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java @@ -20,7 +20,6 @@ package org.neo4j.commandline.dbms; import org.junit.After; -import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -45,6 +44,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; @@ -74,7 +74,7 @@ public void tearDown() public void shouldBeAbleToAttachToPidAndRunThreadDump() throws IOException, CommandFailed, IncorrectUsage { long pid = getPID(); - Assume.assumeTrue( pid != 0 ); + assertThat( pid, is( not( 0 ) ) ); // Write config file Files.createFile( testDirectory.file( "neo4j.conf" ).toPath() ); diff --git a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandTest.java b/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandTest.java index 01429ae5ca3d6..1cf0f0e1418ab 100644 --- a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandTest.java +++ b/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandTest.java @@ -19,6 +19,7 @@ */ package org.neo4j.commandline.dbms; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -69,6 +70,7 @@ public class DiagnosticsReportCommandTest private Path homeDir; private Path configDir; private Path configFile; + private String originalUserDir; public static class MyDiagnosticsOfflineReportProvider extends DiagnosticsOfflineReportProvider { @@ -101,7 +103,14 @@ public void setUp() throws Exception Files.createFile( configFile ); // To make sure files are resolved from the working directory - System.setProperty( "user.dir", testDirectory.absolutePath().getAbsolutePath() ); + originalUserDir = System.setProperty( "user.dir", testDirectory.absolutePath().getAbsolutePath() ); + } + + @After + public void tearDown() + { + // Restore directory + System.setProperty( "user.dir", originalUserDir ); } @Test diff --git a/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReportSources.java b/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReportSources.java index 38d2903ff234b..12e248a8586c0 100644 --- a/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReportSources.java +++ b/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReportSources.java @@ -22,7 +22,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -126,8 +126,10 @@ public void addToArchive( Path archiveDestination, DiagnosticsReporterProgressCa InputStream in = fs.openAsInputStream( source ); // Track progress of the file reading, source might be a very large file - ProgressAwareInputStream inStream = new ProgressAwareInputStream( in, size, monitor::percentChanged ); - Files.copy( inStream, archiveDestination ); + try ( ProgressAwareInputStream inStream = new ProgressAwareInputStream( in, size, monitor::percentChanged ) ) + { + Files.copy( inStream, archiveDestination ); + } } } @@ -153,7 +155,7 @@ public void addToArchive( Path archiveDestination, DiagnosticsReporterProgressCa throws IOException { String message = messageSupplier.get(); - Files.write( archiveDestination, message.getBytes( Charset.forName( "UTF8" ) ), StandardOpenOption.CREATE, + Files.write( archiveDestination, message.getBytes( StandardCharsets.UTF_8 ), StandardOpenOption.CREATE, StandardOpenOption.APPEND ); } } diff --git a/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReporter.java b/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReporter.java index 7de02f813e418..5044194c698af 100644 --- a/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReporter.java +++ b/community/kernel/src/main/java/org/neo4j/diagnostics/DiagnosticsReporter.java @@ -110,6 +110,7 @@ public Set getAvailableClassifiers() return availableClassifiers; } + // TODO: private static class Mon implements DiagnosticsReporterProgressCallback { private final String prefix; diff --git a/pom.xml b/pom.xml index c95a8658be063..de58bc793cb4f 100644 --- a/pom.xml +++ b/pom.xml @@ -1174,6 +1174,11 @@ metrics-graphite 3.1.2 + + org.jprocesses + jProcesses + 1.6.4 + com.google.code.findbugs