Version Notes
The first release!
Download this release
Release Info
Developer | MAG002804728 |
Extension | Bugsnag_Notifier |
Version | 1.0.0 |
Comparing to | |
See all releases |
Version 1.0.0
- app/code/community/Bugsnag/Notifier/Block/Adminhtml/System/Config/Form/Button.php +37 -0
- app/code/community/Bugsnag/Notifier/Model/Observer.php +103 -0
- app/code/community/Bugsnag/Notifier/Model/Severity.php +13 -0
- app/code/community/Bugsnag/Notifier/controllers/Adminhtml/BugsnagController.php +11 -0
- app/code/community/Bugsnag/Notifier/etc/config.xml +37 -0
- app/code/community/Bugsnag/Notifier/etc/system.xml +57 -0
- app/design/adminhtml/default/default/template/bugsnag/system/config/button.phtml +21 -0
- app/etc/modules/Bugsnag_Notifier.xml +9 -0
- lib/bugsnag-php/Autoload.php +15 -0
- lib/bugsnag-php/Client.php +436 -0
- lib/bugsnag-php/Configuration.php +81 -0
- lib/bugsnag-php/Diagnostics.php +54 -0
- lib/bugsnag-php/Error.php +241 -0
- lib/bugsnag-php/ErrorTypes.php +118 -0
- lib/bugsnag-php/Notification.php +174 -0
- lib/bugsnag-php/Request.php +83 -0
- lib/bugsnag-php/Stacktrace.php +95 -0
- package.xml +18 -0
app/code/community/Bugsnag/Notifier/Block/Adminhtml/System/Config/Form/Button.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Notifier_Block_Adminhtml_System_Config_Form_Button extends Mage_Adminhtml_Block_System_Config_Form_Field
|
4 |
+
{
|
5 |
+
protected function _construct()
|
6 |
+
{
|
7 |
+
parent::_construct();
|
8 |
+
$this->setTemplate('bugsnag/system/config/button.phtml');
|
9 |
+
}
|
10 |
+
|
11 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
12 |
+
{
|
13 |
+
return $this->_toHtml();
|
14 |
+
}
|
15 |
+
|
16 |
+
public function getAjaxCheckUrl()
|
17 |
+
{
|
18 |
+
return Mage::helper('adminhtml')->getUrl('adminhtml/adminhtml_bugsnag/check');
|
19 |
+
}
|
20 |
+
|
21 |
+
public function getButtonHtml()
|
22 |
+
{
|
23 |
+
$button = $this
|
24 |
+
->getLayout()
|
25 |
+
->createBlock('adminhtml/widget_button')
|
26 |
+
->setData(
|
27 |
+
array(
|
28 |
+
'type' => 'button',
|
29 |
+
'id' => 'Bugsnag_Notifier',
|
30 |
+
'label' => $this->helper('adminhtml')->__('Fire Test Event'),
|
31 |
+
'onclick' => 'javascript:fireTestEvent(); return false;'
|
32 |
+
)
|
33 |
+
);
|
34 |
+
|
35 |
+
return $button->toHtml();
|
36 |
+
}
|
37 |
+
}
|
app/code/community/Bugsnag/Notifier/Model/Observer.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Notifier_Model_Observer
|
4 |
+
{
|
5 |
+
private static $DEFAULT_NOTIFY_SEVERITIES = "fatal,error";
|
6 |
+
|
7 |
+
private static $NOTIFIER = array(
|
8 |
+
"name" => "Bugsnag Magento (Official)",
|
9 |
+
"version" => "1.0.0",
|
10 |
+
"url" => "https://bugsnag.com/notifiers/magento"
|
11 |
+
);
|
12 |
+
|
13 |
+
private $client;
|
14 |
+
private $apiKey;
|
15 |
+
private $notifySeverities;
|
16 |
+
private $filterFields;
|
17 |
+
|
18 |
+
public static function fireTestEvent($apiKey) {
|
19 |
+
if (strlen($apiKey) != 32) {
|
20 |
+
throw new Exception("Invalid length of the API key");
|
21 |
+
}
|
22 |
+
|
23 |
+
$client = new Bugsnag_Client($apiKey);
|
24 |
+
$client->notifyError(
|
25 |
+
"BugsnagTest",
|
26 |
+
"Testing bugsnag",
|
27 |
+
array("notifier" => self::$NOTIFIER)
|
28 |
+
);
|
29 |
+
}
|
30 |
+
|
31 |
+
public function initBugsnag()
|
32 |
+
{
|
33 |
+
if (file_exists(Mage::getBaseDir('lib') . '/bugsnag-php/Autoload.php')) {
|
34 |
+
require_once(Mage::getBaseDir('lib') . '/bugsnag-php/Autoload.php');
|
35 |
+
} else {
|
36 |
+
error_log("Bugsnag Error: Couldn't activate Bugsnag Error Monitoring due to missing Bugsnag PHP library!");
|
37 |
+
return;
|
38 |
+
}
|
39 |
+
|
40 |
+
$this->apiKey = Mage::getStoreConfig("dev/Bugsnag_Notifier/apiKey");
|
41 |
+
$this->notifySeverities = Mage::getStoreConfig("dev/Bugsnag_Notifier/severites");
|
42 |
+
$this->filterFields = Mage::getStoreConfig("dev/Bugsnag_Notifier/filterFiels");
|
43 |
+
|
44 |
+
// Activate the bugsnag client
|
45 |
+
if (!empty($this->apiKey)) {
|
46 |
+
$this->client = new Bugsnag_Client($this->apiKey);
|
47 |
+
|
48 |
+
$this->client->setReleaseStage($this->releaseStage())
|
49 |
+
->setErrorReportingLevel($this->errorReportingLevel())
|
50 |
+
->setFilters($this->filterFields());
|
51 |
+
|
52 |
+
$this->client->setNotifier(self::$NOTIFIER);
|
53 |
+
|
54 |
+
if (Mage::getSingleton('customer/session')->isLoggedIn()) {
|
55 |
+
$this->addUserTab();
|
56 |
+
}
|
57 |
+
|
58 |
+
set_error_handler(array($this->client, "errorHandler"));
|
59 |
+
set_exception_handler(array($this->client, "exceptionHandler"));
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
private function addUserTab()
|
64 |
+
{
|
65 |
+
$customer = Mage::getSingleton('customer/session')->getCustomer();
|
66 |
+
$this->client->setUser(array(
|
67 |
+
'id' => $customer->getId(),
|
68 |
+
'email' => $customer->getEmail(),
|
69 |
+
'created_at' => $customer->getCreatedAt(),
|
70 |
+
'first_name' => $customer->getFirstname(),
|
71 |
+
'last_name' => $customer->getLastname()
|
72 |
+
));
|
73 |
+
}
|
74 |
+
|
75 |
+
private function releaseStage()
|
76 |
+
{
|
77 |
+
return Mage::getIsDeveloperMode() ? "development" : "production";
|
78 |
+
}
|
79 |
+
|
80 |
+
private function errorReportingLevel()
|
81 |
+
{
|
82 |
+
if (empty($this->notifySeverities)) {
|
83 |
+
$notifySeverities = "fatal,error";
|
84 |
+
} else {
|
85 |
+
$notifySeverities = $this->notifySeverities;
|
86 |
+
}
|
87 |
+
|
88 |
+
$level = 0;
|
89 |
+
$severities = explode(",", $notifySeverities);
|
90 |
+
|
91 |
+
foreach($severities as $severity) {
|
92 |
+
$level |= Bugsnag_ErrorTypes::getLevelsForSeverity($severity);
|
93 |
+
}
|
94 |
+
|
95 |
+
return $level;
|
96 |
+
}
|
97 |
+
|
98 |
+
private function filterFields()
|
99 |
+
{
|
100 |
+
return array_map('trim', explode("\n", $this->filterFields));
|
101 |
+
}
|
102 |
+
|
103 |
+
}
|
app/code/community/Bugsnag/Notifier/Model/Severity.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Notifier_Model_Severity
|
4 |
+
{
|
5 |
+
public function toOptionArray()
|
6 |
+
{
|
7 |
+
return array(
|
8 |
+
array('value' => 'fatal,error', 'label'=>Mage::helper('adminhtml')->__('Crashes & errors')),
|
9 |
+
array('value' => 'fatal,error,warning', 'label'=>Mage::helper('adminhtml')->__('Crashes, errors & warnings')),
|
10 |
+
array('value' => 'fatal,error,warning,info', 'label'=>Mage::helper('adminhtml')->__('Crashes, errors, warnings & info messages'))
|
11 |
+
);
|
12 |
+
}
|
13 |
+
}
|
app/code/community/Bugsnag/Notifier/controllers/Adminhtml/BugsnagController.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Notifier_Adminhtml_BugsnagController extends Mage_Adminhtml_Controller_Action
|
4 |
+
{
|
5 |
+
public function checkAction()
|
6 |
+
{
|
7 |
+
Bugsnag_Notifier_Model_Observer::fireTestEvent($_POST["apiKey"]);
|
8 |
+
$successCode = 1;
|
9 |
+
Mage::app()->getResponse()->setBody($successCode);
|
10 |
+
}
|
11 |
+
}
|
app/code/community/Bugsnag/Notifier/etc/config.xml
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<Bugsnag_Notifier>
|
5 |
+
<version>1.0.0</version>
|
6 |
+
</Bugsnag_Notifier>
|
7 |
+
</modules>
|
8 |
+
<global>
|
9 |
+
<models>
|
10 |
+
<Bugsnag_Notifier>
|
11 |
+
<class>Bugsang_Notifier_Model</class>
|
12 |
+
</Bugsnag_Notifier>
|
13 |
+
</models>
|
14 |
+
<events>
|
15 |
+
<controller_action_predispatch>
|
16 |
+
<observers>
|
17 |
+
<bugsnag_controller_action_predispatch>
|
18 |
+
<type>singleton</type>
|
19 |
+
<class>Bugsnag_Notifier_Model_Observer</class>
|
20 |
+
<method>initBugsnag</method>
|
21 |
+
</bugsnag_controller_action_predispatch>
|
22 |
+
</observers>
|
23 |
+
</controller_action_predispatch>
|
24 |
+
</events>
|
25 |
+
</global>
|
26 |
+
<admin>
|
27 |
+
<routers>
|
28 |
+
<adminhtml>
|
29 |
+
<args>
|
30 |
+
<modules>
|
31 |
+
<Bungsnag_Notifier after="Mage_Adminhtml">Bugsnag_Notifier</Bungsnag_Notifier>
|
32 |
+
</modules>
|
33 |
+
</args>
|
34 |
+
</adminhtml>
|
35 |
+
</routers>
|
36 |
+
</admin>
|
37 |
+
</config>
|
app/code/community/Bugsnag/Notifier/etc/system.xml
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<config>
|
3 |
+
<sections>
|
4 |
+
<dev>
|
5 |
+
<groups>
|
6 |
+
<Bugsnag_Notifier>
|
7 |
+
<label>Bugsnag</label>
|
8 |
+
<sort_order>0</sort_order>
|
9 |
+
<show_in_default>1</show_in_default>
|
10 |
+
<show_in_website>1</show_in_website>
|
11 |
+
<show_in_store>1</show_in_store>
|
12 |
+
<fields>
|
13 |
+
<apiKey>
|
14 |
+
<label>Bugsnag API Key</label>
|
15 |
+
<frontend_type>text</frontend_type>
|
16 |
+
<sort_order>20</sort_order>
|
17 |
+
<show_in_default>1</show_in_default>
|
18 |
+
<show_in_website>1</show_in_website>
|
19 |
+
<show_in_store>1</show_in_store>
|
20 |
+
</apiKey>
|
21 |
+
<severities>
|
22 |
+
<label>Notify Bugsnag about</label>
|
23 |
+
<frontend_type>select</frontend_type>
|
24 |
+
<source_model>Bugsnag_Notifier_Model_Severity</source_model>
|
25 |
+
<sort_order>40</sort_order>
|
26 |
+
<show_in_default>1</show_in_default>
|
27 |
+
<show_in_website>1</show_in_website>
|
28 |
+
<show_in_store>1</show_in_store>
|
29 |
+
</severities>
|
30 |
+
<filterFields>
|
31 |
+
<label>
|
32 |
+
Bugsnag Field Filter. The information to remove from Bugsnag
|
33 |
+
reports, one per line. Use this if you want to ensure you don't
|
34 |
+
send sensitive data such as passwords, and credit card numbers
|
35 |
+
to our servers.
|
36 |
+
</label>
|
37 |
+
<frontend_type>textarea</frontend_type>
|
38 |
+
<sort_order>50</sort_order>
|
39 |
+
<show_in_default>1</show_in_default>
|
40 |
+
<show_in_website>1</show_in_website>
|
41 |
+
<show_in_store>1</show_in_store>
|
42 |
+
</filterFields>
|
43 |
+
<test_bugsnag>
|
44 |
+
<label>Fire a test event to ensure that the API key is correct</label>
|
45 |
+
<frontend_type>button</frontend_type>
|
46 |
+
<frontend_model>Bugsnag_Notifier_Block_Adminhtml_System_Config_Form_Button</frontend_model>
|
47 |
+
<sort_order>60</sort_order>
|
48 |
+
<show_in_default>1</show_in_default>
|
49 |
+
<show_in_website>1</show_in_website>
|
50 |
+
<show_in_store>1</show_in_store>
|
51 |
+
</test_bugsnag>
|
52 |
+
</fields>
|
53 |
+
</Bugsnag_Notifier>
|
54 |
+
</groups>
|
55 |
+
</dev>
|
56 |
+
</sections>
|
57 |
+
</config>
|
app/design/adminhtml/default/default/template/bugsnag/system/config/button.phtml
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script type="text/javascript">
|
2 |
+
//<![CDATA[
|
3 |
+
function fireTestEvent() {
|
4 |
+
new Ajax.Request('<?php echo $this->getAjaxCheckUrl() ?>', {
|
5 |
+
parameters: {
|
6 |
+
apiKey: $('dev_Bugsnag_Notifier_apiKey').value,
|
7 |
+
severities: $('dev_Bugsnag_Notifier_severities').value,
|
8 |
+
filterFiels: $('dev_Bugsnag_Notifier_filterFields').value
|
9 |
+
},
|
10 |
+
onSuccess: function(transport) {
|
11 |
+
alert('Sent notification. Visit https://bugsnag.com/ to see it in your dashboard');
|
12 |
+
},
|
13 |
+
onFailure: function(transport) {
|
14 |
+
alert("Couldn't send a notification. Double check your API key");
|
15 |
+
}
|
16 |
+
});
|
17 |
+
}
|
18 |
+
//]]>
|
19 |
+
</script>
|
20 |
+
|
21 |
+
<?php echo $this->getButtonHtml() ?>
|
app/etc/modules/Bugsnag_Notifier.xml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<Bugsnag_Notifier>
|
5 |
+
<active>true</active>
|
6 |
+
<codePool>community</codePool>
|
7 |
+
</Bugsnag_Notifier>
|
8 |
+
</modules>
|
9 |
+
</config>
|
lib/bugsnag-php/Autoload.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
# We used to have an autoloader, but it caused problems in some
|
4 |
+
# environments. So now we manually load the entire library upfront.
|
5 |
+
#
|
6 |
+
# The file is still called Autoload so that existing integration
|
7 |
+
# instructions continue to work.
|
8 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Client.php";
|
9 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Configuration.php";
|
10 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Diagnostics.php";
|
11 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Error.php";
|
12 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."ErrorTypes.php";
|
13 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Notification.php";
|
14 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Request.php";
|
15 |
+
require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Stacktrace.php";
|
lib/bugsnag-php/Client.php
ADDED
@@ -0,0 +1,436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Client
|
4 |
+
{
|
5 |
+
private $config;
|
6 |
+
private $notification;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Initialize Bugsnag
|
10 |
+
*
|
11 |
+
* @param String $apiKey your Bugsnag API key
|
12 |
+
*/
|
13 |
+
public function __construct($apiKey)
|
14 |
+
{
|
15 |
+
// Check API key has been passed
|
16 |
+
if (!is_string($apiKey)) {
|
17 |
+
throw new Exception('Bugsnag Error: Invalid API key');
|
18 |
+
}
|
19 |
+
|
20 |
+
// Create a configuration object
|
21 |
+
$this->config = new Bugsnag_Configuration();
|
22 |
+
$this->config->apiKey = $apiKey;
|
23 |
+
|
24 |
+
// Build a Diagnostics object
|
25 |
+
$this->diagnostics = new Bugsnag_Diagnostics($this->config);
|
26 |
+
|
27 |
+
// Register a shutdown function to check for fatal errors
|
28 |
+
// and flush any buffered errors
|
29 |
+
register_shutdown_function(array($this, 'shutdownHandler'));
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Set your release stage, eg "production" or "development"
|
34 |
+
*
|
35 |
+
* @param String $releaseStage the app's current release stage
|
36 |
+
*/
|
37 |
+
public function setReleaseStage($releaseStage)
|
38 |
+
{
|
39 |
+
$this->config->releaseStage = $releaseStage;
|
40 |
+
|
41 |
+
return $this;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Set your app's semantic version, eg "1.2.3"
|
46 |
+
*
|
47 |
+
* @param String $appVersion the app's version
|
48 |
+
*/
|
49 |
+
public function setAppVersion($appVersion)
|
50 |
+
{
|
51 |
+
$this->config->appVersion = $appVersion;
|
52 |
+
|
53 |
+
return $this;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Set which release stages should be allowed to notify Bugsnag
|
58 |
+
* eg array("production", "development")
|
59 |
+
*
|
60 |
+
* @param Array $notifyReleaseStages array of release stages to notify for
|
61 |
+
*/
|
62 |
+
public function setNotifyReleaseStages(array $notifyReleaseStages)
|
63 |
+
{
|
64 |
+
$this->config->notifyReleaseStages = $notifyReleaseStages;
|
65 |
+
|
66 |
+
return $this;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Set which Bugsnag endpoint to send errors to.
|
71 |
+
*
|
72 |
+
* @param String $endpoint endpoint URL
|
73 |
+
*/
|
74 |
+
public function setEndpoint($endpoint)
|
75 |
+
{
|
76 |
+
$this->config->endpoint = $endpoint;
|
77 |
+
|
78 |
+
return $this;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Set whether or not to use SSL when notifying bugsnag
|
83 |
+
*
|
84 |
+
* @param Boolean $useSSL whether to use SSL
|
85 |
+
*/
|
86 |
+
public function setUseSSL($useSSL)
|
87 |
+
{
|
88 |
+
$this->config->useSSL = $useSSL;
|
89 |
+
|
90 |
+
return $this;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Set the desired timeout for cURL connection when notifying bugsnag
|
95 |
+
*
|
96 |
+
* @param Integer $timeout the desired timeout in seconds
|
97 |
+
*/
|
98 |
+
public function setTimeout($timeout)
|
99 |
+
{
|
100 |
+
$this->config->timeout = $timeout;
|
101 |
+
|
102 |
+
return $this;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Set the absolute path to the root of your application.
|
107 |
+
* We use this to help with error grouping and to highlight "in project"
|
108 |
+
* stacktrace lines.
|
109 |
+
*
|
110 |
+
* @param String $projectRoot the root path for your application
|
111 |
+
*/
|
112 |
+
public function setProjectRoot($projectRoot)
|
113 |
+
{
|
114 |
+
$this->config->setProjectRoot($projectRoot);
|
115 |
+
|
116 |
+
return $this;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Set the path that should be stripped from the beginning of
|
121 |
+
* any stacktrace file line. This helps to normalise filenames
|
122 |
+
* for grouping and reduces the noise in stack traces.
|
123 |
+
*
|
124 |
+
* @param String $stripPath the path to strip from filenames
|
125 |
+
*/
|
126 |
+
public function setStripPath($stripPath)
|
127 |
+
{
|
128 |
+
$this->config->setStripPath($stripPath);
|
129 |
+
|
130 |
+
return $this;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Set the a regular expression for matching filenames in stacktrace lines
|
135 |
+
* that are part of your application.
|
136 |
+
*
|
137 |
+
* @param String $projectRootRegex regex matching paths belong to your project
|
138 |
+
*/
|
139 |
+
public function setProjectRootRegex($projectRootRegex)
|
140 |
+
{
|
141 |
+
$this->config->projectRootRegex = $projectRootRegex;
|
142 |
+
|
143 |
+
return $this;
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Set the strings to filter out from metaData arrays before sending then
|
148 |
+
* to Bugsnag. Eg. array("password", "credit_card")
|
149 |
+
*
|
150 |
+
* @param Array $filters an array of metaData filters
|
151 |
+
*/
|
152 |
+
public function setFilters(array $filters)
|
153 |
+
{
|
154 |
+
$this->config->filters = $filters;
|
155 |
+
|
156 |
+
return $this;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Set information about the current user of your app, including
|
161 |
+
* id, name and email.
|
162 |
+
*
|
163 |
+
* @param Array $user an array of user information. Eg:
|
164 |
+
* array(
|
165 |
+
* 'name' => 'Bob Hoskins',
|
166 |
+
* 'email' => 'bob@hoskins.com'
|
167 |
+
* )
|
168 |
+
*/
|
169 |
+
public function setUser(array $user)
|
170 |
+
{
|
171 |
+
$this->config->user = $user;
|
172 |
+
|
173 |
+
return $this;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* @deprecated deprecated since version 2.1
|
178 |
+
*/
|
179 |
+
public function setUserId($userId)
|
180 |
+
{
|
181 |
+
if (!is_array($this->config->user)) {
|
182 |
+
$this->config->user = array();
|
183 |
+
}
|
184 |
+
|
185 |
+
$this->config->user['id'] = $userId;
|
186 |
+
|
187 |
+
return $this;
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Set a context representing the current type of request, or location in code.
|
192 |
+
*
|
193 |
+
* @param String $context the current context
|
194 |
+
*/
|
195 |
+
public function setContext($context)
|
196 |
+
{
|
197 |
+
$this->config->context = $context;
|
198 |
+
|
199 |
+
return $this;
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Set the type of application executing the code. This is usually used to
|
204 |
+
* represent if you are running plain PHP code "php", via a framework,
|
205 |
+
* eg "laravel", or executing through delayed worker code, eg "resque".
|
206 |
+
*
|
207 |
+
* @param String $type the current type
|
208 |
+
*/
|
209 |
+
public function setType($type)
|
210 |
+
{
|
211 |
+
$this->config->type = $type;
|
212 |
+
|
213 |
+
return $this;
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Set custom metadata to send to Bugsnag with every error. You can use
|
218 |
+
* this to add custom tabs of data to each error on your Bugsnag dashboard
|
219 |
+
*
|
220 |
+
* @param Array $metaData an array of arrays of custom data. Eg:
|
221 |
+
* array(
|
222 |
+
* "user" => array(
|
223 |
+
* "name" => "James",
|
224 |
+
* "email" => "james@example.com"
|
225 |
+
* )
|
226 |
+
* )
|
227 |
+
*/
|
228 |
+
public function setMetaData(array $metaData)
|
229 |
+
{
|
230 |
+
$this->config->metaData = $metaData;
|
231 |
+
|
232 |
+
return $this;
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Set proxy configuration
|
237 |
+
*
|
238 |
+
* @param Array $proxySettings an array with proxy settings. Eg:
|
239 |
+
* array(
|
240 |
+
* 'host' => "bugsnag.com",
|
241 |
+
* 'port' => 42,
|
242 |
+
* 'user' => "username"
|
243 |
+
* 'password' => "password123"
|
244 |
+
* )
|
245 |
+
*/
|
246 |
+
public function setProxySettings(array $proxySettings)
|
247 |
+
{
|
248 |
+
$this->config->proxySettings = $proxySettings;
|
249 |
+
|
250 |
+
return $this;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Set a custom function to call before notifying Bugsnag of an error.
|
255 |
+
* You can use this to call your own error handling functions, or to add
|
256 |
+
* custom tabs of data to each error on your Bugsnag dashboard.
|
257 |
+
*
|
258 |
+
* // Adding meta-data example
|
259 |
+
* function before_bugsnag_notify($error) {
|
260 |
+
* $error->addMetaData(array(
|
261 |
+
* "user" => array(
|
262 |
+
* "name" => "James"
|
263 |
+
* )
|
264 |
+
* ));
|
265 |
+
* }
|
266 |
+
* $bugsnag->setBeforeNotifyFunction("before_bugsnag_notify");
|
267 |
+
*
|
268 |
+
*/
|
269 |
+
public function setBeforeNotifyFunction($beforeNotifyFunction)
|
270 |
+
{
|
271 |
+
$this->config->beforeNotifyFunction = $beforeNotifyFunction;
|
272 |
+
|
273 |
+
return $this;
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Set Bugsnag's error reporting level.
|
278 |
+
* If this is not set, we'll use your current PHP error_reporting value
|
279 |
+
* from your ini file or error_reporting(...) calls.
|
280 |
+
*
|
281 |
+
* @param Integer $errorReportingLevel the error reporting level integer
|
282 |
+
* exactly as you would pass to PHP's error_reporting
|
283 |
+
*/
|
284 |
+
public function setErrorReportingLevel($errorReportingLevel)
|
285 |
+
{
|
286 |
+
$this->config->errorReportingLevel = $errorReportingLevel;
|
287 |
+
|
288 |
+
return $this;
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Sets whether Bugsnag should be automatically notified of unhandled
|
293 |
+
* exceptions and errors.
|
294 |
+
*
|
295 |
+
* @param Boolean $autoNotify whether to auto notify or not
|
296 |
+
*/
|
297 |
+
public function setAutoNotify($autoNotify)
|
298 |
+
{
|
299 |
+
$this->config->autoNotify = $autoNotify;
|
300 |
+
|
301 |
+
return $this;
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Sets whether errors should be batched together and send at the end of
|
306 |
+
* each request.
|
307 |
+
*
|
308 |
+
* @param Boolean $batchSending whether to batch together errors
|
309 |
+
*/
|
310 |
+
public function setBatchSending($batchSending)
|
311 |
+
{
|
312 |
+
$this->config->batchSending = $batchSending;
|
313 |
+
|
314 |
+
return $this;
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Sets the notifier to report as to Bugsnag. This should only be
|
319 |
+
* set by other notifier libraries.
|
320 |
+
*
|
321 |
+
* @param Array $notifier an array of name, version, url.
|
322 |
+
*/
|
323 |
+
public function setNotifier($notifier)
|
324 |
+
{
|
325 |
+
$this->config->notifier = $notifier;
|
326 |
+
|
327 |
+
return $this;
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Notify Bugsnag of a non-fatal/handled exception
|
332 |
+
*
|
333 |
+
* @param Exception $exception the exception to notify Bugsnag about
|
334 |
+
* @param Array $metaData optional metaData to send with this error
|
335 |
+
* @param String $severity optional severity of this error (fatal/error/warning/info)
|
336 |
+
*/
|
337 |
+
public function notifyException(Exception $exception, array $metaData=null, $severity=null)
|
338 |
+
{
|
339 |
+
$error = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
|
340 |
+
$error->setSeverity($severity);
|
341 |
+
|
342 |
+
$this->notify($error, $metaData);
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Notify Bugsnag of a non-fatal/handled error
|
347 |
+
*
|
348 |
+
* @param String $errorName the name of the error, a short (1 word) string
|
349 |
+
* @param String $errorMessage the error message
|
350 |
+
* @param Array $metaData optional metaData to send with this error
|
351 |
+
* @param String $severity optional severity of this error (fatal/error/warning/info)
|
352 |
+
*/
|
353 |
+
public function notifyError($name, $message, array $metaData=null, $severity=null)
|
354 |
+
{
|
355 |
+
$error = Bugsnag_Error::fromNamedError($this->config, $this->diagnostics, $name, $message);
|
356 |
+
$error->setSeverity($severity);
|
357 |
+
|
358 |
+
$this->notify($error, $metaData);
|
359 |
+
}
|
360 |
+
|
361 |
+
// Exception handler callback, should only be called internally by PHP's set_exception_handler
|
362 |
+
public function exceptionHandler($exception)
|
363 |
+
{
|
364 |
+
$error = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
|
365 |
+
$error->setSeverity("error");
|
366 |
+
|
367 |
+
if (!$error->shouldIgnore() && $this->config->autoNotify) {
|
368 |
+
$this->notify($error);
|
369 |
+
}
|
370 |
+
}
|
371 |
+
|
372 |
+
// Exception handler callback, should only be called internally by PHP's set_error_handler
|
373 |
+
public function errorHandler($errno, $errstr, $errfile='', $errline=0)
|
374 |
+
{
|
375 |
+
$error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $errno, $errstr, $errfile, $errline);
|
376 |
+
|
377 |
+
if (!$error->shouldIgnore() && $this->config->autoNotify) {
|
378 |
+
$this->notify($error);
|
379 |
+
}
|
380 |
+
}
|
381 |
+
|
382 |
+
// Shutdown handler callback, called when the PHP process has finished running
|
383 |
+
// Should only be called internally by PHP's register_shutdown_function
|
384 |
+
public function shutdownHandler()
|
385 |
+
{
|
386 |
+
// Get last error
|
387 |
+
$lastError = error_get_last();
|
388 |
+
|
389 |
+
// Check if a fatal error caused this shutdown
|
390 |
+
if (!is_null($lastError) && Bugsnag_ErrorTypes::isFatal($lastError['type'])) {
|
391 |
+
$error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $lastError['type'], $lastError['message'], $lastError['file'], $lastError['line'], true);
|
392 |
+
$error->setSeverity("error");
|
393 |
+
|
394 |
+
if (!$error->shouldIgnore() && $this->config->autoNotify) {
|
395 |
+
$this->notify($error);
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
// Flush any buffered errors
|
400 |
+
if ($this->notification) {
|
401 |
+
$this->notification->deliver();
|
402 |
+
$this->notification = null;
|
403 |
+
}
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Batches up errors into notifications for later sending
|
408 |
+
*
|
409 |
+
* @param Bugsnag_Error $error the error to batch up
|
410 |
+
* @param array $metaData optional meta data to send with the error
|
411 |
+
*/
|
412 |
+
public function notify(Bugsnag_Error $error, $metaData = array())
|
413 |
+
{
|
414 |
+
// Queue or send the error
|
415 |
+
if ($this->sendErrorsOnShutdown()) {
|
416 |
+
// Create a batch notification unless we already have one
|
417 |
+
if (is_null($this->notification)) {
|
418 |
+
$this->notification = new Bugsnag_Notification($this->config);
|
419 |
+
}
|
420 |
+
|
421 |
+
// Add this error to the notification
|
422 |
+
$this->notification->addError($error, $metaData);
|
423 |
+
} else {
|
424 |
+
// Create and deliver notification immediatelt
|
425 |
+
$notif = new Bugsnag_Notification($this->config);
|
426 |
+
$notif->addError($error, $metaData);
|
427 |
+
$notif->deliver();
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
// Should we send errors immediately or on shutdown
|
432 |
+
private function sendErrorsOnShutdown()
|
433 |
+
{
|
434 |
+
return $this->config->batchSending && Bugsnag_Request::isRequest();
|
435 |
+
}
|
436 |
+
}
|
lib/bugsnag-php/Configuration.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Configuration
|
4 |
+
{
|
5 |
+
public static $DEFAULT_TIMEOUT = 10;
|
6 |
+
|
7 |
+
public $apiKey;
|
8 |
+
public $autoNotify = true;
|
9 |
+
public $batchSending = true;
|
10 |
+
public $useSSL = true;
|
11 |
+
public $endpoint = 'notify.bugsnag.com';
|
12 |
+
public $notifyReleaseStages;
|
13 |
+
public $filters = array('password');
|
14 |
+
public $projectRoot;
|
15 |
+
public $projectRootRegex;
|
16 |
+
public $proxySettings = array();
|
17 |
+
public $notifier = array(
|
18 |
+
'name' => 'Bugsnag PHP (Official)',
|
19 |
+
'version' => '2.3.0',
|
20 |
+
'url' => 'https://bugsnag.com'
|
21 |
+
);
|
22 |
+
public $stripPath;
|
23 |
+
public $stripPathRegex;
|
24 |
+
|
25 |
+
public $context;
|
26 |
+
public $type;
|
27 |
+
public $user;
|
28 |
+
public $releaseStage = 'production';
|
29 |
+
public $appVersion;
|
30 |
+
public $hostname;
|
31 |
+
|
32 |
+
public $metaData;
|
33 |
+
public $beforeNotifyFunction;
|
34 |
+
public $errorReportingLevel;
|
35 |
+
|
36 |
+
public function __construct()
|
37 |
+
{
|
38 |
+
$this->timeout = Bugsnag_Configuration::$DEFAULT_TIMEOUT;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function getNotifyEndpoint()
|
42 |
+
{
|
43 |
+
return $this->getProtocol()."://".$this->endpoint;
|
44 |
+
}
|
45 |
+
|
46 |
+
public function shouldNotify()
|
47 |
+
{
|
48 |
+
return is_null($this->notifyReleaseStages) || (is_array($this->notifyReleaseStages) && in_array($this->releaseStage, $this->notifyReleaseStages));
|
49 |
+
}
|
50 |
+
|
51 |
+
public function setProjectRoot($projectRoot)
|
52 |
+
{
|
53 |
+
$this->projectRoot = $projectRoot;
|
54 |
+
$this->projectRootRegex = '/'.preg_quote($projectRoot, '/')."[\\/]?/i";
|
55 |
+
if (is_null($this->stripPath)) {
|
56 |
+
$this->setStripPath($projectRoot);
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
public function setStripPath($stripPath)
|
61 |
+
{
|
62 |
+
$this->stripPath = $stripPath;
|
63 |
+
$this->stripPathRegex = '/'.preg_quote($stripPath, '/')."[\\/]?/i";
|
64 |
+
}
|
65 |
+
|
66 |
+
public function get($prop, $default=NULL)
|
67 |
+
{
|
68 |
+
$configured = $this->$prop;
|
69 |
+
|
70 |
+
if (is_array($configured) && is_array($default)) {
|
71 |
+
return array_merge($default, $configured);
|
72 |
+
} else {
|
73 |
+
return $configured ? $configured : $default;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
private function getProtocol()
|
78 |
+
{
|
79 |
+
return $this->useSSL ? "https" : "http";
|
80 |
+
}
|
81 |
+
}
|
lib/bugsnag-php/Diagnostics.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Diagnostics
|
4 |
+
{
|
5 |
+
private $config;
|
6 |
+
|
7 |
+
public function __construct(Bugsnag_Configuration $config)
|
8 |
+
{
|
9 |
+
$this->config = $config;
|
10 |
+
}
|
11 |
+
|
12 |
+
public function getAppData()
|
13 |
+
{
|
14 |
+
$appData = array();
|
15 |
+
|
16 |
+
if (!is_null($this->config->appVersion)) {
|
17 |
+
$appData['version'] = $this->config->appVersion;
|
18 |
+
}
|
19 |
+
|
20 |
+
if (!is_null($this->config->releaseStage)) {
|
21 |
+
$appData['releaseStage'] = $this->config->releaseStage;
|
22 |
+
}
|
23 |
+
|
24 |
+
if (!is_null($this->config->type)) {
|
25 |
+
$appData['type'] = $this->config->type;
|
26 |
+
}
|
27 |
+
|
28 |
+
return $appData;
|
29 |
+
}
|
30 |
+
|
31 |
+
public function getDeviceData()
|
32 |
+
{
|
33 |
+
return array(
|
34 |
+
'hostname' => $this->config->get('hostname', php_uname('n'))
|
35 |
+
);
|
36 |
+
}
|
37 |
+
|
38 |
+
public function getContext()
|
39 |
+
{
|
40 |
+
return $this->config->get('context', Bugsnag_Request::getContext());
|
41 |
+
}
|
42 |
+
|
43 |
+
public function getUser()
|
44 |
+
{
|
45 |
+
$defaultUser = array();
|
46 |
+
$userId = Bugsnag_Request::getUserId();
|
47 |
+
|
48 |
+
if (!is_null($userId)) {
|
49 |
+
$defaultUser['id'] = $userId;
|
50 |
+
}
|
51 |
+
|
52 |
+
return $this->config->get('user', $defaultUser);
|
53 |
+
}
|
54 |
+
}
|
lib/bugsnag-php/Error.php
ADDED
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Error
|
4 |
+
{
|
5 |
+
private static $VALID_SEVERITIES = array(
|
6 |
+
'error',
|
7 |
+
'warning',
|
8 |
+
'info'
|
9 |
+
);
|
10 |
+
|
11 |
+
public $name;
|
12 |
+
public $payloadVersion = "2";
|
13 |
+
public $message;
|
14 |
+
public $severity = "warning";
|
15 |
+
public $stacktrace;
|
16 |
+
public $metaData = array();
|
17 |
+
public $config;
|
18 |
+
public $diagnostics;
|
19 |
+
public $code;
|
20 |
+
public $previous;
|
21 |
+
|
22 |
+
// Static error creation methods, to ensure that Error object is always complete
|
23 |
+
public static function fromPHPError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $code, $message, $file, $line, $fatal=false)
|
24 |
+
{
|
25 |
+
$error = new Bugsnag_Error($config, $diagnostics);
|
26 |
+
$error->setPHPError($code, $message, $file, $line, $fatal);
|
27 |
+
|
28 |
+
return $error;
|
29 |
+
}
|
30 |
+
|
31 |
+
public static function fromPHPException(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, Exception $exception)
|
32 |
+
{
|
33 |
+
$error = new Bugsnag_Error($config, $diagnostics);
|
34 |
+
$error->setPHPException($exception);
|
35 |
+
|
36 |
+
return $error;
|
37 |
+
}
|
38 |
+
|
39 |
+
public static function fromNamedError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $name, $message=NULL)
|
40 |
+
{
|
41 |
+
$error = new Bugsnag_Error($config, $diagnostics);
|
42 |
+
$error->setName($name)
|
43 |
+
->setMessage($message)
|
44 |
+
->setStacktrace(Bugsnag_Stacktrace::generate($config));
|
45 |
+
|
46 |
+
return $error;
|
47 |
+
}
|
48 |
+
|
49 |
+
// Private constructor (for use only by the static methods above)
|
50 |
+
private function __construct(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics)
|
51 |
+
{
|
52 |
+
$this->config = $config;
|
53 |
+
$this->diagnostics = $diagnostics;
|
54 |
+
}
|
55 |
+
|
56 |
+
public function setName($name)
|
57 |
+
{
|
58 |
+
$this->name = $name;
|
59 |
+
|
60 |
+
return $this;
|
61 |
+
}
|
62 |
+
|
63 |
+
|
64 |
+
public function setMessage($message)
|
65 |
+
{
|
66 |
+
$this->message = $message;
|
67 |
+
|
68 |
+
return $this;
|
69 |
+
}
|
70 |
+
|
71 |
+
public function setStacktrace($stacktrace)
|
72 |
+
{
|
73 |
+
$this->stacktrace = $stacktrace;
|
74 |
+
|
75 |
+
return $this;
|
76 |
+
}
|
77 |
+
|
78 |
+
public function setCode($code)
|
79 |
+
{
|
80 |
+
$this->code = $code;
|
81 |
+
|
82 |
+
return $this;
|
83 |
+
}
|
84 |
+
|
85 |
+
public function setSeverity($severity)
|
86 |
+
{
|
87 |
+
if (!is_null($severity)) {
|
88 |
+
if (in_array($severity, Bugsnag_Error::$VALID_SEVERITIES)) {
|
89 |
+
$this->severity = $severity;
|
90 |
+
} else {
|
91 |
+
error_log('Bugsnag Warning: Tried to set error severity to '. $severity .' which is not allowed.');
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return $this;
|
96 |
+
}
|
97 |
+
|
98 |
+
public function setPHPException(Exception $exception)
|
99 |
+
{
|
100 |
+
$this->setName(get_class($exception))
|
101 |
+
->setMessage($exception->getMessage())
|
102 |
+
->setStacktrace(Bugsnag_Stacktrace::fromBacktrace($this->config, $exception->getTrace(), $exception->getFile(), $exception->getLine()));
|
103 |
+
|
104 |
+
if (method_exists($exception, 'getPrevious')) {
|
105 |
+
$this->setPrevious($exception->getPrevious());
|
106 |
+
}
|
107 |
+
|
108 |
+
return $this;
|
109 |
+
}
|
110 |
+
|
111 |
+
public function setPHPError($code, $message, $file, $line, $fatal=false)
|
112 |
+
{
|
113 |
+
if ($fatal) {
|
114 |
+
// Generating stacktrace for PHP fatal errors is not possible,
|
115 |
+
// since this code executes when the PHP process shuts down,
|
116 |
+
// rather than at the time of the crash.
|
117 |
+
//
|
118 |
+
// In these situations, we generate a "stacktrace" containing only
|
119 |
+
// the line and file number where the crash occurred.
|
120 |
+
$stacktrace = Bugsnag_Stacktrace::fromFrame($this->config, $file, $line);
|
121 |
+
} else {
|
122 |
+
$stacktrace = Bugsnag_Stacktrace::generate($this->config);
|
123 |
+
}
|
124 |
+
|
125 |
+
$this->setName(Bugsnag_ErrorTypes::getName($code))
|
126 |
+
->setMessage($message)
|
127 |
+
->setSeverity(Bugsnag_ErrorTypes::getSeverity($code))
|
128 |
+
->setStacktrace($stacktrace)
|
129 |
+
->setCode($code);
|
130 |
+
|
131 |
+
return $this;
|
132 |
+
}
|
133 |
+
|
134 |
+
public function setMetaData($metaData)
|
135 |
+
{
|
136 |
+
if (is_array($metaData)) {
|
137 |
+
$this->metaData = array_merge_recursive($this->metaData, $metaData);
|
138 |
+
}
|
139 |
+
|
140 |
+
return $this;
|
141 |
+
}
|
142 |
+
|
143 |
+
public function setPrevious($exception)
|
144 |
+
{
|
145 |
+
if ($exception) {
|
146 |
+
$this->previous = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
|
147 |
+
}
|
148 |
+
|
149 |
+
return $this;
|
150 |
+
}
|
151 |
+
|
152 |
+
public function shouldIgnore()
|
153 |
+
{
|
154 |
+
// Check if we should ignore errors of this type
|
155 |
+
if (isset($this->code)) {
|
156 |
+
if (isset($this->config->errorReportingLevel)) {
|
157 |
+
return !($this->config->errorReportingLevel & $this->code);
|
158 |
+
} else {
|
159 |
+
return !(error_reporting() & $this->code);
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
return false;
|
164 |
+
}
|
165 |
+
|
166 |
+
public function toArray()
|
167 |
+
{
|
168 |
+
return array(
|
169 |
+
'app' => $this->diagnostics->getAppData(),
|
170 |
+
'device' => $this->diagnostics->getDeviceData(),
|
171 |
+
'user' => $this->diagnostics->getUser(),
|
172 |
+
'context' => $this->diagnostics->getContext(),
|
173 |
+
'payloadVersion' => $this->payloadVersion,
|
174 |
+
'severity' => $this->severity,
|
175 |
+
'exceptions' => $this->exceptionArray(),
|
176 |
+
'metaData' => $this->cleanupObj($this->metaData)
|
177 |
+
);
|
178 |
+
}
|
179 |
+
|
180 |
+
public function exceptionArray()
|
181 |
+
{
|
182 |
+
if ($this->previous) {
|
183 |
+
$exceptionArray = $this->previous->exceptionArray();
|
184 |
+
} else {
|
185 |
+
$exceptionArray = array();
|
186 |
+
}
|
187 |
+
|
188 |
+
$exceptionArray[] = array(
|
189 |
+
'errorClass' => $this->name,
|
190 |
+
'message' => $this->message,
|
191 |
+
'stacktrace' => $this->stacktrace->toArray()
|
192 |
+
);
|
193 |
+
|
194 |
+
return $exceptionArray;
|
195 |
+
}
|
196 |
+
|
197 |
+
private function cleanupObj($obj)
|
198 |
+
{
|
199 |
+
if (empty($obj)) {
|
200 |
+
return NULL;
|
201 |
+
}
|
202 |
+
|
203 |
+
if (is_array($obj)) {
|
204 |
+
$cleanArray = array();
|
205 |
+
foreach ($obj as $key => $value) {
|
206 |
+
// Apply filters if required
|
207 |
+
if (is_array($this->config->filters)) {
|
208 |
+
// Check if this key should be filtered
|
209 |
+
$shouldFilter = false;
|
210 |
+
foreach ($this->config->filters as $filter) {
|
211 |
+
if (strpos($key, $filter) !== false) {
|
212 |
+
$shouldFilter = true;
|
213 |
+
break;
|
214 |
+
}
|
215 |
+
}
|
216 |
+
|
217 |
+
// Apply filters
|
218 |
+
if ($shouldFilter) {
|
219 |
+
$cleanArray[$key] = '[FILTERED]';
|
220 |
+
} else {
|
221 |
+
$cleanArray[$key] = $this->cleanupObj($value);
|
222 |
+
}
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
return $cleanArray;
|
227 |
+
} else if (is_string($obj)) {
|
228 |
+
// UTF8-encode if not already encoded
|
229 |
+
if (!mb_detect_encoding($obj, 'UTF-8', true)) {
|
230 |
+
return utf8_encode($obj);
|
231 |
+
} else {
|
232 |
+
return $obj;
|
233 |
+
}
|
234 |
+
} else if (is_object($obj)) {
|
235 |
+
// json_encode -> json_decode trick turns an object into an array
|
236 |
+
return $this->cleanupObj(json_decode(json_encode($obj), true));
|
237 |
+
} else {
|
238 |
+
return $obj;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
}
|
lib/bugsnag-php/ErrorTypes.php
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_ErrorTypes
|
4 |
+
{
|
5 |
+
private static $ERROR_TYPES = array(
|
6 |
+
E_ERROR => array(
|
7 |
+
'name' => 'PHP Fatal Error',
|
8 |
+
'severity' => 'error'
|
9 |
+
),
|
10 |
+
|
11 |
+
E_WARNING => array(
|
12 |
+
'name' => 'PHP Warning',
|
13 |
+
'severity' => 'warning'
|
14 |
+
),
|
15 |
+
|
16 |
+
E_PARSE => array(
|
17 |
+
'name' => 'PHP Parse Error',
|
18 |
+
'severity' => 'error'
|
19 |
+
),
|
20 |
+
|
21 |
+
E_NOTICE => array(
|
22 |
+
'name' => 'PHP Notice',
|
23 |
+
'severity' => 'info'
|
24 |
+
),
|
25 |
+
|
26 |
+
E_CORE_ERROR => array(
|
27 |
+
'name' => 'PHP Core Error',
|
28 |
+
'severity' => 'error'
|
29 |
+
),
|
30 |
+
|
31 |
+
E_CORE_WARNING => array(
|
32 |
+
'name' => 'PHP Core Warning',
|
33 |
+
'severity' => 'warning'
|
34 |
+
),
|
35 |
+
|
36 |
+
E_COMPILE_ERROR => array(
|
37 |
+
'name' => 'PHP Compile Error',
|
38 |
+
'severity' => 'error'
|
39 |
+
),
|
40 |
+
|
41 |
+
E_COMPILE_WARNING => array(
|
42 |
+
'name' => 'PHP Compile Warning',
|
43 |
+
'severity' => 'warning'
|
44 |
+
),
|
45 |
+
|
46 |
+
E_USER_ERROR => array(
|
47 |
+
'name' => 'User Error',
|
48 |
+
'severity' => 'error'
|
49 |
+
),
|
50 |
+
|
51 |
+
E_USER_WARNING => array(
|
52 |
+
'name' => 'User Warning',
|
53 |
+
'severity' => 'warning'
|
54 |
+
),
|
55 |
+
|
56 |
+
E_USER_NOTICE => array(
|
57 |
+
'name' => 'User Notice',
|
58 |
+
'severity' => 'info'
|
59 |
+
),
|
60 |
+
|
61 |
+
E_STRICT => array(
|
62 |
+
'name' => 'PHP Strict',
|
63 |
+
'severity' => 'info'
|
64 |
+
),
|
65 |
+
|
66 |
+
E_RECOVERABLE_ERROR => array(
|
67 |
+
'name' => 'PHP Recoverable Error',
|
68 |
+
'severity' => 'error'
|
69 |
+
),
|
70 |
+
|
71 |
+
// E_DEPRECATED (Since PHP 5.3.0)
|
72 |
+
8192 => array(
|
73 |
+
'name' => 'PHP Deprecated',
|
74 |
+
'severity' => 'info'
|
75 |
+
),
|
76 |
+
|
77 |
+
// E_USER_DEPRECATED (Since PHP 5.3.0)
|
78 |
+
16384 => array(
|
79 |
+
'name' => 'User Deprecated',
|
80 |
+
'severity' => 'info'
|
81 |
+
)
|
82 |
+
);
|
83 |
+
|
84 |
+
public static function isFatal($code)
|
85 |
+
{
|
86 |
+
return self::getSeverity($code) == 'error';
|
87 |
+
}
|
88 |
+
|
89 |
+
public static function getName($code)
|
90 |
+
{
|
91 |
+
if (array_key_exists($code, self::$ERROR_TYPES)) {
|
92 |
+
return self::$ERROR_TYPES[$code]['name'];
|
93 |
+
} else {
|
94 |
+
return "Unknown";
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
public static function getSeverity($code)
|
99 |
+
{
|
100 |
+
if (array_key_exists($code, self::$ERROR_TYPES)) {
|
101 |
+
return self::$ERROR_TYPES[$code]['severity'];
|
102 |
+
} else {
|
103 |
+
return "error";
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
public static function getLevelsForSeverity($severity)
|
108 |
+
{
|
109 |
+
$levels = 0;
|
110 |
+
foreach (Bugsnag_ErrorTypes::$ERROR_TYPES as $level => $info) {
|
111 |
+
if ($info['severity'] == $severity) {
|
112 |
+
$levels |= $level;
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
return $levels;
|
117 |
+
}
|
118 |
+
}
|
lib/bugsnag-php/Notification.php
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Notification
|
4 |
+
{
|
5 |
+
private static $CONTENT_TYPE_HEADER = 'Content-type: application/json';
|
6 |
+
|
7 |
+
private $config;
|
8 |
+
private $errorQueue = array();
|
9 |
+
|
10 |
+
public function __construct(Bugsnag_Configuration $config)
|
11 |
+
{
|
12 |
+
$this->config = $config;
|
13 |
+
}
|
14 |
+
|
15 |
+
public function addError($error, $passedMetaData=array())
|
16 |
+
{
|
17 |
+
// Check if this error should be sent to Bugsnag
|
18 |
+
if (!$this->config->shouldNotify()) {
|
19 |
+
return FALSE;
|
20 |
+
}
|
21 |
+
|
22 |
+
// Add global meta-data to error
|
23 |
+
$error->setMetaData($this->config->metaData);
|
24 |
+
|
25 |
+
// Add request meta-data to error
|
26 |
+
if (Bugsnag_Request::isRequest()) {
|
27 |
+
$error->setMetaData(Bugsnag_Request::getRequestMetaData());
|
28 |
+
}
|
29 |
+
|
30 |
+
// Add environment meta-data to error
|
31 |
+
if (!empty($_ENV)) {
|
32 |
+
$error->setMetaData(array("Environment" => $_ENV));
|
33 |
+
}
|
34 |
+
|
35 |
+
// Add user-specified meta-data to error
|
36 |
+
$error->setMetaData($passedMetaData);
|
37 |
+
|
38 |
+
// Run beforeNotify function (can cause more meta-data to be merged)
|
39 |
+
if (isset($this->config->beforeNotifyFunction) && is_callable($this->config->beforeNotifyFunction)) {
|
40 |
+
$beforeNotifyReturn = call_user_func($this->config->beforeNotifyFunction, $error);
|
41 |
+
}
|
42 |
+
|
43 |
+
// Skip this error if the beforeNotify function returned FALSE
|
44 |
+
if (!isset($beforeNotifyReturn) || $beforeNotifyReturn !== FALSE) {
|
45 |
+
$this->errorQueue[] = $error;
|
46 |
+
|
47 |
+
return TRUE;
|
48 |
+
} else {
|
49 |
+
return FALSE;
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
public function toArray()
|
54 |
+
{
|
55 |
+
$events = array();
|
56 |
+
foreach ($this->errorQueue as $error) {
|
57 |
+
$errorArray = $error->toArray();
|
58 |
+
|
59 |
+
if (!is_null($errorArray)) {
|
60 |
+
$events[] = $errorArray;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
return array(
|
65 |
+
'apiKey' => $this->config->apiKey,
|
66 |
+
'notifier' => $this->config->notifier,
|
67 |
+
'events' => $events
|
68 |
+
);
|
69 |
+
}
|
70 |
+
|
71 |
+
public function deliver()
|
72 |
+
{
|
73 |
+
if (!empty($this->errorQueue)) {
|
74 |
+
// Post the request to bugsnag
|
75 |
+
$this->postJSON($this->config->getNotifyEndpoint(), $this->toArray());
|
76 |
+
|
77 |
+
// Clear the error queue
|
78 |
+
$this->errorQueue = array();
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
public function postJSON($url, $data)
|
83 |
+
{
|
84 |
+
$body = json_encode($data);
|
85 |
+
|
86 |
+
// Prefer cURL if it is installed, otherwise fall back to fopen()
|
87 |
+
// cURL supports both timeouts and proxies
|
88 |
+
if (function_exists('curl_version')) {
|
89 |
+
$this->postWithCurl($url, $body);
|
90 |
+
} else {
|
91 |
+
$this->postWithFopen($url, $body);
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
private function postWithCurl($url, $body)
|
96 |
+
{
|
97 |
+
$http = curl_init($url);
|
98 |
+
|
99 |
+
// Default curl settings
|
100 |
+
curl_setopt($http, CURLOPT_HEADER, false);
|
101 |
+
curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
|
102 |
+
curl_setopt($http, CURLOPT_POST, true);
|
103 |
+
curl_setopt($http, CURLOPT_HTTPHEADER, array(Bugsnag_Notification::$CONTENT_TYPE_HEADER));
|
104 |
+
curl_setopt($http, CURLOPT_POSTFIELDS, $body);
|
105 |
+
curl_setopt($http, CURLOPT_CONNECTTIMEOUT, $this->config->timeout);
|
106 |
+
curl_setopt($http, CURLOPT_SSL_VERIFYPEER, false);
|
107 |
+
curl_setopt($http, CURLOPT_VERBOSE, false);
|
108 |
+
|
109 |
+
// Apply proxy settings (if present)
|
110 |
+
if (count($this->config->proxySettings)) {
|
111 |
+
if (isset($this->config->proxySettings['host'])) {
|
112 |
+
curl_setopt($http, CURLOPT_PROXY, $this->config->proxySettings['host']);
|
113 |
+
}
|
114 |
+
if (isset($this->config->proxySettings['port'])) {
|
115 |
+
curl_setopt($http, CURLOPT_PROXYPORT, $this->config->proxySettings['port']);
|
116 |
+
}
|
117 |
+
if (isset($this->config->proxySettings['user'])) {
|
118 |
+
$userPassword = $this->config->proxySettings['user'] . ':';
|
119 |
+
$userPassword .= isset($this->config->proxySettings['password'])? $this->config->proxySettings['password'] : '';
|
120 |
+
curl_setopt($http, CURLOPT_PROXYUSERPWD, $userPassword);
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
// Execute the request and fetch the response
|
125 |
+
$responseBody = curl_exec($http);
|
126 |
+
$statusCode = curl_getinfo($http, CURLINFO_HTTP_CODE);
|
127 |
+
|
128 |
+
if ($statusCode > 200) {
|
129 |
+
error_log('Bugsnag Warning: Couldn\'t notify ('.$responseBody.')');
|
130 |
+
}
|
131 |
+
|
132 |
+
if (curl_errno($http)) {
|
133 |
+
error_log('Bugsnag Warning: Couldn\'t notify (' . curl_error($http).')');
|
134 |
+
}
|
135 |
+
|
136 |
+
curl_close($http);
|
137 |
+
}
|
138 |
+
|
139 |
+
private function postWithFopen($url, $body)
|
140 |
+
{
|
141 |
+
// Warn about lack of proxy support if we are using fopen()
|
142 |
+
if (count($this->config->proxySettings)) {
|
143 |
+
error_log('Bugsnag Warning: Can\'t use proxy settings unless cURL is installed');
|
144 |
+
}
|
145 |
+
|
146 |
+
// Warn about lack of timeout support if we are using fopen()
|
147 |
+
if ($this->config->timeout != Bugsnag_Configuration::$DEFAULT_TIMEOUT) {
|
148 |
+
error_log('Bugsnag Warning: Can\'t change timeout settings unless cURL is installed');
|
149 |
+
}
|
150 |
+
|
151 |
+
// Create the request context
|
152 |
+
$context = stream_context_create(array(
|
153 |
+
'http' => array(
|
154 |
+
'method' => 'POST',
|
155 |
+
'header' => Bugsnag_Notification::$CONTENT_TYPE_HEADER.'\r\n',
|
156 |
+
'content' => $body
|
157 |
+
),
|
158 |
+
'ssl' => array(
|
159 |
+
'verify_peer' => false,
|
160 |
+
)
|
161 |
+
));
|
162 |
+
|
163 |
+
// Execute the request and fetch the response
|
164 |
+
if ($stream = fopen($url, 'rb', false, $context)) {
|
165 |
+
$response = stream_get_contents($stream);
|
166 |
+
|
167 |
+
if (!$response) {
|
168 |
+
error_log('Bugsnag Warning: Couldn\'t notify (no response)');
|
169 |
+
}
|
170 |
+
} else {
|
171 |
+
error_log('Bugsnag Warning: Couldn\'t notify (fopen failed)');
|
172 |
+
}
|
173 |
+
}
|
174 |
+
}
|
lib/bugsnag-php/Request.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Request
|
4 |
+
{
|
5 |
+
public static function isRequest()
|
6 |
+
{
|
7 |
+
return isset($_SERVER['REQUEST_METHOD']);
|
8 |
+
}
|
9 |
+
|
10 |
+
public static function getRequestMetaData()
|
11 |
+
{
|
12 |
+
$requestData = array();
|
13 |
+
|
14 |
+
// Request Tab
|
15 |
+
$requestData['request'] = array();
|
16 |
+
$requestData['request']['url'] = self::getCurrentUrl();
|
17 |
+
if (isset($_SERVER['REQUEST_METHOD'])) {
|
18 |
+
$requestData['request']['httpMethod'] = $_SERVER['REQUEST_METHOD'];
|
19 |
+
}
|
20 |
+
|
21 |
+
if (!empty($_POST)) {
|
22 |
+
$requestData['request']['params'] = $_POST;
|
23 |
+
} else {
|
24 |
+
if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'], 'application/json') === 0) {
|
25 |
+
$requestData['request']['params'] = json_decode(file_get_contents('php://input'));
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
$requestData['request']['ip'] = self::getRequestIp();
|
30 |
+
if (isset($_SERVER['HTTP_USER_AGENT'])) {
|
31 |
+
$requestData['request']['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
|
32 |
+
}
|
33 |
+
|
34 |
+
if (function_exists("getallheaders")) {
|
35 |
+
$headers = getallheaders();
|
36 |
+
if (!empty($headers)) {
|
37 |
+
$requestData['request']['headers'] = $headers;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
// Session Tab
|
42 |
+
if (!empty($_SESSION)) {
|
43 |
+
$requestData['session'] = $_SESSION;
|
44 |
+
}
|
45 |
+
|
46 |
+
// Cookies Tab
|
47 |
+
if (!empty($_COOKIE)) {
|
48 |
+
$requestData['cookies'] = $_COOKIE;
|
49 |
+
}
|
50 |
+
|
51 |
+
return $requestData;
|
52 |
+
}
|
53 |
+
|
54 |
+
public static function getContext()
|
55 |
+
{
|
56 |
+
if (self::isRequest() && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER["REQUEST_URI"])) {
|
57 |
+
return $_SERVER['REQUEST_METHOD'] . ' ' . strtok($_SERVER["REQUEST_URI"], '?');
|
58 |
+
} else {
|
59 |
+
return null;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
public static function getUserId()
|
64 |
+
{
|
65 |
+
if (self::isRequest()) {
|
66 |
+
return self::getRequestIp();
|
67 |
+
} else {
|
68 |
+
return null;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
public static function getCurrentUrl()
|
73 |
+
{
|
74 |
+
$schema = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://';
|
75 |
+
|
76 |
+
return $schema.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
|
77 |
+
}
|
78 |
+
|
79 |
+
public static function getRequestIp()
|
80 |
+
{
|
81 |
+
return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
|
82 |
+
}
|
83 |
+
}
|
lib/bugsnag-php/Stacktrace.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Bugsnag_Stacktrace
|
4 |
+
{
|
5 |
+
public $frames = array();
|
6 |
+
private $config;
|
7 |
+
|
8 |
+
public static function generate($config)
|
9 |
+
{
|
10 |
+
// Reduce memory usage by omitting args and objects from backtrace
|
11 |
+
if (version_compare(PHP_VERSION, '5.3.6') >= 0) {
|
12 |
+
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS & ~DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
13 |
+
} elseif (version_compare(PHP_VERSION, '5.2.5') >= 0) {
|
14 |
+
$backtrace = debug_backtrace(FALSE);
|
15 |
+
} else {
|
16 |
+
$backtrace = debug_backtrace();
|
17 |
+
}
|
18 |
+
|
19 |
+
return self::fromBacktrace($config, $backtrace, "[generator]", 0);
|
20 |
+
}
|
21 |
+
|
22 |
+
public static function fromFrame($config, $file, $line)
|
23 |
+
{
|
24 |
+
$stacktrace = new Bugsnag_Stacktrace($config);
|
25 |
+
$stacktrace->addFrame($file, $line, "[unknown]");
|
26 |
+
|
27 |
+
return $stacktrace;
|
28 |
+
}
|
29 |
+
|
30 |
+
public static function fromBacktrace($config, $backtrace, $topFile, $topLine)
|
31 |
+
{
|
32 |
+
$stacktrace = new Bugsnag_Stacktrace($config);
|
33 |
+
|
34 |
+
// PHP backtrace's are misaligned, we need to shift the file/line down a frame
|
35 |
+
foreach ($backtrace as $frame) {
|
36 |
+
if (!self::frameInsideBugsnag($frame)) {
|
37 |
+
$stacktrace->addFrame($topFile, $topLine, $frame['function'], isset($frame['class']) ? $frame['class'] : NULL);
|
38 |
+
}
|
39 |
+
|
40 |
+
if (isset($frame['file']) && isset($frame['line'])) {
|
41 |
+
$topFile = $frame['file'];
|
42 |
+
$topLine = $frame['line'];
|
43 |
+
} else {
|
44 |
+
$topFile = "[internal]";
|
45 |
+
$topLine = 0;
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
// Add a final stackframe for the "main" method
|
50 |
+
$stacktrace->addFrame($topFile, $topLine, '[main]');
|
51 |
+
|
52 |
+
return $stacktrace;
|
53 |
+
}
|
54 |
+
|
55 |
+
public static function frameInsideBugsnag($frame)
|
56 |
+
{
|
57 |
+
return isset($frame['class']) && strpos($frame['class'], 'Bugsnag_') === 0;
|
58 |
+
}
|
59 |
+
|
60 |
+
|
61 |
+
public function __construct($config)
|
62 |
+
{
|
63 |
+
$this->config = $config;
|
64 |
+
}
|
65 |
+
|
66 |
+
public function toArray()
|
67 |
+
{
|
68 |
+
return $this->frames;
|
69 |
+
}
|
70 |
+
|
71 |
+
public function addFrame($file, $line, $method, $class=NULL)
|
72 |
+
{
|
73 |
+
// Check if this frame is inProject
|
74 |
+
$inProject = !is_null($this->config->projectRootRegex) && preg_match($this->config->projectRootRegex, $file);
|
75 |
+
|
76 |
+
// Strip out projectRoot from start of file path
|
77 |
+
if (!is_null($this->config->stripPathRegex)) {
|
78 |
+
$file = preg_replace($this->config->stripPathRegex, '', $file);
|
79 |
+
}
|
80 |
+
|
81 |
+
// Construct the frame
|
82 |
+
$frame = array(
|
83 |
+
'file' => $file,
|
84 |
+
'lineNumber' => $line,
|
85 |
+
'method' => $method,
|
86 |
+
'inProject' => $inProject
|
87 |
+
);
|
88 |
+
|
89 |
+
if (!empty($class)) {
|
90 |
+
$frame['class'] = $class;
|
91 |
+
}
|
92 |
+
|
93 |
+
$this->frames[] = $frame;
|
94 |
+
}
|
95 |
+
}
|
package.xml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>Bugsnag_Notifier</name>
|
4 |
+
<version>1.0.0</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license>MIT License</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>The Bugsnag Notifier for Magento gives you instant notification of errors in your Magento applications.</summary>
|
10 |
+
<description>Bugsnag captures errors in real-time from your web, mobile and desktop applications, helping you to understand and resolve them as fast as possible. Create a free account to start capturing errors from your applications.</description>
|
11 |
+
<notes>The first release!</notes>
|
12 |
+
<authors><author><name>MAG002804728</name><user>MAG002804728</user><email>kyrylo@bugsnag.com</email></author></authors>
|
13 |
+
<date>2014-09-05</date>
|
14 |
+
<time>21:05:54</time>
|
15 |
+
<contents><target name="magecommunity"><dir name="Bugsnag"><dir name="Notifier"><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Form"><file name="Button.php" hash="6d8ad5ed45ecddc929d8f627a663779d"/></dir></dir></dir></dir></dir><dir name="Model"><file name="Observer.php" hash="e64e947aaad75845173f73ed611ba334"/><file name="Severity.php" hash="2ffc28c09bee8ae4d3194ceb33e93cd4"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="BugsnagController.php" hash="26f257486b19e4674f47b47f63529821"/></dir></dir><dir name="etc"><file name="config.xml" hash="212f7e4cf9650c2d54f098e56b4d1514"/><file name="system.xml" hash="6c74fe6e6e23fc5685002ed103a53366"/></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="bugsnag"><dir name="system"><dir name="config"><file name="button.phtml" hash="3cbf97942a6124b5bbdc1633dff99352"/></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Bugsnag_Notifier.xml" hash="5af25ac572e2c7e04f03edfa30d36976"/></dir></target><target name="magelib"><dir name="bugsnag-php"><file name="Autoload.php" hash="141a845b8baaebee4232b30540d5cda5"/><file name="Client.php" hash="4c43e67c648cac333f78f363cf37dc69"/><file name="Configuration.php" hash="805b07608edb9a2d930558fa52ade5a7"/><file name="Diagnostics.php" hash="2ee79a831f84025135ef4fc0c8550747"/><file name="Error.php" hash="e8e1cb0bc961263185c4ec9e68387a65"/><file name="ErrorTypes.php" hash="118f8a29bfcd0d33a98363c30eb9f39f"/><file name="Notification.php" hash="6cf0c6f5a30852f559d6beec0a40ef0c"/><file name="Request.php" hash="422a3cc71777af8bb426988a77dcaa6e"/><file name="Stacktrace.php" hash="9d131aa378157acbffba7136128fe57d"/></dir></target></contents>
|
16 |
+
<compatible/>
|
17 |
+
<dependencies><required><php><min>5.2.0</min><max>5.5.15</max></php></required></dependencies>
|
18 |
+
</package>
|