Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

airbrake worker

  • Loading branch information...
commit a5f80ffbb452810ae6bad8d7bf980969de61f38c 1 parent 69ec564
Andrei Dziahel authored
24  php/Airbrake.php
... ...
@@ -0,0 +1,24 @@
  1
+<?php
  2
+include("../IronWorker.class.php");
  3
+
  4
+$name = "airbrake-php";
  5
+
  6
+$iw = new IronWorker('config.ini');
  7
+$iw->debug_enabled = true;
  8
+
  9
+$zipName = "code/$name.zip";
  10
+
  11
+$zipFile = IronWorker::zipDirectory(dirname(__FILE__)."/workers/airbrake", $zipName, true);
  12
+
  13
+$res = $iw->postCode('airbrake.php', $zipName, $name);
  14
+
  15
+$task_id = $iw->postTask($name, $payload);
  16
+echo "task_id = $task_id \n";
  17
+sleep(15);
  18
+$details = $iw->getTaskDetails($task_id);
  19
+print_r($details);
  20
+
  21
+if ($details->status != 'queued'){
  22
+    $log = $iw->getLog($task_id);
  23
+    print_r($log);
  24
+}
12  php/workers/airbrake/airbrake.php
... ...
@@ -0,0 +1,12 @@
  1
+<?php
  2
+require_once dirname(__FILE__) . '/lib/Airbrake.class.php';
  3
+$brake = new Services_Airbrake('API_Key', 'production', 'curl');
  4
+
  5
+// YOUR WORKER CODE HERE
  6
+
  7
+try {
  8
+    //do something
  9
+}
  10
+catch (Exception $e) {
  11
+    $brake->notify(get_class($e), $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTrace(), "worker");
  12
+}
495  php/workers/airbrake/lib/Airbrake.class.php
... ...
@@ -0,0 +1,495 @@
  1
+<?php
  2
+/**
  3
+ * Services_Airbrake
  4
+ *
  5
+ * @category error
  6
+ * @package  Services_Airbrake
  7
+ * @author   Rich Cavanaugh <no@email>
  8
+ * @author   Till Klampaeckel <till@php.net>
  9
+ * @author   Aaron Parecki <aaron@parecki.com>
  10
+ * @license  
  11
+ * @version  GIT: $Id$
  12
+ * @link     http://github.com/geoloqi/php-airbrake-notifier
  13
+ */
  14
+class Services_Airbrake
  15
