laravel-6 support

This commit is contained in:
RafficMohammed
2023-01-08 01:17:22 +05:30
parent 1a5c16ae4b
commit 774eed8b0e
4962 changed files with 279380 additions and 297961 deletions

View File

@@ -0,0 +1,4 @@
source 'https://rubygems.org'
gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v3.6.0'
gem "os", "~> 1.0"

View File

@@ -0,0 +1,58 @@
Upgrading
=========
## Laravel 5.5 to 5.6
Laravel 5.6 has a brand new Monolog-base logging system changing the integration
point for the Bugsnag handlers. The `Illuminate\Contracts\Logging\Log` class has
been removed in favor of `Illuminate\Log\LogManager`.
To upgrade, remove the existing Bugsnag logging integration from the `register`
method of `app/Providers/AppServiceProvider.php`:
```diff
- $this->app->alias('bugsnag.logger', \Illuminate\Contracts\Logging\Log::class);
- $this->app->alias('bugsnag.logger', \Psr\Log\LoggerInterface::class);
```
Or if using the multi-logger:
```diff
- $this->app->alias('bugsnag.multi', \Illuminate\Contracts\Logging\Log::class);
- $this->app->alias('bugsnag.multi', \Psr\Log\LoggerInterface::class);
```
Then add Bugsnag to your logging stack in `config/logging.php`:
```php
'channels' => [
'stack' => [
'driver' => 'stack',
// Add bugsnag to the stack:
'channels' => ['single', 'bugsnag'],
],
// ...
// Create a bugsnag logging channel:
'bugsnag' => [
'driver' => 'bugsnag',
],
],
```
References:
* The [bugsnag-laravel integration guide](https://docs.bugsnag.com/platforms/php/laravel/)
* Our [blog post about the new changes in Laravel 5.6](https://blog.bugsnag.com/laravel-5-6/)
* [Laravel 5.6 Logging documentation](https://laravel.com/docs/5.6/logging)
## 1.x to 2.x
*Our library has gone through some major improvements. The primary change to watch out for is we're no longer overriding your exception handler.*
Since we're no longer overriding your exception handler, you'll need to restore your original handler, and then see our [new integration guide](http://docs.bugsnag.com/platforms/php/laravel/) for how to bind our new logger to the container.
If you'd like access to all our new configuration, you'll need to re-publish our config file.

View File

@@ -11,18 +11,43 @@
}
],
"require": {
"php": ">=5.3.0",
"illuminate/support": "^4.0|^5.0",
"bugsnag/bugsnag": "^2.5"
"php": ">=5.5",
"bugsnag/bugsnag": "^3.29.0",
"bugsnag/bugsnag-psr-logger": "^1.4|^2.0",
"illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^5.0|^6.0|^7.0|^8.0|^9.0",
"monolog/monolog": "^1.12|^2.0|^3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.0"
"orchestra/testbench": "^3.1|^4.0|^5.0|^6.0|^7.0",
"phpunit/phpunit": "^4.8.36|^6.3.1|^7.5.15|^8.3.5|^9.3.10"
},
"autoload": {
"psr-0": {
"psr-4": {
"Bugsnag\\BugsnagLaravel\\": "src/"
}
},
"autoload-dev": {
"psr-4" : {
"Bugsnag\\BugsnagLaravel\\Tests\\" : "tests/"
},
"files": [
"tests/bc.php"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.18-dev"
}
},
"scripts": {
"test": "phpunit"
},
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"config": {
"allow-plugins": {
"kylekatarnls/update-helper": false
}
}
}

View File

@@ -0,0 +1,364 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| API Key
|--------------------------------------------------------------------------
|
| You can find your API key on your Bugsnag dashboard.
|
| This api key points the Bugsnag notifier to the project in your account
| which should receive your application's uncaught exceptions.
|
*/
'api_key' => env('BUGSNAG_API_KEY', ''),
/*
|--------------------------------------------------------------------------
| App Type
|--------------------------------------------------------------------------
|
| Set the type of application executing the current code.
|
*/
'app_type' => env('BUGSNAG_APP_TYPE'),
/*
|--------------------------------------------------------------------------
| App Version
|--------------------------------------------------------------------------
|
| Set the version of application executing the current code.
|
*/
'app_version' => env('BUGSNAG_APP_VERSION'),
/*
|--------------------------------------------------------------------------
| Batch Sending
|--------------------------------------------------------------------------
|
| Set to true to send the errors through to Bugsnag when the PHP process
| shuts down, in order to prevent your app waiting on HTTP requests.
|
| Setting this to false will send an HTTP request straight away for each
| error.
|
*/
'batch_sending' => env('BUGSNAG_BATCH_SENDING'),
/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Set what server the Bugsnag notifier should send errors to. By default
| this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise
| this should be the URL to your Bugsnag instance.
|
*/
'endpoint' => env('BUGSNAG_ENDPOINT'),
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Use this if you want to ensure you don't send sensitive data such as
| passwords, and credit card numbers to our servers. Any keys which
| contain these strings will be filtered.
|
| This option has been deprecated in favour of 'redacted_keys'
|
*/
'filters' => empty(env('BUGSNAG_FILTERS')) ? null : explode(',', str_replace(' ', '', env('BUGSNAG_FILTERS'))),
/*
|--------------------------------------------------------------------------
| Hostname
|--------------------------------------------------------------------------
|
| You can set the hostname of your server to something specific for you to
| identify it by if needed.
|
*/
'hostname' => env('BUGSNAG_HOSTNAME'),
/*
|--------------------------------------------------------------------------
| Proxy
|--------------------------------------------------------------------------
|
| This is where you can set the proxy settings you'd like us to use when
| communicating with Bugsnag when reporting errors.
|
*/
'proxy' => array_filter([
'http' => env('HTTP_PROXY'),
'https' => env('HTTPS_PROXY'),
'no' => empty(env('NO_PROXY')) ? null : explode(',', str_replace(' ', '', env('NO_PROXY'))),
]),
/*
|--------------------------------------------------------------------------
| Project Root
|--------------------------------------------------------------------------
|
| Bugsnag marks stacktrace lines as in-project if they come from files
| inside your “project root”. You can set this here.
|
| If this is not set, we will automatically try to detect it.
|
*/
'project_root' => env('BUGSNAG_PROJECT_ROOT'),
/*
|--------------------------------------------------------------------------
| Project Root Regex
|--------------------------------------------------------------------------
|
| Bugsnag marks stacktrace lines as in-project if they come from files
| inside your “project root”. You can set this here.
|
| This option allows you to set it as a regular expression and will take
| precedence over "project_root" if both are defined.
|
*/
'project_root_regex' => env('BUGSNAG_PROJECT_ROOT_REGEX'),
/*
|--------------------------------------------------------------------------
| Strip Path
|--------------------------------------------------------------------------
|
| The strip path is a path to be trimmed from the start of any filepaths in
| your stacktraces.
|
| If this is not set, we will automatically try to detect it.
|
*/
'strip_path' => env('BUGSNAG_STRIP_PATH'),
/*
|--------------------------------------------------------------------------
| Strip Path Regex
|--------------------------------------------------------------------------
|
| The strip path is a path to be trimmed from the start of any filepaths in
| your stacktraces.
|
| This option allows you to set it as a regular expression and will take
| precedence over "strip_path" if both are defined.
|
*/
'strip_path_regex' => env('BUGSNAG_STRIP_PATH_REGEX'),
/*
|--------------------------------------------------------------------------
| Query
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to automatically record all queries executed
| as breadcrumbs.
|
*/
'query' => env('BUGSNAG_QUERY', true),
/*
|--------------------------------------------------------------------------
| Bindings
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to include the query bindings in our query
| breadcrumbs.
|
*/
'bindings' => env('BUGSNAG_QUERY_BINDINGS', false),
/*
|--------------------------------------------------------------------------
| Release Stage
|--------------------------------------------------------------------------
|
| Set the release stage to use when sending notifications to Bugsnag.
|
| Leaving this unset will default to using the application environment.
|
*/
'release_stage' => env('BUGSNAG_RELEASE_STAGE'),
/*
|--------------------------------------------------------------------------
| Notify Release Stages
|--------------------------------------------------------------------------
|
| Set which release stages should send notifications to Bugsnag.
|
*/
'notify_release_stages' => empty(env('BUGSNAG_NOTIFY_RELEASE_STAGES')) ? null : explode(',', str_replace(' ', '', env('BUGSNAG_NOTIFY_RELEASE_STAGES'))),
/*
|--------------------------------------------------------------------------
| Send Code
|--------------------------------------------------------------------------
|
| Bugsnag automatically sends a small snippet of the code that crashed to
| help you diagnose even faster from within your dashboard. If you dont
| want to send this snippet, then set this to false.
|
*/
'send_code' => env('BUGSNAG_SEND_CODE', true),
/*
|--------------------------------------------------------------------------
| Callbacks
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to enable our default set of notification
| callbacks. These add things like the cookie information and session
| details to the error to be sent to Bugsnag.
|
| If you'd like to add your own callbacks, you can call the
| Bugsnag::registerCallback method from the boot method of your app
| service provider.
|
*/
'callbacks' => env('BUGSNAG_CALLBACKS', true),
/*
|--------------------------------------------------------------------------
| User
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to set the current user logged in via
| Laravel's authentication system.
|
| If you'd like to add your own user resolver, you can do this by using
| callbacks via Bugsnag::registerCallback.
|
*/
'user' => env('BUGSNAG_USER', true),
/*
|--------------------------------------------------------------------------
| Logger Notify Level
|--------------------------------------------------------------------------
|
| This sets the level at which a logged message will trigger a notification
| to Bugsnag. By default this level will be 'notice'.
|
| Must be one of the Psr\Log\LogLevel levels from the Psr specification.
|
*/
'logger_notify_level' => env('BUGSNAG_LOGGER_LEVEL'),
/*
|--------------------------------------------------------------------------
| Auto Capture Sessions
|--------------------------------------------------------------------------
|
| Enable this to start tracking sessions and deliver them to Bugsnag.
|
*/
'auto_capture_sessions' => env('BUGSNAG_CAPTURE_SESSIONS', false),
/*
|--------------------------------------------------------------------------
| Sessions Endpoint
|--------------------------------------------------------------------------
|
| Sets a url to send tracked sessions to.
|
*/
'session_endpoint' => env('BUGSNAG_SESSION_ENDPOINT'),
/*
|--------------------------------------------------------------------------
| Builds Endpoint
|--------------------------------------------------------------------------
|
| Sets a url to send build reports to.
|
*/
'build_endpoint' => env('BUGSNAG_BUILD_ENDPOINT'),
/*
|--------------------------------------------------------------------------
| Discard Classes
|--------------------------------------------------------------------------
|
| An array of classes that should not be sent to Bugsnag.
|
| This can contain both fully qualified class names and regular expressions.
|
*/
'discard_classes' => empty(env('BUGSNAG_DISCARD_CLASSES')) ? null : explode(',', env('BUGSNAG_DISCARD_CLASSES')),
/*
|--------------------------------------------------------------------------
| Redacted Keys
|--------------------------------------------------------------------------
|
| An array of metadata keys that should be redacted.
|
*/
'redacted_keys' => empty(env('BUGSNAG_REDACTED_KEYS')) ? null : explode(',', env('BUGSNAG_REDACTED_KEYS')),
/*
|--------------------------------------------------------------------------
| Feature flags
|--------------------------------------------------------------------------
|
| An array of feature flags to add to all reports.
|
| Each element in the array must have a "name" key and can optionally have a
| "variant" key, for example:
|
| [
| ['name' => 'example without a variant'],
| ['name' => 'example with a variant', 'variant' => 'example of a variant'],
| ]
|
*/
'feature_flags' => [],
/*
|--------------------------------------------------------------------------
| Max breadcrumbs
|--------------------------------------------------------------------------
|
| The maximum number of breadcrumbs to send with a report.
|
| This should be an integer between 0-100 (inclusive).
*/
'max_breadcrumbs' => null,
];

View File

@@ -1,11 +0,0 @@
{
"providers": [
"Bugsnag\BugsnagLaravel\BugsnagLaravelServiceProvider"
],
"aliases": [
{
"alias": "Bugsnag",
"facade": "Bugsnag\BugsnagLaravel\BugsnagFacade"
}
]
}

View File

@@ -1,27 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class BugsnagExceptionHandler extends ExceptionHandler
{
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
*
* @return void
*/
public function report(Exception $e)
{
if ($this->shouldReport($e) && app()->bound('bugsnag')) {
app('bugsnag')->notifyException($e, null, 'error');
}
return parent::report($e);
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Illuminate\Support\Facades\Facade;
class BugsnagFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'bugsnag';
}
}

View File

@@ -1,115 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Illuminate\Support\ServiceProvider;
class BugsnagLaravelServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$app = $this->app;
if (version_compare($app::VERSION, '5.0') < 0) {
$this->package('bugsnag/bugsnag-laravel', 'bugsnag');
// Register for exception handling
$app->error(function (\Exception $exception) use ($app) {
if ('Symfony\Component\Debug\Exception\FatalErrorException'
!== get_class($exception)
) {
$app['bugsnag']->notifyException($exception, null, 'error');
}
});
// Register for fatal error handling
$app->fatal(function ($exception) use ($app) {
$app['bugsnag']->notifyException($exception, null, 'error');
});
} else {
$this->publishes(array(
__DIR__.'/config.php' => config_path('bugsnag.php'),
), 'config');
}
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('bugsnag', function ($app) {
$config = isset($app['config']['services']['bugsnag']) ? $app['config']['services']['bugsnag'] : null;
if (is_null($config)) {
$config = $app['config']['bugsnag'] ?: $app['config']['bugsnag::config'];
}
$client = new \Bugsnag_Client($config['api_key']);
$client->setStripPath(base_path());
$client->setProjectRoot(app_path());
$client->setAutoNotify(false);
$client->setBatchSending(false);
$client->setReleaseStage($app->environment());
$client->setNotifier(array(
'name' => 'Bugsnag Laravel',
'version' => '1.7.0',
'url' => 'https://github.com/bugsnag/bugsnag-laravel',
));
if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
$client->setNotifyReleaseStages($config['notify_release_stages']);
}
if (isset($config['endpoint'])) {
$client->setEndpoint($config['endpoint']);
}
if (isset($config['filters']) && is_array($config['filters'])) {
$client->setFilters($config['filters']);
}
if (isset($config['proxy']) && is_array($config['proxy'])) {
$client->setProxySettings($config['proxy']);
}
// Check if someone is logged in.
try {
if ($app['auth']->check()) {
// User is logged in.
$user = $app['auth']->user();
// If these attributes are available: pass them on.
$client->setUser(array('id' => $user->getAuthIdentifier()));
}
} catch (\Exception $e) {
// Do nothing.
}
return $client;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array('bugsnag');
}
}

