Skip to content

Commit

Permalink
Merge pull request #286 from scoutapp/instrumentation-for-artisan-com…
Browse files Browse the repository at this point in the history
…mands

Added instrumentation for artisan console commands
  • Loading branch information
asgrim authored Sep 13, 2022
2 parents 58756cb + 696f6db commit 0bdd3ab
Show file tree
Hide file tree
Showing 17 changed files with 458 additions and 40 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,7 @@ jobs:
uses: octokit/[email protected]
id: get_latest_ext_release
with:
route: GET /repos/{owner}/{repo}/releases/latest
owner: scoutapp
repo: scout-apm-php-ext
route: GET /repos/scoutapp/scout-apm-php-ext/releases/latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Install scoutapm extension"
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ coverage: ## generate code coverage reports
$(PHP_PATH) vendor/bin/phpunit --testsuite unit --coverage-html build/coverage-html --coverage-text $(OPTS)

deps-install: ## Install the currently-locked set of dependencies
git restore composer.lock
rm -Rf vendor
$(PHP_PATH) $(COMPOSER_PATH) install

Expand Down
18 changes: 6 additions & 12 deletions known-issues.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,9 @@
</MixedAssignment>
</file>
<file src="src/Laravel/Providers/ScoutApmServiceProvider.php">
<InvalidArgument occurrences="2">
<InvalidArgument occurrences="1">
<code>$app-&gt;make(self::CACHE_SERVICE_KEY)</code>
<code>$app-&gt;make(self::CONFIG_SERVICE_KEY)</code>
</InvalidArgument>
<MixedArgument occurrences="1">
<code>$app-&gt;make(self::CONFIG_SERVICE_KEY)-&gt;get(ConfigKey::LOG_LEVEL)</code>
</MixedArgument>
<UndefinedClass occurrences="1">
<code>$app-&gt;make(self::CONFIG_SERVICE_KEY)</code>
</UndefinedClass>
<UnusedClosureParam occurrences="1">
<code>$event</code>
</UnusedClosureParam>
</file>
<file src="src/Logger/FilteredLogLevelDecorator.php">
<MixedArrayOffset occurrences="1">
Expand Down Expand Up @@ -302,7 +292,11 @@
<PossiblyInvalidArgument occurrences="1">
<code>$this-&gt;application</code>
</PossiblyInvalidArgument>
<UndefinedClass occurrences="6">
<UndefinedClass occurrences="10">
<code>$events</code>
<code>$events</code>
<code>$events</code>
<code>$events</code>
<code>$events</code>
<code>$events</code>
<code>$events</code>
Expand Down
7 changes: 7 additions & 0 deletions src/Helper/Superglobals/Superglobals.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,11 @@ public function env(): array;
* @return array<string, string>
*/
public function server(): array;

/**
* @internal This is not covered by BC promise
*
* @return list<string>
*/
public function argv(): array;
}
39 changes: 37 additions & 2 deletions src/Helper/Superglobals/SuperglobalsArrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

namespace Scoutapm\Helper\Superglobals;

use Throwable;
use Webmozart\Assert\Assert;

use function array_combine;
use function array_filter;
use function array_key_exists;
use function array_keys;
use function array_map;
use function array_values;
use function is_object;
use function is_scalar;
use function method_exists;
Expand All @@ -25,6 +30,8 @@ final class SuperglobalsArrays implements Superglobals
private $env;
/** @var array<string, string> */
private $server;
/** @var list<string> */
private $argv;

/**
* @internal This is not covered by BC promise
Expand All @@ -33,13 +40,34 @@ final class SuperglobalsArrays implements Superglobals
* @param array<array-key, mixed> $request
* @param array<string, string> $env
* @param array<string, string> $server
* @param list<string> $argv
*/
public function __construct(array $session, array $request, array $env, array $server)
public function __construct(array $session, array $request, array $env, array $server, array $argv)
{
$this->session = $session;
$this->request = $request;
$this->env = $env;
$this->server = $server;
$this->argv = $argv;
}

/** @return list<string> */
private static function typeSafeArgvFromGlobals(): array
{
if (! array_key_exists('argv', $GLOBALS)) {
return [];
}

$argv = $GLOBALS['argv'];

try {
Assert::isArray($argv);
Assert::allString($argv);

return array_values($argv);
} catch (Throwable $anything) {
return [];
}
}

/**
Expand All @@ -51,7 +79,8 @@ public static function fromGlobalState(): self
$_SESSION ?? [],
$_REQUEST,
self::convertKeysAndValuesToStrings($_ENV),
self::convertKeysAndValuesToStrings($_SERVER)
self::convertKeysAndValuesToStrings($_SERVER),
self::typeSafeArgvFromGlobals()
);
}

Expand Down Expand Up @@ -109,4 +138,10 @@ public function server(): array
{
return $this->server;
}

/** @return list<string> */
public function argv(): array
{
return $this->argv;
}
}
61 changes: 61 additions & 0 deletions src/Laravel/Console/ConsoleListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Scoutapm\Laravel\Console;

