Skip to content

Commit

Permalink
Reporter can now report environment variables and the output of dbms.…
Browse files Browse the repository at this point in the history
…listTransactions().

Windows file paths should work correctly now with .toUri() doing the escaping.
  • Loading branch information
klaren committed Jan 4, 2018
1 parent ddf832f commit 8f489f3
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 68 deletions.
40 changes: 5 additions & 35 deletions community/dbms/pom.xml
Expand Up @@ -93,42 +93,7 @@
</profile>
</profiles>

<!--<build>-->
<!--<plugins>-->
<!--<plugin>-->
<!--<groupId>org.apache.maven.plugins</groupId>-->
<!--<artifactId>maven-shade-plugin</artifactId>-->
<!--<executions>-->
<!--<execution>-->
<!--<phase>package</phase>-->
<!--<goals><goal>shade</goal></goals>-->
<!--<configuration>-->
<!--<artifactSet>-->
<!--<includes>-->
<!--<include>org.neo4j.driver:*</include>-->
<!--</includes>-->
<!--</artifactSet>-->
<!--<relocations>-->
<!--<relocation>-->
<!--<pattern>org.neo4j.driver</pattern>-->
<!--<shadedPattern>org.neo4j.not.driver</shadedPattern>-->
<!--</relocation>-->
<!--</relocations>-->
<!--<createDependencyReducedPom>false</createDependencyReducedPom>-->
<!--</configuration>-->
<!--</execution>-->
<!--</executions>-->
<!--</plugin>-->
<!--</plugins>-->
<!--</build>-->

<dependencies>
<!--<dependency>-->
<!--<groupId>org.neo4j.driver</groupId>-->
<!--<artifactId>neo4j-java-driver</artifactId>-->
<!--<scope>provided</scope> &lt;!&ndash; shaded inside the jar file &ndash;&gt;-->
<!--</dependency>-->

