Skip to content

Commit

Permalink
add pg_stat_activity_display
Browse files Browse the repository at this point in the history
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
markwkm committed Jul 6, 2010
1 parent 51dc000 commit 3f5b766
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 36 deletions.
1 change: 1 addition & 0 deletions AndroidManifest.xml
Expand Up @@ -9,6 +9,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PGStatActivity"></activity>
<activity android:name=".PGStatBgwriter"></activity>
<activity android:name=".PGStatDatabase"></activity>
</application>
Expand Down
71 changes: 38 additions & 33 deletions res/layout/main.xml
@@ -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>
17 changes: 17 additions & 0 deletions res/layout/pg_stat_activity.xml
@@ -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>
220 changes: 220 additions & 0 deletions src/org/postgresql/top/PGStatActivity.java
@@ -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);
}
};
}
2 changes: 1 addition & 1 deletion src/org/postgresql/top/PGStatBgwriter.java
Expand Up @@ -128,7 +128,7 @@ public void onCreate(Bundle savedInstanceState) {

thread = new Thread(this);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
Expand Down
9 changes: 7 additions & 2 deletions src/org/postgresql/top/PGTop.java
Expand Up @@ -56,6 +56,9 @@ public void onClick(View view) {

Intent myIntent = null;
switch (view.getId()) {
case R.id.activity:
myIntent = new Intent(view.getContext(), PGStatActivity.class);
break;
case R.id.database:
myIntent = new Intent(view.getContext(), PGStatDatabase.class);
break;
Expand All @@ -72,10 +75,12 @@ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

final Button databaseButton = (Button) findViewById(R.id.database);
databaseButton.setOnClickListener(this);
final Button activityButton = (Button) findViewById(R.id.activity);
activityButton.setOnClickListener(this);
final Button bgwriterButton = (Button) findViewById(R.id.bgwriter);
bgwriterButton.setOnClickListener(this);
final Button databaseButton = (Button) findViewById(R.id.database);
databaseButton.setOnClickListener(this);
}

@Override
Expand Down

0 comments on commit 3f5b766

Please sign in to comment.