use Exception;
use Illuminate\Console\Events\CommandFinished;
use Illuminate\Console\Events\CommandStarting;
use Scoutapm\Events\Span\SpanReference;
use Scoutapm\Events\Tag\Tag;
use Scoutapm\ScoutApmAgent;

use function implode;
use function sprintf;

final class ConsoleListener
{
/** @var ScoutApmAgent */
private $agent;
/** @var list<string> */
private $argv;

/** @param list<string> $argv */
public function __construct(ScoutApmAgent $agent, array $argv)
{
$this->agent = $agent;
$this->argv = $argv;
}

/** @throws Exception */
public function startSpanForCommand(CommandStarting $commandStartingEvent): void
{
if ($commandStartingEvent->command === null) {
return;
}

$this->agent->startNewRequest();

$this->agent->addContext(Tag::TAG_ARGUMENTS, implode(' ', $this->argv));

/** @noinspection UnusedFunctionResultInspection */
$this->agent->startSpan(sprintf(
'%s/artisan/%s',
SpanReference::INSTRUMENT_JOB,
$commandStartingEvent->command
));
}

/** @throws Exception */
public function stopSpanForCommand(CommandFinished $commandFinishedEvent): void
{
if ($commandFinishedEvent->command === null) {
return;
}

$this->agent->stopSpan();
$this->agent->connect();
$this->agent->send();
}
}
27 changes: 25 additions & 2 deletions src/Laravel/Providers/ScoutApmServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Dingo\Api\Routing\Router as DingoRouter;
use Illuminate\Cache\CacheManager;
use Illuminate\Console\Events\CommandFinished;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Container\Container;
Expand All @@ -30,7 +32,9 @@
use Scoutapm\CoreAgent\Launcher;
use Scoutapm\CoreAgent\Verifier;
use Scoutapm\Helper\ComposerPackagesCheck;
use Scoutapm\Helper\Superglobals\SuperglobalsArrays;
use Scoutapm\Laravel\Console\Commands;
use Scoutapm\Laravel\Console\ConsoleListener;
use Scoutapm\Laravel\Database\QueryListener;
use Scoutapm\Laravel\Middleware\ActionInstrument;
use Scoutapm\Laravel\Middleware\IgnoredEndpoints;
Expand All @@ -51,6 +55,7 @@
use function array_filter;
use function array_map;
use function array_merge;
use function array_splice;
use function config_path;
use function count;
use function sprintf;
Expand All @@ -62,7 +67,8 @@ final class ScoutApmServiceProvider extends ServiceProvider

private const VIEW_ENGINES_TO_WRAP = ['file', 'php', 'blade'];

public const INSTRUMENT_LARAVEL_QUEUES = 'laravel_queues';
public const INSTRUMENT_LARAVEL_QUEUES = 'laravel_queues';
public const INSTRUMENT_LARAVEL_CONSOLE = 'laravel_console';

/** @var bool */
private $resolveViewEngineResolverOnBoot = false;
Expand Down Expand Up @@ -260,6 +266,10 @@ public function boot(
$this->instrumentQueues($agent, $application->make('events'), $runningInConsole);
}

if ($runningInConsole && $agent->shouldInstrument(self::INSTRUMENT_LARAVEL_CONSOLE)) {
$this->instrumentConsole($agent, $application->make('events'));
}

if ($this->resolveViewEngineResolverOnBoot) {
$engineResolver = $this->app->make('view.engine.resolver');
$this->registerWrappedEngines($engineResolver);
Expand Down Expand Up @@ -299,6 +309,19 @@ private function instrumentDatabaseQueries(ScoutApmAgent $agent, Connection $con
});
}

private function instrumentConsole(ScoutApmAgent $agent, Dispatcher $eventDispatcher): void
{
$argv = SuperglobalsArrays::fromGlobalState()->argv();
$listener = new ConsoleListener($agent, array_splice($argv, 2));

$eventDispatcher->listen(CommandStarting::class, static function (CommandStarting $event) use ($listener): void {
$listener->startSpanForCommand($event);
});
$eventDispatcher->listen(CommandFinished::class, static function (CommandFinished $event) use ($listener): void {
$listener->stopSpanForCommand($event);
});
}

private function instrumentQueues(ScoutApmAgent $agent, Dispatcher $eventDispatcher, bool $runningInConsole): void
{
$listener = new JobQueueListener($agent);
Expand All @@ -311,7 +334,7 @@ private function instrumentQueues(ScoutApmAgent $agent, Dispatcher $eventDispatc
$listener->startSpanForJob($event);
});