<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-kernel</artifactId>
Expand Down Expand Up @@ -163,6 +128,11 @@
<artifactId>neo4j-import-tool</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-jmx</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.jprocesses</groupId>
Expand Down
Expand Up @@ -191,7 +191,8 @@ private DiagnosticsReporter createAndRegisterSources( Config config, File config
reporter.registerSource( "threads", jmx.threadDump() );
reporter.registerSource( "heap", jmx.heapDump() );
reporter.registerSource( "sysprop", jmx.systemProperties() );
//reporter.registerSource( "env", null ); // TODO:
reporter.registerSource( "env", jmx.environmentVariables() );
reporter.registerSource( "activetxs", jmx.listTransactions() );
}
return reporter;
}
Expand All @@ -206,7 +207,7 @@ private Optional<JmxDump> connectToNeo4jInstance()
try
{
LocalVirtualMachine vm = LocalVirtualMachine.from( pid.get() );
out.println( "Attached to running process with process id " + pid );
out.println( "Attached to running process with process id " + pid.get() );
try
{
JmxDump jmxDump = JmxDump.connectTo( vm.getJmxAddress() );
Expand Down Expand Up @@ -298,8 +299,8 @@ static String describeClassifier( String classifier )
return "include a list of java system properties";
case "raft":
return "include the raft log";
case "queries":
return "include the output of dbms.listQueries()";
case "activetxs":
return "include the output of dbms.listTransactions()";
case "ps":
return "include a list of running processes";
default:
Expand All @@ -309,26 +310,46 @@ static String describeClassifier( String classifier )

private static DiagnosticsReportSource runningProcesses()
{
return DiagnosticsReportSources.newDiagnosticsString( "ps.txt", () ->
return DiagnosticsReportSources.newDiagnosticsString( "ps.csv", () ->
{
List<ProcessInfo> 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' );
sb.append( csvEscapeString( "Process PID" ) ).append( ',' )
.append( csvEscapeString( "Process Name" ) ).append( ',' )
.append( csvEscapeString( "Process Time" ) ).append( ',' )
.append( csvEscapeString( "User" ) ).append( ',' )
.append( csvEscapeString( "Virtual Memory" ) ).append( ',' )
.append( csvEscapeString( "Physical Memory" ) ).append( ',' )
.append( csvEscapeString( "CPU usage" ) ).append( ',' )
.append( csvEscapeString( "Start Time" ) ).append( ',' )
.append( csvEscapeString( "Priority" ) ).append( ',' )
.append( csvEscapeString( "Full command" ) ).append( '\n' );

for ( final ProcessInfo processInfo : processesList )
{
sb.append( processInfo.getPid() ).append( ',' )
.append( csvEscapeString( processInfo.getName() ) ).append( ',' )
.append( processInfo.getTime() ).append( ',' )
.append( csvEscapeString( processInfo.getUser() ) ).append( ',' )
.append( processInfo.getVirtualMemory() ).append( ',' )
.append( processInfo.getPhysicalMemory() ).append( ',' )
.append( processInfo.getCpuUsage() ).append( ',' )
.append( processInfo.getStartTime() ).append( ',' )
.append( processInfo.getStartTime() ).append( ',' )
.append( csvEscapeString( processInfo.getCommand() ) ).append( '\n' );
}
return sb.toString();
});
} );
}

private static String csvEscapeString( String s )
{
if ( s == null )
{
return "";
}
return "\"" + s.replace( "\"", "\"\"" ) + "\"";
}

/**
Expand Down
Expand Up @@ -32,6 +32,7 @@
import java.nio.file.Path;
import java.util.Properties;
import javax.management.InstanceNotFoundException;
import javax.management.JMX;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
Expand Down Expand Up @@ -236,4 +237,36 @@ public void addToArchive( Path archiveDestination, DiagnosticsReporterProgressCa
}
};
}

public DiagnosticsReportSource environmentVariables()
{
return newReportsBeanSource( "env.prop", Reports::getEnvironmentVariables );
}

public DiagnosticsReportSource listTransactions()
{
return newReportsBeanSource( "listTransactions.txt", Reports::listTransactions );
}

private DiagnosticsReportSource newReportsBeanSource( String destination, ReportsInvoker reportsInvoker )
{
return DiagnosticsReportSources.newDiagnosticsString( destination, () ->
{
try
{
ObjectName name = new ObjectName( "org.neo4j:instance=kernel#0,name=Reports" ); // TODO: instance #0 ??
Reports reportsBean = JMX.newMBeanProxy( mBeanServer, name, Reports.class );
return reportsInvoker.invoke( reportsBean );
}
catch ( MalformedObjectNameException ignored )
{
}
return "Unable to invoke ReportsBean";
} );
}

private interface ReportsInvoker
{
String invoke( Reports r );
}
}
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2002-2018 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.dbms.diagnostics.jmx;

import org.neo4j.jmx.Description;
import org.neo4j.jmx.ManagementInterface;

@ManagementInterface( name = Reports.NAME )
@Description( "Reports operations" )
public interface Reports
{
String NAME = "Reports";

@Description( "List all active transactions" )
String listTransactions();

@Description( "Returns a map if the current environment variables" )
String getEnvironmentVariables();
}
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2002-2018 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.dbms.diagnostics.jmx;

import java.util.Map;

import org.neo4j.graphdb.QueryExecutionException;
import org.neo4j.graphdb.Transaction;
import org.neo4j.jmx.impl.ManagementBeanProvider;
import org.neo4j.jmx.impl.ManagementData;
import org.neo4j.jmx.impl.Neo4jMBean;
import org.neo4j.kernel.internal.GraphDatabaseAPI;

public class ReportsBean extends ManagementBeanProvider
{
public ReportsBean()
{
super( Reports.class );
}

@Override
protected Neo4jMBean createMBean( ManagementData management )
{
return new ReportsImpl( management, false );
}

@Override
protected Neo4jMBean createMXBean( ManagementData management )
{
return new ReportsImpl( management, true );
}

private static class ReportsImpl extends Neo4jMBean implements Reports
{
private final GraphDatabaseAPI graphDatabaseAPI;

ReportsImpl( ManagementData management, boolean isMXBean )
{
super( management, isMXBean );
graphDatabaseAPI = management.getKernelData().graphDatabase();
}

@Override
public String listTransactions()
{
String res;
try ( Transaction tx = graphDatabaseAPI.beginTx() )
{
res = graphDatabaseAPI.execute( "CALL dbms.listTransactions()" ).resultAsString();

tx.success();
}
catch ( QueryExecutionException e )
{
res = "dbms.listTransactions() is not available";
}
return res;
}

@Override
public String getEnvironmentVariables()
{
StringBuilder sb = new StringBuilder();
for ( Map.Entry<String,String> env : System.getenv().entrySet() )
{
sb.append( env.getKey() ).append( '=' ).append( env.getValue() ).append( '\n' );
}
return sb.toString();
}
}
}
@@ -0,0 +1 @@
org.neo4j.dbms.diagnostics.jmx.ReportsBean
Expand Up @@ -84,10 +84,12 @@ public void dump( Set<String> classifiers, Path destination ) throws IOException
Map<String,String> env = new HashMap<>();
env.put( "create", "true" );

URI uri = URI.create("jar:file:" + destination.toAbsolutePath());
// NOTE: we need the toUri() in order to handle windows file paths
URI uri = URI.create("jar:file:" + destination.toAbsolutePath().toUri().getPath() );

try ( FileSystem fs = FileSystems.newFileSystem( uri, env ) )
{
DiagnosticsReporterProgressCallback progress = new InteractiveProgress( sources.size(), out );
for ( int i = 0; i < sources.size(); i++ )
{
DiagnosticsReportSource source = sources.get( i );
Expand All @@ -97,10 +99,9 @@ public void dump( Set<String> classifiers, Path destination ) throws IOException
Files.createDirectories( path.getParent() );
}

Mon monitor = new Mon( (i + 1) + "/" + sources.size(), " " + path, out );
monitor.started();
source.addToArchive( path, monitor );
monitor.finished();
progress.started( i + 1, path.toString() );
source.addToArchive( path, progress );
progress.finished();
}
}
}
Expand All @@ -111,18 +112,18 @@ public Set<String> getAvailableClassifiers()
}

// TODO:
private static class Mon implements DiagnosticsReporterProgressCallback
private static class InteractiveProgress implements DiagnosticsReporterProgressCallback
{
private final String prefix;
private final String suffix;
private String prefix;
private String suffix;
private final int numberOfFiles;
private final PrintStream out;
private String info = "";
private int longestInfo;

private Mon( String prefix, String suffix, PrintStream out )
private InteractiveProgress( int numberOfFiles, PrintStream out )
{
this.prefix = prefix;
this.suffix = suffix;
this.numberOfFiles = numberOfFiles;
this.out = out;
}

Expand All @@ -144,12 +145,14 @@ public void percentChanged( int percent )
out.print( ' ' );
}
}
out.print( String.format( "] %3s%% %s %s", percent, suffix, info ) );
out.print( String.format( "] %3s%% %s %s", percent, suffix, info ) );
}

@Override
public void started()
public void started( long currentFileIndex, String target )
{
this.prefix = currentFileIndex + "/" + numberOfFiles;
this.suffix = target;
percentChanged( 0 );
}

Expand Down
Expand Up @@ -43,7 +43,7 @@ public interface DiagnosticsReporterProgressCallback
/**
* @apiNote Called by dispatching class. Should not be called from diagnostics sources.
*/
void started();
void started( long currentFileIndex, String target );

/**
* @apiNote Called by dispatching class. Should not be called from diagnostics sources.
Expand Down

0 comments on commit 8f489f3

Please sign in to comment.