+{
  16
+	const NOTIFIER_NAME = 'php-airbrake-notifier';
  17
+	const NOTIFIER_VERSION = '0.2.2';
  18
+	const NOTIFIER_URL = 'http://github.com/geoloqi/php-airbrake-notifier';
  19
+	const NOTIFIER_API_VERSION = '2.0';
  20
+
  21
+	protected $error_class;
  22
+	protected $message;
  23
+	protected $file;
  24
+	protected $line;
  25
+	protected $trace;
  26
+
  27
+	/**
  28
+	 * Report E_STRICT
  29
+	 *
  30
+	 * @var bool $reportESTRICT
  31
+	 * @todo Implement set!
  32
+	 */
  33
+	protected $reportESTRICT;
  34
+
  35
+	/**
  36
+	 * Timeout for cUrl.
  37
+	 * @var int $timeout
  38
+	 */
  39
+	protected $timeout;
  40
+
  41
+	public $client; // pear, curl, zend or beanstalk
  42
+
  43
+	/**
  44
+	 * @var mixed $apiKey
  45
+	 */
  46
+	public $apiKey;
  47
+
  48
+	/**
  49
+	 * @var string
  50
+	 **/
  51
+	public $environment;
  52
+
  53
+	/**
  54
+	 * Initialize the chosen notifier and install the error
  55
+	 * and exception handlers that connect to Airbrake
  56
+	 *
  57
+	 * @return void
  58
+	 * @author Rich Cavanaugh
  59
+	 */
  60
+	public static function installHandlers($apiKey=NULL, $environment=NULL, $client=NULL, $class='Services_Airbrake')
  61
+	{
  62
+		$airbrake = new $class($apiKey, $environment, $client);
  63
+		$airbrake->installNotifierHandlers();
  64
+	}
  65
+
  66
+	/**
  67
+	 * Hook's this notifier to PHP error and exception handlers
  68
+	 * @return void
  69
+	 * @author Rich Cavanaugh
  70
+	 **/
  71
+	public function installNotifierHandlers()
  72
+	{
  73
+		register_shutdown_function(array($this, "fatalErrorHandler"));
  74
+		set_error_handler(array($this, "errorHandler"));
  75
+		set_exception_handler(array($this, "exceptionHandler"));		
  76
+	}
  77
+
  78
+	/**
  79
+	 * Initialize the Hoptad client
  80
+	 *
  81
+	 * @param string $apiKey
  82
+	 * @param string $environment
  83
+	 * @param string $client
  84
+	 * @param string $reportESTRICT
  85
+	 * @param int $timeout
  86
+	 * @return void
  87
+	 * @author Rich Cavanaugh
  88
+	 */	
  89
+	function __construct($apiKey, $environment='production', $client='pear', $reportESTRICT=false, $timeout=2)
  90
+	{
  91
+		$this->apiKey = $apiKey;
  92
+		$this->environment = $environment;
  93
+		$this->client = $client;
  94
+		$this->reportESTRICT = $reportESTRICT;
  95
+		$this->timeout = $timeout;
  96
+		$this->setup();
  97
+	}
  98
+
  99
+	/**
  100
+	 * A method meant specifically for subclasses to override so they don't need
  101
+	 * to handle the constructor
  102
+	 * @return void
  103
+	 * @author Rich Cavanaugh
  104
+	 **/
  105
+	public function setup()
  106
+	{
  107
+		// we don't do anything here in the base class
  108
+	}
  109
+
  110
+	/**
  111
+	 * Handle a php error
  112
+	 *
  113
+	 * @param string $code 
  114
+	 * @param string $message 
  115
+	 * @param string $file 
  116
+	 * @param string $line 
  117
+	 * @return void
  118
+	 * @author Rich Cavanaugh
  119
+	 */
  120
+	public function errorHandler($code, $message, $file, $line)
  121
+	{
  122
+		if ($code == E_STRICT && $this->reportESTRICT === false) return;
  123
+
  124
+		$this->notify($code, $message, $file, $line, debug_backtrace());
  125
+	}
  126
+
  127
+	/**
  128
+	 * Handle a raised exception
  129
+	 *
  130
+	 * @param Exception $exception 
  131
+	 * @return void
  132
+	 * @author Rich Cavanaugh
  133
+	 */
  134
+	public function exceptionHandler($exception)
  135
+	{
  136
+		$this->notify(get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine(), $exception->getTrace());
  137
+	}
  138
+
  139
+	/**
  140
+	* Handle fatal errors
  141
+	*
  142
+	* @return void
  143
+	* @author Robert Rotarius
  144
+	*/
  145
+	public function fatalErrorHandler() 
  146
+	{
  147
+		$error = error_get_last(); 
  148
+		if($error)
  149
+			$this->notify($error['type'], $error['message'], $error['file'], $error['line'], debug_backtrace());
  150
+	}
  151
+  
  152
+	/**
  153
+	 * Set the values to be used for the next notice sent to Airbrake
  154
+	 * @return void
  155
+	 * @author Rich Cavanaugh
  156
+	 **/
  157
+	public function setParamsForNotify($error_class, $message, $file, $line, $trace, $component=NULL)
  158
+	{
  159
+		$this->error_class = $error_class;
  160
+		$this->message = $message;
  161
+		$this->file = $file;
  162
+		$this->line = $line;
  163
+		$this->trace = $trace;
  164
+		$this->component = $component;
  165
+	}
  166
+
  167
+	/**
  168
+	 * Pass the error and environment data on to Airbrake
  169
+	 *
  170
+	 * @param mixed  $error_class
  171
+	 * @param string $message
  172
+	 * @param string $file
  173
+	 * @param string $line
  174
+	 * @param array  $trace
  175
+	 * @param string $environment
  176
+	 *
  177
+	 * @author Rich Cavanaugh
  178
+	 * @todo   Handle response (e.g. errors)
  179
+	 */
  180
+	function notify($error_class, $message, $file, $line, $trace, $component=NULL)
  181
+	{
  182
+		$this->setParamsForNotify($error_class, $message, $file, $line, $trace, $component);
  183
+
  184
+		$url = "http://airbrakeapp.com/notifier_api/v2/notices";
  185
+		$headers = array(
  186
+			'Accept'				=> 'text/xml, application/xml',
  187
+			'Content-Type'	=> 'text/xml'
  188
+		);
  189
+		$body = $this->buildXmlNotice();
  190
+
  191
+		try {
  192
+			$status = call_user_func_array(array($this, $this->client . 'Request'), array($url, $headers, $body));
  193
+			if ($status != 200) $this->handleErrorResponse($status);
  194
+		} catch (RuntimeException $e) {
  195
+			// TODO do something reasonable with the runtime exception.
  196
+			// we can't really throw our runtime exception since we're likely in
  197
+			// an exception handler. Punt on this for now and come back to it.
  198
+		}
  199
+	}
  200
+
  201
+	/**
  202
+	 * Build up the XML to post according to the documentation at:
  203
+	 * http://help.airbrakeapp.com/faqs/api-2/notifier-api-v2
  204
+	 * @return string
  205
+	 * @author Rich Cavanaugh
  206
+	 **/
  207
+	function buildXmlNotice()
  208
+	{
  209
+		$doc = new SimpleXMLElement('<notice />');
  210
+		$doc->addAttribute('version', self::NOTIFIER_API_VERSION);
  211
+		$doc->addChild('api-key', $this->apiKey);
  212
+
  213
+		$notifier = $doc->addChild('notifier');
  214
+		$notifier->addChild('name', self::NOTIFIER_NAME);
  215
+		$notifier->addChild('version', self::NOTIFIER_VERSION);
  216
+		$notifier->addChild('url', self::NOTIFIER_URL);
  217
+
  218
+		$error = $doc->addChild('error');
  219
+		$error->addChild('class', $this->error_class);
  220
+		$error->addChild('message', $this->message);
  221
+		$this->addXmlBacktrace($error);
  222
+
  223
+		$request = $doc->addChild('request');
  224
+		$request->addChild('url', $this->request_uri());
  225
+		$request->addChild('component', $this->component());
  226
+		$request->addChild('action', $this->action());
  227
+
  228
+		if (isset($_REQUEST)) $this->addXmlVars($request, 'params', $this->params());
  229
+		if (isset($_SESSION)) $this->addXmlVars($request, 'session', $this->session());
  230
+		if (isset($_SERVER)) {
  231
+			if(isset($_SERVER['argv']))
  232
+				unset($_SERVER['argv']);
  233
+			$this->addXmlVars($request, 'cgi-data', $this->cgi_data());
  234
+		}
  235
+
  236
+		$env = $doc->addChild('server-environment');
  237
+		$env->addChild('project-root', $this->project_root());
  238
+		$env->addChild('environment-name', $this->environment());
  239
+
  240
+		return $doc->asXML();
  241
+	}
  242
+
  243
+	/**
  244
+	 * Add a Airbrake var block to the XML
  245
+	 * @return void
  246
+	 * @author Rich Cavanaugh
  247
+	 **/
  248
+	function addXmlVars($parent, $key, $source)
  249
+	{
  250
+		if (empty($source)) return;
  251
+
  252
+		$node = $parent->addChild($key);
  253
+		foreach ($source as $key => $val) {
  254
+			$var_node = $node->addChild('var', $val);
  255
+			$var_node->addAttribute('key', $key);
  256
+		}
  257
+	}
  258
+
  259
+	/**
  260
+	 * Add a Airbrake backtrace to the XML
  261
+	 * @return void
  262
+	 * @author Rich Cavanaugh
  263
+	 **/
  264
+	function addXmlBacktrace($parent)
  265
+	{
  266
+		$backtrace = $parent->addChild('backtrace');
  267
+		$line_node = $backtrace->addChild('line');
  268
+		$line_node->addAttribute('file', $this->file);
  269
+		$line_node->addAttribute('number', $this->line);
  270
+
  271
+		foreach ($this->trace as $entry) {
  272
+			if (isset($entry['class']) && $entry['class'] == 'Services_Airbrake') continue;
  273
+
  274
+			$line_node = $backtrace->addChild('line');
  275
+			$line_node->addAttribute('file', $entry['file']);
  276
+			$line_node->addAttribute('number', $entry['line']);
  277
+			$line_node->addAttribute('method', $entry['function']);
  278
+		}
  279
+	}
  280
+
  281
+	/**
  282
+	 * params
  283
+	 * @return array
  284
+	 * @author Scott Woods
  285
+	 **/
  286
+	function params() {
  287
+		return $_REQUEST;
  288
+	}
  289
+
  290
+	/**
  291
+	 * session
  292
+	 * @return array
  293
+	 * @author Scott Woods
  294
+	 **/
  295
+	function session() {
  296
+		return $_SESSION;
  297
+	}
  298
+
  299
+	/**
  300
+	 * cgi_data
  301
+	 * @return array
  302
+	 * @author Scott Woods
  303
+	 **/
  304
+	function cgi_data() {
  305
+		if (isset($_ENV) && !empty($_ENV)) {
  306
+			return array_merge($_SERVER, $_ENV);
  307
+		}
  308
+		return $_SERVER;
  309
+	}
  310
+
  311
+	/**
  312
+	 * component
  313
+	 * @return mixed
  314
+	 * @author Scott Woods
  315
+	 **/
  316
+	function component() {
  317
+		return $this->component;
  318
+	}
  319
+
  320
+	/**
  321
+	 * action
  322
+	 * @return mixed
  323
+	 * @author Scott Woods
  324
+	 **/
  325
+	function action() {
  326
+		return '';
  327
+	}
  328
+
  329
+	/**
  330
+	 * environment
  331
+	 * @return string
  332
+	 * @author Rich Cavanaugh
  333
+	 **/
  334
+	function environment() {
  335
+		return $this->environment;
  336
+	}
  337
+	
  338
+	/**
  339
+	 * project_root
  340
+	 * @return string
  341
+	 * @author Scott Woods
  342
+	 **/
  343
+	function project_root() {
  344
+		if (isset($_SERVER['DOCUMENT_ROOT'])) {
  345
+			return $_SERVER['DOCUMENT_ROOT'];
  346
+		} else {
  347
+			return dirname(__FILE__);
  348
+		}
  349
+	}
  350
+
  351
+
  352
+	/**
  353
+	 * get the request uri, or a pseudo request uri for CLI scripts
  354
+	 * @return string
  355
+	 * @author Aaron Parecki
  356
+	 **/
  357
+	function request_uri() {
  358
+		if(isset($_SERVER['argv'])) {
  359
+			$protocol = 'cli';
  360
+			$host = gethostname();
  361
+			$path = '/' . $_SERVER['SCRIPT_FILENAME'];
  362
+			if(count($_SERVER['argv']) > 1) {
  363
+				unset($_SERVER['argv'][0]);
  364
+				$query_string = '?' . http_build_query($_SERVER['argv']);
  365
+			} else {
  366
+				$query_string = '';
  367
+			}
  368
+		
  369
+			return $protocol . '://' . $host . $path . $query_string;
  370
+		} else {
  371
+			if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
  372
+				$protocol = 'https';
  373
+			} else {
  374
+				$protocol = 'http';
  375
+			}
  376
+			$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
  377
+			$path = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
  378
+			$query_string = isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) ? ('?' . $_SERVER['QUERY_STRING']) : '';
  379
+			return $protocol . '://' . $host . $path . $query_string;
  380
+		}
  381
