Skip to content

Commit

Permalink
[EasyBugsnag] Implement AWS ECS Fargate integration (#618)
Browse files Browse the repository at this point in the history
  • Loading branch information
natepage committed Jul 1, 2021
1 parent 2e00632 commit 8598146
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/EasyBugsnag/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
10 changes: 10 additions & 0 deletions packages/EasyBugsnag/src/Bridge/BridgeConstantsInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
{
Expand All @@ -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();
Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions packages/EasyBugsnag/src/Bridge/Laravel/config/easy-bugsnag.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

use EonX\EasyBugsnag\Configurators\AwsEcsFargateConfigurator;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $container): void {
$services = $container->services();
$services->defaults()
->autoconfigure()
->autowire();

$services->set(AwsEcsFargateConfigurator::class);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace EonX\EasyBugsnag\Configurators;

use Bugsnag\Client;
use Bugsnag\Middleware\CallbackBridge;
use Bugsnag\Report;
use Symfony\Component\Filesystem\Filesystem;

final class AwsEcsFargateConfigurator extends AbstractClientConfigurator
{
/**
* @var \Symfony\Component\Filesystem\Filesystem
*/
private $filesystem;

/**
* @var string
*/
private $storageFilename;

/**
* @var string
*/
private $url;

public function __construct(string $storageFilename, string $url, ?int $priority = null)
{
$this->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);
}
}

0 comments on commit 8598146

Please sign in to comment.