View File

@@ -1,93 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Illuminate\Support\ServiceProvider;
class BugsnagLumenServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$this->app->configure('bugsnag');
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('bugsnag', function ($app) {
$config = isset($app['config']['services']['bugsnag']) ? $app['config']['services']['bugsnag'] : null;
if (is_null($config)) {
$config = $app['config']['bugsnag'] ?: $app['config']['bugsnag::config'];
}
$client = new \Bugsnag_Client($config['api_key']);
$client->setStripPath(base_path());
$client->setProjectRoot(base_path().'/app');
$client->setAutoNotify(false);
$client->setBatchSending(false);
$client->setReleaseStage($app->environment());
$client->setNotifier(array(
'name' => 'Bugsnag Lumen',
'version' => '1.7.0',
'url' => 'https://github.com/bugsnag/bugsnag-laravel',
));
if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
$client->setNotifyReleaseStages($config['notify_release_stages']);
}
if (isset($config['endpoint'])) {
$client->setEndpoint($config['endpoint']);
}
if (isset($config['filters']) && is_array($config['filters'])) {
$client->setFilters($config['filters']);
}
if (isset($config['proxy']) && is_array($config['proxy'])) {
$client->setProxySettings($config['proxy']);
}
// Check if someone is logged in.
try {
if ($app['auth']->check()) {
// User is logged in.
$user = $app['auth']->user();
// If these attributes are available: pass them on.
$client->setUser(array('id' => $user->getAuthIdentifier()));
}
} catch (\Exception $e) {
// Do nothing.
}
return $client;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array('bugsnag');
}
}