+	}
  382
+
  383
+	/**
  384
+	 * @param mixed $code The HTTP status code from Airbrake.
  385
+	 *
  386
+	 * @return void
  387
+	 * @throws RuntimeException Error message from airbrake, translated to a RuntimeException.
  388
+	 */
  389
+	protected function handleErrorResponse($code)
  390
+	{
  391
+		switch ($code) {
  392
+		case '403':
  393
+			$msg = 'The requested project does not support SSL - resubmit in an http request.';
  394
+			break;
  395
+		case '422':
  396
+			$msg = 'The submitted notice was invalid - check the notice xml against the schema.';
  397
+			break;
  398
+		case '500':
  399
+			$msg = 'Unexpected errors - submit a bug report at http://help.airbrakeapp.com.';
  400
+			break;
  401
+		default:
  402
+			$msg = 'Unknown error code from Airbrake\'s API: ' . $code;
  403
+			break;
  404
+		}
  405
+
  406
+		throw new RuntimeException($msg, $code);
  407
+	}
  408
+
  409
+	/**
  410
+	 * Send the request to Airbrake using PEAR
  411
+	 * @return integer
  412
+	 * @author Rich Cavanaugh
  413
+	 **/
  414
+	public function pearRequest($url, $headers, $body)
  415
+	{
  416
+		if (!class_exists('HTTP_Request2')) require_once('HTTP/Request2.php');
  417
+		if (!class_exists('HTTP_Request2_Adapter_Socket')) require_once 'HTTP/Request2/Adapter/Socket.php';
  418
+
  419
+		$adapter = new HTTP_Request2_Adapter_Socket;
  420
+		$req = new HTTP_Request2($url, HTTP_Request2::METHOD_POST);
  421
+		$req->setAdapter($adapter);
  422
+		$req->setHeader($headers);
  423
+		$req->setBody($body);
  424
+		return $req->send()->getStatus();
  425
+	}
  426
