diff --git a/config/larvabug.php b/config/larvabug.php
index 276ec99..3c9baf0 100644
--- a/config/larvabug.php
+++ b/config/larvabug.php
@@ -2,16 +2,41 @@
return [
+ /*
+ * Id of project from larvabug.com
+ *
+ */
'project_id' => env('LB_PROJECT_ID',''),
+ /*
+ * Project secret from larvabug.com
+ *
+ */
'project_secret' => env('LB_SECRET',''),
+ /*
+ * Array of environments to enable error reporting
+ * If environment configured in the array will match with the laravel app environment
+ * then error will be reported to larvabug.com
+ *
+ */
'environment' => ['production','local'],
+ /*
+ * Mention any error that should skip from reporting to laravbug
+ * Must be mentioned as a string
+ *
+ */
'skip_errors' => [
'\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class'
],
+ /*
+ * Add request parameters to be black listed
+ * any parameter defined here will not be reported to larvabug.com and
+ * all request, session and cookies will be filtered
+ *
+ */
'blacklist' => [
'password'
]
diff --git a/resources/views/analytics.blade.php b/resources/views/analytics.blade.php
new file mode 100644
index 0000000..b1c3474
--- /dev/null
+++ b/resources/views/analytics.blade.php
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/resources/views/feedback.blade.php b/resources/views/feedback.blade.php
new file mode 100644
index 0000000..b98624a
--- /dev/null
+++ b/resources/views/feedback.blade.php
@@ -0,0 +1,517 @@
+
+
+ 500 Server Error
+
+
+
+
+
500 :(
+
Seems Like we have an internal issue, Please tell us more to batter understand the issue
+
+
+
+
+ Powered By:
LarvaBug
+
+
+
+
diff --git a/routes/api.php b/routes/api.php
index b3d9bbc..e4df666 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1 +1,4 @@
projectId = $projectId;
$this->projectSecret = $projectSecret;
@@ -39,30 +49,89 @@ public function report($exceptionData)
try {
$data_string = json_encode($exceptionData);
- $header = [
- 'Content-Type:application/json',
- 'Authorization-APP:' . $this->projectId,
- 'Authorization-KEY:' . $this->projectSecret
- ];
+ $result = $this->postRequest($data_string,self::POST_EXCEPTION);
- $ch = curl_init(self::URL);
+ if ($result &&
+ isset($result['status']) &&
+ isset($result['exceptionId']) &&
+ $result['status'] == 200
+ ){
+ app('larvabug')->setLastExceptionId($result['exceptionId']);
+ }
+
+ return true;
+ }catch (\Exception $exception) {
+ return false;
+ }
+ }
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ /**
+ * Validate env project id and secret
+ *
+ * @param array $credentials
+ * @return bool
+ * @author Syed Faisal
+ */
+ public function validateCredentials(array $credentials)
+ {
+ $result = $this->postRequest(json_encode($credentials),self::VALIDATE_CREDENTIALS);
- $result = curl_exec($ch);
+ if ($result && isset($result['status']) && $result['status'] == 200){
+ return true;
+ }
- curl_close($ch);
+ return false;
- if ($result && $result != 404){
- Session::put('lb.lastExceptionId', $result);
- }
+ }
+ /**
+ * Curl Request
+ *
+ * @param $requestData
+ * @param $url
+ * @return bool|mixed
+ */
+ private function postRequest($requestData, $url)
+ {
+ $header = [
+ 'Content-Type:application/json',
+ 'Authorization-APP:' . $this->projectId,
+ 'Authorization-KEY:' . $this->projectSecret
+ ];
+
+ $ch = curl_init($url);
+
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $requestData);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ if ($result) {
+ return json_decode($result, true);
+ }
+
+ return false;
+ }
+
+ /**
+ * Submit last exception feedback
+ *
+ * @param $data
+ * @return bool
+ */
+ public function submitFeedback($data)
+ {
+ $result = $this->postRequest(json_encode($data),self::POST_FEEDBACK);
+
+ if ($result && isset($result['status']) && $result['status'] == 200){
return true;
- }catch (\Exception $exception) {
- return false;
}
+
+ return false;
+
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/LarvaBugTestCommand.php b/src/Commands/LarvaBugTestCommand.php
index a43f5ce..b764737 100644
--- a/src/Commands/LarvaBugTestCommand.php
+++ b/src/Commands/LarvaBugTestCommand.php
@@ -3,6 +3,7 @@
namespace LarvaBug\Commands;
use Illuminate\Console\Command;
+use LarvaBug\Facade\LarvaBug;
class LarvaBugTestCommand extends Command
{
@@ -39,23 +40,35 @@ public function handle()
{
$this->info('Testing LarvaBug Configurations');
- if (config('larvabug.project_id')){
+ if (env('LB_PROJECT_ID') && !is_null(env('LB_PROJECT_ID'))){
$this->info('1. ✓ [Larvabug] Found project id');
}else{
$this->info('1. ✗ [Larvabug] Could not find your project id, please set this in your .env');
}
- if (config('larvabug.project_secret')){
+ if (env('LB_SECRET') && !is_null(env('LB_SECRET'))){
$this->info('2. ✓ [Larvabug] Found secret key');
}else{
$this->info('2. ✗ [Larvabug] Could not find LarvaBug secret, please set this in your .env');
}
+ $requestData = [
+ 'projectId' => env('LB_PROJECT_ID'),
+ 'projectSecret' => env('LB_SECRET')
+ ];
- }
+ if (app('larvabug') && app('larvabug')->validateCredentials($requestData)){
+ $this->info('3. ✓ [Larvabug] Validation Success');
+ }else{
+ $this->info('3. ✗ [Larvabug] Project id and secret do not match our records');
+ }
- public function testCredentials()
- {
+ try{
+ throw new \Exception('Larvabug Test Exception');
+ }catch (\Exception $exception){
+ LarvaBug::report($exception);
+ }
}
+
}
\ No newline at end of file
diff --git a/src/Facade/LarvaBug.php b/src/Facade/LarvaBug.php
index c48de7d..b64a8af 100644
--- a/src/Facade/LarvaBug.php
+++ b/src/Facade/LarvaBug.php
@@ -8,6 +8,10 @@
class LarvaBug extends FacadeAlias
{
+ /**
+ * @return string
+ *
+ */
protected static function getFacadeAccessor()
{
return 'larvabug';
diff --git a/src/Handler/LarvaBugExceptionHandler.php b/src/Handler/LarvaBugExceptionHandler.php
index d4f03bf..cf6a418 100644
--- a/src/Handler/LarvaBugExceptionHandler.php
+++ b/src/Handler/LarvaBugExceptionHandler.php
@@ -4,6 +4,10 @@
namespace LarvaBug\Handler;
use Illuminate\Support\Facades\App;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Session;
+use Illuminate\Support\Facades\URL;
use LarvaBug\Client\HttpClient;
class LarvaBugExceptionHandler
@@ -16,6 +20,10 @@ class LarvaBugExceptionHandler
* @var PrepareExceptionData
*/
private $exceptionData;
+ /**
+ * @var string
+ */
+ private $lastExceptionId;
/**
* LarvaBugExceptionHandler constructor.
@@ -31,21 +39,53 @@ public function __construct(
$this->exceptionData = $exceptionData;
}
- public function handle(\Throwable $exception)
+ /**
+ * Report exception to larvabug
+ *
+ * @param \Throwable $exception
+ * @return bool
+ */
+ public function report(\Throwable $exception)
{
- if (!$this->checkAppEnvironment()){
- return false;
- }
+ try {
+ if (!$this->checkAppEnvironment()) {
+ return false;
+ }
+
+ if ($this->skipError(get_class($exception))) {
+ return false;
+ }
- if ($this->skipError(get_class($exception))){
+ $data = $this->exceptionData->prepareException($exception);
+
+ $this->client->report($data);
+
+ return true;
+ }catch (\Exception $exception){
+ Log::info('Lavabug Exception :'.$exception->getMessage());
return false;
}
+ }
- $data = $this->exceptionData->prepare($exception);
+ /**
+ * Log details to larvabug
+ *
+ * @param $message
+ * @param array $meta
+ * @return bool
+ */
+ public function log($message, array $meta = [])
+ {
+ try {
+ $data = $this->exceptionData->prepareLogData($message, $meta);
- $this->client->report($data);
+ $this->client->report($data);
- return true;
+ return true;
+ }catch (\Exception $exception){
+ Log::info('Lavabug Exception :'.$exception->getMessage());
+ return false;
+ }
}
/**
@@ -53,7 +93,7 @@ public function handle(\Throwable $exception)
*
* @return bool
*/
- public function checkAppEnvironment()
+ private function checkAppEnvironment()
{
if (!config('larvabug.environment')){
return false;
@@ -72,7 +112,13 @@ public function checkAppEnvironment()
return false;
}
- protected function skipError($class)
+ /**
+ * Error dont report, configured in config file
+ *
+ * @param $class
+ * @return bool
+ */
+ private function skipError($class)
{
if (in_array($class,config('larvabug.skip_errors'))){
return true;
@@ -80,4 +126,64 @@ protected function skipError($class)
return false;
}
+
+ /**
+ * Validate env credentials from larvabug
+ *
+ * @param array $credentials
+ * @return bool
+ */
+ public function validateCredentials(array $credentials)
+ {
+ return $this->client->validateCredentials($credentials);
+ }
+
+ /**
+ * Collect error feedback from user
+ *
+ * @return \Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+ */
+ public function collectFeedback()
+ {
+ if ($this->lastExceptionId) {
+ return redirect($this->feedbackUrl().'?exceptionId='.$this->lastExceptionId);
+ }
+
+ return redirect('/');
+ }
+
+ /**
+ * Get feedback url
+ *
+ * @return mixed
+ * @author Syed Faisal
+ */
+ public function feedbackUrl()
+ {
+ return URL::to('larvabug-api/collect/feedback');
+ }
+
+ /**
+ * Submit user collected feedback
+ *
+ * @param $data
+ * @return bool
+ */
+ public function submitFeedback($data)
+ {
+ $this->client->submitFeedback($data);
+
+ return true;
+ }
+
+ public function setLastExceptionId(string $exceptionId)
+ {
+ return $this->lastExceptionId = $exceptionId;
+ }
+
+ public function getLastExceptionId()
+ {
+ return $this->lastExceptionId;
+ }
+
}
\ No newline at end of file
diff --git a/src/Handler/PrepareExceptionData.php b/src/Handler/PrepareExceptionData.php
index da1c1e3..936f79d 100644
--- a/src/Handler/PrepareExceptionData.php
+++ b/src/Handler/PrepareExceptionData.php
@@ -27,9 +27,53 @@ public function __construct()
* @author Syed Faisal
* @param \Throwable $exception
*/
- public function prepare(\Throwable $exception)
+ public function prepareException(\Throwable $exception)
{
- return $this->getExceptionData($exception);
+ $data = $this->getRequestInfo($exception);
+
+ $data['exception'] = $exception->getMessage();
+ $data['class'] = get_class($exception);
+ $data['file'] = $exception->getFile();
+ $data['line'] = $exception->getLine();
+ $data['error'] = $exception->getTraceAsString();
+ $data['trace_with_details'] = $this->prepareTraceData($exception->getTrace());
+
+ $count = config('larvabug.lines_count');
+
+ if (!$count || $count > 12) {
+ $count = 10;
+ }
+
+ $lines = file($data['file']);
+ $data['executor'] = [];
+
+ for ($i = -1 * abs($count); $i <= abs($count); $i++) {
+ $data['executor'][] = $this->getLineInfo($lines, $data['line'], $i);
+ }
+
+ $data['executor'] = array_filter($data['executor']);
+
+ // to make symfony exception more readable
+ if ($data['class'] == 'Symfony\Component\Debug\Exception\FatalErrorException') {
+ preg_match("~^(.+)' in ~", $data['exception'], $matches);
+ if (isset($matches[1])) {
+ $data['exception'] = $matches[1];
+ }
+ }
+
+ return $data;
+ }
+
+ public function prepareLogData(string $message,array $meta = [])
+ {
+ $data = $this->getRequestInfo();
+ $data['exception'] = $message;
+ $data['class'] = 'Log Information';
+ $data['type'] = 'log';
+ $data['meta_data'] = $meta;
+
+ return $data;
+
}
/**
@@ -38,22 +82,23 @@ public function prepare(\Throwable $exception)
* @param \Throwable $exception
* @return array
*/
- private function getExceptionData(\Throwable $exception)
+ private function getRequestInfo()
{
$data = [];
- $data['exception'] = $exception->getMessage();
- $data['class'] = get_class($exception);
- $data['file'] = $exception->getFile();
- $data['enviroment'] = App::environment();
+ $data['php_version'] = PHP_VERSION;
+ $data['server_ip'] = $_SERVER['SERVER_ADDR'] ?? null;
+ $data['environment'] = App::environment();
$data['server_name'] = @gethostname();
$data['browser'] = $this->getUserBrowser();
$data['userOs'] = $this->getUserOS();
$data['host'] = Request::server('SERVER_NAME');
$data['method'] = Request::method();
$data['fullUrl'] = Request::fullUrl();
- $data['line'] = $exception->getLine();
- $data['date_time'] = date("Y-m-d H:i:s");;
+ $data['url'] = Request::path();
+ $data['userIp'] = Request::ip();
+ $data['date_time'] = date("Y-m-d H:i:s");
+ $data['session_id'] = Session::getId();
$data['storage'] = [
'SERVER' => [
'USER' => Request::server('USER'),
@@ -71,34 +116,8 @@ private function getExceptionData(\Throwable $exception)
'HEADERS' => $this->filterBlackList(Request::header()),
];
$data['auth_user'] = $this->getAuthUser();
- $data['error'] = $exception->getTraceAsString();
- $data['trace_with_details'] = $this->prepareTraceData($exception->getTrace());
-
$data['storage'] = array_filter($data['storage']);
- $count = config('larvabug.lines_count');
-
- if (!$count || $count > 12) {
- $count = 5;
- }
-
- $lines = file($data['file']);
- $data['executor'] = [];
-
- for ($i = -1 * abs($count); $i <= abs($count); $i++) {
- $data['executor'][] = $this->getLineInfo($lines, $data['line'], $i);
- }
-
- $data['executor'] = array_filter($data['executor']);
-
- // to make symfony exception more readable
- if ($data['class'] == 'Symfony\Component\Debug\Exception\FatalErrorException') {
- preg_match("~^(.+)' in ~", $data['exception'], $matches);
- if (isset($matches[1])) {
- $data['exception'] = $matches[1];
- }
- }
-
return $data;
}
@@ -156,6 +175,11 @@ private function prepareTraceData($trace): array
return $response;
}
+ /**
+ * get auth user if exists
+ *
+ * @return |null
+ */
private function getAuthUser()
{
if (function_exists('auth') && auth()->check()) {
diff --git a/src/Http/Controllers/LarvaBugMainController.php b/src/Http/Controllers/LarvaBugMainController.php
new file mode 100644
index 0000000..d7c20a0
--- /dev/null
+++ b/src/Http/Controllers/LarvaBugMainController.php
@@ -0,0 +1,37 @@
+submitFeedback($data);
+
+ return Redirect::to('/');
+ }
+
+ /**
+ * Collect feedback view
+ *
+ * @return \Illuminate\Contracts\View\Factory|\Illuminate\Foundation\Application|\Illuminate\View\View
+ */
+ public function postFeedback()
+ {
+ return view('larvabug::feedback');
+ }
+
+}
\ No newline at end of file
diff --git a/src/Provider/BootServices.php b/src/Provider/BootServices.php
index ee20ad6..381356e 100644
--- a/src/Provider/BootServices.php
+++ b/src/Provider/BootServices.php
@@ -4,27 +4,53 @@
namespace LarvaBug\Provider;
+use Illuminate\Support\Facades\Route;
use LarvaBug\Commands\LarvaBugTestCommand;
trait BootServices
{
+ /**
+ * List of commands to be registered along with provider
+ *
+ * @var string[]
+ */
protected $commands = [
LarvaBugTestCommand::class
];
- protected function bootServices()
+ /**
+ * BootServices method contains all service that needs to be registered
+ */
+ private function bootServices()
{
$this->publishConfig();
$this->registerView();
$this->registerCommands();
+ $this->mapLaraBugApiRoutes();
}
- protected function registerView()
+ /**
+ * Register view directory with larvabug namespace
+ */
+ private function registerView()
{
- $this->app['view']->addNamespace('larvabug',__DIR__.'../../resources/views');
+ $this->app['view']->addNamespace('larvabug',__DIR__.'/../../resources/views');
}
- protected function publishConfig()
+ /**
+ * Map api routes directory to enable all api routes for larvabug
+ */
+ private function mapLaraBugApiRoutes()
+ {
+ Route::namespace('\LarvaBug\Http\Controllers')
+ ->prefix('larvabug-api')
+ ->group(__DIR__ . '/../../routes/api.php');
+ }
+
+ /**
+ * Publish package config files that contains package configurations
+ */
+ private function publishConfig()
{
if (function_exists('config_path')) {
$this->publishes([
@@ -33,7 +59,10 @@ protected function publishConfig()
}
}
- public function registerCommands()
+ /**
+ * Register array of commands
+ */
+ private function registerCommands()
{
$this->commands($this->commands);
}
diff --git a/src/Provider/ServiceProvider.php b/src/Provider/ServiceProvider.php
index 0e846cb..3eb05dd 100644
--- a/src/Provider/ServiceProvider.php
+++ b/src/Provider/ServiceProvider.php
@@ -13,11 +13,17 @@ class ServiceProvider extends BaseServiceProvider
{
use BootServices;
+ /**
+ * boot method of service provider
+ */
public function boot()
{
$this->bootServices();
}
+ /**
+ * Register method of service provider
+ */
public function register()
{
$this->app->singleton('larvabug',function ($app){