View File

@@ -1,77 +0,0 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| API Key
|--------------------------------------------------------------------------
|
| You can find your API key on your Bugsnag dashboard.
|
| This api key points the Bugsnag notifier to the project in your account
| which should receive your application's uncaught exceptions.
|
*/
'api_key' => env('BUGSNAG_API_KEY', ''),
/*
|--------------------------------------------------------------------------
| Notify Release Stages
|--------------------------------------------------------------------------
|
| Set which release stages should send notifications to Bugsnag.
|
| Example: array('development', 'production')
|
*/
'notify_release_stages' => env('BUGSNAG_NOTIFY_RELEASE_STAGES', null),
/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Set what server the Bugsnag notifier should send errors to. By default
| this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise
| this should be the URL to your Bugsnag instance.
|
*/
'endpoint' => env('BUGSNAG_ENDPOINT', null),
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Use this if you want to ensure you don't send sensitive data such as
| passwords, and credit card numbers to our servers. Any keys which
| contain these strings will be filtered.
|
*/
'filters' => env('BUGSNAG_FILTERS', array('password')),
/*
|--------------------------------------------------------------------------
| Proxy
|--------------------------------------------------------------------------
|
| If your server is behind a proxy server, you can configure this as well.
| Other than the host, none of these settings are mandatory.
|
| Note: Proxy configuration is only possible if the PHP cURL extension
| is installed.
|
| Example:
|
| 'proxy' => array(
| 'host' => 'bugsnag.com',
| 'port' => 42,
| 'user' => 'username',
| 'password' => 'password123'
| )
|
*/
'proxy' => env('BUGSNAG_PROXY', null),
);

View File

@@ -0,0 +1,490 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Bugsnag\Breadcrumbs\Breadcrumb;
use Bugsnag\BugsnagLaravel\Middleware\UnhandledState;
use Bugsnag\BugsnagLaravel\Queue\Tracker;
use Bugsnag\BugsnagLaravel\Request\LaravelResolver;
use Bugsnag\Callbacks\CustomUser;
use Bugsnag\Client;
use Bugsnag\Configuration;
use Bugsnag\FeatureFlag;
use Bugsnag\PsrLogger\BugsnagLogger;
use Bugsnag\PsrLogger\MultiLogger as BaseMultiLogger;
use Bugsnag\Report;
use DateTime;
use Illuminate\Auth\GenericUser;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Logging\Log;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Foundation\Application as LaravelApplication;
use Illuminate\Log\LogManager;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\QueueManager;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\ServiceProvider;
use Laravel\Lumen\Application as LumenApplication;
use Monolog\Handler\PsrHandler;
use Monolog\Logger;
use ReflectionClass;
class BugsnagServiceProvider extends ServiceProvider
{
/**
* The package version.
*
* @var string
*/
const VERSION = '2.25.0';
/**
* Boot the service provider.
*
* @return void
*/
public function boot()
{
$this->setupConfig($this->app);
$this->setupEvents($this->app->events, $this->app->config->get('bugsnag'));
$this->setupQueue($this->app->queue);
// Load the Client instance up-front if the OOM bootstrapper has been
// loaded. This avoids the possibility of initialising during an OOM,
// which can take a non-trivial amount of memory
if (class_exists(OomBootstrapper::class, false) && !$this->app->runningUnitTests()) {
$this->app->make('bugsnag');
}
}
/**
* Setup the config.
*
* @param \Illuminate\Contracts\Container\Container $app
*
* @return void
*/
protected function setupConfig(Container $app)
{
$source = realpath($raw = __DIR__.'/../config/bugsnag.php') ?: $raw;
if ($app instanceof LaravelApplication && $app->runningInConsole()) {
$this->publishes([$source => config_path('bugsnag.php')]);
} elseif ($app instanceof LumenApplication) {
$app->configure('bugsnag');
}
$this->mergeConfigFrom($source, 'bugsnag');
}
/**
* Setup the events.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @param array $config
*
* @return void
*/
protected function setupEvents(Dispatcher $events, array $config)
{
if ($this->isSessionTrackingAllowed($config)) {
$events->listen(RouteMatched::class, function ($event) {
$this->app->bugsnag->getSessionTracker()->startSession();
});
}
if (isset($config['query']) && !$config['query']) {
return;
}
$show = isset($config['bindings']) && $config['bindings'];
if (class_exists(QueryExecuted::class)) {
$events->listen(QueryExecuted::class, function (QueryExecuted $query) use ($show) {
$this->app->bugsnag->leaveBreadcrumb(
'Query executed',
Breadcrumb::PROCESS_TYPE,
$this->formatQuery($query->sql, $show ? $query->bindings : [], $query->time, $query->connectionName)
);
});
} else {
$events->listen('illuminate.query', function ($sql, array $bindings, $time, $connection) use ($show) {
$this->app->bugsnag->leaveBreadcrumb(
'Query executed',
Breadcrumb::PROCESS_TYPE,
$this->formatQuery($sql, $show ? $bindings : [], $time, $connection)
);
});
}
}
/**
* Format the query as breadcrumb metadata.
*
* @param string $sql
* @param array $bindings
* @param float $time
* @param string $connection
*
* @return array
*/
protected function formatQuery($sql, array $bindings, $time, $connection)
{
$data = ['sql' => $sql];
foreach ($bindings as $index => $binding) {
$data["binding {$index}"] = $binding;
}
$data['time'] = "{$time}ms";
$data['connection'] = $connection;
return $data;
}
/**
* Setup the queue.
*
* @param \Illuminate\Queue\QueueManager $queue
*
* @return void
*/
protected function setupQueue(QueueManager $queue)
{
$queue->looping(function () {
$this->app->bugsnag->flush();
$this->app->bugsnag->clearBreadcrumbs();
$this->app->make(Tracker::class)->clear();
});
if (!class_exists(JobProcessing::class)) {
return;
}
$queue->before(function (JobProcessing $event) {
$this->app->bugsnag->setFallbackType('Queue');
$job = [
'name' => $event->job->getName(),
'queue' => $event->job->getQueue(),
'attempts' => $event->job->attempts(),
'connection' => $event->connectionName,
];
if (method_exists($event->job, 'resolveName')) {
$job['resolved'] = $event->job->resolveName();
}
$this->app->make(Tracker::class)->set($job);
});
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('bugsnag', function (Container $app) {
$config = $app->config->get('bugsnag');
$client = new Client(new Configuration($config['api_key']), new LaravelResolver($app), $this->getGuzzle($config));
$this->setupCallbacks($client, $app, $config);
$this->setupPaths($client, $app, $config);
$client->setReleaseStage(isset($config['release_stage']) ? $config['release_stage'] : $app->environment());
$client->setHostname(isset($config['hostname']) ? $config['hostname'] : null);
$client->getConfig()->mergeDeviceData(['runtimeVersions' => $this->getRuntimeVersion()]);
$client->setFallbackType($app->runningInConsole() ? 'Console' : 'HTTP');
$client->setAppType(isset($config['app_type']) ? $config['app_type'] : null);
$client->setAppVersion(isset($config['app_version']) ? $config['app_version'] : null);
$client->setBatchSending(isset($config['batch_sending']) ? $config['batch_sending'] : true);
$client->setSendCode(isset($config['send_code']) ? $config['send_code'] : true);
$client->getPipeline()->insertBefore(new UnhandledState(), 'Bugsnag\\Middleware\\SessionData');
$client->setNotifier([
'name' => 'Bugsnag Laravel',
'version' => static::VERSION,
'url' => 'https://github.com/bugsnag/bugsnag-laravel',
]);
if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
$client->setNotifyReleaseStages($config['notify_release_stages']);
}
if (isset($config['filters']) && is_array($config['filters'])) {
$client->setFilters($config['filters']);
}
if (isset($config['endpoint'])) {
$client->setNotifyEndpoint($config['endpoint']);
}
if ($this->isSessionTrackingAllowed($config)) {
$endpoint = isset($config['session_endpoint']) ? $config['session_endpoint'] : null;
$this->setupSessionTracking($client, $endpoint, $this->app->events);
}
if (isset($config['build_endpoint'])) {
$client->setBuildEndpoint($config['build_endpoint']);
}
if (array_key_exists('memory_limit_increase', $config)) {
$client->setMemoryLimitIncrease($config['memory_limit_increase']);
}
if (isset($config['discard_classes']) && is_array($config['discard_classes'])) {
$client->setDiscardClasses($config['discard_classes']);
}
if (isset($config['redacted_keys']) && is_array($config['redacted_keys'])) {
$client->setRedactedKeys($config['redacted_keys']);
}
if (isset($config['feature_flags']) && is_array($config['feature_flags']) && $config['feature_flags'] !== []) {
$featureFlags = [];
foreach ($config['feature_flags'] as $flag) {
if (!is_array($flag) || !array_key_exists('name', $flag)) {
continue;
}
if (array_key_exists('variant', $flag)) {
$featureFlags[] = new FeatureFlag($flag['name'], $flag['variant']);
} else {
$featureFlags[] = new FeatureFlag($flag['name']);
}
}
$client->addFeatureFlags($featureFlags);
}
if (isset($config['max_breadcrumbs'])) {
$client->setMaxBreadcrumbs($config['max_breadcrumbs']);
}
return $client;
});
$this->app->singleton('bugsnag.tracker', function () {
return new Tracker();
});
$this->app->singleton('bugsnag.logger', function (Container $app) {
$config = $app->config->get('bugsnag');
$logger = interface_exists(Log::class) ? new LaravelLogger($app['bugsnag'], $app['events']) : new BugsnagLogger($app['bugsnag']);
if (isset($config['logger_notify_level'])) {
$logger->setNotifyLevel($config['logger_notify_level']);
}
return $logger;
});
$this->app->singleton('bugsnag.multi', function (Container $app) {
return interface_exists(Log::class) ? new MultiLogger([$app['log'], $app['bugsnag.logger']]) : new BaseMultiLogger([$app['log'], $app['bugsnag.logger']]);
});
if ($this->app['log'] instanceof LogManager) {
$this->app['log']->extend('bugsnag', function (Container $app, array $config) {
$handler = new PsrHandler($app['bugsnag.logger']);
return new Logger('bugsnag', [$handler]);
});
}
$this->app->alias('bugsnag', Client::class);
$this->app->alias('bugsnag.tracker', Tracker::class);
$this->app->alias('bugsnag.logger', interface_exists(Log::class) ? LaravelLogger::class : BugsnagLogger::class);
$this->app->alias('bugsnag.multi', interface_exists(Log::class) ? MultiLogger::class : BaseMultiLogger::class);
}
/**
* Get the guzzle client instance.
*
* @param array $config
*
* @return \GuzzleHttp\ClientInterface
*/
protected function getGuzzle(array $config)
{
// If a 'bugsnag.guzzle' instance exists in the container, use it
if ($this->app->bound('bugsnag.guzzle')) {
return $this->app->make('bugsnag.guzzle');
}
$options = [];
if (isset($config['proxy']) && $config['proxy']) {
if (isset($config['proxy']['http']) && php_sapi_name() != 'cli') {
unset($config['proxy']['http']);
}
$options['proxy'] = $config['proxy'];
}
return Client::makeGuzzle(null, $options);
}
/**
* Setup the callbacks.
*
* @param \Bugsnag\Client $client
* @param \Illuminate\Contracts\Container\Container $app
* @param array $config
*
* @return void
*/
protected function setupCallbacks(Client $client, Container $app, array $config)
{
if (!isset($config['callbacks']) || $config['callbacks']) {
$client->registerDefaultCallbacks();
$client->registerCallback(function (Report $report) use ($app) {
$tracker = $app->make(Tracker::class);
if ($context = $tracker->context()) {
$report->setContext($context);
}
if ($job = $tracker->get()) {
$report->setMetaData(['job' => $job]);
}
});
}
if (!isset($config['user']) || $config['user']) {
$client->registerCallback(new CustomUser(function () use ($app) {
if ($user = $app->auth->user()) {
if (method_exists($user, 'attributesToArray') && is_callable([$user, 'attributesToArray'])) {
return $user->attributesToArray();
}
if ($user instanceof GenericUser) {
$reflection = new ReflectionClass($user);
$property = $reflection->getProperty('attributes');
$property->setAccessible(true);
return $property->getValue($user);
}
}
}));
}
}
/**
* Setup the client paths.
*
* @param \Bugsnag\Client $client
* @param \Illuminate\Contracts\Container\Container $app
* @param array $config
*
* @return void
*/
protected function setupPaths(Client $client, Container $app, array $config)
{
if (isset($config['project_root_regex'])) {
$client->setProjectRootRegex($config['project_root_regex']);
} elseif (isset($config['project_root'])) {
$client->setProjectRoot($config['project_root']);
} else {
$client->setProjectRoot($app->path());
}
if (isset($config['strip_path_regex'])) {
$client->setStripPathRegex($config['strip_path_regex']);
} elseif (isset($config['strip_path'])) {
$client->setStripPath($config['strip_path']);
} else {
$client->setStripPath($app->basePath());
}
}
/**
* Setup session tracking.
*
* @param \Bugsnag\Client $client
* @param string $endpoint
*
* @return void
*/
protected function setupSessionTracking(Client $client, $endpoint, $events)
{
$client->setAutoCaptureSessions(true);
if (!is_null($endpoint)) {
$client->setSessionEndpoint($endpoint);
}
$sessionTracker = $client->getSessionTracker();
$sessionStorage = function ($session = null) {
if (is_null($session)) {
return session('bugsnag-session', []);
} else {
session(['bugsnag-session' => $session]);
}
};
$sessionTracker->setSessionFunction($sessionStorage);
$cache = $this->app->cache;
$genericStorage = function ($key, $value = null) use ($cache) {
if (is_null($value)) {
return $cache->get($key, null);
} else {
$cache->put($key, $value, new DateTime('+ 1 hour'));
}
};
$sessionTracker->setStorageFunction($genericStorage);
}
/**
* Returns the framework name and version to add to the device data.
*
* Attempt to parse a semantic framework version from $app or else return
* the full version string.
* e.g. Lumen: "Lumen (x.x.x) (Laravel Components y.y.*)" => "x.x.x"
*
* @return array
*/
protected function getRuntimeVersion()
{
$version = $this->app->version();
if (preg_match('/(\d+\.\d+\.\d+)/', $version, $versionMatches)) {
$version = $versionMatches[0];
}
return [($this->app instanceof LumenApplication ? 'lumen' : 'laravel') => $version];
}
/**
* Tests whether session tracking can/should be enabled.
*
* @param array $config The configuration array
*
* @return bool true if session tracking should be enabled.
*/
protected function isSessionTrackingAllowed($config)
{
// Session support removed in Lumen 5.3 - only setup automatic session
// tracking if the session function is avaiable
return isset($config['auto_capture_sessions'])
&& $config['auto_capture_sessions']
&& function_exists('session');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['bugsnag', 'bugsnag.tracker', 'bugsnag.logger', 'bugsnag.multi'];
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Bugsnag\BugsnagLaravel\Commands;
use Bugsnag\BugsnagLaravel\Facades\Bugsnag;
use Bugsnag\Utils;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
class DeployCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'bugsnag:deploy';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Notifies Bugsnag of a build';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
Bugsnag::build(
$this->option('repository'),
$this->option('revision'),
$this->option('provider'),
$this->option('builder') ?: Utils::getBuilderName()
);
$this->info('Notified Bugsnag of the build!');
}
/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$this->handle();
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['repository', null, InputOption::VALUE_OPTIONAL, 'The repository from which you are deploying the code.', null],
['branch', null, InputOption::VALUE_OPTIONAL, 'The source control branch from which you are deploying. Deprecated.', null],
['revision', null, InputOption::VALUE_OPTIONAL, 'The source control revision you are currently deploying.', null],
['provider', null, InputOption::VALUE_OPTIONAL, 'The provider of your source control repository.', null],
['builder', null, InputOption::VALUE_OPTIONAL, 'The machine or person who has executed the build', null],
];
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Closure;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Log\Events\MessageLogged;
use RuntimeException;
trait EventTrait
{
/**
* The event dispatcher instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $dispatcher;
/**
* Get the event dispatcher instance.
*
* @return \Illuminate\Contracts\Events\Dispatcher
*/
public function getEventDispatcher()
{
return $this->dispatcher;
}
/**
* Set the event dispatcher instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
*
* @return void
*/
public function setEventDispatcher(Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Register a new callback handler for when a log event is triggered.
*
* @param \Closure $callback
*
* @throws \RuntimeException
*
* @return void
*/
public function listen(Closure $callback)
{
if (!isset($this->dispatcher)) {
throw new RuntimeException('Events dispatcher has not been set.');
}
$this->dispatcher->listen(class_exists(MessageLogged::class) ? MessageLogged::class : 'illuminate.log', $callback);
}
/**
* Log a message to the logs.
*
* @param string $level
* @param mixed $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = [])
{
parent::log($level, $message, $context);
$this->fireLogEvent($level, $message, $context);
}
/**
* Fires a log event.
*
* @param string $level
* @param string $message
* @param array $context
*
* @return void
*/
protected function fireLogEvent($level, $message, array $context = [])
{
// If the event dispatcher is set, we will pass along the parameters to the
// log listeners. These are useful for building profilers or other tools
// that aggregate all of the log messages for a given "request" cycle.
if (!isset($this->dispatcher)) {
return;
}
if (class_exists(MessageLogged::class)) {
$this->dispatcher->dispatch(new MessageLogged($level, $message, $context));
} else {
$this->dispatcher->fire('illuminate.log', compact('level', 'message', 'context'));
}
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Bugsnag\BugsnagLaravel\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static void build(string|null $repository = null, string|null $revision = null, string|null $provider = null, string|null $builderName = null)
* @method static void clearBreadcrumbs()
* @method static void flush()
* @method static string getApiKey()
* @method static array getAppData()
* @method static string getBuildEndpoint()
* @method static \Bugsnag\Configuration getConfig()
* @method static array getDeviceData()
* @method static array getDiscardClasses()
* @method static array getFilters()
* @method static int|null getMemoryLimitIncrease()
* @method static array getMetaData()
* @method static array getNotifier()
* @method static string getNotifyEndpoint()
* @method static \Bugsnag\Pipeline getPipeline()
* @method static array getRedactedKeys()
* @method static string getSessionEndpoint()
* @method static \Bugsnag\SessionTracker getSessionTracker()
* @method static string getStrippedFilePath(string $file)
* @method static bool isBatchSending()
* @method static string isInProject(string $file)
* @method static void leaveBreadcrumb(string $name, string|null $type = null, array $metaData = [])
* @method static void notify(\Bugsnag\Report $report, callable|null $callback = null)
* @method static void notifyError(string $name, string $message, callable|null $callback = null)
* @method static void notifyException(\Throwable $throwable, callable|null $callback = null)
* @method static void registerCallback(callable $callback)
* @method static void registerDefaultCallbacks()
* @method static void registerMiddleware(callable $middleware)
* @method static \Bugsnag\Client setAppType(string|null $type)
* @method static \Bugsnag\Client setAppVersion(string|null $appVersion)
* @method static \Bugsnag\Client setAutoCaptureSessions(bool $track)
* @method static \Bugsnag\Client setBatchSending(bool $batchSending)
* @method static \Bugsnag\Client setBuildEndpoint(string $endpoint)
* @method static \Bugsnag\Client setDiscardClasses(array $discardClasses)
* @method static \Bugsnag\Client setErrorReportingLevel(int|null $errorReportingLevel)
* @method static \Bugsnag\Client setFallbackType(string|null $type)
* @method static \Bugsnag\Client setFilters(array $filters)
* @method static \Bugsnag\Client setHostname(string|null $hostname)
* @method static \Bugsnag\Client setMemoryLimitIncrease(int|null $value)
* @method static \Bugsnag\Client setMetaData(array $metaData, bool $merge = true)
* @method static \Bugsnag\Client setNotifier(array $notifier)
* @method static \Bugsnag\Client setNotifyEndpoint(string $endpoint)
* @method static \Bugsnag\Client setNotifyReleaseStages(array|null $notifyReleaseStages = null)
* @method static \Bugsnag\Client setProjectRoot(string|null $projectRoot)
* @method static \Bugsnag\Client setProjectRootRegex(string|null $projectRootRegex)
* @method static \Bugsnag\Client setReleaseStage(string|null $releaseStage)
* @method static \Bugsnag\Client setRedactedKeys(array $redactedKeys)
* @method static \Bugsnag\Client setSendCode(bool $sendCode)
* @method static \Bugsnag\Client setSessionEndpoint(string $endpoint)
* @method static \Bugsnag\Client setStripPath(string|null $stripPath)
* @method static \Bugsnag\Client setStripPathRegex(string|null $stripPathRegex)
* @method static bool shouldCaptureSessions()
* @method static bool shouldIgnoreErrorCode(int $code)
* @method static bool shouldNotify()
* @method static bool shouldSendCode()
* @method static void startSession()
*
* @see \Bugsnag\Client
*/
class Bugsnag extends Facade
{
protected static function getFacadeAccessor()
{
return 'bugsnag';
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace Bugsnag\BugsnagLaravel\Internal;
/**
* The criteria for an error to be unhandled in a Laravel or Lumen app is as
* follows.
*
* 1. All unhandled exceptions must pass through one of the `HANDLER_CLASSES`
* report method
* 2. Unhandled exceptions will have had a caller from inside a vendor namespace
* or the App exception handler
* 3. The above exception handler must have originally been called from
* within a vendor namespace
*/
final class BacktraceProcessor
{
/**
* Searching for a frame where the framework's error handler is called.
*/
const STATE_FRAMEWORK_HANDLER = 1;
/**
* Searching for a frame where the framework's error handler was called by
* the framework itself, or the user's exception handler.
*/
const STATE_HANDLER_CALLER = 2;
/**
* Deciding if the frame was unhandled.
*/
const STATE_IS_UNHANDLED = 3;
/**
* A state to signal that we're done processing frames and know if the error
* was unhandled.
*/
const STATE_DONE = 4;
/**
* Laravel and Lumen use different exception handlers which live in different
* namespaces.
*/
const LARAVEL_HANDLER_CLASS = \Illuminate\Foundation\Exceptions\Handler::class;
const LUMEN_HANDLER_CLASS = \Laravel\Lumen\Exceptions\Handler::class;
/**
* The method used by the x_HANDLER_CLASS to report errors.
*/
const HANDLER_METHOD = 'report';
/**
* Laravel uses the "Exception" namespace, Lumen uses the plural "Exceptions"
* for the default app exception handler.
*/
const LARAVEL_APP_EXCEPTION_HANDLER = \App\Exception\Handler::class;
const LUMEN_APP_EXCEPTION_HANDLER = \App\Exceptions\Handler::class;
/**
* Namespaces used by Laravel and Lumen so we can determine if code was
* called by the user's app or the framework itself.
*
* Note this is not a mistake - Lumen uses the 'Laravel' namespace but
* Laravel itself does not
*/
const LARAVEL_VENDOR_NAMESPACE = 'Illuminate\\';
const LUMEN_VENDOR_NAMESPACE = 'Laravel\\';
/**
* The current state; one of the self::STATE_ constants.
*
* @var int
*/
private $state = self::STATE_FRAMEWORK_HANDLER;
/**
* This flag will be set to 'true' if we determine the error was unhandled.
*
* @var bool
*/
private $unhandled = false;
/**
* A backtrace matching the format of PHP's 'debug_backtrace'.
*
* @var array
*/
private $backtrace;
/**
* @param array $backtrace
*/
public function __construct(array $backtrace)
{
$this->backtrace = $backtrace;
}
/**
* Determine if the backtrace was from an unhandled error.
*
* @return bool
*/
public function isUnhandled()
{
foreach ($this->backtrace as $frame) {
$this->processFrame($frame);
// stop iterating early if we know we're done
if ($this->state === self::STATE_DONE) {
break;
}
}
return $this->unhandled;
}
/**
* @param array $frame
*
* @return void
*/
private function processFrame(array $frame)
{
if (!isset($frame['class'])) {
return;
}
$class = $frame['class'];
switch ($this->state) {
case self::STATE_FRAMEWORK_HANDLER:
// if this class is a framework exception handler and the function
// matches self::HANDLER_METHOD, we can move on to searching for
// the caller
if (($class === self::LARAVEL_HANDLER_CLASS || $class === self::LUMEN_HANDLER_CLASS)
&& isset($frame['function'])
&& $frame['function'] === self::HANDLER_METHOD
) {
$this->state = self::STATE_HANDLER_CALLER;
}
break;
case self::STATE_HANDLER_CALLER:
// if this is an app exception handler or a framework class, we
// can move on to determine if this was unhandled or not
if ($class === self::LARAVEL_APP_EXCEPTION_HANDLER
|| $class === self::LUMEN_APP_EXCEPTION_HANDLER
|| $this->isVendor($class)
) {
$this->state = self::STATE_IS_UNHANDLED;
}
break;
case self::STATE_IS_UNHANDLED:
// we are only interested in running this once so move immediately
// into the "done" state. This ensures we only check the frame
// immediately before the caller of the exception handler
$this->state = self::STATE_DONE;
// if this class is internal to the framework then the exception
// was unhandled
if ($this->isVendor($class)) {
$this->unhandled = true;
}
break;
}
}
/**
* Does the given class belong to a vendor namespace?
*
* @see self::VENDOR_NAMESPACES
*
* @param string $class
*
* @return bool
*/
private function isVendor($class)
{
return substr($class, 0, strlen(self::LARAVEL_VENDOR_NAMESPACE)) === self::LARAVEL_VENDOR_NAMESPACE
|| substr($class, 0, strlen(self::LUMEN_VENDOR_NAMESPACE)) === self::LUMEN_VENDOR_NAMESPACE;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Bugsnag\Client;
use Bugsnag\PsrLogger\BugsnagLogger;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Logging\Log;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
class LaravelLogger extends BugsnagLogger implements Log
{
use EventTrait;
/**
* Create a new laravel logger instance.
*
* @param \Bugsnag\Client $client
* @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
*
* @return void
*/
public function __construct(Client $client, Dispatcher $dispatcher = null)
{
parent::__construct($client);
$this->dispatcher = $dispatcher;
}
/**
* Register a file log handler.
*
* @param string $path
* @param string $level
*
* @return void
*/
public function useFiles($path, $level = 'debug')
{
//
}
/**
* Register a daily file log handler.
*
* @param string $path
* @param int $days
* @param string $level
*
* @return void
*/
public function useDailyFiles($path, $days = 0, $level = 'debug')
{
//
}
/**
* Get the underlying Monolog instance.
*
* @return \Monolog\Logger
*/
public function getMonolog()
{
//
}
/**
* Format the parameters for the logger.
*
* @param mixed $message
*
* @return string
*/
protected function formatMessage($message)
{
if (is_array($message)) {
return var_export($message, true);
}
if ($message instanceof Jsonable) {
return $message->toJson();
}
if ($message instanceof Arrayable) {
return var_export($message->toArray(), true);
}
return $message;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Bugsnag\BugsnagLaravel\Middleware;
use Bugsnag\BugsnagLaravel\Internal\BacktraceProcessor;
use Bugsnag\Report;
class UnhandledState
{
/**
* Execute the unhandled state middleware.
*
* @param \Bugsnag\Report $report the bugsnag report instance
* @param callable $next the next stage callback
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
$stackFrames = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
if (!is_array($stackFrames)) {
$stackFrames = [];
}
$backtraceProcessor = new BacktraceProcessor($stackFrames);
if ($backtraceProcessor->isUnhandled()) {
$report->setUnhandled(true);
$report->setSeverityReason([
'type' => 'unhandledExceptionMiddleware',
'attributes' => ['framework' => 'Laravel'],
]);
}
$next($report);
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Bugsnag\PsrLogger\MultiLogger as BaseLogger;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Logging\Log;
class MultiLogger extends BaseLogger implements Log
{
use EventTrait;
/**
* Create a new multi logger instance.
*
* @param \Psr\Log\LoggerInterface[] $loggers
* @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
*
* @return void
*/
public function __construct(array $loggers, Dispatcher $dispatcher = null)
{
parent::__construct($loggers);
$this->dispatcher = $dispatcher;
}
/**
* Register a file log handler.
*
* @param string $path
* @param string $level
*
* @return void
*/
public function useFiles($path, $level = 'debug')
{
foreach ($this->loggers as $logger) {
if ($logger instanceof Log) {
$logger->useFiles($path, $level);
}
}
}
/**
* Register a daily file log handler.
*
* @param string $path
* @param int $days
* @param string $level
*
* @return void
*/
public function useDailyFiles($path, $days = 0, $level = 'debug')
{
foreach ($this->loggers as $logger) {
if ($logger instanceof Log) {
$logger->useDailyFiles($path, $days, $level);
}
}
}
/**
* Get the underlying Monolog instance.
*
* @return \Monolog\Logger
*/
public function getMonolog()
{
foreach ($this->loggers as $logger) {
if (is_callable([$logger, 'getMonolog'])) {
$monolog = $logger->getMonolog();
if ($monolog === null) {
continue;
}
return $monolog;
}
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Bugsnag\BugsnagLaravel;
class OomBootstrapper
{
/**
* A bit of reserved memory to ensure we are able to increase the memory
* limit on an OOM.
*
* We can't reserve all of the memory that we need to send OOM reports
* because this would have a big overhead on every request, instead of just
* on shutdown in requests with errors.
*
* @var string|null
*/
private $reservedMemory;
/**
* A regex that matches PHP OOM errors.
*
* @var string
*/
private $oomRegex = '/^Allowed memory size of (\d+) bytes exhausted \(tried to allocate \d+ bytes\)/';
/**
* Allow Bugsnag to handle OOMs by registering a shutdown function that
* increases the memory limit. This must happen before Laravel's shutdown
* function is registered or it will have no effect.
*
* @return void
*/
public function bootstrap()
{
$this->reservedMemory = str_repeat(' ', 1024 * 256);
register_shutdown_function(function () {
$this->reservedMemory = null;
$lastError = error_get_last();
if (!$lastError) {
return;
}
$isOom = preg_match($this->oomRegex, $lastError['message'], $matches) === 1;
if (!$isOom) {
return;
}
/** @var \Bugsnag\Client|null $client */
$client = app('bugsnag');
// If the client exists and memory increase is enabled, bump the
// memory limit so we can report it. The client can be missing when
// the container isn't complete, e.g. when unit tests are running
if ($client && $client->getMemoryLimitIncrease() !== null) {
$currentMemoryLimit = (int) $matches[1];
ini_set('memory_limit', $currentMemoryLimit + $client->getMemoryLimitIncrease());
}
});
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Bugsnag\BugsnagLaravel\Queue;
class Tracker
{
/**
* The current job information.
*
* @var array|null
*/
protected $job;
/**
* Get the current context.
*
* @return string|null
*/
public function context()
{
if (isset($this->job['resolved'])) {
return $this->job['resolved'];
}
}
/**
* Get the current job information.
*
* @return array|null
*/
public function get()
{
return $this->job;
}
/**
* Set the current job information.
*
* @param array $job
*
* @return void
*/
public function set(array $job)
{
$this->job = $job;
}
/**
* Clear the current job information.
*
* @return void
*/
public function clear()
{
$this->job = null;
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Bugsnag\BugsnagLaravel\Request;
use Bugsnag\Request\RequestInterface;
use Exception;
use Illuminate\Http\Request;
class LaravelRequest implements RequestInterface
{
/**
* The illuminate request instance.
*
* @var \Illuminate\Http\Request
*/
protected $request;
/**
* Create a new laravel request instance.
*
* @param \Illuminate\Http\Request $request
*
* @return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Are we currently processing a request?
*
* @return bool
*/
public function isRequest()
{
return true;
}
/**
* Get the session data.
*
* @return array
*/
public function getSession()
{
try {
$session = $this->request->getSession();
} catch (Exception $e) {
return [];
}
return $session ? $session->all() : [];
}
/**
* Get the cookies.
*
* @return array
*/
public function getCookies()
{
return $this->request->cookies->all();
}
/**
* Get the request formatted as meta data.
*
* @return array
*/
public function getMetaData()
{
$data = [];
$data['url'] = $this->request->fullUrl();
$data['httpMethod'] = $this->request->getMethod();
$data['params'] = $this->request->input();
$data['clientIp'] = $this->request->getClientIp();
if ($agent = $this->request->header('User-Agent')) {
$data['userAgent'] = $agent;
}
if ($headers = $this->request->headers->all()) {
$data['headers'] = $headers;
}
return ['request' => $data];
}
/**
* Get the request context.
*
* @return string|null
*/
public function getContext()
{
return $this->request->getMethod().' '.$this->request->getPathInfo();
}
/**
* Get the request user id.
*
* @return string|null
*/
public function getUserId()
{
return $this->request->getClientIp();
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Bugsnag\BugsnagLaravel\Request;
use Bugsnag\Request\ConsoleRequest;
use Bugsnag\Request\ResolverInterface;
use Illuminate\Contracts\Container\Container;
use Illuminate\Http\Request;
class LaravelResolver implements ResolverInterface
{
/**
* The application instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
protected $app;
/**
* Create a new laravel request resolver instance.
*
* @param \Illuminate\Contracts\Container\Container $app
*
* @return void
*/
public function __construct(Container $app)
{
$this->app = $app;
}
/**
* Resolve the current request.
*
* @return \Bugsnag\Request\RequestInterface
*/
public function resolve()
{
$request = $this->app->make(Request::class);
if ($this->app->runningInConsole()) {
$command = $request->server('argv', []);
if (!is_array($command)) {
$command = explode(' ', $command);
}
return new ConsoleRequest($command);
}
return new LaravelRequest($request);
}
}

View File

@@ -1,77 +0,0 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| API Key
|--------------------------------------------------------------------------
|
| You can find your API key on your Bugsnag dashboard.
|
| This api key points the Bugsnag notifier to the project in your account
| which should receive your application's uncaught exceptions.
|
*/
'api_key' => 'YOUR-API-KEY-HERE',
/*
|--------------------------------------------------------------------------
| Notify Release Stages
|--------------------------------------------------------------------------
|
| Set which release stages should send notifications to Bugsnag.
|
| Example: array('development', 'production')
|
*/
'notify_release_stages' => null,
/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Set what server the Bugsnag notifier should send errors to. By default
| this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise
| this should be the URL to your Bugsnag instance.
|
*/
'endpoint' => null,
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Use this if you want to ensure you don't send sensitive data such as
| passwords, and credit card numbers to our servers. Any keys which
| contain these strings will be filtered.
|
*/
'filters' => array('password'),
/*
|--------------------------------------------------------------------------
| Proxy
|--------------------------------------------------------------------------
|
| If your server is behind a proxy server, you can configure this as well.
| Other than the host, none of these settings are mandatory.
|
| Note: Proxy configuration is only possible if the PHP cURL extension
| is installed.
|
| Example:
|
| 'proxy' => array(
| 'host' => 'bugsnag.com',
| 'port' => 42,
| 'user' => 'username',
| 'password' => 'password123'
| )
|
*/
'proxy' => null,
);