AWS Official Blog

How to Use the New CloudWatch User-Defined Metrics Feature With PHP

by Jeff Barr | on |

I thought it would be fun to show the new CloudWatch User-Defined metrics in action so I spent a couple of hours cooking up a little example. I launched an EC2 Micro instance, installed PHP, Emacs, the MySQL development package, and the AWS SDK for PHP.

The first step is to include the master file for the SDK:

require_once(“sdk.class.php”);

Access objects are created for RDS and CloudWatch:

// Access RDS and CloudWatch
$RDS  = new AmazonRDS ();
$CW   = new AmazonCloudWatch ();

The RDS object is then used to fetch a list of DB Instances:

// Get list of RDS DB Instances
$db_instances = $RDS->describe_db_instances();

A simple for loop is used to process each DB Instance in turn. Within the loop, we focus on the database instances that are available (as opposed to other states, such as modifying) and that are running MySQL:

foreach ( $db_instances -> body -> DescribeDBInstancesResult -> DBInstances -> DBInstance  as  $db_instance )
{
  if (
$db_instance -> DBInstanceStatus == ‘available’ &&
      
$db_instance -> Engine == ‘mysql’ )
  {
  }
)

The rest of the code is within the if statement. I’ve broken it out for clarity. The DB Instance Id, Endpoint, and master user name are extracted:

     $DBInstanceId    $db_instance -> DBInstanceIdentifier ;
   
$Endpoint        $db_instance -> Endpoint -> Address  ‘:’ $db_instance -> Endpoint -> Port ;
   
$MasterUsername  $db_instance -> MasterUsername ;

This information is used to establish a connection to the MySQL server running on the DB Instance and to run the “show status” command on the instance. RDS knows (but refuses to divulge for security reasons) the password for the master user, so I’ve hard-coded it for this example.

    if (!( $db = mysql_connect ( $Endpoint , $MasterUsername ‘dbpass’ )))
    {
      exit(
“Could not connect to MySQL database ${ DBInstanceId} at ${Endpoint}\n”);
    }

    if (!($res = mysql_query(‘show status’)))
    {
      exit(
“Could not query database\n”);
    }

The show status command returns multiple rows of data. The next step is to convert all of this data into a single associative array and to close the connection to the MySQL server.

     $status  = array();
    while (
$row  mysql_fetch_assoc ( $res ))
    {
      
$status [ $row [ ‘Variable_name’ ]] = $row [ ‘Value’ ];
    }
   
mysql_close ( $db );

Now it is time to extract the information to be stored in CloudWatch, and to display it for debugging:

    print( “ID ${ DBInstanceId } :\n” );

    $Uptime               $status[‘Uptime’];
   
$TableLocksImmediate  $status[‘Table_locks_immediate’];
   
$ThreadsCreated       $status[‘Threads_created’];
    
    print(
”  Uptime:  ${Uptime}\n”);
    print(
”  Locks immediate:  ${TableLocksImmediate}\n”);
    print(
”  Threads created:  ${ThreadsCreated}\n”);

Now we get to the heart of the matter, actually storing the information into CloudWatch. It took me a couple of tries to get the call to work, so feel free to start from where I left off! Here’s all the code that’s needed to store three separate user-defined metrics in CloudWatch:

     // Put metrics into CloudWatch
    
$res1  $CW -> put_metric_data ( ‘Sys/RDS’ ,
                 array(array(
‘MetricName’  => ‘Uptime’ ,
                         
   ‘Dimensions’  => array(array( ‘Name’  => ‘DBInstanceIdentifier’ ,
                          
                                ‘Value’ => $DBInstanceId )),
                         
   ‘Value’       => $Uptime ,
                         
   ‘Unit’         => ‘Seconds’ )));

     $res2  $CW->put_metric_data(‘Sys/RDS’ ,
                 array(array(
‘MetricName’  => ‘LocksImmediate’ ,
                        
    ‘Dimensions’  => array(array(‘Name’   => ‘DBInstanceIdentifier’,
                           
                              ‘Value’  => $DBInstanceId)),
                         
   ‘Value’       => $TableLocksImmediate,
                         
   ‘Unit’        => ‘Count’)));

     $res3  $CW->put_metric_data (‘Sys/RDS’ ,
                 array(array(
‘MetricName’  => ‘ThreadsCreated’ ,
                         
   ‘Dimensions’  => array(array(‘Name’   => ‘DBInstanceIdentifier’,
                                     
                    ‘Value’  =>  $DBInstanceId)),
                         
   ‘Value’       => $ThreadsCreated,
                         
   ‘Unit’        => ‘Count’)));

Remember that you can store any sort of metrics you want, for AWS resources or for your own application-level resources.

The calls to put_metric_data will return errors if the parameters aren’t correct, so error checking is (as always) a good idea:

    if ( $res1 -> isOK () &&  $res2 -> isOK () &&  $res3 -> isOK ())
    {
      print(
”  Metrics stored!\n\n” );
    }
    else
    {
      print(
”  Metric not stored!\n\n” );
    }
  }

Once I had everything running I set up a crontab entry to invoke the code above every 5 minutes:

*/5 * * * * /home/ec2-user/cw_rds.php

That’s all the code and setup needed to locate a set of RDS DB Instances, connect to each of them in turn, get some statistics, and store the statistics in CloudWatch. Pretty simple, don’t you think?

– Jeff;