+
  427
+	/**
  428
+	 * Send the request to Airbrake using Curl
  429
+	 * @return integer
  430
+	 * @author Rich Cavanaugh
  431
+	 **/
  432
+	public function curlRequest($url, $headers, $body)
  433
+	{
  434
+		$header_strings = array();
  435
+		foreach ($headers as $key => $val) {
  436
+			$header_strings[] = "{$key}: {$val}";
  437
+		}
  438
+		
  439
+		$curlHandle = curl_init();
  440
+		curl_setopt($curlHandle, CURLOPT_URL,            $url);
  441
+		curl_setopt($curlHandle, CURLOPT_POST,           1);
  442
+		curl_setopt($curlHandle, CURLOPT_HEADER,         0);
  443
+		curl_setopt($curlHandle, CURLOPT_TIMEOUT,        $this->timeout);
  444
+		curl_setopt($curlHandle, CURLOPT_POSTFIELDS,     $body);
  445
+		curl_setopt($curlHandle, CURLOPT_HTTPHEADER,     $header_strings);
  446
+		curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1);
  447
+		curl_exec($curlHandle);
  448
+		$status = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
  449
+		curl_close($curlHandle);
  450
+		return $status;
  451
+	}
  452
+	
  453
+	/**
  454
+	 * Put the error on a beanstalk queue so a separate process can
  455
+	 * send it off to Airbrake via HTTP. Requires the pheanstalk client.
  456
+	 * Config:
  457
+	 * $BEANSTALK_SERVERS[] = array(
  458
+	 *    'host' => 'example.com',
  459
+	 *    'port' => '11300'
  460
+	 * );
  461
+	 * @author Aaron Parecki
  462
+	 */
  463