$eventDispatcher->listen(JobProcessed::class, static function (JobProcessed $event) use ($listener, $runningInConsole): void {
$eventDispatcher->listen(JobProcessed::class, static function () use ($listener, $runningInConsole): void {
$listener->stopSpanForJob();

if (! $runningInConsole) {
Expand Down
5 changes: 3 additions & 2 deletions tests/Unit/Errors/ErrorEventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function testToJsonableArray(): void
$exception = new RuntimeException($exceptionMessage);

$request = Request::fromConfigAndOverrideTime(
new SuperglobalsArrays([], [], [], []),
new SuperglobalsArrays([], [], [], [], []),
$config,
$this->createMock(FindRequestHeaders::class)
);
Expand Down Expand Up @@ -58,7 +58,8 @@ public function testToJsonableArray(): void
[
'HTTPS' => 'on',
'HTTP_HOST' => 'the-great-website',
]
],
[]
),
$determineHostname,
$findRootPackageGitSha
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function setUp(): void
$this->config = Config::fromArray([]);
$this->logger = new TestLogger();
$this->findApplicationRoot = $this->createMock(FindApplicationRoot::class);
$this->superglobals = new SuperglobalsArrays([], [], [], []);
$this->superglobals = new SuperglobalsArrays([], [], [], [], []);
$this->determineHostname = $this->createMock(DetermineHostname::class);
$this->findRootPackageGitSha = $this->createMock(FindRootPackageGitSha::class);

Expand Down
1 change: 1 addition & 0 deletions tests/Unit/Events/MetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ public function testHerokuSlugCommitOverridesTheGitSha(): void
[],
[],
['HEROKU_SLUG_COMMIT' => $testHerokuSlugCommit],
[],
[]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function testConfigurationOverridesApplicationRoot(): void
$findApplicationRoot = new FindApplicationRootWithConfigOverride(
$this->locateFileOrFolder,
Config::fromArray([Config\ConfigKey::APPLICATION_ROOT => '/my/configured/app/root']),
new SuperglobalsArrays([], [], [], ['DOCUMENT_ROOT' => '/my/document/root/path'])
new SuperglobalsArrays([], [], [], ['DOCUMENT_ROOT' => '/my/document/root/path'], [])
);

self::assertSame('/my/configured/app/root', ($findApplicationRoot)());
Expand All @@ -40,7 +40,7 @@ public function testComposerJsonLocationCanBeUsedAsApplicationRoot(): void
$findApplicationRoot = new FindApplicationRootWithConfigOverride(
$this->locateFileOrFolder,
Config::fromArray([]),
new SuperglobalsArrays([], [], [], ['DOCUMENT_ROOT' => '/my/document/root/path'])
new SuperglobalsArrays([], [], [], ['DOCUMENT_ROOT' => '/my/document/root/path'], [])
);

$this->locateFileOrFolder
Expand All @@ -57,7 +57,7 @@ public function testMissingDocumentRootInServerWillReturnEmptyString(): void
$findApplicationRoot = new FindApplicationRootWithConfigOverride(
$this->locateFileOrFolder,
Config::fromArray([]),
new SuperglobalsArrays([], [], [], [])
new SuperglobalsArrays([], [], [], [], [])
);
self::assertSame('', ($findApplicationRoot)());
}
Expand All @@ -67,7 +67,7 @@ public function testDocumentRootIsReturned(): void
$findApplicationRoot = new FindApplicationRootWithConfigOverride(
$this->locateFileOrFolder,
Config::fromArray([]),
new SuperglobalsArrays([], [], [], ['DOCUMENT_ROOT' => '/my/document/root/path'])
new SuperglobalsArrays([], [], [], ['DOCUMENT_ROOT' => '/my/document/root/path'], [])
);
self::assertSame('/my/document/root/path', ($findApplicationRoot)());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public function testFromServerGlobal(): void
'HTTP_ACCEPT' => '*/*',
'HTTP_X_SOMETHING_EMPTY' => '',
'HTTP_X_SOMETHING_CUSTOM' => 'Something custom',
]
],
[]
)))->__invoke()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function testFindingRootPackageGitShaFromConfigOverride(): void
'abcdef',
(new FindRootPackageGitShaWithHerokuAndConfigOverride(
Config::fromArray([Config\ConfigKey::REVISION_SHA => 'abcdef']),
new SuperglobalsArrays([], [], [], [])
new SuperglobalsArrays([], [], [], [], [])
))()
);
}
Expand All @@ -30,7 +30,7 @@ public function testFindingRootPackageGitShaFromHerokuSlugCommit(): void
'bcdef1',
(new FindRootPackageGitShaWithHerokuAndConfigOverride(
Config::fromArray([]),
new SuperglobalsArrays([], [], ['HEROKU_SLUG_COMMIT' => 'bcdef1'], [])
new SuperglobalsArrays([], [], ['HEROKU_SLUG_COMMIT' => 'bcdef1'], [], [])
))()
);
}
Expand All @@ -41,7 +41,7 @@ public function testFindingRootPackageGitShaFallbackUsingComposer(): void
InstalledVersions::getRootPackage()['reference'],
(new FindRootPackageGitShaWithHerokuAndConfigOverride(
Config::fromArray([]),
new SuperglobalsArrays([], [], [], [])
new SuperglobalsArrays([], [], [], [], [])
))()
);
}
Expand Down
Loading

0 comments on commit 0bdd3ab

Please sign in to comment.