PHP
  Home arrow PHP arrow Page 3 - A Basic Monitoring Engine in PHP
Dev Shed Forums  
Administration  
AJAX  
Apache  
BrainDump  
DHTML  
Flash  
Java  
JavaScript  
Multimedia  
MySQL  
Oracle  
Perl  
PHP  
Practices  
Python  
Reviews  
Security  
Smartphone Development  
Style-Sheets  
Web Services  
XML  
Zend  
Zope  
Mobile Linux  
App Generation ROI  
IBM® developerWorks  
Forums Sitemap  
E-Commerce Hosting  
Linux Web Hosting  
Managed Hosting  
Small Business Hosting  
VPS Hosting  
Weekly Newsletter

 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid  
Request Media Kit
Contact Us  
Site Map  
Privacy Policy  
Support  
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
Google.com  
PHP

A Basic Monitoring Engine in PHP
By: Sams Publishing
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: starstarstarstarstar / 5
    2006-09-14


    Table of Contents:
  • A Basic Monitoring Engine in PHP
  • Giving Up Privileges
  • Combining What You've Learned: Monitoring Services
  • Sample ServiceLogger Process

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      error-file:tidyout.log Del.ici.ous error-file:tidyout.log Digg
      error-file:tidyout.log Blink error-file:tidyout.log Simpy
      error-file:tidyout.log Google error-file:tidyout.log Spurl
      error-file:tidyout.log Y! MyWeb error-file:tidyout.log Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article

     
     
    ADVERTISEMENT


    A Basic Monitoring Engine in PHP - Combining What You've Learned: Monitoring Services
    ( Page 3 of 4 )

    In this section you bring together your skills to write a basic monitoring engine in PHP. Because you never know how your needs will change, you should make it as flexible as possible.

    The logger should be able to support arbitrary service checks (for example, HTTP and FTP services) and be able to log events in arbitrary ways (via email, to a logfile, and so on). You, of course, want it to run as a daemon, so you should be able to request it to give its complete current state.

    A service needs to implement the following abstract class:

    abstract class ServiceCheck {
    
     const FAILURE = 0;
     const SUCCESS = 1;
     
     protected $timeout = 30;
     protected $next_attempt;
     protected $current_status = ServiceCheck::SUCCESS;
     protected $previous_status = ServiceCheck::SUCCESS;
     protected $frequency = 30;
     protected $description;
     protected $consecutive_failures = 0;
     protected $status_time;
     protected $failure_time;
     protected $loggers = array();
     
     abstract public function _ _construct($params);
    
     public function _ _call($name, $args)
     {
      if(isset($this->$name)) {
       return $this->$name;
      }
     }
     
     public function set_next_attempt()
     {
      $this->next_attempt = time() + $this->frequency;
     }
     
     public abstract function run();
    
     public function post_run($status)
     {
      if($status !== $this->current_status) {
       $this->previous_status = $this->current_status;
      }
      if($status === self::FAILURE) {
       if( $this->current_status === self::FAILURE ) {
        $this->consecutive_failures++;
       }
       else {
        $this->failure_time = time();
       }
      }
      else {
       $this->consecutive_failures = 0;
      }
      $this->status_time = time();
      $this->current_status = $status;
      $this->log_service_event();
     }  
    
     public function log_current_status()
     {
      foreach($this->loggers as $logger) {
       $logger->log_current_status($this);
      }
     }
    
     private function log_service_event()
     {
      foreach($this->loggers as $logger) {
       $logger->log_service_event($this);
      }
     }
    
     public function register_logger(ServiceLogger
    $logger) { $this->loggers[] = $logger; } }

    The _ _call() overload method provides read-only access to the parameters of a ServiceCheck object:

    • timeout—How long the check can hang before it is to be terminated by the engine.

    • next_attempt—When the next attempt to contact this server should be made.

    • current_status—The current state of the service: SUCCESS or FAILURE.

    • previous_status—The status before the current one.

    • frequency—How often the service should be checked.

    • description—A description of the service.

    • consecutive_failures—The number of consecutive times the service check has failed because it was last successful.

    • status_time—The last time the service was checked.

    • failure_time—If the status is FAILED, the time that failure occurred.

    The class also implements the observer pattern, allowing objects of type ServiceLogger to register themselves and then be called whenever log_current_status() or log_service_event() is called.

    The critical function to implement is run(), which defines how the check should be run. It should return SUCCESS if the check succeeded and FAILURE if not.

    The post_run() method is called after the service check defined in run() returns. It handles setting the status of the object and performing logging.

    The ServiceLogger interface: specifies that a logging class need only implement two methods, log_service_event() and log_current_status(), which are called when a run() check returns and when a generic status request is made, respectively.

    The interface is as follows:

    interface ServiceLogger {
     public function log_service_event(ServiceCheck
    $service); public function log_current_status(ServiceCheck
    $service); }

    Finally, you need to write the engine itself. The idea is similar to the ideas behind the simple programs in the "Writing Daemons" section earlier in this chapter: The server should fork off a new process to handle each check and use a SIGCHLD handler to check the return value of checks when they complete. The maximum number of checks that will be performed simultaneously should be configurable to prevent overutilization of system resources. All the services and logging will be defined in an XML file.

    The following is the ServiceCheckRunner class that defines the engine:

    class ServiceCheckRunner {
    
     private $num_children;
     private $services = array();
     private $children = array();
    
     public function _ _construct($conf, $num_children)
     {
      $loggers = array();
      $this->num_children = $num_children;
      $conf = simplexml_load_file($conf);
      foreach($conf->loggers->logger as $logger) {
       $class = new Reflection_Class("$logger->class");
       if($class->isInstantiable()) {
        $loggers["$logger->id"] = $class->newInstance();
       }
       else {
        fputs(STDERR, "{$logger->class} cannot be
    instantiated.\n"); exit; } } foreach($conf->services->service as $service) { $class = new Reflection_Class("$service->class"); if($class->isInstantiable()) { $item = $class->newInstance($service->params); foreach($service->loggers->logger as $logger) { $item->register_logger($loggers["$logger"]); } $this->services[] = $item; } else { fputs(STDERR, "{$service->class} is not
    instantiable.\n"); exit; } } } private function next_attempt_sort($a, $b) { if($a->next_attempt() == $b->next_attempt()) { return 0; } return ($a->next_attempt() < $b->next_attempt())
    ? -1 : 1; } private function next() { usort($this->services,
    array($this,'next_attempt_sort')); return $this->services[0]; } public function loop() { declare(ticks=1); pcntl_signal(SIGCHLD, array($this, "sig_child")); pcntl_signal(SIGUSR1, array($this, "sig_usr1")); while(1) { $now = time(); if(count($this->children)
    < $this->num_children) { $service = $this->next(); if($now < $service->next_attempt()) { sleep(1); continue; } $service->set_next_attempt(); if($pid = pcntl_fork()) { $this->children[$pid] = $service; } else { pcntl_alarm($service->timeout()); exit($service->run()); } } } } public function log_current_status() { foreach($this->services as $service) { $service->log_current_status(); } } private function sig_child($signal) { $status = ServiceCheck::FAILURE; pcntl_signal(SIGCHLD, array($this, "sig_child")); while(($pid = pcntl_wait($status, WNOHANG)) > 0)
    { $service = $this->children[$pid]; unset($this->children[$pid]); if(pcntl_wifexited($status) && pcntl_wexitstatus($status) ==
    ServiceCheck::SUCCESS) { $status = ServiceCheck::SUCCESS; } $service->post_run($status); } } private function sig_usr1($signal) { pcntl_signal(SIGUSR1, array($this, "sig_usr1")); $this->log_current_status(); } }

    This is an elaborate class. The constructor reads in and parses an XML file, creating all the services to be monitored and the loggers to record them. You'll learn more details on this in a moment.

    The loop() method is the main method in the class. It sets the required signal handlers and checks whether a new child process can be created. If the next event (sorted by next_attempt timestamp) is okay to run now, a new process is forked off. Inside the child process, an alarm is set to keep the test from lasting longer than its timeout, and then the test defined by run() is executed.

    There are also two signal handlers. The SIGCHLD handler sig_child() collects on the terminated child processes and executes their service's post_run() method. The SIGUSR1 handler sig_usr1() simply calls the log_current_status() methods of all registered loggers, which can be used to get the current status of the entire system.

    As it stands, of course, the monitoring architecture doesn't do anything. First, you need a service to check. The following is a class that checks whether you get back a 200 Server OK response from an HTTP server:

    class HTTP_ServiceCheck extends ServiceCheck
    {
     public $url;
     public function _ _construct($params)
     {
      foreach($params as $k => $v) {
       $k = "$k";
       $this->$k = "$v";
      }
     }
    
     public function run()
     {
      if(is_resource(@fopen($this->url, "r"))) {
       return ServiceCheck::SUCCESS;
      }
      else {
       return ServiceCheck::FAILURE;
      }
     }
    }

    Compared to the framework you built earlier, this service is extremely simple—and that's the point: the effort goes into building the framework, and the extensions are very simple.



     
     
    >>> More PHP Articles          >>> More By Sams Publishing
     

       

    PHP ARTICLES

    - Implementing Factory Methods in PHP 5
    - Merging a File Split for FTP Upload using PHP
    - Getting Data from Yahoo Site Explorer Inboun...
    - Method Chaining: Adding More Selecting Metho...
    - How to Split a File During an FTP Upload Usi...
    - Expanding a Custom CodeIgniter Library with ...
    - Using the Yahoo Site Explorer Inbound Links ...
    - Building a CodeIgniter Custom Library with M...
    - Building an E-mini Trading System Using PHP ...
    - Completing the MySQL Class with Method Chain...
    - Building Dynamic Queries with Chainable Meth...
    - PHP Encryption and Decryption Methods
    - Building a MySQL Abstraction Class with Meth...
    - Completing a Sample String Processor with Me...
    - Mastering WHILE Loops for PHP and MySQL





    © 2003-2009 by Developer Shed. All rights reserved. DS Cluster 4 Hosted by Hostway
    For more Enterprise Application Development news, visit eWeek