+	public function beanstalkRequest($url, $headers, $body)
  464
+	{
  465
+		global $BEANSTALK_SERVERS;
  466
+		require_once('pheanstalk/pheanstalk_init.php');
  467
+
  468
+		$k = array_rand($BEANSTALK_SERVERS);
  469
+		$pheanstalk = new Pheanstalk($BEANSTALK_SERVERS[$k]['host'], $BEANSTALK_SERVERS[$k]['port']);
  470
+
  471
+		$pheanstalk->useTube('airbrake');
  472
+		$pheanstalk->put(json_encode(array('url'=>$url, 'headers'=>$headers, 'body'=>$body)));
  473
+	}
  474
+
  475
+	/**
  476
+	 * Send the request to Airbrake using Zend
  477
+	 * @return integer
  478
+	 * @author Rich Cavanaugh
  479
+	 **/
  480
+	public function zendRequest($url, $headers, $body)
  481
+	{
  482
+		$header_strings = array();
  483
+		foreach ($headers as $key => $val) {
  484
+			$header_strings[] = "{$key}: {$val}";
  485
+		}
  486
+
  487
+		$client = new Zend_Http_Client($url);
  488
+		$client->setHeaders($header_strings);
  489
+		$client->setRawData($body, 'text/xml');
  490
+
  491
+		$response = $client->request('POST');
  492
+
  493
+		return $response->getStatus();
  494
+	}
  495
+}

0 notes on commit a5f80ff

Please sign in to comment.
Something went wrong with that request. Please try again.