Permalink
Browse files

add pg_stat_activity_display

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 3f5b766bdc980f314412c61460f695bcba57c0d7
View
@@ -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>
View
@@ -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>
@@ -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>
@@ -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);
+ }
+ };
+}
@@ -128,7 +128,7 @@ public void onCreate(Bundle savedInstanceState) {
thread = new Thread(this);
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
@@ -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;
@@ -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

0 comments on commit 3f5b766

Please sign in to comment.