improves sentry integration and order of exceptions

This commit is contained in:
Irfan 2022-07-07 21:28:01 +05:00
parent 91ced83394
commit 072409410e
5 changed files with 74 additions and 97 deletions

View File

@ -121,12 +121,12 @@ INSIGHTS_MAX_STORE_TIME=172800
### ###
# Error reporting # Error reporting
### ###
REPORTING=true REPORTING=false
REPORTING_DRIVER=sentry REPORTING_DRIVER=sentry
SENTRY_LARAVEL_DSN="https://examplePublicKey@o0.ingest.sentry.io/0" SENTRY_LARAVEL_DSN="https://examplePublicKey@o0.ingest.sentry.io/0"
SENTRY_TRACES_SAMPLE_RATE=1 SENTRY_TRACES_SAMPLE_RATE=0.5
### ###
# Endpoints # Endpoints
### ###
DISABLE_USER_LISTS=false DISABLE_USER_LISTS=false

View File

@ -15,54 +15,54 @@ class GithubReport
/** /**
* @var string * @var string
*/ */
private $name; private string $name;
/** /**
* @var string * @var string
*/ */
private $code; private string $code;
/** /**
* @var string * @var string
*/ */
private $error; private string $error;
/** /**
* @var string * @var string
*/ */
private $requestUri; private string $requestUri;
/** /**
* @var string * @var string
*/ */
private $requestMethod; private string $requestMethod;
/** /**
* @var string * @var string
*/ */
private $repo; private string $repo;
/** /**
* @var string * @var string
*/ */
private $trace; private string $trace;
/** /**
* @var string * @var string
*/ */
private $jikanVersion; private string $jikanVersion;
/** /**
* @var string * @var string
*/ */
private $redisRunning; private string $redisRunning;
/** /**
* @var string * @var string
*/ */
private $instanceType; private string $instanceType;
/** /**
* @var * @var string
*/ */
private $phpVersion; private string $phpVersion;
/** /**
* @param \Exception $exception * @param \Exception $exception

View File

@ -3,10 +3,7 @@
namespace App\Exceptions; namespace App\Exceptions;
use App\Events\SourceHeartbeatEvent; use App\Events\SourceHeartbeatEvent;
use App\Http\HttpHelper;
use Exception; use Exception;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@ -16,11 +13,9 @@ use Jikan\Exception\BadResponseException;
use Jikan\Exception\ParserException; use Jikan\Exception\ParserException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler; use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Predis\Connection\ConnectionException; use Predis\Connection\ConnectionException;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpClient\Exception\TimeoutException; use Symfony\Component\HttpClient\Exception\TimeoutException;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Support\Facades\Cache;
/** /**
* Class Handler * Class Handler
@ -41,6 +36,13 @@ class Handler extends ExceptionHandler
BadResponseException::class BadResponseException::class
]; ];
/**
* @var string[]
*/
protected array $acceptableForReportingDriver = [
ParserException::class
];
/** /**
* @param \Throwable $e * @param \Throwable $e
* @throws Exception * @throws Exception
@ -55,13 +57,10 @@ class Handler extends ExceptionHandler
* @param \Throwable $e * @param \Throwable $e
* @return JsonResponse|Response * @return JsonResponse|Response
*/ */
public function render($request, \Throwable $e) public function render($request, \Throwable $e): JsonResponse|Response
{ {
$githubReport = GithubReport::make($e, $request); $githubReport = GithubReport::make($e, $request);
$this->reportToSentry($e);
if (app()->bound('sentry') && $this->shouldReport($e)) {
app('sentry')->captureException($e);
}
// ConnectionException from Redis server // ConnectionException from Redis server
if ($e instanceof ConnectionException) { if ($e instanceof ConnectionException) {
@ -83,18 +82,6 @@ class Handler extends ExceptionHandler
], 500); ], 500);
} }
if ($e instanceof ConnectException) {
event(new SourceHeartbeatEvent(SourceHeartbeatEvent::BAD_HEALTH, $e->getCode()));
return response()
->json([
'status' => $e->getCode(),
'type' => 'BadResponseException',
'message' => 'Jikan failed to connect to MyAnimeList.net. MyAnimeList.net may be down/unavailable, refuses to connect or took too long to respond.',
'error' => $e->getMessage()
], 503);
}
// ParserException from Jikan PHP API // ParserException from Jikan PHP API
if ($e instanceof ParserException) { if ($e instanceof ParserException) {
$githubReport->setRepo(env('GITHUB_API', 'jikan-me/jikan')); $githubReport->setRepo(env('GITHUB_API', 'jikan-me/jikan'));
@ -108,9 +95,9 @@ class Handler extends ExceptionHandler
], 500); ], 500);
} }
// BadResponseException from Guzzle dep via Jikan PHP API // BadResponseException from Jikan PHP API
// This is basically the response MyAnimeList returns to Jikan // This is basically the response MyAnimeList returns to Jikan
if ($e instanceof BadResponseException || $e instanceof ClientException) { if ($e instanceof BadResponseException) {
switch ($e->getCode()) { switch ($e->getCode()) {
case 404: case 404:
// $this->set404Cache($request, $e); // $this->set404Cache($request, $e);
@ -157,6 +144,28 @@ class Handler extends ExceptionHandler
} }
} }
if ($e instanceof TimeoutException) {
return response()
->json([
'status' => 408,
'type' => 'TimeoutException',
'message' => 'Request to MyAnimeList.net timed out (' .env('SOURCE_TIMEOUT', 5) . ' seconds)',
'error' => $e->getMessage()
], 408);
}
if ($e instanceof Exception && $e->getMessage() === "Undefined index: url") {
event(new SourceHeartbeatEvent(SourceHeartbeatEvent::BAD_HEALTH, $e->getCode()));
return response()
->json([
'status' => $e->getCode(),
'type' => 'BadResponseException',
'message' => 'Jikan failed to connect to MyAnimeList.net. MyAnimeList.net may be down/unavailable, refuses to connect or took too long to respond. Retry the request!',
'error' => $e->getMessage()
], 503);
}
// Bad REST API requests // Bad REST API requests
if ($e instanceof HttpException) { if ($e instanceof HttpException) {
return response() return response()
@ -168,71 +177,32 @@ class Handler extends ExceptionHandler
], $e->getStatusCode()); ], $e->getStatusCode());
} }
if ($e instanceof TimeoutException) { return response()
return response() ->json([
->json([ 'status' => 500,
'status' => 408, 'type' => "Exception",
'type' => 'TimeoutException', 'message' => 'Unhandled Exception. Please follow report_url to generate an issue on GitHub',
'message' => 'Request to MyAnimeList.net timed out (' .env('SOURCE_TIMEOUT', 5) . ' seconds)', 'trace' => "{$e->getFile()} at line {$e->getLine()}",
'error' => $e->getMessage() 'error' => $e->getMessage(),
], 408); 'report_url' => env('GITHUB_REPORTING', true) ? (string) $githubReport : null
} ], 500);
if ($e instanceof Exception) {
if ($e->getMessage() === "Undefined index: url") {
event(new SourceHeartbeatEvent(SourceHeartbeatEvent::BAD_HEALTH, $e->getCode()));
return response()
->json([
'status' => $e->getCode(),
'type' => 'BadResponseException',
'message' => 'Jikan failed to connect to MyAnimeList.net. MyAnimeList.net may be down/unavailable, refuses to connect or took too long to respond. Retry the request!',
'error' => $e->getMessage()
], 503);
}
return response()
->json([
'status' => 500,
'type' => "Exception",
'message' => 'Unhandled Exception. Please follow report_url to generate an issue on GitHub',
'trace' => "{$e->getFile()} at line {$e->getLine()}",
'error' => $e->getMessage(),
'report_url' => env('GITHUB_REPORTING', true) ? (string) $githubReport : null
], 500);
}
} }
/** /**
* @param Request $request * @param Exception|\Throwable $e
* @param BadResponseException $e * @return void
*/ */
private function set404Cache(Request $request, BadResponseException $e) private function reportToSentry(\Exception|\Throwable $e): void
{ {
if (!env('CACHING') || env('MICROCACHING')) { if (!app()->bound('sentry')) {
return; return;
} }
$fingerprint = "request:404:".sha1(env('APP_URL') . $request->getRequestUri()); foreach ($this->acceptableForReportingDriver as $type) {
if ($e instanceof $type) {
if (Cache::has($fingerprint)) { app('sentry')->captureException($e);
return; }
} }
$routeController = HttpHelper::requestControllerName($request);
$cacheTtl = env('CACHE_DEFAULT_EXPIRE', 86400);
if (\in_array($routeController, [
'AnimeController',
'MangaController',
'CharacterController',
'PersonController'
])) {
$cacheTtl = env('CACHE_404_EXPIRE', 604800);
}
Cache::put($fingerprint, $e->getMessage(), $cacheTtl);
} }
} }

