Search This Blog

Saturday, April 20, 2013

Symfony Project SfServermon - Post 8 Notification

Having got the server status in the previous post, we now handle any notifications resulting.


The notify() method in UpdaterStatus is called from ServerUpdate.
It can use the Entity Manager and configuration references created when UpdaterStatus was initalised.
The notification in phpServerMon and sfServermon support:
  • Email.
  • Logging.
  • SMS - implemented as a seperate bundle.
These are enabled (and checked for here, in the application configuration).
Also each server can be specified as supporting any of the 3 methods to notify:
  • Always - everytime the status is checked - as a 'heartbeat'.
  • If server is offline.
  • If server status has changed.
These are checked for first:
public function notify() {
if ($this->config['email_status'] == false && ! $this->config['config_status'] == false && $this->config['log_status'] == false) {
//nothing to log;
return;
}
$notify = false;
$logWarning = false;

switch($this->config['alert_type']) {
case 'always':
if($this->status_new == 'off') {
// server is offline. we are in error state.
$notify = true;
}
break;
case 'offline':
// only send a notification if the server goes down for the first time!
if($this->status_new == 'off' && $this->status_org == 'on') {
$notify = true;
}
break;
case 'status':
if($this->status_new != $this->status_org) {
// status has been changed!
$notify = true;
}
break;
}

if(!$notify && !$logWarning) {
return false;
}

Log Notification:

Log notification is simply created a new log entity and saving it to the database:
if($this->config['log_status']) {
$log = new MonitorLog();
$log->setServer($this->server);
$log->setType('status');
$log->setMessage('status');
$log->setDateTime(new \DateTime());
$log->setuserId(0);
$log->setMessage($this->server->parseMessage($this->status_new, 'sms'
, array('label'=>$this->server->getLabel()
, 'ip'=>$this->server->getIp()
, 'port'=>$this->server->getPort()
, 'error'=>$this->server->getError())
));
$this->em->persist($log);
$this->em->flush();

phpServerMon had the parseMessage in a utility class, however I have moved it to the server entity, as I thought it made sense for an entity to know how to generate a status message for itself.
The parseMessage method:
public function parseMessage($status, $type, $vars)
{
    $notifications = array(
      'off_sms' => 'Server \'%LABEL%\' is DOWN: ip=%IP%, port=%PORT%. Error=%ERROR%',
      'off_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is DOWN',
      'off_email_body' => "Failed to connect to the following server:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Error: %ERROR%<br/>Date: %DATE%",
      'on_sms' => 'Server \'%LABEL%\' is RUNNING: ip=%IP%, port=%PORT%',
      'on_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is RUNNING',
      'on_email_body' => "Server '%LABEL%' is running again:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Date: %DATE%",
      'warn_sms' => 'Server \'%LABEL%\' needs ATTENTION: ip=%IP%, port=%PORT%. Error=%ERROR%',
      'warn_email_subject' => 'IMPORTANT: Server \'%LABEL%\' need ATTENTION',
      'warn_email_body' => "check server load or disk usage:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Error: %ERROR%<br/>Date: %DATE%",
    );


    $message = '';
  
    //$message = sm_get_lang('notifications', $status . '_' . $type);
    $message=$notifications[$status . '_' . $type];

    if(!$message) {
      return $message;
    }
    $vars['date'] = date('Y-m-d H:i:s');
  
    foreach($vars as $k => $v) {
      $message = str_replace('%' . strtoupper($k) . '%', $v, $message);
    }
  
    return $this->message = $message;
  }

If course the vars associative array should be generated within the class, so the method becomes:


public function parseMessage($status, $type)
{
$vars = array('label'=>$this->getLabel()
, 'ip'=>$this->getIp()
, 'port'=>$this->getPort()
, 'error'=>$this->getError());

$message = '';

//$message = sm_get_lang('notifications', $status . '_' . $type);
$message=$this->notifications[$status . '_' . $type];

if(!$message) {
return $message;
}

$vars = array('label'=>$this->getLabel()
, 'ip'=>$this->getIp()
, 'port'=>$this->getPort()
, 'error'=>$this->getError());

$vars['date'] = date('Y-m-d H:i:s');

foreach($vars as $k => $v) {
$message = str_replace('%' . strtoupper($k) . '%', $v, $message);
}

return $this->message = $message;
}

Note - this is an initial version of the parseMessage method will be recoded as part of a future post to add internationalisation support.

Email Notification.

For email notification we will use Swiftmailerbundle - available as a common addition to Symfony installations.

The code for this:
 protected function notifyByEmail() {

      $message = \Swift_Message::newInstance();
   $message->setSubject($this->server->parseMessage($this->status_new, 'email_subject'))
      ->setFrom($this->config['email_from_email'])
      ->setTo('johnr@reidyint.com')
      ->setBody($this->server->parseMessage($this->status_new, 'email_body'));
       
   $this->mailer->send($message);
}


This version has a hard coded recipient email address, the users to notify are specified in the users table - which is document in a future post in this series.
This needs a reference to the mailer. One was added to the service configuration for ServerUpdate:

services:
  server_update:
    class: JMPR\ServerMonBundle\Update\ServerUpdate
    arguments: [ @doctrine.orm.entity_manager, @mailer ]

And the constructor was updated:
class ServerUpdate
{
protected $em;
protected $mailer;

 public function __construct($em, $mailer)
   {
       $this->em = $em;
       $this->mailer = $mailer;
   }

A reference is then passed to the constructor for the UpdaterStatus class.


SMS Notification - SMS Messages.

The third type of notification supported are SMS text messages. This functionality is implemented as a seperate bundle and documented in the next post.

No comments:

Post a Comment