diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d65a26..7206415 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to `LERN` will be documented in this file. +### 3.0.0 +- When enabled in the config file you can now collect: + - user_id - The id of the currently logged in user. + - method - Then method of the request: GET, POST, DELETE, PUT, etc... + - url - The full URL of the request. + - data - The input data of the request, if any. + +*__Reason for Major release: 3.0.0 introduces a new migration file and structure changes that could cause issues for 2.x users__* + ### 2.3.0 - Added support for [Twilio](https://www.twilio.com/) an SMS messaging service. diff --git a/README.md b/README.md index 693201d..a92abe2 100755 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ Currently supported notification channels via [Monolog](https://github.com/Selda - [Plivo](https://www.plivo.com/) an SMS messaging service. - [Twilio](https://www.twilio.com/) an SMS messaging service. +## Migrating from `2.x` to `3.x` +Version 3.x introduces the ability to collect more information from the error such as the user_id, url, method, and input data. In order to use 3.x you will need to copy over the new [config file](https://github.com/tylercd100/lern/blob/master/config/lern.php), the migration file and then migrate it. +```php +# This will only copy over the migration file. For the config file you can either include the --force flag (Which will overwrite it) or copy it manually from github +php artisan vendor:publish --provider="Tylercd100\LERN\LERNServiceProvider" +php artisan migrate +``` + ## Installation Install via [composer](https://getcomposer.org/) - In the terminal: @@ -32,12 +40,12 @@ Now add the following to the `providers` array in your `config/app.php` ```php Tylercd100\LERN\LERNServiceProvider::class ``` + and this to the `aliases` array in `config/app.php` ```php "LERN" => "Tylercd100\LERN\Facades\LERN", ``` - Then you will need to run these commands in the terminal in order to copy the config and migration files ```bash php artisan vendor:publish --provider="Tylercd100\LERN\LERNServiceProvider" @@ -79,7 +87,21 @@ You can call `LERN::record($exception);` to record an Exception to the database. To query any Exception that has been recorded you can use `ExceptionModel` which is an Eloquent Model ```php use Tylercd100\LERN\Model\ExceptionModel; -$mostRecentException = ExceptionModel::orderBy('created_at','DESC')->first() +$mostRecentException = ExceptionModel::orderBy('created_at','DESC')->first(); +``` + +To change what is recorded in to the database take a look at `config/lern.php` +```php +'record'=>[ + 'table'=>'vendor_tylercd100_lern_exceptions', + 'collect'=>[ + 'method'=>false, //When true it will collect GET, POST, DELETE, PUT, etc... + 'data'=>false, //When true it will collect Input data + 'status_code'=>true, + 'user_id'=>false, + 'url'=>false, + ], +], ``` ### Notifications diff --git a/composer.json b/composer.json index bb5e142..12364c7 100755 --- a/composer.json +++ b/composer.json @@ -37,7 +37,8 @@ }, "require-dev": { "orchestra/testbench": "^3.1.0", - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8 || ^5.0", + "doctrine/dbal": "~2.3" }, "suggest": { } diff --git a/config/lern.php b/config/lern.php index 5b37597..7a64ab5 100755 --- a/config/lern.php +++ b/config/lern.php @@ -4,6 +4,13 @@ 'record'=>[ 'table'=>'vendor_tylercd100_lern_exceptions', + 'collect'=>[ + 'method'=>false, //When true it will collect GET, POST, DELETE, PUT, etc... + 'data'=>false, //When true it will collect Input data + 'status_code'=>true, + 'user_id'=>false, + 'url'=>false, + ], ], 'notify'=>[ diff --git a/migrations/2016_03_27_000000_add_user_data_and_url_to_lern_tables.php b/migrations/2016_03_27_000000_add_user_data_and_url_to_lern_tables.php new file mode 100644 index 0000000..b5aaff6 --- /dev/null +++ b/migrations/2016_03_27_000000_add_user_data_and_url_to_lern_tables.php @@ -0,0 +1,38 @@ +integer('user_id')->nullable(); + $table->text('data')->nullable(); + $table->string('url')->nullable(); + $table->string('method')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table(config('lern.record.table'), function(Blueprint $table) { + $table->dropColumn('user_id'); + $table->dropColumn('data'); + $table->dropColumn('url'); + $table->dropColumn('method'); + }); + } + +} \ No newline at end of file diff --git a/src/Notifications/Notifier.php b/src/Components/Notifier.php old mode 100755 new mode 100644 similarity index 97% rename from src/Notifications/Notifier.php rename to src/Components/Notifier.php index 925240f..d02e1d5 --- a/src/Notifications/Notifier.php +++ b/src/Components/Notifier.php @@ -1,11 +1,11 @@ config = config('lern.record'); + } + + /** + * Records an Exception to the database + * @param Exception $e The exception you want to record + * @return ExceptionModel + */ + public function record(Exception $e) + { + $opts = [ + 'class' => get_class($e), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]; + + + $configDependant = ['user_id', 'status_code', 'method', 'data', 'url']; + + foreach ($configDependant as $key) { + if ($this->canCollect($key)) { + $opts[$key] = $this->collect($key, $e); + } + } + + return ExceptionModel::create($opts); + } + + /** + * Checks the config to see if you can collect certain information + * @param string $type the config value you want to check + * @return boolean + */ + private function canCollect($type) { + if (!empty($this->config) && !empty($this->config['collect']) && !empty($this->config['collect'][$type])) { + return $this->config['collect'][$type] === true; + } + return false; + } + + /** + * @param string $key + */ + protected function collect($key,Exception $e = null){ + switch ($key) { + case 'user_id': + return $this->getUserId(); + case 'method': + return $this->getMethod(); + case 'status_code': + return $this->getStatusCode($e); + case 'url': + return $this->getUrl(); + case 'data': + return $this->getData(); + default: + throw new Exception("{$key} is not supported! Therefore it cannot be collected!"); + } + } + + /** + * Gets the ID of the User that is logged in + * @return integer|null The ID of the User or Null if not logged in + */ + protected function getUserId() { + $user = Auth::user(); + if (is_object($user)) { + return $user->id; + } else { + return null; + } + } + + /** + * Gets the Method of the Request + * @return string|null Possible values are null or GET, POST, DELETE, PUT, etc... + */ + protected function getMethod() { + $method = Request::method(); + if (!empty($method)) { + return $method; + } else { + return null; + } + } + + /** + * Gets the input data of the Request + * @return array|null The Input data or null + */ + protected function getData() { + $data = Input::all(); + if (is_array($data)) { + return $data; + } else { + return null; + } + } + + /** + * Gets the URL of the Request + * @return string|null Returns a URL string or null + */ + protected function getUrl() { + $url = Request::url(); + if (is_string($url)) { + return $url; + } else { + return null; + } + } + + /** + * Gets the status code of the Exception + * @param Exception $e The Exception to check + * @return string|integer The status code value + */ + protected function getStatusCode(Exception $e) { + if ($e instanceof HttpExceptionInterface) { + return $e->getStatusCode(); + } else { + return 0; + } + } +} \ No newline at end of file diff --git a/src/Notifications/MonologHandlerFactory.php b/src/Factories/MonologHandlerFactory.php similarity index 99% rename from src/Notifications/MonologHandlerFactory.php rename to src/Factories/MonologHandlerFactory.php index 6218e55..475ae63 100755 --- a/src/Notifications/MonologHandlerFactory.php +++ b/src/Factories/MonologHandlerFactory.php @@ -1,6 +1,6 @@ notifier = $notifier; + + if (empty($recorder)) { + $recorder = new Recorder(); + } + $this->recorder = $recorder; } /** @@ -43,25 +59,12 @@ public function handle(Exception $e) /** * Stores the exception in the database * @param Exception $e The exception to use - * @return ExceptionModel the recorded Eloquent Model + * @return \Tylercd100\LERN\Models\ExceptionModel The recorded Exception as an Eloquent Model */ public function record(Exception $e) { $this->exception = $e; - $opts = [ - 'class' => get_class($e), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'code' => $e->getCode(), - 'message' => $e->getMessage(), - 'trace' => $e->getTraceAsString(), - ]; - - if ($e instanceof HttpExceptionInterface) { - $opts['status_code'] = $e->getStatusCode(); - } - - return ExceptionModel::create($opts); + return $this->recorder->record($e); } /** @@ -78,7 +81,6 @@ public function notify(Exception $e) /** * Pushes on another Monolog Handler * @param HandlerInterface $handler The handler instance to add on - * @return Notifier Returns this */ public function pushHandler(HandlerInterface $handler) { $this->notifier->pushHandler($handler); @@ -87,7 +89,7 @@ public function pushHandler(HandlerInterface $handler) { /** * Get Notifier - * @return Notifier + * @return \Tylercd100\LERN\Components\Notifier */ public function getNotifier() { @@ -96,7 +98,8 @@ public function getNotifier() /** * Set Notifier - * @param Notifier $notifier A Notifier instance to use + * @param \Tylercd100\LERN\Components\Notifier $notifier A Notifier instance to use + * @return \Tylercd100\LERN\LERN */ public function setNotifier(Notifier $notifier) { @@ -104,6 +107,26 @@ public function setNotifier(Notifier $notifier) return $this; } + /** + * Get Recorder + * @return \Tylercd100\LERN\Components\Recorder + */ + public function getRecorder() + { + return $this->recorder; + } + + /** + * Set Recorder + * @param \Tylercd100\LERN\Components\Recorder $recorder A Recorder instance to use + * @return \Tylercd100\LERN\LERN + */ + public function setRecorder(Recorder $recorder) + { + $this->recorder = $recorder; + return $this; + } + /** * Set a string or a closure to be called that will generate the message body for the notification * @param function|string $cb This closure function will be passed an Exception and must return a string diff --git a/src/LERNServiceProvider.php b/src/LERNServiceProvider.php index a937730..42e5984 100755 --- a/src/LERNServiceProvider.php +++ b/src/LERNServiceProvider.php @@ -18,6 +18,7 @@ public function boot() { $this->publishes([ __DIR__ . '/../migrations/2016_03_17_000000_create_lern_tables.php' => base_path('database/migrations/2016_03_17_000000_create_lern_tables.php'), + __DIR__ . '/../migrations/2016_03_27_000000_add_user_data_and_url_to_lern_tables.php' => base_path('database/migrations/2016_03_27_000000_add_user_data_and_url_to_lern_tables.php'), __DIR__ . '/../config/lern.php' => base_path('config/lern.php'), ]); } diff --git a/src/Models/ExceptionModel.php b/src/Models/ExceptionModel.php index a8d4c03..7167fb7 100755 --- a/src/Models/ExceptionModel.php +++ b/src/Models/ExceptionModel.php @@ -13,4 +13,12 @@ public function __construct(array $attributes = []) $this->table = config('lern.record.table'); parent::__construct($attributes); } + + public function setDataAttribute($value) { + $this->attributes['data'] = json_encode($value); + } + + public function getDataAttribute($value) { + return json_decode($value); + } } \ No newline at end of file diff --git a/tests/LERNTest.php b/tests/LERNTest.php index a8fbeb2..c64d5af 100644 --- a/tests/LERNTest.php +++ b/tests/LERNTest.php @@ -3,8 +3,8 @@ namespace Tylercd100\LERN\Tests; use Tylercd100\LERN\LERN; -use Tylercd100\LERN\Notifications\MonologHandlerFactory; -use Tylercd100\LERN\Notifications\Notifier; +use Tylercd100\LERN\Factories\MonologHandlerFactory; +use Tylercd100\LERN\Components\Notifier; use Exception; class LERNTest extends TestCase @@ -42,7 +42,7 @@ public function testRecordReturnsCorrectInstance() { } public function testItCallsNotifierSendMethod() { - $mock = $this->getMock('Tylercd100\LERN\Notifications\Notifier', array('send')); + $mock = $this->getMock('Tylercd100\LERN\Components\Notifier', array('send')); $mock->expects($this->once()) ->method('send'); $lern = new LERN($mock); @@ -50,7 +50,7 @@ public function testItCallsNotifierSendMethod() { } public function testItCallsNotifierPushHandlerMethod() { - $mock = $this->getMock('Tylercd100\LERN\Notifications\Notifier', array('pushHandler')); + $mock = $this->getMock('Tylercd100\LERN\Components\Notifier', array('pushHandler')); $mock->expects($this->once()) ->method('pushHandler'); $lern = new LERN($mock); @@ -59,7 +59,7 @@ public function testItCallsNotifierPushHandlerMethod() { } public function testItCallsNotifierSetSubjectMethod() { - $mock = $this->getMock('Tylercd100\LERN\Notifications\Notifier', array('setSubject')); + $mock = $this->getMock('Tylercd100\LERN\Components\Notifier', array('setSubject')); $mock->expects($this->once()) ->method('setSubject'); @@ -69,7 +69,7 @@ public function testItCallsNotifierSetSubjectMethod() { } public function testItCallsNotifierSetMessageMethod() { - $mock = $this->getMock('Tylercd100\LERN\Notifications\Notifier', array('setMessage')); + $mock = $this->getMock('Tylercd100\LERN\Components\Notifier', array('setMessage')); $mock->expects($this->once()) ->method('setMessage'); diff --git a/tests/MonologHandlerFactoryTest.php b/tests/MonologHandlerFactoryTest.php index af567b1..2fdd2c6 100755 --- a/tests/MonologHandlerFactoryTest.php +++ b/tests/MonologHandlerFactoryTest.php @@ -3,7 +3,7 @@ namespace Tylercd100\LERN\Tests; use Exception; -use Tylercd100\LERN\Notifications\MonologHandlerFactory; +use Tylercd100\LERN\Factories\MonologHandlerFactory; class MonologHandlerFactoryTest extends TestCase { diff --git a/tests/NotifierTest.php b/tests/NotifierTest.php index cab319c..6640436 100755 --- a/tests/NotifierTest.php +++ b/tests/NotifierTest.php @@ -3,8 +3,8 @@ namespace Tylercd100\LERN\Tests; use Exception; -use Tylercd100\LERN\Notifications\Notifier; -use Tylercd100\LERN\Notifications\MonologHandlerFactory; +use Tylercd100\LERN\Components\Notifier; +use Tylercd100\LERN\Factories\MonologHandlerFactory; class NotifierTest extends TestCase { @@ -62,7 +62,7 @@ public function testNotifierReturnsTheCorrectSubjectWhenUsingClosure(){ $this->assertEquals($result,"This is a test"); } - public function itReturnsTheCorrectSubjectWhenUsingString(){ + public function testItReturnsTheCorrectSubjectWhenUsingString(){ $this->notifier->setSubject("This is a test"); $result = $this->notifier->getSubject(new Exception); $this->assertEquals($result,"This is a test"); diff --git a/tests/TestCase.php b/tests/TestCase.php index 816f45d..0c651b3 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,7 +3,7 @@ namespace Tylercd100\LERN\Tests; use Orchestra\Testbench\TestCase as Orchestra; -use Tylercd100\LERN\Notifications\MonologHandlerFactory; +use Tylercd100\LERN\Factories\MonologHandlerFactory; class TestCase extends Orchestra { @@ -72,7 +72,7 @@ public function tearDown() protected function migrate(){ $this->artisan('migrate', [ '--database' => 'testbench', - '--realpath' => realpath(__DIR__.'/../migrations'), + '--realpath' => realpath(__DIR__.'/migrations'), ]); } @@ -104,6 +104,17 @@ protected function getEnvironmentSetUp($app) 'prefix' => '', ]); + $app['config']->set('lern.record', [ + 'table'=>'vendor_tylercd100_lern_exceptions', + 'collect'=>[ + 'method'=>true,//When true it will collect GET, POST, DELETE, PUT, etc... + 'data'=>true,//When true it will collect Input data + 'status_code'=>true, + 'user_id'=>true, + 'url'=>true, + ], + ]); + $app['config']->set('lern.notify', [ 'channel'=>'Tylercd100\LERN', 'includeExceptionStackTrace'=>true, diff --git a/tests/migrations/2016_03_17_000000_create_lern_tables_for_tests.php b/tests/migrations/2016_03_17_000000_create_lern_tables_for_tests.php new file mode 100644 index 0000000..7b4df85 --- /dev/null +++ b/tests/migrations/2016_03_17_000000_create_lern_tables_for_tests.php @@ -0,0 +1,42 @@ +increments('id')->unsigned(); + $table->string('class'); + $table->string('file'); + $table->integer('code'); + $table->integer('status_code')->default(0); + $table->integer('line'); + $table->text('message'); + $table->mediumText('trace'); + $table->timestamps(); + $table->integer('user_id')->nullable(); + $table->text('data')->nullable(); + $table->string('url')->nullable(); + $table->string('method')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop(config('lern.record.table')); + } + +} \ No newline at end of file