├── README.md
├── app
├── code
│ └── community
│ │ └── Hackathon
│ │ └── LoggerSentry
│ │ ├── Helper
│ │ └── Data.php
│ │ ├── Model
│ │ └── Sentry.php
│ │ └── etc
│ │ ├── config.xml
│ │ └── system.xml
└── etc
│ └── modules
│ └── Hackathon_LoggerSentry.xml
├── composer.json
├── lib
└── sentry
│ └── sentry
│ ├── .gitattributes
│ ├── .gitignore
│ ├── .gitmodules
│ ├── .php_cs
│ ├── .scrutinizer.yml
│ ├── .travis.yml
│ ├── AUTHORS
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── Makefile
│ ├── README.md
│ ├── bin
│ └── sentry
│ ├── composer.json
│ ├── lib
│ └── Raven
│ │ ├── Autoloader.php
│ │ ├── Breadcrumbs.php
│ │ ├── Breadcrumbs
│ │ ├── ErrorHandler.php
│ │ └── MonologHandler.php
│ │ ├── Client.php
│ │ ├── Compat.php
│ │ ├── Context.php
│ │ ├── CurlHandler.php
│ │ ├── ErrorHandler.php
│ │ ├── Exception.php
│ │ ├── Processor.php
│ │ ├── Processor
│ │ ├── RemoveCookiesProcessor.php
│ │ ├── RemoveHttpBodyProcessor.php
│ │ ├── SanitizeDataProcessor.php
│ │ ├── SanitizeHttpHeadersProcessor.php
│ │ └── SanitizeStacktraceProcessor.php
│ │ ├── ReprSerializer.php
│ │ ├── SanitizeDataProcessor.php
│ │ ├── Serializer.php
│ │ ├── Stacktrace.php
│ │ ├── TransactionStack.php
│ │ ├── Util.php
│ │ └── data
│ │ └── cacert.pem
│ └── phpunit.xml
└── modman
/README.md:
--------------------------------------------------------------------------------
1 | # Magento To Sentry Logger
2 |
3 | The purpose of this project is to log the magento error and exception messages to sentry, too. This extension is an extension of the [Firegento Logger module](https://github.com/firegento/firegento-logger), so you need the Logger module to use the Sentry logger.
4 |
5 | # Installation
6 | ## Installing Without Composer
7 | If you're not using composer to manage non-magento 3rd party packages, then you can install simply by:
8 | 1. Pull down the code somewhere (`git clone git@github.com:magento-hackathon/LoggerSentry.git`)
9 | 2. Copy over all files associatively.
10 | 3. Configure the module (see below).
11 |
12 | ## Installtion With Composer
13 | Add to your repositories:
14 |
15 | ```
16 | "repositories": [
17 | {
18 | "type": "composer",
19 | "url": "http://packages.firegento.com"
20 | }
21 | ],
22 | ```
23 |
24 | Install with composer:
25 |
26 | `composer require magento-hackathon/loggersentry`
27 |
28 | Additional requirements:
29 |
30 | [firegento/logger](https://github.com/firegento/firegento-logger)
31 |
32 | ## Configuration
33 |
34 | After you install the module you can configure it in the backend at: `System > Configuration > Advanced > FireGento Logger > Sentry Logger`
35 |
36 | ## Further Information
37 |
38 | ### Core Contributors
39 |
40 | * Fabian Blechschmidt
41 |
42 | ### Current Status of Project
43 |
44 | Complete, but needs to be tested in the wild. If there are problems, just open an issue, we'll have a look on it.
45 |
--------------------------------------------------------------------------------
/app/code/community/Hackathon/LoggerSentry/Helper/Data.php:
--------------------------------------------------------------------------------
1 | 'fatal',
22 | 1 => 'fatal',
23 | 2 => 'fatal',
24 | 3 => 'error',
25 | 4 => 'warning',
26 | 5 => 'info',
27 | 6 => 'info',
28 | 7 => 'debug'
29 | );
30 |
31 | /**
32 | *
33 | *
34 | * ignore filename - it is Zend_Log_Writer_Abstract dependency
35 | *
36 | * @param string $filename
37 | *
38 | * @return \Hackathon_LoggerSentry_Model_Sentry
39 | */
40 | public function __construct($filename)
41 | {
42 | /* @var $helper Hackathon_Logger_Helper_Data */
43 | $helper = Mage::helper('firegento_logger');
44 | $options = array(
45 | 'logger' => $helper->getLoggerConfig('sentry/logger_name')
46 | );
47 | try {
48 | $this->_sentryClient = new Raven_Client($helper->getLoggerConfig('sentry/apikey'), $options);
49 | } catch (Exception $e) {
50 | // Ignore errors so that it doesn't crush the website when/if Sentry goes down.
51 | }
52 |
53 | }
54 |
55 | /**
56 | * Places event line into array of lines to be used as message body.
57 | *
58 | * @param FireGento_Logger_Model_Event $event Event data
59 | *
60 | * @throws Zend_Log_Exception
61 | * @return void
62 | */
63 | protected function _write($eventObj)
64 | {
65 | try {
66 | /* @var $helper Hackathon_Logger_Helper_Data */
67 | $helper = Mage::helper('firegento_logger');
68 | $helper->addEventMetadata($eventObj);
69 |
70 | $event = $eventObj->getEventDataArray();
71 |
72 | $additional = array(
73 | 'file' => $event['file'],
74 | 'line' => $event['line'],
75 | );
76 |
77 | foreach (array('REQUEST_METHOD', 'REQUEST_URI', 'REMOTE_IP', 'HTTP_USER_AGENT') as $key) {
78 | if (!empty($event[$key])) {
79 | $additional[$key] = $event[$key];
80 | }
81 | }
82 |
83 | $this->_assumePriorityByMessage($event);
84 |
85 | // if we still can't figure it out, assume it's an error
86 | $priority = isset($event['priority']) && !empty($event['priority']) ? $event['priority'] : 3;
87 |
88 | if (!$this->_isHighEnoughPriorityToReport($priority)) {
89 | return $this; // Don't log anything warning or less severe.
90 | }
91 |
92 | $this->_sentryClient->captureMessage(
93 | $event['message'], array(), $this->_priorityToLevelMapping[$priority], true, $additional
94 | );
95 |
96 | } catch (Exception $e) {
97 | throw new Zend_Log_Exception($e->getMessage(), $e->getCode());
98 | }
99 | }
100 |
101 | /**
102 | * @param int $priority
103 | * @return boolean True if we should be reporting this, false otherwise.
104 | */
105 | protected function _isHighEnoughPriorityToReport($priority)
106 | {
107 | if ($priority > (int)Mage::helper('firegento_logger')->getLoggerConfig('sentry/priority')) {
108 | return false; // Don't log anything warning or less severe than configured.
109 | }
110 | return true;
111 | }
112 |
113 | /**
114 | * Try to attach a priority # based on the error message string (since sometimes it is not specified)
115 | * @param FireGento_Logger_Model_Event &$event Event data
116 | * @return \Hackathon_LoggerSentry_Model_Sentry
117 | */
118 | protected function _assumePriorityByMessage(&$event)
119 | {
120 | if (stripos($event['message'], "warn") === 0) {
121 | $event['priority'] = 4;
122 | }
123 | if (stripos($event['message'], "notice") === 0) {
124 | $event['priority'] = 5;
125 | }
126 |
127 | return $this;
128 | }
129 |
130 | /**
131 | * Satisfy newer Zend Framework
132 | *
133 | * @static
134 | *
135 | * @param $config
136 | *
137 | * @return void
138 | */
139 | static public function factory($config)
140 | {
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/code/community/Hackathon/LoggerSentry/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 1.1.0
6 |
7 |
8 |
9 |
10 |
11 | Hackathon_LoggerSentry_Model
12 |
13 |
14 |
15 |
16 | Hackathon_LoggerSentry_Helper
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Hackathon_LoggerSentry_Model_Sentry
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | https://XXXXXXXXXXXXXXX:XXXXXXXXXXXXX@app.getsentry.com/7202
34 | Magento Sentry Logger
35 | 4
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/code/community/Hackathon/LoggerSentry/etc/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | text
9 | 90
10 | 1
11 |
12 |
13 |
14 | textarea
15 | 0
16 | 1
17 | Add here your API key
18 |
19 |
20 |
21 | text
22 | 10
23 | 1
24 |
25 |
26 |
27 | select
28 | firegento_logger/system_config_source_prioritydefault
29 | 20
30 | 1
31 | Choose the lowest priority level to be sent to Sentry. If a log level is lower priority than this then it will not be sent to Sentry.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/etc/modules/Hackathon_LoggerSentry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | community
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "magento-hackathon/loggersentry",
3 | "license": [
4 | "GPL-3.0"
5 | ],
6 | "type": "magento-module",
7 | "description": "Extension for Firegento Logger module to log to Sentry Service, too.",
8 | "repositories": [
9 | {
10 | "type": "vcs",
11 | "url": "https://github.com/magento-hackathon/magento-composer-installer"
12 | },
13 | {
14 | "type": "composer",
15 | "url": "https://packages.firegento.com"
16 | }
17 | ],
18 | "require": {
19 | "magento-hackathon/magento-composer-installer": "*",
20 | "firegento/logger": "1.*"
21 | },
22 | "extra": {
23 | "magento-root-dir": "./",
24 | "magento-deploystrategy": "link",
25 | "map": [
26 | [
27 | "./app/code/community/Hackathon/LoggerSentry",
28 | "./app/code/community/Hackathon/LoggerSentry"
29 | ],
30 | [
31 | "./lib/sentry",
32 | "./lib/sentry"
33 | ],
34 | [
35 | "./app/etc/modules/Hackathon_LoggerSentry.xml",
36 | "./app/etc/modules/Hackathon_LoggerSentry.xml"
37 | ]
38 | ]
39 | },
40 | "authors": [
41 | {
42 | "name": "Schrank",
43 | "email": "",
44 | "homepage": "https://github.com/Schrank",
45 | "role": "Developer"
46 | },
47 | {
48 | "name": "Jay El-Kaake",
49 | "email": "",
50 | "homepage": "https://github.com/jayelkaake",
51 | "role": "Developer"
52 | },
53 | {
54 | "name": "Sylvain Rayé",
55 | "email": "",
56 | "homepage": "https://github.com/diglin",
57 | "role": "Developer"
58 | },
59 | {
60 | "name": "Kevin Krieger",
61 | "email": "kk@kkrieger.de",
62 | "homepage": "https://kkrieger.de",
63 | "role": "Developer"
64 | }
65 | ]
66 | }
--------------------------------------------------------------------------------
/lib/sentry/sentry/.gitattributes:
--------------------------------------------------------------------------------
1 | /examples export-ignore
2 | /docs export-ignore
3 | /test export-ignore
4 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/.gitignore:
--------------------------------------------------------------------------------
1 | *.lock
2 | package.xml
3 | /vendor
4 | .idea
5 | .php_cs.cache
6 | docs/_build
7 | test/clover.xml
8 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "docs/_sentryext"]
2 | path = docs/_sentryext
3 | url = https://github.com/getsentry/sentry-doc-support
4 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/.php_cs:
--------------------------------------------------------------------------------
1 | in(__DIR__)
5 | ;
6 |
7 | return Symfony\CS\Config\Config::create()
8 | ->setUsingCache(true)
9 | ->setUsingLinter(true)
10 | ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
11 | ->finder($finder)
12 | ;
13 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | tools:
2 | php_sim: false
3 | php_pdepend: true
4 | php_analyzer: true
5 | php_code_coverage: true
6 | external_code_coverage:
7 | timeout: 2400 # There can be another pull request in progress
8 | runs: 6 # PHP 5.3 + PHP 5.4 + PHP 5.5 + PHP 5.6 + PHP 7.0 + PHP 7.1
9 |
10 | build:
11 | environment:
12 | php:
13 | version: 5.6.0
14 | redis: false
15 | postgresql: false
16 | mongodb: false
17 |
18 | filter:
19 | excluded_paths: [vendor/*, test/*, bin/*, docs/*, examples/*]
20 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | sudo: false
3 |
4 | php:
5 | - 5.3
6 | - 5.4
7 | - 5.5
8 | - 5.6
9 | - 7.0
10 | - 7.1
11 | - nightly
12 | env:
13 | - REMOVE_XDEBUG="0"
14 | - REMOVE_XDEBUG="1"
15 |
16 | matrix:
17 | allow_failures:
18 | - php: hhvm-3.12
19 | - php: nightly
20 | fast_finish: true
21 | include:
22 | - php: hhvm-3.12
23 | env: REMOVE_XDEBUG="0" HHVM="1"
24 | dist: trusty
25 |
26 | cache:
27 | directories:
28 | - $HOME/.composer/cache
29 |
30 | before_install:
31 | - if [ "$REMOVE_XDEBUG" = "1" ]; then phpenv config-rm xdebug.ini; fi
32 | - composer self-update
33 |
34 | install: travis_retry composer install --no-interaction --prefer-dist
35 |
36 | script:
37 | - composer phpcs
38 | - composer tests-travis
39 |
40 | after_script:
41 | - wget https://scrutinizer-ci.com/ocular.phar
42 | - if [ $(phpenv version-name) = "5.3" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
43 | - if [ $(phpenv version-name) = "5.4" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
44 | - if [ $(phpenv version-name) = "5.5" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
45 | - if [ $(phpenv version-name) = "5.6" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
46 | - if [ $(phpenv version-name) = "7.0" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
47 | - if [ $(phpenv version-name) = "7.1" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
48 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/AUTHORS:
--------------------------------------------------------------------------------
1 | The Sentry PHP SDK was originally written by Michael van Tellingen
2 | and is maintained by the Sentry Team.
3 |
4 | http://github.com/getsentry/sentry-php/contributors
5 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## Unreleased
4 |
5 | ## 1.7.1 (2017-08-02)
6 | - Fix of filtering sensitive data when there is an exception with multiple 'values' (#483)
7 |
8 | ## 1.7.0 (2017-06-07)
9 |
10 | - Corrected some issues with argument serialization in stacktraces (#399).
11 | - The default exception handler will now re-raise exceptions when `call_existing` is true and no exception handler is registered (#421).
12 | - Collect `User.ip_address` automatically (#419).
13 | - Added a processor to remove web cookies. It will be enabled by default in `2.0` (#405).
14 | - Added a processor to remove HTTP body data for POST, PUT, PATCH and DELETE requests. It will be enabled by default in `2.0` (#405).
15 | - Added a processor to sanitize HTTP headers (e.g. the Authorization header) (#428).
16 | - Added a processor to remove `pre_context`, `context_line` and `post_context` informations from reported exceptions (#429).
17 |
18 | ## 1.6.2 (2017-02-03)
19 |
20 | - Fixed behavior where fatal errors weren't correctly being reported in most situations.
21 |
22 | ## 1.6.1 (2016-12-14)
23 |
24 | - Correct handling of null in `user_context`.
25 |
26 | ## 1.6.0 (2016-12-09)
27 |
28 | - Improved serialization of certain types to be more restrictive.
29 | - `error_types` can now be configured via `RavenClient`.
30 | - Class serialization has been expanded to include attributes.
31 | - The session extension is no longer required.
32 | - Monolog is no longer a required dependency.
33 | - `user_context` now merges by default.
34 |
35 | ## 1.5.0 (2016-09-29)
36 |
37 | - Added named transaction support.
38 |
39 | ## 1.4.0 (2016-09-20)
40 |
41 | This version primarily overhauls the exception/stacktrace generation to fix
42 | a few bugs and improve the quality of data (#359).
43 |
44 | - Added `excluded_app_paths` config.
45 | - Removed `shift_vars` config.
46 | - Correct fatal error handling to only operate on expected types. This also fixes some behavior with the error suppression operator.
47 | - Expose anonymous and similar frames in the stacktrace.
48 | - Default `prefixes` to PHP's include paths.
49 | - Remove `module` usage.
50 | - Better handle empty argument context.
51 | - Correct alignment of filename (current frame) and function (caller frame)
52 |
53 | ## 1.3.0 (2016-12-19)
54 |
55 | - Fixed an issue causing the error suppression operator to not be respected (#335)
56 | - Fixed some serialization behavior (#352)
57 | - Fixed an issue with app paths and trailing slashes (#350)
58 | - Handle non-latin encoding with source code context line (#345)
59 |
60 | ## 1.2.0 (2016-12-08)
61 |
62 | - Handle non-latin encoding in source code and exception values (#342)
63 | - Ensure pending events are sent on shutdown by default (#338)
64 | - Add `captureLastError` helper (#334)
65 | - Dont report duplicate errors with fatal error handler (#334)
66 | - Enforce maximum length for string serialization (#329)
67 |
68 | ## 1.1.0 (2016-07-30)
69 |
70 | - Uncoercable values should no longer prevent exceptions from sending
71 | to the Sentry server.
72 | - `install()` can no longer be called multiple times.
73 |
74 | ## 1.0.0 (2016-07-28)
75 |
76 | - Removed deprecated error codes configuration from ErrorHandler.
77 | - Removed env data from HTTP interface.
78 | - Removed `message` attribute from exceptions.
79 | - appPath and prefixes are now resolved fully.
80 | - Fixed various getter methods requiring invalid args.
81 | - Fixed data mutation with `send_callback`.
82 |
83 | ## 0.22.0 (2016-06-23)
84 |
85 | - Improve handling of encodings.
86 | - Improve resiliency of variable serialization.
87 | - Add 'formatted' attribute to Message interface.
88 |
89 | ## 0.21.0 (2016-06-10)
90 |
91 | - Added `transport` option.
92 | - Added `install()` shortcut.
93 |
94 | ## 0.20.0 (2016-06-02)
95 |
96 | - Handle missing function names on frames.
97 | - Remove suppression operator usage in breadcrumbs buffer.
98 | - Force serialization of context values.
99 |
100 | ## 0.19.0 (2016-05-27)
101 |
102 | - Add `error_reporting` breadcrumb handler.
103 |
104 | ## 0.18.0 (2016-05-17)
105 |
106 | - Remove session from serialized data.
107 | - `send_callback` return value must now be false to prevent capture.
108 | - Add various getter/setter methods for configuration.
109 |
110 | ## 0.17.0 (2016-05-11)
111 |
112 | - Don't attempt to serialize fixed SDK inputs.
113 | - Improvements to breadcrumbs support in Monolog.
114 |
115 | ## 0.16.0 (2016-05-03)
116 |
117 | - Initial breadcrumbs support with Monolog handler.
118 |
119 | ## 0.15.0 (2016-04-29)
120 |
121 | - Fixed some cases where serialization wouldn't happen.
122 | - Added sdk attribute.
123 |
124 | ## 0.14.0 (2016-04-27)
125 |
126 | - Added `prefixes` option for stripping absolute paths.
127 | - Removed `abs_path` from stacktraces.
128 | - Added `app_path` to specify application root for resolving `in_app` on frames.
129 | - Moved Laravel support to `sentry-laravel` project.
130 | - Fixed duplicate stack computation.
131 | - Added `dsn` option to ease configuration.
132 | - Fixed an issue with the curl async transport.
133 | - Improved serialization of values.
134 |
135 | ## 0.13.0 (2015-09-09)
136 |
137 | - Updated API to use new style interfaces.
138 | - Remove session cookie in default processor.
139 | - Expand docs for Laravel, Symfony2, and Monolog.
140 | - Default error types can now be set as part of ErrorHandler configuration.
141 |
142 | ## 0.12.1 (2015-07-26)
143 |
144 | - Dont send empty values for various context.
145 |
146 | ## 0.12.0 (2015-05-19)
147 |
148 | - Bumped protocol version to 6.
149 | - Fixed an issue with the async curl handler (GH-216).
150 | - Removed UDP transport.
151 |
152 | ## 0.11.0 (2015-03-25)
153 |
154 | - New configuration parameter: `release`
155 | - New configuration parameter: `message_limit`
156 | - New configuration parameter: `curl_ssl_version`
157 | - New configuration parameter: `curl_ipv4`
158 | - New configuration parameter: `verify_ssl`
159 | - Updated remote endpoint to use modern project-based path.
160 | - Expanded default sanitizer support to include `auth_pw` attribute.
161 |
162 | ## 0.10.0 (2014-09-03)
163 |
164 | - Added a default certificate bundle which includes common root CA's as well as getsentry.com's CA.
165 |
166 | ## 0.9.1 (2014-08-26)
167 |
168 | - Change default curl connection to `sync`
169 | - Improve CLI reporting
170 |
171 | ## 0.9.0 (2014-06-04)
172 |
173 | - Protocol version 5
174 | - Default to asynchronous HTTP handler using curl_multi.
175 |
176 |
177 | (For previous versions see the commit history)
178 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Sentry Team and individual contributors.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | 3. Neither the name of the Raven, Sentry, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: test
2 |
3 | develop: update-submodules
4 | composer install --dev
5 | make setup-git
6 |
7 | update-submodules:
8 | git submodule init
9 | git submodule update
10 |
11 | cs:
12 | vendor/bin/php-cs-fixer fix --config-file=.php_cs --verbose --diff
13 |
14 | cs-dry-run:
15 | vendor/bin/php-cs-fixer fix --config-file=.php_cs --verbose --diff --dry-run
16 |
17 | test: cs-dry-run
18 | vendor/bin/phpunit
19 |
20 | setup-git:
21 | git config branch.autosetuprebase always
22 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # Sentry for PHP
8 |
9 | [](http://travis-ci.org/getsentry/sentry-php)
10 | [](https://packagist.org/packages/sentry/sentry)
11 | [](https://packagist.org/packages/sentry/sentry)
12 | [](https://packagist.org/packages/sentry/sentry)
13 | [](https://packagist.org/packages/sentry/sentry)
14 | [](https://scrutinizer-ci.com/g/getsentry/sentry-php/)
15 | [](https://scrutinizer-ci.com/g/getsentry/sentry-php/)
16 |
17 | The Sentry PHP error reporter tracks errors and exceptions that happen during the
18 | execution of your application and provides instant notification with detailed
19 | informations needed to prioritize, identify, reproduce and fix each issue. Learn
20 | more about [automatic PHP error reporting with Sentry](https://sentry.io/for/php/).
21 |
22 | ## Features
23 |
24 | - Automatically report (un)handled exceptions and errors
25 | - Send customized diagnostic data
26 | - Process and sanitize data before sending it over the network
27 |
28 | ## Usage
29 |
30 | ```php
31 | // Instantiate a new client with a compatible DSN and install built-in
32 | // handlers
33 | $client = (new Raven_Client('http://public:secret@example.com/1'))->install();
34 |
35 | // Capture an exception
36 | $event_id = $client->captureException($ex);
37 |
38 | // Give the user feedback
39 | echo "Sorry, there was an error!";
40 | echo "Your reference ID is " . $event_id;
41 | ```
42 |
43 | For more information, see our [documentation](https://docs.getsentry.com/hosted/clients/php/).
44 |
45 |
46 | ## Integration with frameworks
47 |
48 | Other packages exists to integrate this SDK into the most common frameworks.
49 |
50 | ### Official integrations
51 |
52 | The following integrations are fully supported and maintained by the Sentry team.
53 |
54 | - [Symfony](https://github.com/getsentry/sentry-symfony)
55 | - [Laravel](https://github.com/getsentry/sentry-laravel)
56 |
57 | ### 3rd party integrations
58 |
59 | The following integrations are available and maintained by members of the Sentry community.
60 |
61 | - [Nette](https://github.com/Salamek/raven-nette)
62 | - [ZendFramework](https://github.com/facile-it/sentry-module)
63 | - [WordPress](https://wordpress.org/plugins/wp-sentry-integration/)
64 | - [Drupal](https://www.drupal.org/project/raven)
65 | - [OpenCart](https://github.com/BurdaPraha/oc_sentry)
66 | - ... feel free to be famous, create a port to your favourite platform!
67 |
68 | ## Community
69 |
70 | - [Documentation](https://docs.getsentry.com/hosted/clients/php/)
71 | - [Bug Tracker](http://github.com/getsentry/sentry-php/issues)
72 | - [Code](http://github.com/getsentry/sentry-php)
73 | - [Mailing List](https://groups.google.com/group/getsentry)
74 | - [IRC](irc://irc.freenode.net/sentry) (irc.freenode.net, #sentry)
75 |
76 |
77 | Contributing
78 | ------------
79 |
80 | Dependencies are managed through composer:
81 |
82 | ```
83 | $ composer install
84 | ```
85 |
86 | Tests can then be run via phpunit:
87 |
88 | ```
89 | $ vendor/bin/phpunit
90 | ```
91 |
92 |
93 | Tagging a Release
94 | -----------------
95 |
96 | 1. Make sure ``CHANGES`` is up to date (add the release date) and ``master`` is green.
97 |
98 | 2. Create a new branch for the minor version (if not present):
99 |
100 | ```
101 | $ git checkout -b releases/1.7.x
102 | ```
103 |
104 | 3. Update the hardcoded version tag in ``Client.php``:
105 |
106 | ```
107 | class Raven_Client
108 | {
109 | const VERSION = '1.7.0';
110 | }
111 | ```
112 |
113 | 4. Commit the change:
114 |
115 | ```
116 | $ git commit -a -m "1.7.0"
117 | ```
118 |
119 | 5. Tag the branch:
120 |
121 | ```
122 | git tag 1.7.0
123 | ```
124 |
125 | 6. Push the tag:
126 |
127 | ```
128 | git push --tags
129 | ```
130 |
131 | 7. Switch back to ``master``:
132 |
133 | ```
134 | git checkout master
135 | ```
136 |
137 | 8. Add the next minor release to the ``CHANGES`` file:
138 |
139 | ```
140 | ## 1.8.0 (unreleased)
141 | ```
142 |
143 | 9. Update the version in ``Client.php``:
144 |
145 | ```
146 | class Raven_Client
147 | {
148 | const VERSION = '1.8.x-dev';
149 | }
150 | ```
151 |
152 | 10. Lastly, update the composer version in ``composer.json``:
153 |
154 | ```
155 | "extra": {
156 | "branch-alias": {
157 | "dev-master": "1.8.x-dev"
158 | }
159 | }
160 | ```
161 |
162 | All done! Composer will pick up the tag and configuration automatically.
163 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/bin/sentry:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | getMessage());
33 | }
34 |
35 | $client = new Raven_Client($dsn, array(
36 | 'trace' => true,
37 | 'curl_method' => 'sync',
38 | 'app_path' => realpath(__DIR__ . '/..'),
39 | 'base_path' => realpath(__DIR__ . '/..'),
40 | ));
41 |
42 | $config = get_object_vars($client);
43 | $required_keys = array('server', 'project', 'public_key', 'secret_key');
44 |
45 | echo "Client configuration:\n";
46 | foreach ($required_keys as $key) {
47 | if (empty($config[$key])) {
48 | exit("ERROR: Missing configuration for $key");
49 | }
50 | if (is_array($config[$key])) {
51 | echo "-> $key: [".implode(", ", $config[$key])."]\n";
52 | } else {
53 | echo "-> $key: $config[$key]\n";
54 | }
55 |
56 | }
57 | echo "\n";
58 |
59 | echo "Sending a test event:\n";
60 |
61 | $ex = raven_cli_test("command name", array("foo" => "bar"));
62 | $event_id = $client->captureException($ex);
63 |
64 | echo "-> event ID: $event_id\n";
65 |
66 | $last_error = $client->getLastError();
67 | if (!empty($last_error)) {
68 | exit("ERROR: There was an error sending the test event:\n " . $last_error);
69 | }
70 |
71 | echo "\n";
72 | echo "Done!";
73 | }
74 |
75 |
76 | function main() {
77 | global $argv;
78 |
79 | if (!isset($argv[1])) {
80 | exit('Usage: sentry test ');
81 | }
82 |
83 | $cmd = $argv[1];
84 |
85 | switch ($cmd) {
86 | case 'test':
87 | cmd_test(@$argv[2]);
88 | break;
89 | default:
90 | exit('Usage: sentry test ');
91 | }
92 | }
93 |
94 | main();
95 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sentry/sentry",
3 | "type": "library",
4 | "description": "A PHP client for Sentry (http://getsentry.com)",
5 | "keywords": ["log", "logging"],
6 | "homepage": "http://getsentry.com",
7 | "license": "BSD-3-Clause",
8 | "authors": [
9 | {
10 | "name": "David Cramer",
11 | "email": "dcramer@gmail.com"
12 | }
13 | ],
14 | "require-dev": {
15 | "friendsofphp/php-cs-fixer": "^1.8.0",
16 | "phpunit/phpunit": "^4.8 || ^5.0",
17 | "monolog/monolog": "*"
18 | },
19 | "require": {
20 | "php": "^5.3|^7.0",
21 | "ext-curl": "*"
22 | },
23 | "suggest": {
24 | "ext-hash": "*",
25 | "ext-json": "*",
26 | "ext-mbstring": "*",
27 | "immobiliare/sentry-php": "Fork that fixes support for PHP 5.2",
28 | "monolog/monolog": "Automatically capture Monolog events as breadcrumbs"
29 | },
30 | "conflict": {
31 | "raven/raven": "*"
32 | },
33 | "bin": [
34 | "bin/sentry"
35 | ],
36 | "autoload": {
37 | "psr-0" : {
38 | "Raven_" : "lib/"
39 | }
40 | },
41 | "scripts": {
42 | "tests": [
43 | "vendor/bin/phpunit --verbose"
44 | ],
45 | "tests-travis": [
46 | "vendor/bin/phpunit --verbose --configuration phpunit.xml --coverage-clover test/clover.xml"
47 | ],
48 | "tests-report": [
49 | "vendor/bin/phpunit --verbose --configuration phpunit.xml --coverage-html test/html-report"
50 | ],
51 | "phpcs": [
52 | "vendor/bin/php-cs-fixer fix --config-file=.php_cs --verbose --diff --dry-run"
53 | ]
54 | },
55 | "extra": {
56 | "branch-alias": {
57 | "dev-master": "1.8.x-dev"
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Autoloader.php:
--------------------------------------------------------------------------------
1 | size = $size;
30 | $this->reset();
31 | }
32 |
33 | public function reset()
34 | {
35 | $this->count = 0;
36 | $this->pos = 0;
37 | $this->buffer = array();
38 | }
39 |
40 | public function record($crumb)
41 | {
42 | if (empty($crumb['timestamp'])) {
43 | $crumb['timestamp'] = microtime(true);
44 | }
45 | $this->buffer[$this->pos] = $crumb;
46 | $this->pos = ($this->pos + 1) % $this->size;
47 | $this->count++;
48 | }
49 |
50 | /**
51 | * @return array[]
52 | */
53 | public function fetch()
54 | {
55 | $results = array();
56 | for ($i = 0; $i <= ($this->size - 1); $i++) {
57 | $idx = ($this->pos + $i) % $this->size;
58 | if (isset($this->buffer[$idx])) {
59 | $results[] = $this->buffer[$idx];
60 | }
61 | }
62 | return $results;
63 | }
64 |
65 | public function is_empty()
66 | {
67 | return $this->count === 0;
68 | }
69 |
70 | public function to_json()
71 | {
72 | return array(
73 | 'values' => $this->fetch(),
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Breadcrumbs/ErrorHandler.php:
--------------------------------------------------------------------------------
1 | ravenClient = $ravenClient;
18 | }
19 |
20 | public function handleError($code, $message, $file = '', $line = 0, $context = array())
21 | {
22 | $this->ravenClient->breadcrumbs->record(array(
23 | 'category' => 'error_reporting',
24 | 'message' => $message,
25 | 'level' => $this->ravenClient->translateSeverity($code),
26 | 'data' => array(
27 | 'code' => $code,
28 | 'line' => $line,
29 | 'file' => $file,
30 | ),
31 | ));
32 |
33 | if ($this->existingHandler !== null) {
34 | return call_user_func($this->existingHandler, $code, $message, $file, $line, $context);
35 | } else {
36 | return false;
37 | }
38 | }
39 |
40 | public function install()
41 | {
42 | $this->existingHandler = set_error_handler(array($this, 'handleError'), E_ALL);
43 | return $this;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Breadcrumbs/MonologHandler.php:
--------------------------------------------------------------------------------
1 | Raven_Client::DEBUG,
13 | Logger::INFO => Raven_Client::INFO,
14 | Logger::NOTICE => Raven_Client::INFO,
15 | Logger::WARNING => Raven_Client::WARNING,
16 | Logger::ERROR => Raven_Client::ERROR,
17 | Logger::CRITICAL => Raven_Client::FATAL,
18 | Logger::ALERT => Raven_Client::FATAL,
19 | Logger::EMERGENCY => Raven_Client::FATAL,
20 | );
21 |
22 | protected $excMatch = '/^exception \'([^\']+)\' with message \'(.+)\' in .+$/s';
23 |
24 | /**
25 | * @var Raven_Client the client object that sends the message to the server
26 | */
27 | protected $ravenClient;
28 |
29 | /**
30 | * @param Raven_Client $ravenClient
31 | * @param int $level The minimum logging level at which this handler will be triggered
32 | * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
33 | */
34 | public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true)
35 | {
36 | parent::__construct($level, $bubble);
37 |
38 | $this->ravenClient = $ravenClient;
39 | }
40 |
41 | /**
42 | * @param string $message
43 | * @return array|null
44 | */
45 | protected function parseException($message)
46 | {
47 | if (preg_match($this->excMatch, $message, $matches)) {
48 | return array($matches[1], $matches[2]);
49 | }
50 |
51 | return null;
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | protected function write(array $record)
58 | {
59 | // sentry uses the 'nobreadcrumb' attribute to skip reporting
60 | if (!empty($record['context']['nobreadcrumb'])) {
61 | return;
62 | }
63 |
64 | if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) {
65 | /**
66 | * @var Exception $exc
67 | */
68 | $exc = $record['context']['exception'];
69 | $crumb = array(
70 | 'type' => 'error',
71 | 'level' => $this->logLevels[$record['level']],
72 | 'category' => $record['channel'],
73 | 'data' => array(
74 | 'type' => get_class($exc),
75 | 'value' => $exc->getMessage(),
76 | ),
77 | );
78 | } else {
79 | // TODO(dcramer): parse exceptions out of messages and format as above
80 | if ($error = $this->parseException($record['message'])) {
81 | $crumb = array(
82 | 'type' => 'error',
83 | 'level' => $this->logLevels[$record['level']],
84 | 'category' => $record['channel'],
85 | 'data' => array(
86 | 'type' => $error[0],
87 | 'value' => $error[1],
88 | ),
89 | );
90 | } else {
91 | $crumb = array(
92 | 'level' => $this->logLevels[$record['level']],
93 | 'category' => $record['channel'],
94 | 'message' => $record['message'],
95 | );
96 | }
97 | }
98 |
99 | $this->ravenClient->breadcrumbs->record($crumb);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Client.php:
--------------------------------------------------------------------------------
1 | logger = Raven_Util::get($options, 'logger', 'php');
146 | $this->server = Raven_Util::get($options, 'server');
147 | $this->secret_key = Raven_Util::get($options, 'secret_key');
148 | $this->public_key = Raven_Util::get($options, 'public_key');
149 | $this->project = Raven_Util::get($options, 'project', 1);
150 | $this->auto_log_stacks = (bool) Raven_Util::get($options, 'auto_log_stacks', false);
151 | $this->name = Raven_Util::get($options, 'name', Raven_Compat::gethostname());
152 | $this->site = Raven_Util::get($options, 'site', self::_server_variable('SERVER_NAME'));
153 | $this->tags = Raven_Util::get($options, 'tags', array());
154 | $this->release = Raven_Util::get($options, 'release', null);
155 | $this->environment = Raven_Util::get($options, 'environment', null);
156 | $this->sample_rate = Raven_Util::get($options, 'sample_rate', 1);
157 | $this->trace = (bool) Raven_Util::get($options, 'trace', true);
158 | $this->timeout = Raven_Util::get($options, 'timeout', 2);
159 | $this->message_limit = Raven_Util::get($options, 'message_limit', self::MESSAGE_LIMIT);
160 | $this->exclude = Raven_Util::get($options, 'exclude', array());
161 | $this->severity_map = null;
162 | $this->http_proxy = Raven_Util::get($options, 'http_proxy');
163 | $this->extra_data = Raven_Util::get($options, 'extra', array());
164 | $this->send_callback = Raven_Util::get($options, 'send_callback', null);
165 | $this->curl_method = Raven_Util::get($options, 'curl_method', 'sync');
166 | $this->curl_path = Raven_Util::get($options, 'curl_path', 'curl');
167 | $this->curl_ipv4 = Raven_Util::get($options, 'curl_ipv4', true);
168 | $this->ca_cert = Raven_Util::get($options, 'ca_cert', static::get_default_ca_cert());
169 | $this->verify_ssl = Raven_Util::get($options, 'verify_ssl', true);
170 | $this->curl_ssl_version = Raven_Util::get($options, 'curl_ssl_version');
171 | $this->trust_x_forwarded_proto = Raven_Util::get($options, 'trust_x_forwarded_proto');
172 | $this->transport = Raven_Util::get($options, 'transport', null);
173 | $this->mb_detect_order = Raven_Util::get($options, 'mb_detect_order', null);
174 | $this->error_types = Raven_Util::get($options, 'error_types', null);
175 |
176 | // app path is used to determine if code is part of your application
177 | $this->setAppPath(Raven_Util::get($options, 'app_path', null));
178 | $this->setExcludedAppPaths(Raven_Util::get($options, 'excluded_app_paths', null));
179 | // a list of prefixes used to coerce absolute paths into relative
180 | $this->setPrefixes(Raven_Util::get($options, 'prefixes', static::getDefaultPrefixes()));
181 | $this->processors = $this->setProcessorsFromOptions($options);
182 |
183 | $this->_lasterror = null;
184 | $this->_last_sentry_error = null;
185 | $this->_curl_instance = null;
186 | $this->_last_event_id = null;
187 | $this->_user = null;
188 | $this->_pending_events = array();
189 | $this->context = new Raven_Context();
190 | $this->breadcrumbs = new Raven_Breadcrumbs();
191 | $this->_shutdown_function_has_been_set = false;
192 |
193 | $this->sdk = Raven_Util::get($options, 'sdk', array(
194 | 'name' => 'sentry-php',
195 | 'version' => self::VERSION,
196 | ));
197 | $this->serializer = new Raven_Serializer($this->mb_detect_order);
198 | $this->reprSerializer = new Raven_ReprSerializer($this->mb_detect_order);
199 |
200 | if ($this->curl_method == 'async') {
201 | $this->_curl_handler = new Raven_CurlHandler($this->get_curl_options());
202 | }
203 |
204 | $this->transaction = new Raven_TransactionStack();
205 | if (static::is_http_request() && isset($_SERVER['PATH_INFO'])) {
206 | // @codeCoverageIgnoreStart
207 | $this->transaction->push($_SERVER['PATH_INFO']);
208 | // @codeCoverageIgnoreEnd
209 | }
210 |
211 | if (Raven_Util::get($options, 'install_default_breadcrumb_handlers', true)) {
212 | $this->registerDefaultBreadcrumbHandlers();
213 | }
214 |
215 | if (Raven_Util::get($options, 'install_shutdown_handler', true)) {
216 | $this->registerShutdownFunction();
217 | }
218 | }
219 |
220 | public function __destruct()
221 | {
222 | // Force close curl resource
223 | $this->close_curl_resource();
224 | }
225 |
226 | /**
227 | * Destruct all objects contain link to this object
228 | *
229 | * This method can not delete shutdown handler
230 | */
231 | public function close_all_children_link()
232 | {
233 | $this->processors = array();
234 | }
235 |
236 | /**
237 | * Installs any available automated hooks (such as error_reporting).
238 | */
239 | public function install()
240 | {
241 | if ($this->error_handler) {
242 | throw new Raven_Exception(sprintf('%s->install() must only be called once', get_class($this)));
243 | }
244 | $this->error_handler = new Raven_ErrorHandler($this, false, $this->error_types);
245 | $this->error_handler->registerExceptionHandler();
246 | $this->error_handler->registerErrorHandler();
247 | $this->error_handler->registerShutdownFunction();
248 | return $this;
249 | }
250 |
251 | public function getRelease()
252 | {
253 | return $this->release;
254 | }
255 |
256 | public function setRelease($value)
257 | {
258 | $this->release = $value;
259 | return $this;
260 | }
261 |
262 | public function getEnvironment()
263 | {
264 | return $this->environment;
265 | }
266 |
267 | public function setEnvironment($value)
268 | {
269 | $this->environment = $value;
270 | return $this;
271 | }
272 |
273 | private static function getDefaultPrefixes()
274 | {
275 | $value = get_include_path();
276 | return explode(PATH_SEPARATOR, $value);
277 | }
278 |
279 | private static function _convertPath($value)
280 | {
281 | $path = @realpath($value);
282 | if ($path === false) {
283 | $path = $value;
284 | }
285 | // we need app_path to have a trailing slash otherwise
286 | // base path detection becomes complex if the same
287 | // prefix is matched
288 | if (substr($path, 0, 1) === DIRECTORY_SEPARATOR && substr($path, -1) !== DIRECTORY_SEPARATOR) {
289 | $path = $path.DIRECTORY_SEPARATOR;
290 | }
291 | return $path;
292 | }
293 |
294 | public function getAppPath()
295 | {
296 | return $this->app_path;
297 | }
298 |
299 | public function setAppPath($value)
300 | {
301 | if ($value) {
302 | $this->app_path = static::_convertPath($value);
303 | } else {
304 | $this->app_path = null;
305 | }
306 | return $this;
307 | }
308 |
309 | public function getExcludedAppPaths()
310 | {
311 | return $this->excluded_app_paths;
312 | }
313 |
314 | public function setExcludedAppPaths($value)
315 | {
316 | $this->excluded_app_paths = $value ? array_map(array($this, '_convertPath'), $value) : null;
317 | return $this;
318 | }
319 |
320 | public function getPrefixes()
321 | {
322 | return $this->prefixes;
323 | }
324 |
325 | /**
326 | * @param array $value
327 | * @return Raven_Client
328 | */
329 | public function setPrefixes($value)
330 | {
331 | $this->prefixes = $value ? array_map(array($this, '_convertPath'), $value) : $value;
332 | return $this;
333 | }
334 |
335 | public function getSendCallback()
336 | {
337 | return $this->send_callback;
338 | }
339 |
340 | public function setSendCallback($value)
341 | {
342 | $this->send_callback = $value;
343 | return $this;
344 | }
345 |
346 | public function getTransport()
347 | {
348 | return $this->transport;
349 | }
350 |
351 | public function getServerEndpoint($value = '')
352 | {
353 | return $this->server;
354 | }
355 |
356 | public static function getUserAgent()
357 | {
358 | return 'sentry-php/' . self::VERSION;
359 | }
360 |
361 | /**
362 | * Set a custom transport to override how Sentry events are sent upstream.
363 | *
364 | * The bound function will be called with ``$client`` and ``$data`` arguments
365 | * and is responsible for encoding the data, authenticating, and sending
366 | * the data to the upstream Sentry server.
367 | *
368 | * @param Callable $value Function to be called
369 | * @return Raven_Client
370 | */
371 | public function setTransport($value)
372 | {
373 | $this->transport = $value;
374 | return $this;
375 | }
376 |
377 | /**
378 | * @return string[]|Raven_Processor[]
379 | */
380 | public static function getDefaultProcessors()
381 | {
382 | return array(
383 | 'Raven_Processor_SanitizeDataProcessor',
384 | );
385 | }
386 |
387 | /**
388 | * Sets the Raven_Processor sub-classes to be used when data is processed before being
389 | * sent to Sentry.
390 | *
391 | * @param $options
392 | * @return Raven_Processor[]
393 | */
394 | public function setProcessorsFromOptions($options)
395 | {
396 | $processors = array();
397 | foreach (Raven_util::get($options, 'processors', static::getDefaultProcessors()) as $processor) {
398 | /**
399 | * @var Raven_Processor $new_processor
400 | * @var Raven_Processor|string $processor
401 | */
402 | $new_processor = new $processor($this);
403 |
404 | if (isset($options['processorOptions']) && is_array($options['processorOptions'])) {
405 | if (isset($options['processorOptions'][$processor])
406 | && method_exists($processor, 'setProcessorOptions')
407 | ) {
408 | $new_processor->setProcessorOptions($options['processorOptions'][$processor]);
409 | }
410 | }
411 | $processors[] = $new_processor;
412 | }
413 | return $processors;
414 | }
415 |
416 | /**
417 | * Parses a Raven-compatible DSN and returns an array of its values.
418 | *
419 | * @param string $dsn Raven compatible DSN
420 | * @return array parsed DSN
421 | *
422 | * @doc http://raven.readthedocs.org/en/latest/config/#the-sentry-dsn
423 | */
424 | public static function parseDSN($dsn)
425 | {
426 | $url = parse_url($dsn);
427 | $scheme = (isset($url['scheme']) ? $url['scheme'] : '');
428 | if (!in_array($scheme, array('http', 'https'))) {
429 | throw new InvalidArgumentException(
430 | 'Unsupported Sentry DSN scheme: '.
431 | (!empty($scheme) ? $scheme : '')
432 | );
433 | }
434 | $netloc = (isset($url['host']) ? $url['host'] : null);
435 | $netloc .= (isset($url['port']) ? ':'.$url['port'] : null);
436 | $rawpath = (isset($url['path']) ? $url['path'] : null);
437 | if ($rawpath) {
438 | $pos = strrpos($rawpath, '/', 1);
439 | if ($pos !== false) {
440 | $path = substr($rawpath, 0, $pos);
441 | $project = substr($rawpath, $pos + 1);
442 | } else {
443 | $path = '';
444 | $project = substr($rawpath, 1);
445 | }
446 | } else {
447 | $project = null;
448 | $path = '';
449 | }
450 | $username = (isset($url['user']) ? $url['user'] : null);
451 | $password = (isset($url['pass']) ? $url['pass'] : null);
452 | if (empty($netloc) || empty($project) || empty($username) || empty($password)) {
453 | throw new InvalidArgumentException('Invalid Sentry DSN: ' . $dsn);
454 | }
455 |
456 | return array(
457 | 'server' => sprintf('%s://%s%s/api/%s/store/', $scheme, $netloc, $path, $project),
458 | 'project' => $project,
459 | 'public_key' => $username,
460 | 'secret_key' => $password,
461 | );
462 | }
463 |
464 | public function getLastError()
465 | {
466 | return $this->_lasterror;
467 | }
468 |
469 | /**
470 | * Given an identifier, returns a Sentry searchable string.
471 | *
472 | * @param mixed $ident
473 | * @return mixed
474 | * @codeCoverageIgnore
475 | */
476 | public function getIdent($ident)
477 | {
478 | // XXX: We don't calculate checksums yet, so we only have the ident.
479 | return $ident;
480 | }
481 |
482 | /**
483 | * @param string $message The message (primary description) for the event.
484 | * @param array $params params to use when formatting the message.
485 | * @param string $level Log level group
486 | * @param bool|array $stack
487 | * @param mixed $vars
488 | * @return string|null
489 | * @deprecated
490 | * @codeCoverageIgnore
491 | */
492 | public function message($message, $params = array(), $level = self::INFO,
493 | $stack = false, $vars = null)
494 | {
495 | return $this->captureMessage($message, $params, $level, $stack, $vars);
496 | }
497 |
498 | /**
499 | * @param Exception $exception
500 | * @return string|null
501 | * @deprecated
502 | * @codeCoverageIgnore
503 | */
504 | public function exception($exception)
505 | {
506 | return $this->captureException($exception);
507 | }
508 |
509 | /**
510 | * Log a message to sentry
511 | *
512 | * @param string $message The message (primary description) for the event.
513 | * @param array $params params to use when formatting the message.
514 | * @param array $data Additional attributes to pass with this event (see Sentry docs).
515 | * @param bool|array $stack
516 | * @param mixed $vars
517 | * @return string|null
518 | */
519 | public function captureMessage($message, $params = array(), $data = array(),
520 | $stack = false, $vars = null)
521 | {
522 | // Gracefully handle messages which contain formatting characters, but were not
523 | // intended to be used with formatting.
524 | if (!empty($params)) {
525 | $formatted_message = vsprintf($message, $params);
526 | } else {
527 | $formatted_message = $message;
528 | }
529 |
530 | if ($data === null) {
531 | $data = array();
532 | // support legacy method of passing in a level name as the third arg
533 | } elseif (!is_array($data)) {
534 | $data = array(
535 | 'level' => $data,
536 | );
537 | }
538 |
539 | $data['message'] = $formatted_message;
540 | $data['sentry.interfaces.Message'] = array(
541 | 'message' => $message,
542 | 'params' => $params,
543 | 'formatted' => $formatted_message,
544 | );
545 |
546 | return $this->capture($data, $stack, $vars);
547 | }
548 |
549 | /**
550 | * Log an exception to sentry
551 | *
552 | * @param Exception $exception The Exception object.
553 | * @param array $data Additional attributes to pass with this event (see Sentry docs).
554 | * @param mixed $logger
555 | * @param mixed $vars
556 | * @return string|null
557 | */
558 | public function captureException($exception, $data = null, $logger = null, $vars = null)
559 | {
560 | $has_chained_exceptions = version_compare(PHP_VERSION, '5.3.0', '>=');
561 |
562 | if (in_array(get_class($exception), $this->exclude)) {
563 | return null;
564 | }
565 |
566 | if ($data === null) {
567 | $data = array();
568 | }
569 |
570 | $exc = $exception;
571 | do {
572 | $exc_data = array(
573 | 'value' => $this->serializer->serialize($exc->getMessage()),
574 | 'type' => get_class($exc),
575 | );
576 |
577 | /**'exception'
578 | * Exception::getTrace doesn't store the point at where the exception
579 | * was thrown, so we have to stuff it in ourselves. Ugh.
580 | */
581 | $trace = $exc->getTrace();
582 | $frame_where_exception_thrown = array(
583 | 'file' => $exc->getFile(),
584 | 'line' => $exc->getLine(),
585 | );
586 |
587 | array_unshift($trace, $frame_where_exception_thrown);
588 |
589 | // manually trigger autoloading, as it's not done in some edge cases due to PHP bugs (see #60149)
590 | if (!class_exists('Raven_Stacktrace')) {
591 | // @codeCoverageIgnoreStart
592 | spl_autoload_call('Raven_Stacktrace');
593 | // @codeCoverageIgnoreEnd
594 | }
595 |
596 | $exc_data['stacktrace'] = array(
597 | 'frames' => Raven_Stacktrace::get_stack_info(
598 | $trace, $this->trace, $vars, $this->message_limit, $this->prefixes,
599 | $this->app_path, $this->excluded_app_paths, $this->serializer, $this->reprSerializer
600 | ),
601 | );
602 |
603 | $exceptions[] = $exc_data;
604 | } while ($has_chained_exceptions && $exc = $exc->getPrevious());
605 |
606 | $data['exception'] = array(
607 | 'values' => array_reverse($exceptions),
608 | );
609 | if ($logger !== null) {
610 | $data['logger'] = $logger;
611 | }
612 |
613 | if (empty($data['level'])) {
614 | if (method_exists($exception, 'getSeverity')) {
615 | $data['level'] = $this->translateSeverity($exception->getSeverity());
616 | } else {
617 | $data['level'] = self::ERROR;
618 | }
619 | }
620 |
621 | return $this->capture($data, $trace, $vars);
622 | }
623 |
624 |
625 | /**
626 | * Capture the most recent error (obtained with ``error_get_last``).
627 | * @return string|null
628 | */
629 | public function captureLastError()
630 | {
631 | if (null === $error = error_get_last()) {
632 | return null;
633 | }
634 |
635 | $e = new ErrorException(
636 | @$error['message'], 0, @$error['type'],
637 | @$error['file'], @$error['line']
638 | );
639 |
640 | return $this->captureException($e);
641 | }
642 |
643 | /**
644 | * Log an query to sentry
645 | *
646 | * @param string|null $query
647 | * @param string $level
648 | * @param string $engine
649 | */
650 | public function captureQuery($query, $level = self::INFO, $engine = '')
651 | {
652 | $data = array(
653 | 'message' => $query,
654 | 'level' => $level,
655 | 'sentry.interfaces.Query' => array(
656 | 'query' => $query
657 | )
658 | );
659 |
660 | if ($engine !== '') {
661 | $data['sentry.interfaces.Query']['engine'] = $engine;
662 | }
663 | return $this->capture($data, false);
664 | }
665 |
666 | /**
667 | * Return the last captured event's ID or null if none available.
668 | */
669 | public function getLastEventID()
670 | {
671 | return $this->_last_event_id;
672 | }
673 |
674 | protected function registerDefaultBreadcrumbHandlers()
675 | {
676 | $handler = new Raven_Breadcrumbs_ErrorHandler($this);
677 | $handler->install();
678 | }
679 |
680 | protected function registerShutdownFunction()
681 | {
682 | if (!$this->_shutdown_function_has_been_set) {
683 | $this->_shutdown_function_has_been_set = true;
684 | register_shutdown_function(array($this, 'onShutdown'));
685 | }
686 | }
687 |
688 | /**
689 | * @return bool
690 | * @codeCoverageIgnore
691 | */
692 | protected static function is_http_request()
693 | {
694 | return isset($_SERVER['REQUEST_METHOD']) && PHP_SAPI !== 'cli';
695 | }
696 |
697 | protected function get_http_data()
698 | {
699 | $headers = array();
700 |
701 | foreach ($_SERVER as $key => $value) {
702 | if (0 === strpos($key, 'HTTP_')) {
703 | $header_key =
704 | str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
705 | $headers[$header_key] = $value;
706 | } elseif (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH')) && $value !== '') {
707 | $header_key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
708 | $headers[$header_key] = $value;
709 | }
710 | }
711 |
712 | $result = array(
713 | 'method' => self::_server_variable('REQUEST_METHOD'),
714 | 'url' => $this->get_current_url(),
715 | 'query_string' => self::_server_variable('QUERY_STRING'),
716 | );
717 |
718 | // dont set this as an empty array as PHP will treat it as a numeric array
719 | // instead of a mapping which goes against the defined Sentry spec
720 | if (!empty($_POST)) {
721 | $result['data'] = $_POST;
722 | }
723 | if (!empty($_COOKIE)) {
724 | $result['cookies'] = $_COOKIE;
725 | }
726 | if (!empty($headers)) {
727 | $result['headers'] = $headers;
728 | }
729 |
730 | return array(
731 | 'request' => $result,
732 | );
733 | }
734 |
735 | protected function get_user_data()
736 | {
737 | $user = $this->context->user;
738 | if ($user === null) {
739 | if (!function_exists('session_id') || !session_id()) {
740 | return array();
741 | }
742 | $user = array(
743 | 'id' => session_id(),
744 | );
745 | if (!empty($_SERVER['REMOTE_ADDR'])) {
746 | $user['ip_address'] = $_SERVER['REMOTE_ADDR'];
747 | }
748 | if (!empty($_SESSION)) {
749 | $user['data'] = $_SESSION;
750 | }
751 | }
752 | return array(
753 | 'user' => $user,
754 | );
755 | }
756 |
757 | protected function get_extra_data()
758 | {
759 | return $this->extra_data;
760 | }
761 |
762 | public function get_default_data()
763 | {
764 | return array(
765 | 'server_name' => $this->name,
766 | 'project' => $this->project,
767 | 'site' => $this->site,
768 | 'logger' => $this->logger,
769 | 'tags' => $this->tags,
770 | 'platform' => 'php',
771 | 'sdk' => $this->sdk,
772 | 'culprit' => $this->transaction->peek(),
773 | );
774 | }
775 |
776 | public function capture($data, $stack = null, $vars = null)
777 | {
778 | if (!isset($data['timestamp'])) {
779 | $data['timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
780 | }
781 | if (!isset($data['level'])) {
782 | $data['level'] = self::ERROR;
783 | }
784 | if (!isset($data['tags'])) {
785 | $data['tags'] = array();
786 | }
787 | if (!isset($data['extra'])) {
788 | $data['extra'] = array();
789 | }
790 | if (!isset($data['event_id'])) {
791 | $data['event_id'] = static::uuid4();
792 | }
793 |
794 | if (isset($data['message'])) {
795 | $data['message'] = substr($data['message'], 0, $this->message_limit);
796 | }
797 |
798 | $data = array_merge($this->get_default_data(), $data);
799 |
800 | if (static::is_http_request()) {
801 | $data = array_merge($this->get_http_data(), $data);
802 | }
803 |
804 | $data = array_merge($this->get_user_data(), $data);
805 |
806 | if ($this->release) {
807 | $data['release'] = $this->release;
808 | }
809 | if ($this->environment) {
810 | $data['environment'] = $this->environment;
811 | }
812 |
813 | $data['tags'] = array_merge(
814 | $this->tags,
815 | $this->context->tags,
816 | $data['tags']);
817 |
818 | $data['extra'] = array_merge(
819 | $this->get_extra_data(),
820 | $this->context->extra,
821 | $data['extra']);
822 |
823 | if (empty($data['extra'])) {
824 | unset($data['extra']);
825 | }
826 | if (empty($data['tags'])) {
827 | unset($data['tags']);
828 | }
829 | if (empty($data['user'])) {
830 | unset($data['user']);
831 | }
832 | if (empty($data['request'])) {
833 | unset($data['request']);
834 | }
835 |
836 | if (!$this->breadcrumbs->is_empty()) {
837 | $data['breadcrumbs'] = $this->breadcrumbs->fetch();
838 | }
839 |
840 | if ((!$stack && $this->auto_log_stacks) || $stack === true) {
841 | $stack = debug_backtrace();
842 |
843 | // Drop last stack
844 | array_shift($stack);
845 | }
846 |
847 | if (!empty($stack)) {
848 | // manually trigger autoloading, as it's not done in some edge cases due to PHP bugs (see #60149)
849 | if (!class_exists('Raven_Stacktrace')) {
850 | // @codeCoverageIgnoreStart
851 | spl_autoload_call('Raven_Stacktrace');
852 | // @codeCoverageIgnoreEnd
853 | }
854 |
855 | if (!isset($data['stacktrace']) && !isset($data['exception'])) {
856 | $data['stacktrace'] = array(
857 | 'frames' => Raven_Stacktrace::get_stack_info(
858 | $stack, $this->trace, $vars, $this->message_limit, $this->prefixes,
859 | $this->app_path, $this->excluded_app_paths, $this->serializer, $this->reprSerializer
860 | ),
861 | );
862 | }
863 | }
864 |
865 | $this->sanitize($data);
866 | $this->process($data);
867 |
868 | if (!$this->store_errors_for_bulk_send) {
869 | $this->send($data);
870 | } else {
871 | $this->_pending_events[] = $data;
872 | }
873 |
874 | $this->_last_event_id = $data['event_id'];
875 |
876 | return $data['event_id'];
877 | }
878 |
879 | public function sanitize(&$data)
880 | {
881 | // attempt to sanitize any user provided data
882 | if (!empty($data['request'])) {
883 | $data['request'] = $this->serializer->serialize($data['request']);
884 | }
885 | if (!empty($data['user'])) {
886 | $data['user'] = $this->serializer->serialize($data['user'], 3);
887 | }
888 | if (!empty($data['extra'])) {
889 | $data['extra'] = $this->serializer->serialize($data['extra']);
890 | }
891 | if (!empty($data['tags'])) {
892 | foreach ($data['tags'] as $key => $value) {
893 | $data['tags'][$key] = @(string)$value;
894 | }
895 | }
896 | if (!empty($data['contexts'])) {
897 | $data['contexts'] = $this->serializer->serialize($data['contexts'], 5);
898 | }
899 | }
900 |
901 | /**
902 | * Process data through all defined Raven_Processor sub-classes
903 | *
904 | * @param array $data Associative array of data to log
905 | */
906 | public function process(&$data)
907 | {
908 | foreach ($this->processors as $processor) {
909 | $processor->process($data);
910 | }
911 | }
912 |
913 | public function sendUnsentErrors()
914 | {
915 | foreach ($this->_pending_events as $data) {
916 | $this->send($data);
917 | }
918 | $this->_pending_events = array();
919 | if ($this->store_errors_for_bulk_send) {
920 | //in case an error occurs after this is called, on shutdown, send any new errors.
921 | $this->store_errors_for_bulk_send = !defined('RAVEN_CLIENT_END_REACHED');
922 | }
923 | }
924 |
925 | /**
926 | * @param array $data
927 | * @return string|bool
928 | */
929 | public function encode(&$data)
930 | {
931 | $message = Raven_Compat::json_encode($data);
932 | if ($message === false) {
933 | if (function_exists('json_last_error_msg')) {
934 | $this->_lasterror = json_last_error_msg();
935 | } else {
936 | // @codeCoverageIgnoreStart
937 | $this->_lasterror = json_last_error();
938 | // @codeCoverageIgnoreEnd
939 | }
940 | return false;
941 | }
942 |
943 | if (function_exists("gzcompress")) {
944 | $message = gzcompress($message);
945 | }
946 |
947 | // PHP's builtin curl_* function are happy without this, but the exec method requires it
948 | $message = base64_encode($message);
949 |
950 | return $message;
951 | }
952 |
953 | /**
954 | * Wrapper to handle encoding and sending data to the Sentry API server.
955 | *
956 | * @param array $data Associative array of data to log
957 | */
958 | public function send(&$data)
959 | {
960 | if (is_callable($this->send_callback)
961 | && call_user_func_array($this->send_callback, array(&$data)) === false
962 | ) {
963 | // if send_callback returns false, end native send
964 | return;
965 | }
966 |
967 | if (!$this->server) {
968 | return;
969 | }
970 |
971 | if ($this->transport) {
972 | call_user_func($this->transport, $this, $data);
973 | return;
974 | }
975 |
976 | // should this event be sampled?
977 | if (rand(1, 100) / 100.0 > $this->sample_rate) {
978 | return;
979 | }
980 |
981 | $message = $this->encode($data);
982 |
983 | $headers = array(
984 | 'User-Agent' => static::getUserAgent(),
985 | 'X-Sentry-Auth' => $this->getAuthHeader(),
986 | 'Content-Type' => 'application/octet-stream'
987 | );
988 |
989 | $this->send_remote($this->server, $message, $headers);
990 | }
991 |
992 | /**
993 | * Send data to Sentry
994 | *
995 | * @param string $url Full URL to Sentry
996 | * @param array|string $data Associative array of data to log
997 | * @param array $headers Associative array of headers
998 | */
999 | protected function send_remote($url, $data, $headers = array())
1000 | {
1001 | $parts = parse_url($url);
1002 | $parts['netloc'] = $parts['host'].(isset($parts['port']) ? ':'.$parts['port'] : null);
1003 | $this->send_http($url, $data, $headers);
1004 | }
1005 |
1006 | protected static function get_default_ca_cert()
1007 | {
1008 | return dirname(__FILE__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'cacert.pem';
1009 | }
1010 |
1011 | /**
1012 | * @return array
1013 | * @doc http://stackoverflow.com/questions/9062798/php-curl-timeout-is-not-working/9063006#9063006
1014 | */
1015 | protected function get_curl_options()
1016 | {
1017 | $options = array(
1018 | CURLOPT_VERBOSE => false,
1019 | CURLOPT_SSL_VERIFYHOST => 2,
1020 | CURLOPT_SSL_VERIFYPEER => $this->verify_ssl,
1021 | CURLOPT_CAINFO => $this->ca_cert,
1022 | CURLOPT_USERAGENT => 'sentry-php/' . self::VERSION,
1023 | );
1024 | if ($this->http_proxy) {
1025 | $options[CURLOPT_PROXY] = $this->http_proxy;
1026 | }
1027 | if ($this->curl_ssl_version) {
1028 | $options[CURLOPT_SSLVERSION] = $this->curl_ssl_version;
1029 | }
1030 | if ($this->curl_ipv4) {
1031 | $options[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
1032 | }
1033 | if (defined('CURLOPT_TIMEOUT_MS')) {
1034 | // MS is available in curl >= 7.16.2
1035 | $timeout = max(1, ceil(1000 * $this->timeout));
1036 |
1037 | // some versions of PHP 5.3 don't have this defined correctly
1038 | if (!defined('CURLOPT_CONNECTTIMEOUT_MS')) {
1039 | //see stackoverflow link in the phpdoc
1040 | define('CURLOPT_CONNECTTIMEOUT_MS', 156);
1041 | }
1042 |
1043 | $options[CURLOPT_CONNECTTIMEOUT_MS] = $timeout;
1044 | $options[CURLOPT_TIMEOUT_MS] = $timeout;
1045 | } else {
1046 | // fall back to the lower-precision timeout.
1047 | $timeout = max(1, ceil($this->timeout));
1048 | $options[CURLOPT_CONNECTTIMEOUT] = $timeout;
1049 | $options[CURLOPT_TIMEOUT] = $timeout;
1050 | }
1051 | return $options;
1052 | }
1053 |
1054 | /**
1055 | * Send the message over http to the sentry url given
1056 | *
1057 | * @param string $url URL of the Sentry instance to log to
1058 | * @param array|string $data Associative array of data to log
1059 | * @param array $headers Associative array of headers
1060 | */
1061 | protected function send_http($url, $data, $headers = array())
1062 | {
1063 | if ($this->curl_method == 'async') {
1064 | $this->_curl_handler->enqueue($url, $data, $headers);
1065 | } elseif ($this->curl_method == 'exec') {
1066 | $this->send_http_asynchronous_curl_exec($url, $data, $headers);
1067 | } else {
1068 | $this->send_http_synchronous($url, $data, $headers);
1069 | }
1070 | }
1071 |
1072 | protected function buildCurlCommand($url, $data, $headers)
1073 | {
1074 | // TODO(dcramer): support ca_cert
1075 | $cmd = $this->curl_path.' -X POST ';
1076 | foreach ($headers as $key => $value) {
1077 | $cmd .= '-H ' . escapeshellarg($key.': '.$value). ' ';
1078 | }
1079 | $cmd .= '-d ' . escapeshellarg($data) . ' ';
1080 | $cmd .= escapeshellarg($url) . ' ';
1081 | $cmd .= '-m 5 '; // 5 second timeout for the whole process (connect + send)
1082 | if (!$this->verify_ssl) {
1083 | $cmd .= '-k ';
1084 | }
1085 | $cmd .= '> /dev/null 2>&1 &'; // ensure exec returns immediately while curl runs in the background
1086 |
1087 | return $cmd;
1088 | }
1089 |
1090 | /**
1091 | * Send the cURL to Sentry asynchronously. No errors will be returned from cURL
1092 | *
1093 | * @param string $url URL of the Sentry instance to log to
1094 | * @param array|string $data Associative array of data to log
1095 | * @param array $headers Associative array of headers
1096 | * @return bool
1097 | */
1098 | protected function send_http_asynchronous_curl_exec($url, $data, $headers)
1099 | {
1100 | exec($this->buildCurlCommand($url, $data, $headers));
1101 | return true; // The exec method is just fire and forget, so just assume it always works
1102 | }
1103 |
1104 | /**
1105 | * Send a blocking cURL to Sentry and check for errors from cURL
1106 | *
1107 | * @param string $url URL of the Sentry instance to log to
1108 | * @param array|string $data Associative array of data to log
1109 | * @param array $headers Associative array of headers
1110 | * @return bool
1111 | */
1112 | protected function send_http_synchronous($url, $data, $headers)
1113 | {
1114 | $new_headers = array();
1115 | foreach ($headers as $key => $value) {
1116 | array_push($new_headers, $key .': '. $value);
1117 | }
1118 | // XXX(dcramer): Prevent 100-continue response form server (Fixes GH-216)
1119 | $new_headers[] = 'Expect:';
1120 |
1121 | if (is_null($this->_curl_instance)) {
1122 | $this->_curl_instance = curl_init($url);
1123 | }
1124 | curl_setopt($this->_curl_instance, CURLOPT_POST, 1);
1125 | curl_setopt($this->_curl_instance, CURLOPT_HTTPHEADER, $new_headers);
1126 | curl_setopt($this->_curl_instance, CURLOPT_POSTFIELDS, $data);
1127 | curl_setopt($this->_curl_instance, CURLOPT_RETURNTRANSFER, true);
1128 |
1129 | $options = $this->get_curl_options();
1130 | if (isset($options[CURLOPT_CAINFO])) {
1131 | $ca_cert = $options[CURLOPT_CAINFO];
1132 | unset($options[CURLOPT_CAINFO]);
1133 | } else {
1134 | $ca_cert = null;
1135 | }
1136 | curl_setopt_array($this->_curl_instance, $options);
1137 |
1138 | $buffer = curl_exec($this->_curl_instance);
1139 |
1140 | $errno = curl_errno($this->_curl_instance);
1141 | // CURLE_SSL_CACERT || CURLE_SSL_CACERT_BADFILE
1142 | if ((($errno == 60) || ($errno == 77)) && !is_null($ca_cert)) {
1143 | curl_setopt($this->_curl_instance, CURLOPT_CAINFO, $ca_cert);
1144 | $buffer = curl_exec($this->_curl_instance);
1145 | }
1146 | if ($errno != 0) {
1147 | $this->_lasterror = curl_error($this->_curl_instance);
1148 | $this->_last_sentry_error = null;
1149 | return false;
1150 | }
1151 |
1152 | $code = curl_getinfo($this->_curl_instance, CURLINFO_HTTP_CODE);
1153 | $success = ($code == 200);
1154 | if ($success) {
1155 | $this->_lasterror = null;
1156 | $this->_last_sentry_error = null;
1157 | } else {
1158 | // It'd be nice just to raise an exception here, but it's not very PHP-like
1159 | $this->_lasterror = curl_error($this->_curl_instance);
1160 | $this->_last_sentry_error = @json_decode($buffer);
1161 | }
1162 |
1163 | return $success;
1164 | }
1165 |
1166 | /**
1167 | * Generate a Sentry authorization header string
1168 | *
1169 | * @param string $timestamp Timestamp when the event occurred
1170 | * @param string $client HTTP client name (not Raven_Client object)
1171 | * @param string $api_key Sentry API key
1172 | * @param string $secret_key Sentry API key
1173 | * @return string
1174 | */
1175 | protected static function get_auth_header($timestamp, $client, $api_key, $secret_key)
1176 | {
1177 | $header = array(
1178 | sprintf('sentry_timestamp=%F', $timestamp),
1179 | "sentry_client={$client}",
1180 | sprintf('sentry_version=%s', self::PROTOCOL),
1181 | );
1182 |
1183 | if ($api_key) {
1184 | $header[] = "sentry_key={$api_key}";
1185 | }
1186 |
1187 | if ($secret_key) {
1188 | $header[] = "sentry_secret={$secret_key}";
1189 | }
1190 |
1191 |
1192 | return sprintf('Sentry %s', implode(', ', $header));
1193 | }
1194 |
1195 | public function getAuthHeader()
1196 | {
1197 | $timestamp = microtime(true);
1198 | return $this->get_auth_header(
1199 | $timestamp, static::getUserAgent(), $this->public_key, $this->secret_key
1200 | );
1201 | }
1202 |
1203 | /**
1204 | * Generate an uuid4 value
1205 | *
1206 | * @return string
1207 | */
1208 | protected static function uuid4()
1209 | {
1210 | $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
1211 | // 32 bits for "time_low"
1212 | mt_rand(0, 0xffff), mt_rand(0, 0xffff),
1213 |
1214 | // 16 bits for "time_mid"
1215 | mt_rand(0, 0xffff),
1216 |
1217 | // 16 bits for "time_hi_and_version",
1218 | // four most significant bits holds version number 4
1219 | mt_rand(0, 0x0fff) | 0x4000,
1220 |
1221 | // 16 bits, 8 bits for "clk_seq_hi_res",
1222 | // 8 bits for "clk_seq_low",
1223 | // two most significant bits holds zero and one for variant DCE1.1
1224 | mt_rand(0, 0x3fff) | 0x8000,
1225 |
1226 | // 48 bits for "node"
1227 | mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
1228 | );
1229 |
1230 | return str_replace('-', '', $uuid);
1231 | }
1232 |
1233 | /**
1234 | * Return the URL for the current request
1235 | *
1236 | * @return string|null
1237 | */
1238 | protected function get_current_url()
1239 | {
1240 | // When running from commandline the REQUEST_URI is missing.
1241 | if (!isset($_SERVER['REQUEST_URI'])) {
1242 | return null;
1243 | }
1244 |
1245 | // HTTP_HOST is a client-supplied header that is optional in HTTP 1.0
1246 | $host = (!empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
1247 | : (!empty($_SERVER['LOCAL_ADDR']) ? $_SERVER['LOCAL_ADDR']
1248 | : (!empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '')));
1249 |
1250 | $httpS = $this->isHttps() ? 's' : '';
1251 | return "http{$httpS}://{$host}{$_SERVER['REQUEST_URI']}";
1252 | }
1253 |
1254 | /**
1255 | * Was the current request made over https?
1256 | *
1257 | * @return bool
1258 | */
1259 | protected function isHttps()
1260 | {
1261 | if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
1262 | return true;
1263 | }
1264 |
1265 | if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
1266 | return true;
1267 | }
1268 |
1269 | if (!empty($this->trust_x_forwarded_proto) &&
1270 | !empty($_SERVER['X-FORWARDED-PROTO']) &&
1271 | $_SERVER['X-FORWARDED-PROTO'] === 'https') {
1272 | return true;
1273 | }
1274 |
1275 | return false;
1276 | }
1277 |
1278 | /**
1279 | * Get the value of a key from $_SERVER
1280 | *
1281 | * @param string $key Key whose value you wish to obtain
1282 | * @return string Key's value
1283 | */
1284 | private static function _server_variable($key)
1285 | {
1286 | if (isset($_SERVER[$key])) {
1287 | return $_SERVER[$key];
1288 | }
1289 |
1290 | return '';
1291 | }
1292 |
1293 | /**
1294 | * Translate a PHP Error constant into a Sentry log level group
1295 | *
1296 | * @param string $severity PHP E_$x error constant
1297 | * @return string Sentry log level group
1298 | */
1299 | public function translateSeverity($severity)
1300 | {
1301 | if (is_array($this->severity_map) && isset($this->severity_map[$severity])) {
1302 | return $this->severity_map[$severity];
1303 | }
1304 | switch ($severity) {
1305 | case E_ERROR: return Raven_Client::ERROR;
1306 | case E_WARNING: return Raven_Client::WARN;
1307 | case E_PARSE: return Raven_Client::ERROR;
1308 | case E_NOTICE: return Raven_Client::INFO;
1309 | case E_CORE_ERROR: return Raven_Client::ERROR;
1310 | case E_CORE_WARNING: return Raven_Client::WARN;
1311 | case E_COMPILE_ERROR: return Raven_Client::ERROR;
1312 | case E_COMPILE_WARNING: return Raven_Client::WARN;
1313 | case E_USER_ERROR: return Raven_Client::ERROR;
1314 | case E_USER_WARNING: return Raven_Client::WARN;
1315 | case E_USER_NOTICE: return Raven_Client::INFO;
1316 | case E_STRICT: return Raven_Client::INFO;
1317 | case E_RECOVERABLE_ERROR: return Raven_Client::ERROR;
1318 | }
1319 | if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
1320 | switch ($severity) {
1321 | case E_DEPRECATED: return Raven_Client::WARN;
1322 | case E_USER_DEPRECATED: return Raven_Client::WARN;
1323 | }
1324 | }
1325 | return Raven_Client::ERROR;
1326 | }
1327 |
1328 | /**
1329 | * Provide a map of PHP Error constants to Sentry logging groups to use instead
1330 | * of the defaults in translateSeverity()
1331 | *
1332 | * @param array $map
1333 | */
1334 | public function registerSeverityMap($map)
1335 | {
1336 | $this->severity_map = $map;
1337 | }
1338 |
1339 | /**
1340 | * Convenience function for setting a user's ID and Email
1341 | *
1342 | * @deprecated
1343 | * @param string $id User's ID
1344 | * @param string|null $email User's email
1345 | * @param array $data Additional user data
1346 | * @codeCoverageIgnore
1347 | */
1348 | public function set_user_data($id, $email = null, $data = array())
1349 | {
1350 | $user = array('id' => $id);
1351 | if (isset($email)) {
1352 | $user['email'] = $email;
1353 | }
1354 | $this->user_context(array_merge($user, $data));
1355 | }
1356 |
1357 | public function onShutdown()
1358 | {
1359 | if (!defined('RAVEN_CLIENT_END_REACHED')) {
1360 | define('RAVEN_CLIENT_END_REACHED', true);
1361 | }
1362 | $this->sendUnsentErrors();
1363 | if ($this->curl_method == 'async') {
1364 | $this->_curl_handler->join();
1365 | }
1366 | }
1367 |
1368 | /**
1369 | * Sets user context.
1370 | *
1371 | * @param array $data Associative array of user data
1372 | * @param bool $merge Merge existing context with new context
1373 | */
1374 | public function user_context($data, $merge = true)
1375 | {
1376 | if ($merge && $this->context->user !== null) {
1377 | // bail if data is null
1378 | if (!$data) {
1379 | return;
1380 | }
1381 | $this->context->user = array_merge($this->context->user, $data);
1382 | } else {
1383 | $this->context->user = $data;
1384 | }
1385 | }
1386 |
1387 | /**
1388 | * Appends tags context.
1389 | *
1390 | * @param array $data Associative array of tags
1391 | */
1392 | public function tags_context($data)
1393 | {
1394 | $this->context->tags = array_merge($this->context->tags, $data);
1395 | }
1396 |
1397 | /**
1398 | * Appends additional context.
1399 | *
1400 | * @param array $data Associative array of extra data
1401 | */
1402 | public function extra_context($data)
1403 | {
1404 | $this->context->extra = array_merge($this->context->extra, $data);
1405 | }
1406 |
1407 | /**
1408 | * @param array $processors
1409 | */
1410 | public function setProcessors(array $processors)
1411 | {
1412 | $this->processors = $processors;
1413 | }
1414 |
1415 | /**
1416 | * @return object|null
1417 | */
1418 | public function getLastSentryError()
1419 | {
1420 | return $this->_last_sentry_error;
1421 | }
1422 |
1423 | /**
1424 | * @return bool
1425 | */
1426 | public function getShutdownFunctionHasBeenSet()
1427 | {
1428 | return $this->_shutdown_function_has_been_set;
1429 | }
1430 |
1431 | public function close_curl_resource()
1432 | {
1433 | if (!is_null($this->_curl_instance)) {
1434 | curl_close($this->_curl_instance);
1435 | $this->_curl_instance = null;
1436 | }
1437 | }
1438 | }
1439 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Compat.php:
--------------------------------------------------------------------------------
1 | $size) {
56 | $key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
57 | } else {
58 | $key = str_pad($key, $size, chr(0x00));
59 | }
60 |
61 | $keyLastPos = strlen($key) - 1;
62 | for ($i = 0; $i < $keyLastPos; $i++) {
63 | $opad[$i] = $opad[$i] ^ $key[$i];
64 | $ipad[$i] = $ipad[$i] ^ $key[$i];
65 | }
66 |
67 | $output = $algo($opad.pack($pack, $algo($ipad.$data)));
68 |
69 | return ($raw_output) ? pack($pack, $output) : $output;
70 | }
71 |
72 | /**
73 | * Note that we discard the options given to be compatible
74 | * with PHP < 5.3
75 | *
76 | * @param mixed $value
77 | * @param int $options
78 | * @param int $depth Set the maximum depth
79 | * @return string
80 | */
81 | public static function json_encode($value, $options = 0, $depth = 512)
82 | {
83 | if (function_exists('json_encode')) {
84 | if (version_compare(PHP_VERSION, '5.3.0', '<')) {
85 | return json_encode($value);
86 | } elseif (version_compare(PHP_VERSION, '5.5.0', '<')) {
87 | return json_encode($value, $options);
88 | } else {
89 | return json_encode($value, $options, $depth);
90 | }
91 | }
92 |
93 | // @codeCoverageIgnoreStart
94 | return self::_json_encode($value, $depth);
95 | // @codeCoverageIgnoreEnd
96 | }
97 |
98 | /**
99 | * @param mixed $value
100 | * @param int $depth Set the maximum depth
101 | * @return string|false
102 | */
103 | public static function _json_encode($value, $depth = 513)
104 | {
105 | if (ini_get('xdebug.extended_info') !== false) {
106 | ini_set('xdebug.max_nesting_level', 2048);
107 | }
108 | return self::_json_encode_lowlevel($value, $depth);
109 | }
110 |
111 | /**
112 | * Implementation taken from
113 | * http://www.mike-griffiths.co.uk/php-json_encode-alternative/
114 | *
115 | * @param mixed $value
116 | * @param int $depth Set the maximum depth
117 | * @return string|false
118 | */
119 | private static function _json_encode_lowlevel($value, $depth)
120 | {
121 | static $jsonReplaces = array(
122 | array('\\', '/', "\n", "\t", "\r", "\f", '"'),
123 | array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\f', '\"'));
124 |
125 | if (is_null($value)) {
126 | return 'null';
127 | }
128 | if ($value === false) {
129 | return 'false';
130 | }
131 | if ($value === true) {
132 | return 'true';
133 | }
134 |
135 | if (is_scalar($value)) {
136 | // Always use '.' for floats.
137 | if (is_float($value)) {
138 | return floatval(str_replace(',', '.', strval($value)));
139 | }
140 | if (is_string($value)) {
141 | return sprintf('"%s"',
142 | str_replace($jsonReplaces[0], $jsonReplaces[1], $value));
143 | } else {
144 | return $value;
145 | }
146 | } elseif ($depth <= 1) {
147 | return false;
148 | }
149 |
150 | $isList = true;
151 | for ($i = 0, reset($value); $i $v) {
170 | $this_value = self::_json_encode($v, $depth - 1);
171 | if ($this_value === false) {
172 | return false;
173 | }
174 | $result[] = self::_json_encode($k, $depth - 1).':'.$this_value;
175 | }
176 |
177 | return '{' . join(',', $result) . '}';
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Context.php:
--------------------------------------------------------------------------------
1 | clear();
25 | }
26 |
27 | /**
28 | * Clean up existing context.
29 | */
30 | public function clear()
31 | {
32 | $this->tags = array();
33 | $this->extra = array();
34 | $this->user = null;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/CurlHandler.php:
--------------------------------------------------------------------------------
1 | options = $options;
28 | $this->multi_handle = curl_multi_init();
29 | $this->requests = array();
30 | $this->join_timeout = 5;
31 |
32 | register_shutdown_function(array($this, 'join'));
33 | }
34 |
35 | public function __destruct()
36 | {
37 | $this->join();
38 | }
39 |
40 | public function enqueue($url, $data = null, $headers = array())
41 | {
42 | $ch = curl_init();
43 |
44 | $new_headers = array();
45 | foreach ($headers as $key => $value) {
46 | array_push($new_headers, $key .': '. $value);
47 | }
48 | // XXX(dcramer): Prevent 100-continue response form server (Fixes GH-216)
49 | $new_headers[] = 'Expect:';
50 |
51 | curl_setopt($ch, CURLOPT_HTTPHEADER, $new_headers);
52 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
53 | curl_setopt($ch, CURLOPT_URL, $url);
54 |
55 | curl_setopt_array($ch, $this->options);
56 |
57 | if (isset($data)) {
58 | curl_setopt($ch, CURLOPT_POST, true);
59 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
60 | }
61 |
62 | curl_multi_add_handle($this->multi_handle, $ch);
63 |
64 | $fd = (int)$ch;
65 | $this->requests[$fd] = 1;
66 |
67 | $this->select();
68 |
69 | return $fd;
70 | }
71 |
72 | public function join($timeout = null)
73 | {
74 | if (!isset($timeout)) {
75 | $timeout = $this->join_timeout;
76 | }
77 | $start = time();
78 | do {
79 | $this->select();
80 | if (count($this->requests) === 0) {
81 | break;
82 | }
83 | usleep(10000);
84 | } while ($timeout !== 0 && time() - $start < $timeout);
85 | }
86 |
87 | /**
88 | * @doc http://php.net/manual/en/function.curl-multi-exec.php
89 | */
90 | protected function select()
91 | {
92 | do {
93 | $mrc = curl_multi_exec($this->multi_handle, $active);
94 | } while ($mrc == CURLM_CALL_MULTI_PERFORM);
95 |
96 | while ($active && $mrc == CURLM_OK) {
97 | if (curl_multi_select($this->multi_handle) !== -1) {
98 | do {
99 | $mrc = curl_multi_exec($this->multi_handle, $active);
100 | } while ($mrc == CURLM_CALL_MULTI_PERFORM);
101 | } else {
102 | return;
103 | }
104 | }
105 |
106 | while ($info = curl_multi_info_read($this->multi_handle)) {
107 | $ch = $info['handle'];
108 | $fd = (int)$ch;
109 |
110 | curl_multi_remove_handle($this->multi_handle, $ch);
111 |
112 | if (!isset($this->requests[$fd])) {
113 | return;
114 | }
115 |
116 | unset($this->requests[$fd]);
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/ErrorHandler.php:
--------------------------------------------------------------------------------
1 | registerExceptionHandler();
18 | * $error_handler->registerErrorHandler();
19 | * $error_handler->registerShutdownFunction();
20 | *
21 | * @package raven
22 | */
23 |
24 | // TODO(dcramer): deprecate default error types in favor of runtime configuration
25 | // unless a reason can be determined that making them dynamic is better. They
26 | // currently are not used outside of the fatal handler.
27 | class Raven_ErrorHandler
28 | {
29 | protected $old_exception_handler;
30 | protected $call_existing_exception_handler = false;
31 | protected $old_error_handler;
32 | protected $call_existing_error_handler = false;
33 | protected $reservedMemory;
34 | /** @var Raven_Client */
35 | protected $client;
36 | protected $send_errors_last = false;
37 | protected $fatal_error_types = array(
38 | E_ERROR,
39 | E_PARSE,
40 | E_CORE_ERROR,
41 | E_CORE_WARNING,
42 | E_COMPILE_ERROR,
43 | E_COMPILE_WARNING,
44 | E_STRICT,
45 | );
46 |
47 | /**
48 | * @var array
49 | * Error types which should be processed by the handler.
50 | * A 'null' value implies "whatever error_reporting is at time of error".
51 | */
52 | protected $error_types = null;
53 |
54 | public function __construct($client, $send_errors_last = false, $error_types = null,
55 | $__error_types = null)
56 | {
57 | // support legacy fourth argument for error types
58 | if ($error_types === null) {
59 | $error_types = $__error_types;
60 | }
61 |
62 | $this->client = $client;
63 | $this->error_types = $error_types;
64 | $this->fatal_error_types = array_reduce($this->fatal_error_types, array($this, 'bitwiseOr'));
65 | if ($send_errors_last) {
66 | $this->send_errors_last = true;
67 | $this->client->store_errors_for_bulk_send = true;
68 | }
69 | }
70 |
71 | public function bitwiseOr($a, $b)
72 | {
73 | return $a | $b;
74 | }
75 |
76 | public function handleException($e, $isError = false, $vars = null)
77 | {
78 | $e->event_id = $this->client->captureException($e, null, null, $vars);
79 |
80 | if (!$isError && $this->call_existing_exception_handler) {
81 | if ($this->old_exception_handler !== null) {
82 | call_user_func($this->old_exception_handler, $e);
83 | } else {
84 | throw $e;
85 | }
86 | }
87 | }
88 |
89 | public function handleError($type, $message, $file = '', $line = 0, $context = array())
90 | {
91 | // http://php.net/set_error_handler
92 | // The following error types cannot be handled with a user defined function: E_ERROR,
93 | // E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and
94 | // most of E_STRICT raised in the file where set_error_handler() is called.
95 |
96 | if (error_reporting() !== 0) {
97 | $error_types = $this->error_types;
98 | if ($error_types === null) {
99 | $error_types = error_reporting();
100 | }
101 | if ($error_types & $type) {
102 | $e = new ErrorException($message, 0, $type, $file, $line);
103 | $this->handleException($e, true, $context);
104 | }
105 | }
106 |
107 | if ($this->call_existing_error_handler) {
108 | if ($this->old_error_handler !== null) {
109 | return call_user_func(
110 | $this->old_error_handler,
111 | $type,
112 | $message,
113 | $file,
114 | $line,
115 | $context
116 | );
117 | } else {
118 | return false;
119 | }
120 | }
121 | return true;
122 | }
123 |
124 | public function handleFatalError()
125 | {
126 | unset($this->reservedMemory);
127 |
128 | if (null === $error = error_get_last()) {
129 | return;
130 | }
131 |
132 | if ($this->shouldCaptureFatalError($error['type'])) {
133 | $e = new ErrorException(
134 | @$error['message'], 0, @$error['type'],
135 | @$error['file'], @$error['line']
136 | );
137 | $this->handleException($e, true);
138 | }
139 | }
140 |
141 | public function shouldCaptureFatalError($type)
142 | {
143 | return $type & $this->fatal_error_types;
144 | }
145 |
146 | /**
147 | * Register a handler which will intercept unhandled exceptions and report them to the
148 | * associated Sentry client.
149 | *
150 | * @param bool $call_existing Call any existing exception handlers after processing
151 | * this instance.
152 | * @return Raven_ErrorHandler
153 | */
154 | public function registerExceptionHandler($call_existing = true)
155 | {
156 | $this->old_exception_handler = set_exception_handler(array($this, 'handleException'));
157 | $this->call_existing_exception_handler = $call_existing;
158 | return $this;
159 | }
160 |
161 | /**
162 | * Register a handler which will intercept standard PHP errors and report them to the
163 | * associated Sentry client.
164 | *
165 | * @param bool $call_existing Call any existing errors handlers after processing
166 | * this instance.
167 | * @param array $error_types All error types that should be sent.
168 | * @return Raven_ErrorHandler
169 | */
170 | public function registerErrorHandler($call_existing = true, $error_types = null)
171 | {
172 | if ($error_types !== null) {
173 | $this->error_types = $error_types;
174 | }
175 | $this->old_error_handler = set_error_handler(array($this, 'handleError'), E_ALL);
176 | $this->call_existing_error_handler = $call_existing;
177 | return $this;
178 | }
179 |
180 | /**
181 | * Register a fatal error handler, which will attempt to capture errors which
182 | * shutdown the PHP process. These are commonly things like OOM or timeouts.
183 | *
184 | * @param int $reservedMemorySize Number of kilobytes memory space to reserve,
185 | * which is utilized when handling fatal errors.
186 | * @return Raven_ErrorHandler
187 | */
188 | public function registerShutdownFunction($reservedMemorySize = 10)
189 | {
190 | register_shutdown_function(array($this, 'handleFatalError'));
191 |
192 | $this->reservedMemory = str_repeat('x', 1024 * $reservedMemorySize);
193 | return $this;
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Exception.php:
--------------------------------------------------------------------------------
1 | client = $client;
28 | }
29 |
30 | /**
31 | * Override the default processor options
32 | *
33 | * @param array $options Associative array of processor options
34 | */
35 | public function setProcessorOptions(array $options)
36 | {
37 | }
38 |
39 | /**
40 | * Process and sanitize data, modifying the existing value if necessary.
41 | *
42 | * @param array $data Array of log data
43 | */
44 | abstract public function process(&$data);
45 | }
46 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Processor/RemoveCookiesProcessor.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | final class Raven_Processor_RemoveCookiesProcessor extends Raven_Processor
19 | {
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function process(&$data)
24 | {
25 | if (isset($data['request'])) {
26 | if (isset($data['request']['cookies'])) {
27 | $data['request']['cookies'] = self::STRING_MASK;
28 | }
29 |
30 | if (isset($data['request']['headers']) && isset($data['request']['headers']['Cookie'])) {
31 | $data['request']['headers']['Cookie'] = self::STRING_MASK;
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Processor/RemoveHttpBodyProcessor.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | final class Raven_Processor_RemoveHttpBodyProcessor extends Raven_Processor
20 | {
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function process(&$data)
25 | {
26 | if (isset($data['request'], $data['request']['method']) && in_array(strtoupper($data['request']['method']), array('POST', 'PUT', 'PATCH', 'DELETE'))) {
27 | $data['request']['data'] = self::STRING_MASK;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Processor/SanitizeDataProcessor.php:
--------------------------------------------------------------------------------
1 | fields_re = self::FIELDS_RE;
36 | $this->values_re = self::VALUES_RE;
37 | $this->session_cookie_name = ini_get('session.name');
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | public function setProcessorOptions(array $options)
44 | {
45 | if (isset($options['fields_re'])) {
46 | $this->fields_re = $options['fields_re'];
47 | }
48 |
49 | if (isset($options['values_re'])) {
50 | $this->values_re = $options['values_re'];
51 | }
52 | }
53 |
54 | /**
55 | * Replace any array values with our mask if the field name or the value matches a respective regex
56 | *
57 | * @param mixed $item Associative array value
58 | * @param string $key Associative array key
59 | */
60 | public function sanitize(&$item, $key)
61 | {
62 | if (empty($item)) {
63 | return;
64 | }
65 |
66 | if (preg_match($this->values_re, $item)) {
67 | $item = self::STRING_MASK;
68 | }
69 |
70 | if (empty($key)) {
71 | return;
72 | }
73 |
74 | if (preg_match($this->fields_re, $key)) {
75 | $item = self::STRING_MASK;
76 | }
77 | }
78 |
79 | public function sanitizeException(&$data)
80 | {
81 | foreach ($data['exception']['values'] as &$value) {
82 | $this->sanitizeStacktrace($value['stacktrace']);
83 | }
84 | }
85 |
86 | public function sanitizeHttp(&$data)
87 | {
88 | $http = &$data['request'];
89 | if (!empty($http['cookies']) && is_array($http['cookies'])) {
90 | $cookies = &$http['cookies'];
91 | if (!empty($cookies[$this->session_cookie_name])) {
92 | $cookies[$this->session_cookie_name] = self::STRING_MASK;
93 | }
94 | }
95 | if (!empty($http['data']) && is_array($http['data'])) {
96 | array_walk_recursive($http['data'], array($this, 'sanitize'));
97 | }
98 | }
99 |
100 | public function sanitizeStacktrace(&$data)
101 | {
102 | foreach ($data['frames'] as &$frame) {
103 | if (empty($frame['vars'])) {
104 | continue;
105 | }
106 | array_walk_recursive($frame['vars'], array($this, 'sanitize'));
107 | }
108 | }
109 |
110 | /**
111 | * {@inheritdoc}
112 | */
113 | public function process(&$data)
114 | {
115 | if (!empty($data['exception'])) {
116 | $this->sanitizeException($data);
117 | }
118 | if (!empty($data['stacktrace'])) {
119 | $this->sanitizeStacktrace($data['stacktrace']);
120 | }
121 | if (!empty($data['request'])) {
122 | $this->sanitizeHttp($data);
123 | }
124 | if (!empty($data['extra'])) {
125 | array_walk_recursive($data['extra'], array($this, 'sanitize'));
126 | }
127 | }
128 |
129 | /**
130 | * @return string
131 | */
132 | public function getFieldsRe()
133 | {
134 | return $this->fields_re;
135 | }
136 |
137 | /**
138 | * @param string $fields_re
139 | */
140 | public function setFieldsRe($fields_re)
141 | {
142 | $this->fields_re = $fields_re;
143 | }
144 |
145 | /**
146 | * @return string
147 | */
148 | public function getValuesRe()
149 | {
150 | return $this->values_re;
151 | }
152 |
153 | /**
154 | * @param string $values_re
155 | */
156 | public function setValuesRe($values_re)
157 | {
158 | $this->values_re = $values_re;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Processor/SanitizeHttpHeadersProcessor.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | final class Raven_Processor_SanitizeHttpHeadersProcessor extends Raven_Processor
19 | {
20 | /**
21 | * @var string[] $httpHeadersToSanitize The list of HTTP headers to sanitize
22 | */
23 | private $httpHeadersToSanitize = array();
24 |
25 | /**
26 | * {@inheritdoc}
27 | */
28 | public function __construct(Raven_Client $client)
29 | {
30 | parent::__construct($client);
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function setProcessorOptions(array $options)
37 | {
38 | $this->httpHeadersToSanitize = array_merge($this->getDefaultHeaders(), isset($options['sanitize_http_headers']) ? $options['sanitize_http_headers'] : array());
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function process(&$data)
45 | {
46 | if (isset($data['request']) && isset($data['request']['headers'])) {
47 | foreach ($data['request']['headers'] as $header => &$value) {
48 | if (in_array($header, $this->httpHeadersToSanitize)) {
49 | $value = self::STRING_MASK;
50 | }
51 | }
52 | }
53 | }
54 |
55 | /**
56 | * Gets the list of default headers that must be sanitized.
57 | *
58 | * @return string[]
59 | */
60 | private function getDefaultHeaders()
61 | {
62 | return array('Authorization', 'Proxy-Authorization', 'X-Csrf-Token', 'X-CSRFToken', 'X-XSRF-TOKEN');
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Processor/SanitizeStacktraceProcessor.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Raven_Processor_SanitizeStacktraceProcessor extends Raven_Processor
19 | {
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function process(&$data)
24 | {
25 | if (!isset($data['exception'], $data['exception']['values'])) {
26 | return;
27 | }
28 |
29 | foreach ($data['exception']['values'] as &$exception) {
30 | if (!isset($exception['stacktrace'])) {
31 | continue;
32 | }
33 |
34 | foreach ($exception['stacktrace']['frames'] as &$frame) {
35 | unset($frame['pre_context'], $frame['context_line'], $frame['post_context']);
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/ReprSerializer.php:
--------------------------------------------------------------------------------
1 | serializeString($value);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/SanitizeDataProcessor.php:
--------------------------------------------------------------------------------
1 | mb_detect_order = $mb_detect_order;
55 | }
56 | }
57 |
58 | /**
59 | * Serialize an object (recursively) into something safe for data
60 | * sanitization and encoding.
61 | *
62 | * @param mixed $value
63 | * @param int $max_depth
64 | * @param int $_depth
65 | * @return string|bool|double|int|null|object|array
66 | */
67 | public function serialize($value, $max_depth = 3, $_depth = 0)
68 | {
69 | $className = is_object($value) ? get_class($value) : null;
70 | $toArray = is_array($value) || $className === 'stdClass';
71 | if ($toArray && $_depth < $max_depth) {
72 | $new = array();
73 | foreach ($value as $k => $v) {
74 | $new[$this->serializeValue($k)] = $this->serialize($v, $max_depth, $_depth + 1);
75 | }
76 |
77 | return $new;
78 | }
79 | return $this->serializeValue($value);
80 | }
81 |
82 | protected function serializeString($value)
83 | {
84 | $value = (string) $value;
85 | if (function_exists('mb_detect_encoding')
86 | && function_exists('mb_convert_encoding')
87 | ) {
88 | // we always guarantee this is coerced, even if we can't detect encoding
89 | if ($currentEncoding = mb_detect_encoding($value, $this->mb_detect_order)) {
90 | $value = mb_convert_encoding($value, 'UTF-8', $currentEncoding);
91 | } else {
92 | $value = mb_convert_encoding($value, 'UTF-8');
93 | }
94 | }
95 |
96 | if (strlen($value) > 1024) {
97 | $value = substr($value, 0, 1014) . ' {clipped}';
98 | }
99 |
100 | return $value;
101 | }
102 |
103 | /**
104 | * @param mixed $value
105 | * @return string|bool|double|int|null
106 | */
107 | protected function serializeValue($value)
108 | {
109 | if (is_null($value) || is_bool($value) || is_float($value) || is_integer($value)) {
110 | return $value;
111 | } elseif (is_object($value) || gettype($value) == 'object') {
112 | return 'Object '.get_class($value);
113 | } elseif (is_resource($value)) {
114 | return 'Resource '.get_resource_type($value);
115 | } elseif (is_array($value)) {
116 | return 'Array of length ' . count($value);
117 | } else {
118 | return $this->serializeString($value);
119 | }
120 | }
121 |
122 |
123 | /**
124 | * @return string
125 | * @codeCoverageIgnore
126 | */
127 | public function getMbDetectOrder()
128 | {
129 | return $this->mb_detect_order;
130 | }
131 |
132 | /**
133 | * @param string $mb_detect_order
134 | *
135 | * @return Raven_Serializer
136 | * @codeCoverageIgnore
137 | */
138 | public function setMbDetectOrder($mb_detect_order)
139 | {
140 | $this->mb_detect_order = $mb_detect_order;
141 |
142 | return $this;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Stacktrace.php:
--------------------------------------------------------------------------------
1 | $context['filename'],
73 | 'lineno' => (int) $context['lineno'],
74 | 'function' => isset($nextframe['function']) ? $nextframe['function'] : null,
75 | 'pre_context' => $serializer->serialize($context['prefix']),
76 | 'context_line' => $serializer->serialize($context['line']),
77 | 'post_context' => $serializer->serialize($context['suffix']),
78 | );
79 |
80 | // detect in_app based on app path
81 | if ($app_path) {
82 | $norm_abs_path = @realpath($abs_path) ?: $abs_path;
83 | $in_app = (bool)(substr($norm_abs_path, 0, strlen($app_path)) === $app_path);
84 | if ($in_app && $excluded_app_paths) {
85 | foreach ($excluded_app_paths as $path) {
86 | if (substr($norm_abs_path, 0, strlen($path)) === $path) {
87 | $in_app = false;
88 | break;
89 | }
90 | }
91 | }
92 | $data['in_app'] = $in_app;
93 | }
94 |
95 | // dont set this as an empty array as PHP will treat it as a numeric array
96 | // instead of a mapping which goes against the defined Sentry spec
97 | if (!empty($vars)) {
98 | $cleanVars = array();
99 | foreach ($vars as $key => $value) {
100 | $value = $reprSerializer->serialize($value);
101 | if (is_string($value) || is_numeric($value)) {
102 | $cleanVars[(string)$key] = substr($value, 0, $frame_var_limit);
103 | } else {
104 | $cleanVars[(string)$key] = $value;
105 | }
106 | }
107 | $data['vars'] = $cleanVars;
108 | }
109 |
110 | $result[] = $data;
111 | }
112 |
113 | return array_reverse($result);
114 | }
115 |
116 | public static function get_default_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT)
117 | {
118 | if (!isset($frame['args'])) {
119 | return array();
120 | }
121 |
122 | $i = 1;
123 | $args = array();
124 | foreach ($frame['args'] as $arg) {
125 | $args['param'.$i] = self::serialize_argument($arg, $frame_arg_limit);
126 | $i++;
127 | }
128 | return $args;
129 | }
130 |
131 | public static function get_frame_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT)
132 | {
133 | if (!isset($frame['args'])) {
134 | return array();
135 | }
136 |
137 | // The reflection API seems more appropriate if we associate it with the frame
138 | // where the function is actually called (since we're treating them as function context)
139 | if (!isset($frame['function'])) {
140 | return self::get_default_context($frame, $frame_arg_limit);
141 | }
142 | if (strpos($frame['function'], '__lambda_func') !== false) {
143 | return self::get_default_context($frame, $frame_arg_limit);
144 | }
145 | if (isset($frame['class']) && $frame['class'] == 'Closure') {
146 | return self::get_default_context($frame, $frame_arg_limit);
147 | }
148 | if (strpos($frame['function'], '{closure}') !== false) {
149 | return self::get_default_context($frame, $frame_arg_limit);
150 | }
151 | if (in_array($frame['function'], self::$statements)) {
152 | if (empty($frame['args'])) {
153 | // No arguments
154 | return array();
155 | } else {
156 | // Sanitize the file path
157 | return array(
158 | 'param1' => self::serialize_argument($frame['args'][0], $frame_arg_limit),
159 | );
160 | }
161 | }
162 | try {
163 | if (isset($frame['class'])) {
164 | if (method_exists($frame['class'], $frame['function'])) {
165 | $reflection = new ReflectionMethod($frame['class'], $frame['function']);
166 | } elseif ($frame['type'] === '::') {
167 | $reflection = new ReflectionMethod($frame['class'], '__callStatic');
168 | } else {
169 | $reflection = new ReflectionMethod($frame['class'], '__call');
170 | }
171 | } else {
172 | $reflection = new ReflectionFunction($frame['function']);
173 | }
174 | } catch (ReflectionException $e) {
175 | return self::get_default_context($frame, $frame_arg_limit);
176 | }
177 |
178 | $params = $reflection->getParameters();
179 |
180 | $args = array();
181 | foreach ($frame['args'] as $i => $arg) {
182 | $arg = self::serialize_argument($arg, $frame_arg_limit);
183 | if (isset($params[$i])) {
184 | // Assign the argument by the parameter name
185 | $args[$params[$i]->name] = $arg;
186 | } else {
187 | $args['param'.$i] = $arg;
188 | }
189 | }
190 |
191 | return $args;
192 | }
193 |
194 | private static function serialize_argument($arg, $frame_arg_limit)
195 | {
196 | if (is_array($arg)) {
197 | $_arg = array();
198 | foreach ($arg as $key => $value) {
199 | if (is_string($value) || is_numeric($value)) {
200 | $_arg[$key] = substr($value, 0, $frame_arg_limit);
201 | } else {
202 | $_arg[$key] = $value;
203 | }
204 | }
205 | return $_arg;
206 | } elseif (is_string($arg) || is_numeric($arg)) {
207 | return substr($arg, 0, $frame_arg_limit);
208 | } else {
209 | return $arg;
210 | }
211 | }
212 |
213 | private static function strip_prefixes($filename, $prefixes)
214 | {
215 | if ($prefixes === null) {
216 | return $filename;
217 | }
218 | foreach ($prefixes as $prefix) {
219 | if (substr($filename, 0, strlen($prefix)) === $prefix) {
220 | return substr($filename, strlen($prefix));
221 | }
222 | }
223 | return $filename;
224 | }
225 |
226 | private static function read_source_file($filename, $lineno, $context_lines = 5)
227 | {
228 | $frame = array(
229 | 'prefix' => array(),
230 | 'line' => '',
231 | 'suffix' => array(),
232 | 'filename' => $filename,
233 | 'lineno' => $lineno,
234 | );
235 |
236 | if ($filename === null || $lineno === null) {
237 | return $frame;
238 | }
239 |
240 | // Code which is eval'ed have a modified filename.. Extract the
241 | // correct filename + linenumber from the string.
242 | $matches = array();
243 | $matched = preg_match("/^(.*?)\\((\\d+)\\) : eval\\(\\)'d code$/",
244 | $filename, $matches);
245 | if ($matched) {
246 | $frame['filename'] = $filename = $matches[1];
247 | $frame['lineno'] = $lineno = $matches[2];
248 | }
249 |
250 | // In the case of an anonymous function, the filename is sent as:
251 | // "() : runtime-created function"
252 | // Extract the correct filename + linenumber from the string.
253 | $matches = array();
254 | $matched = preg_match("/^(.*?)\\((\\d+)\\) : runtime-created function$/",
255 | $filename, $matches);
256 | if ($matched) {
257 | $frame['filename'] = $filename = $matches[1];
258 | $frame['lineno'] = $lineno = $matches[2];
259 | }
260 |
261 | try {
262 | $file = new SplFileObject($filename);
263 | $target = max(0, ($lineno - ($context_lines + 1)));
264 | $file->seek($target);
265 | $cur_lineno = $target+1;
266 | while (!$file->eof()) {
267 | $line = rtrim($file->current(), "\r\n");
268 | if ($cur_lineno == $lineno) {
269 | $frame['line'] = $line;
270 | } elseif ($cur_lineno < $lineno) {
271 | $frame['prefix'][] = $line;
272 | } elseif ($cur_lineno > $lineno) {
273 | $frame['suffix'][] = $line;
274 | }
275 | $cur_lineno++;
276 | if ($cur_lineno > $lineno + $context_lines) {
277 | break;
278 | }
279 | $file->next();
280 | }
281 | } catch (RuntimeException $exc) {
282 | return $frame;
283 | }
284 |
285 | return $frame;
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/TransactionStack.php:
--------------------------------------------------------------------------------
1 | stack = array();
16 | }
17 |
18 | public function clear()
19 | {
20 | $this->stack = array();
21 | }
22 |
23 | public function peek()
24 | {
25 | $len = count($this->stack);
26 | if ($len === 0) {
27 | return null;
28 | }
29 | return $this->stack[$len - 1];
30 | }
31 |
32 | public function push($context)
33 | {
34 | $this->stack[] = $context;
35 | }
36 |
37 | /** @noinspection PhpInconsistentReturnPointsInspection
38 | * @param string|null $context
39 | * @return mixed
40 | */
41 | public function pop($context = null)
42 | {
43 | if (!$context) {
44 | return array_pop($this->stack);
45 | }
46 | while (!empty($this->stack)) {
47 | if (array_pop($this->stack) === $context) {
48 | return $context;
49 | }
50 | }
51 | // @codeCoverageIgnoreStart
52 | }
53 | // @codeCoverageIgnoreEnd
54 | }
55 |
--------------------------------------------------------------------------------
/lib/sentry/sentry/lib/Raven/Util.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 | ./test/Raven/
16 |
17 |
18 |
19 |
20 |
21 | ./lib/Raven/
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/modman:
--------------------------------------------------------------------------------
1 | app/code/community/Hackathon/LoggerSentry app/code/community/Hackathon/LoggerSentry
2 | app/etc/modules/Hackathon_LoggerSentry.xml app/etc/modules/Hackathon_LoggerSentry.xml
3 | lib/sentry lib/sentry
--------------------------------------------------------------------------------