Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added PGStatActivity Activity class to show some of the stats from the pg_stat_activity table such as: number of idle transaction, number of idle connections, and the current longest running SQL statement. Also added a ScrollView to the layout since the new buttons were not fitting into the fixed screen size.
- Loading branch information
Showing
6 changed files
with
284 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,39 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:orientation="vertical" android:layout_width="fill_parent" | ||
android:layout_height="fill_parent"> | ||
<TextView android:id="@+id/heading" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content" android:text="@string/hello" | ||
android:layout_alignParentTop="true" android:layout_alignParentLeft="true" /> | ||
<TextView android:text="Host:" android:id="@+id/TextView01" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content" | ||
android:layout_below="@+id/heading"></TextView> | ||
<EditText android:id="@+id/pghost" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="Port:" android:id="@+id/TextView02" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pgport" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="Database:" android:id="@+id/TextView03" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pgdatabase" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="User:" android:id="@+id/TextView04" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pguser" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="Password:" android:id="@+id/TextView05" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pgpassword" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content" android:password="true"></EditText> | ||
<Button android:text="Monitor Database Stats" android:id="@+id/database" | ||
android:layout_width="fill_parent" android:layout_height="wrap_content"></Button> | ||
<Button android:text="Monitor Background Writer Stats" | ||
android:id="@+id/bgwriter" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></Button> | ||
</LinearLayout> | ||
<ScrollView android:id="@+id/widget54" android:layout_width="fill_parent" | ||
android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:orientation="vertical" android:layout_width="fill_parent" | ||
android:layout_height="fill_parent"> | ||
<TextView android:id="@+id/heading" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content" android:text="@string/hello" | ||
android:layout_alignParentTop="true" android:layout_alignParentLeft="true" /> | ||
<TextView android:text="Host:" android:id="@+id/TextView01" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content" | ||
android:layout_below="@+id/heading"></TextView> | ||
<EditText android:id="@+id/pghost" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="Port:" android:id="@+id/TextView02" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pgport" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="Database:" android:id="@+id/TextView03" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pgdatabase" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="User:" android:id="@+id/TextView04" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pguser" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></EditText> | ||
<TextView android:text="Password:" android:id="@+id/TextView05" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<EditText android:id="@+id/pgpassword" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content" android:password="true"></EditText> | ||
<Button android:text="Monitor Database Stats" android:id="@+id/database" | ||
android:layout_width="fill_parent" android:layout_height="wrap_content"></Button> | ||
<Button android:text="Monitor Background Writer Stats" | ||
android:id="@+id/bgwriter" android:layout_width="fill_parent" | ||
android:layout_height="wrap_content"></Button> | ||
<Button android:text="Monitor Activity" android:id="@+id/activity" | ||
android:layout_width="fill_parent" android:layout_height="wrap_content"></Button> | ||
</LinearLayout> | ||
</ScrollView> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:orientation="vertical" android:layout_width="fill_parent" | ||
android:layout_height="fill_parent"> | ||
<TextView android:id="@+id/displayheader" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<TextView android:id="@+id/idle_connections" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<TextView android:id="@+id/idle_transactions" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
<TextView android:id="@+id/waiting" android:layout_width="wrap_content" | ||
android:layout_height="wrap_content"></TextView> | ||
<TextView android:id="@+id/query_time" android:layout_width="wrap_content" | ||
android:layout_height="wrap_content"></TextView> | ||
<TextView android:id="@+id/current_query" | ||
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> | ||
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
package org.postgresql.top; | ||
|
||
import java.sql.Connection; | ||
import java.sql.DriverManager; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.sql.Statement; | ||
|
||
import org.postgresql.top.PGTop.State; | ||
|
||
import android.app.Activity; | ||
import android.content.Intent; | ||
import android.content.SharedPreferences; | ||
import android.os.Bundle; | ||
import android.os.Handler; | ||
import android.os.Message; | ||
import android.view.Menu; | ||
import android.view.MenuInflater; | ||
import android.view.MenuItem; | ||
import android.widget.TextView; | ||
import android.widget.Toast; | ||
|
||
public class PGStatActivity extends Activity implements Runnable { | ||
private String pgDatabase; | ||
private String url; | ||
private String pgUser; | ||
private String pgPassword; | ||
|
||
Thread thread; | ||
|
||
private String headerString; | ||
private long idleConnections = 0; | ||
private long idleTransactions = 0; | ||
private long waiting = 0; | ||
private String currentQueryString; | ||
private String queryTimeString; | ||
|
||
private TextView headerTextView; | ||
private TextView idleConnectionsTextView; | ||
private TextView idleTransactionsTextView; | ||
private TextView waitingTextView; | ||
private TextView currentQueryTextView; | ||
private TextView queryTimeTextView; | ||
|
||
private State state; | ||
|
||
private void getActivityStats() throws SQLException { | ||
Connection conn; | ||
Statement st; | ||
ResultSet rs; | ||
|
||
String sql = null; | ||
|
||
conn = DriverManager.getConnection(url, pgUser, pgPassword); | ||
|
||
st = conn.createStatement(); | ||
|
||
sql = "" | ||
+ "SELECT NOW(), " | ||
+ " (SELECT COUNT(*) " | ||
+ " FROM pg_stat_activity " | ||
+ " WHERE current_query = '<IDLE>') AS idle_connections, " | ||
+ " (SELECT COUNT(*) " | ||
+ " FROM pg_stat_activity " | ||
+ " WHERE current_query = '<IDLE> in transaction') AS idle_transactions, " | ||
+ " (SELECT COUNT(*) " | ||
+ " FROM pg_stat_activity " | ||
+ " WHERE waiting IS TRUE) AS waiting;"; | ||
rs = st.executeQuery(sql); | ||
if (rs.next()) { | ||
headerString = pgDatabase + " " + rs.getString(1); | ||
idleConnections = rs.getLong(2); | ||
idleTransactions = rs.getLong(3); | ||
waiting = rs.getLong(4); | ||
} | ||
rs.close(); | ||
|
||
/* | ||
* Don't show this query if it's the only run running on the system, and | ||
* only query for SQL running against the database we're currently | ||
* connected to. | ||
*/ | ||
sql = "" + "SELECT NOW() - query_start, current_query " | ||
+ "FROM pg_stat_activity " | ||
+ "WHERE datname = '" + pgDatabase + "' " | ||
+ " AND current_query <> '<IDLE>' " | ||
+ " AND current_query <> '<IDLE> in transaction' " | ||
+ " AND procpid <> PG_BACKEND_PID() " | ||
+ "ORDER BY 1 DESC " | ||
+ "LIMIT 1;"; | ||
rs = st.executeQuery(sql); | ||
if (rs.next()) { | ||
queryTimeString = rs.getString(1); | ||
currentQueryString = rs.getString(2); | ||
} else { | ||
queryTimeString = "No SQL statements currently running..."; | ||
currentQueryString = ""; | ||
} | ||
rs.close(); | ||
|
||
st.close(); | ||
conn.close(); | ||
} | ||
|
||
@Override | ||
public void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
setContentView(R.layout.pg_stat_activity); | ||
|
||
state = State.RUNNING; | ||
|
||
try { | ||
Class.forName("org.postgresql.Driver"); | ||
} catch (ClassNotFoundException e) { | ||
Toast | ||
.makeText(PGStatActivity.this, e.toString(), | ||
Toast.LENGTH_LONG).show(); | ||
return; | ||
} | ||
|
||
SharedPreferences preferences = getSharedPreferences("PGTopPrefs", 0); | ||
pgDatabase = preferences.getString("pgdatabase", ""); | ||
url = preferences.getString("pgurl", ""); | ||
pgUser = preferences.getString("pguser", ""); | ||
pgPassword = preferences.getString("pgpassword", ""); | ||
|
||
headerTextView = (TextView) findViewById(R.id.displayheader); | ||
idleConnectionsTextView = (TextView) findViewById(R.id.idle_connections); | ||
idleTransactionsTextView = (TextView) findViewById(R.id.idle_transactions); | ||
waitingTextView = (TextView) findViewById(R.id.waiting); | ||
queryTimeTextView = (TextView) findViewById(R.id.query_time); | ||
currentQueryTextView = (TextView) findViewById(R.id.current_query); | ||
|
||
thread = new Thread(this); | ||
} | ||
|
||
@Override | ||
public boolean onCreateOptionsMenu(Menu menu) { | ||
MenuInflater inflater = getMenuInflater(); | ||
inflater.inflate(R.menu.menu, menu); | ||
return true; | ||
} | ||
|
||
@Override | ||
protected void onDestroy() { | ||
super.onDestroy(); | ||
state = State.EXITING; | ||
} | ||
|
||
@Override | ||
public boolean onOptionsItemSelected(MenuItem item) { | ||
switch (item.getItemId()) { | ||
case R.id.stop: | ||
Intent intent = new Intent(); | ||
setResult(RESULT_OK, intent); | ||
finish(); | ||
return true; | ||
default: | ||
return super.onOptionsItemSelected(item); | ||
} | ||
} | ||
|
||
@Override | ||
protected void onPause() { | ||
super.onPause(); | ||
state = State.PAUSED; | ||
} | ||
|
||
protected void onResume() { | ||
super.onResume(); | ||
state = State.RUNNING; | ||
thread.start(); | ||
} | ||
|
||
@Override | ||
protected void onStop() { | ||
super.onStop(); | ||
state = State.EXITING; | ||
} | ||
|
||
public void run() { | ||
/* | ||
* Loop to refresh the display of activity statistics. Open and close a | ||
* connection on each loop. | ||
*/ | ||
while (state == State.RUNNING) { | ||
try { | ||
getActivityStats(); | ||
|
||
handler.sendEmptyMessage(0); | ||
// FIXME: Make the refresh rate a configuration parameter. | ||
Thread.sleep(2000); | ||
} catch (SQLException e) { | ||
Toast.makeText(PGStatActivity.this, e.toString(), | ||
Toast.LENGTH_LONG).show(); | ||
return; | ||
} catch (InterruptedException e) { | ||
Toast.makeText(PGStatActivity.this, e.toString(), | ||
Toast.LENGTH_LONG).show(); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
private Handler handler = new Handler() { | ||
@Override | ||
public void handleMessage(Message msg) { | ||
headerTextView.setText(headerString); | ||
idleConnectionsTextView.setText("Idle Connections: " | ||
+ Long.toString(idleConnections)); | ||
idleTransactionsTextView.setText("Idle Transactions: " | ||
+ Long.toString(idleTransactions)); | ||
waitingTextView.setText("Connections Waiting: " | ||
+ Long.toString(waiting)); | ||
queryTimeTextView.setText("Longest Running Query: " | ||
+ queryTimeString); | ||
currentQueryTextView.setText(currentQueryString); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters