diff --git a/packages/EasyBugsnag/composer.json b/packages/EasyBugsnag/composer.json index 60152ed3e..5d73e1b52 100644 --- a/packages/EasyBugsnag/composer.json +++ b/packages/EasyBugsnag/composer.json @@ -9,6 +9,7 @@ "eonx-com/easy-utils": "^3.2", "nesbot/carbon": "^1.39 || ^2.22", "nette/utils": "^3.1", + "symfony/filesystem": "^4.4 || ^5.1.5", "symfony/var-dumper": "^4.4 || ^5.1.5" }, "require-dev": { diff --git a/packages/EasyBugsnag/src/Bridge/BridgeConstantsInterface.php b/packages/EasyBugsnag/src/Bridge/BridgeConstantsInterface.php index 738e7fc44..ed49139d2 100644 --- a/packages/EasyBugsnag/src/Bridge/BridgeConstantsInterface.php +++ b/packages/EasyBugsnag/src/Bridge/BridgeConstantsInterface.php @@ -11,6 +11,16 @@ interface BridgeConstantsInterface */ public const PARAM_API_KEY = 'easy_bugsnag.api_key'; + /** + * @var string + */ + public const PARAM_AWS_ECS_FARGATE_META_STORAGE_FILENAME = 'easy_bugsnag.aws_ecs_fargate_meta_storage_filename'; + + /** + * @var string + */ + public const PARAM_AWS_ECS_FARGATE_META_URL = 'easy_bugsnag.aws_ecs_fargate_meta_url'; + /** * @var string */ diff --git a/packages/EasyBugsnag/src/Bridge/Laravel/EasyBugsnagServiceProvider.php b/packages/EasyBugsnag/src/Bridge/Laravel/EasyBugsnagServiceProvider.php index 1bd99fc85..4be815d3f 100644 --- a/packages/EasyBugsnag/src/Bridge/Laravel/EasyBugsnagServiceProvider.php +++ b/packages/EasyBugsnag/src/Bridge/Laravel/EasyBugsnagServiceProvider.php @@ -11,6 +11,7 @@ use EonX\EasyBugsnag\Bridge\Laravel\Session\SessionTrackingListener; use EonX\EasyBugsnag\Bridge\Laravel\Session\SessionTrackingMiddleware; use EonX\EasyBugsnag\ClientFactory; +use EonX\EasyBugsnag\Configurators\AwsEcsFargateConfigurator; use EonX\EasyBugsnag\Configurators\BasicsConfigurator; use EonX\EasyBugsnag\Configurators\RuntimeVersionConfigurator; use EonX\EasyBugsnag\Interfaces\ClientFactoryInterface; @@ -20,8 +21,8 @@ use Illuminate\Routing\Events\RouteMatched; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; -use LaravelDoctrine\ORM\Loggers\Logger; use Laravel\Lumen\Application as LumenApplication; +use LaravelDoctrine\ORM\Loggers\Logger; final class EasyBugsnagServiceProvider extends ServiceProvider { @@ -39,6 +40,7 @@ public function register(): void { $this->mergeConfigFrom(__DIR__ . '/config/easy-bugsnag.php', 'easy-bugsnag'); + $this->registerAwsEcsFargate(); $this->registerClient(); $this->registerConfigurators(); $this->registerDoctrineOrm(); @@ -47,6 +49,19 @@ public function register(): void $this->registerShutdownStrategy(); } + private function registerAwsEcsFargate(): void + { + if (\config('easy-bugsnag.aws_ecs_fargate', false)) { + $this->app->singleton(AwsEcsFargateConfigurator::class, static function (): AwsEcsFargateConfigurator { + return new AwsEcsFargateConfigurator( + \config('easy-bugsnag.aws_ecs_fargate_meta_storage_filename'), + \config('easy-bugsnag.aws_ecs_fargate_meta_url') + ); + }); + $this->app->tag(AwsEcsFargateConfigurator::class, [BridgeConstantsInterface::TAG_CLIENT_CONFIGURATOR]); + } + } + private function registerClient(): void { // Client Factory + Client diff --git a/packages/EasyBugsnag/src/Bridge/Laravel/config/easy-bugsnag.php b/packages/EasyBugsnag/src/Bridge/Laravel/config/easy-bugsnag.php index d4addb470..545630867 100644 --- a/packages/EasyBugsnag/src/Bridge/Laravel/config/easy-bugsnag.php +++ b/packages/EasyBugsnag/src/Bridge/Laravel/config/easy-bugsnag.php @@ -8,6 +8,21 @@ */ 'api_key' => \env('BUGSNAG_API_KEY'), + /** + * Enable AWS ECS Fargate info in bugsnag. + */ + 'aws_ecs_fargate' => false, + + /** + * Filename to store AWS ECS Fargate meta, prevent requesting them each time. + */ + 'aws_ecs_fargate_meta_storage_filename' => '/var/www/storage/aws_ecs_fargate_meta.json', + + /** + * URL to request AWS ECS Fargate meta from. + */ + 'aws_ecs_fargate_meta_url' => \sprintf('%s/task', \env('ECS_CONTAINER_METADATA_URI_V4')), + /** * Enable Doctrine SQL Queries Breadcrumbs. */ diff --git a/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/Configuration.php b/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/Configuration.php index 70c76f753..9203d7be1 100644 --- a/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/Configuration.php +++ b/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/Configuration.php @@ -16,6 +16,13 @@ public function getConfigTreeBuilder(): TreeBuilder $treeBuilder->getRootNode() ->children() ->scalarNode('api_key')->isRequired()->end() + ->booleanNode('aws_ecs_fargate')->defaultFalse()->end() + ->scalarNode('aws_ecs_fargate_meta_url') + ->defaultValue('env(ECS_CONTAINER_METADATA_URI_V4)/task') + ->end() + ->scalarNode('aws_ecs_fargate_meta_storage_filename') + ->defaultValue('/var/www/var/aws_ecs_fargate_meta.json') + ->end() ->arrayNode('doctrine_dbal') ->beforeNormalization() ->always(static function ($v): array { diff --git a/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/EasyBugsnagExtension.php b/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/EasyBugsnagExtension.php index c2f669b0f..0d6424bb1 100644 --- a/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/EasyBugsnagExtension.php +++ b/packages/EasyBugsnag/src/Bridge/Symfony/DependencyInjection/EasyBugsnagExtension.php @@ -39,6 +39,18 @@ public function load(array $configs, ContainerBuilder $container): void ->registerForAutoconfiguration(ClientConfiguratorInterface::class) ->addTag(BridgeConstantsInterface::TAG_CLIENT_CONFIGURATOR); + if ($config['aws_ecs_fargate'] ?? false) { + $container->setParameter( + BridgeConstantsInterface::PARAM_AWS_ECS_FARGATE_META_STORAGE_FILENAME, + $config['aws_ecs_fargate_meta_storage_filename'] + ); + + $container->setParameter( + BridgeConstantsInterface::PARAM_AWS_ECS_FARGATE_META_URL, + $config['aws_ecs_fargate_meta_url'] + ); + } + if ($config['session_tracking'] ?? false) { $container->setParameter( BridgeConstantsInterface::PARAM_SESSION_TRACKING_EXCLUDE, diff --git a/packages/EasyBugsnag/src/Bridge/Symfony/Resources/config/aws_ecs_fargate.php b/packages/EasyBugsnag/src/Bridge/Symfony/Resources/config/aws_ecs_fargate.php new file mode 100644 index 000000000..be23543e6 --- /dev/null +++ b/packages/EasyBugsnag/src/Bridge/Symfony/Resources/config/aws_ecs_fargate.php @@ -0,0 +1,15 @@ +services(); + $services->defaults() + ->autoconfigure() + ->autowire(); + + $services->set(AwsEcsFargateConfigurator::class); +}; diff --git a/packages/EasyBugsnag/src/Configurators/AwsEcsFargateConfigurator.php b/packages/EasyBugsnag/src/Configurators/AwsEcsFargateConfigurator.php new file mode 100644 index 000000000..79f497533 --- /dev/null +++ b/packages/EasyBugsnag/src/Configurators/AwsEcsFargateConfigurator.php @@ -0,0 +1,85 @@ +filesystem = new Filesystem(); + $this->storageFilename = $storageFilename; + $this->url = $url; + + parent::__construct($priority); + } + + public function configure(Client $bugsnag): void + { + $appVersion = $this->resolveAppVersion(); + + $bugsnag->setAppVersion($appVersion); + + $bugsnag + ->getPipeline() + ->pipe(new CallbackBridge(function (Report $report): void { + $awsData = $this->getAwsFargateTaskData(); + + $report->addMetaData([ + 'aws' => [ + 'AvailabilityZone' => $awsData['AvailabilityZone'] ?? null, + 'Cluster' => $awsData['Cluster'] ?? null, + 'TaskArn' => $awsData['TaskARN'] ?? null, + 'TaskDefinition' => \sprintf('%s:%s', $awsData['Family'] ?? null, $awsData['Revision'] ?? null), + ], + ]); + })); + } + + private function resolveAppVersion(): ?string + { + $appVersion = \getenv('APP_VERSION'); + + if (\is_string($appVersion)) { + return $appVersion; + } + + $awsData = $this->getAwsFargateTaskData(); + $image = (string)($awsData['Containers'][0]['Image'] ?? ''); + + return \explode(':', $image)[1] ?? null; + } + + /** + * @return mixed[] + */ + private function getAwsFargateTaskData(): array + { + if ($this->filesystem->exists($this->storageFilename) === false) { + $this->filesystem->dumpFile($this->storageFilename, (string)\file_get_contents($this->url)); + } + + return \json_decode((string)\file_get_contents($this->storageFilename), true); + } +}