View File

@ -117,6 +117,11 @@ if (env('REPORTING') && env('REPORTING_DRIVER') === 'sentry') {
$app->register(\Sentry\Laravel\ServiceProvider::class); $app->register(\Sentry\Laravel\ServiceProvider::class);
// Sentry Performance Monitoring (optional) // Sentry Performance Monitoring (optional)
$app->register(\Sentry\Laravel\Tracing\ServiceProvider::class); $app->register(\Sentry\Laravel\Tracing\ServiceProvider::class);
\Sentry\configureScope(function (\Sentry\State\Scope $scope): void {
$scope->setTag('rest.jikan.version', env('APP_VERSION'));
$scope->setTag('parser.jikan.version', JIKAN_PARSER_VERSION);
});
} }
// Guzzle removed as of lumen 8.x // Guzzle removed as of lumen 8.x

View File

@ -8,7 +8,7 @@ return [
// 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')), // 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')),
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => env('SENTRY_ENVIRONMENT'), 'environment' => env('APP_ENV'),
'breadcrumbs' => [ 'breadcrumbs' => [
// Capture Laravel logs in breadcrumbs // Capture Laravel logs in breadcrumbs
@ -54,4 +54,6 @@ return [
'controllers_base_namespace' => env('SENTRY_CONTROLLERS_BASE_NAMESPACE', 'App\\Http\\Controllers'), 'controllers_base_namespace' => env('SENTRY_CONTROLLERS_BASE_NAMESPACE', 'App\\Http\\Controllers'),
'release' => env('APP_VERSION')
]; ];