Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect application root, add some configurations #88

Merged
merged 11 commits into from
Oct 14, 2019
Merged
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

- New `\Scoutapm\Config\ConfigKey` class containing `public const`s for configuration key names (#83)
- Added config key `log_level` which overrides Scout APM's minimum log level (#83)
- Added more new config keys (#88):
- `application_root` (defaults to `composer.json` location, or `$_SERVER['DOCUMENT_ROOT']`
- `scm_subdirectory` (defaults to `.git` location, or `application_root` value)
- `revision_sha` (defaults to version detected by `ocramius/package-versions`)
- `hostname` (defaults to value of `gethostname()`)
- `core_agent_permissions` (defaults to `0777`)
- Added warning when `name` or `key` configurations are not set

### Fixed

Expand Down
24 changes: 22 additions & 2 deletions src/Agent.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
use Scoutapm\Extension\ExtentionCapabilities;
use Scoutapm\Extension\PotentiallyAvailableExtensionCapabilities;
use Scoutapm\Logger\FilteredLogLevelDecorator;
use function is_string;
use function sprintf;

final class Agent implements ScoutApmAgent
{
Expand Down Expand Up @@ -72,11 +74,27 @@ public function __construct(Config $configuration, Connector $connector, LoggerI
);
}

if ($this->config->get(ConfigKey::MONITORING_ENABLED)) {
$this->warnIfConfigValueIsNotSet(ConfigKey::APPLICATION_NAME);
$this->warnIfConfigValueIsNotSet(ConfigKey::APPLICATION_KEY);
}

$this->request = new Request();

$this->ignoredEndpoints = new IgnoredEndpoints($configuration->get(ConfigKey::IGNORED_ENDPOINTS));
}

private function warnIfConfigValueIsNotSet(string $configKey) : void
{
$configValue = $this->config->get($configKey);

if ($configValue !== null && (! is_string($configValue) || $configValue !== '')) {
return;
}

$this->logger->warning(sprintf('Config key "%s" should be set, but it was empty', $configKey));
asgrim marked this conversation as resolved.
Show resolved Hide resolved
}

private static function createConnectorFromConfig(Config $config) : SocketConnector
{
return new SocketConnector($config->get(ConfigKey::CORE_AGENT_SOCKET_PATH));
Expand Down Expand Up @@ -120,7 +138,8 @@ public function connect() : void
$this->config->get(ConfigKey::CORE_AGENT_DIRECTORY) . '/' . $this->config->get(ConfigKey::CORE_AGENT_FULL_NAME),
$this->config->get(ConfigKey::CORE_AGENT_FULL_NAME),
$this->logger,
$this->config->get(ConfigKey::CORE_AGENT_DOWNLOAD_URL)
$this->config->get(ConfigKey::CORE_AGENT_DOWNLOAD_URL),
$this->config->get(ConfigKey::CORE_AGENT_PERMISSIONS)
)
);
$manager->launch();
Expand Down Expand Up @@ -275,7 +294,8 @@ public function send() : bool
}

if (! $this->connector->sendCommand(new Metadata(
new DateTimeImmutable('now', new DateTimeZone('UTC'))
new DateTimeImmutable('now', new DateTimeZone('UTC')),
$this->config
))) {
return false;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Config/ConfigKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ abstract class ConfigKey
public const LOG_LEVEL = 'log_level';
public const API_VERSION = 'api_version';
public const IGNORED_ENDPOINTS = 'ignore';
public const APPLICATION_ROOT = 'application_root';
public const SCM_SUBDIRECTORY = 'scm_subdirectory';
public const REVISION_SHA = 'revision_sha';
public const HOSTNAME = 'hostname';
public const CORE_AGENT_LOG_LEVEL = 'core_agent_log_level';
public const CORE_AGENT_LOG_FILE = 'core_agent_log_file';
public const CORE_AGENT_CONFIG_FILE = 'core_agent_config_file';
Expand All @@ -23,4 +27,5 @@ abstract class ConfigKey
public const CORE_AGENT_DOWNLOAD_ENABLED = 'core_agent_download';
public const CORE_AGENT_VERSION = 'core_agent_version';
public const CORE_AGENT_TRIPLE = 'core_agent_triple';
public const CORE_AGENT_PERMISSIONS = 'core_agent_permissions';
}
7 changes: 4 additions & 3 deletions src/Config/Source/DefaultSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/** @internal */
class DefaultSource
{
/** @var array<string, (string|bool|array<int, string>)> */
/** @var array<string, (string|bool|array<int, string>|int)> */
private $defaults;

public function __construct()
Expand All @@ -37,7 +37,7 @@ public function hasKey(string $key) : bool
*
* Only valid if the Source has previously returned "true" to `hasKey`
*
* @return string|bool|array<int, string>|null
* @return string|bool|array<int, string>|int|null
*/
public function get(string $key)
{
Expand All @@ -49,7 +49,7 @@ public function get(string $key)
*
* Only valid if the Source has previously returned "true" to `hasKey`
*
* @return array<string, (string|bool|array<int, string>)>
* @return array<string, (string|bool|array<int, string>|int)>
*/
private function getDefaultConfig() : array
{
Expand All @@ -60,6 +60,7 @@ private function getDefaultConfig() : array
ConfigKey::CORE_AGENT_LAUNCH_ENABLED => true,
ConfigKey::CORE_AGENT_VERSION => 'v1.2.2',
ConfigKey::CORE_AGENT_DOWNLOAD_URL => 'https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release',
ConfigKey::CORE_AGENT_PERMISSIONS => 0777,
asgrim marked this conversation as resolved.
Show resolved Hide resolved
ConfigKey::MONITORING_ENABLED => false,
ConfigKey::IGNORED_ENDPOINTS => [],
ConfigKey::LOG_LEVEL => 'debug',
Expand Down
26 changes: 18 additions & 8 deletions src/CoreAgent/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,16 @@ class Downloader
/** @var string */
private $downloadUrl;

public function __construct(string $coreAgentDir, string $coreAgentFullName, LoggerInterface $logger, string $downloadUrl)
{
/** @var int */
private $coreAgentPermissions;

public function __construct(
string $coreAgentDir,
string $coreAgentFullName,
LoggerInterface $logger,
string $downloadUrl,
int $coreAgentPermissions
) {
$this->logger = $logger;

$this->coreAgentDir = $coreAgentDir;
Expand All @@ -77,9 +85,10 @@ public function __construct(string $coreAgentDir, string $coreAgentFullName, Log
*
* @link https://bugs.php.net/bug.php?id=58852
*/
$this->package_location = $coreAgentDir . '/' . str_replace('.', '_', $coreAgentFullName) . '.tgz';
$this->download_lock_path = $coreAgentDir . '/download.lock';
$this->downloadUrl = $downloadUrl;
$this->package_location = $coreAgentDir . '/' . str_replace('.', '_', $coreAgentFullName) . '.tgz';
$this->download_lock_path = $coreAgentDir . '/download.lock';
$this->downloadUrl = $downloadUrl;
$this->coreAgentPermissions = $coreAgentPermissions;
}

public function download() : void
Expand All @@ -104,12 +113,13 @@ public function download() : void
private function createCoreAgentDir() : void
{
try {
$permissions = 0777; // TODO: AgentContext.instance.config.core_agent_permissions()
$recursive = true;
$destination = $this->coreAgentDir;

if (! is_dir($destination)) {
mkdir($destination, $permissions, $recursive);
if (! is_dir($destination)
&& ! mkdir($destination, $this->coreAgentPermissions, $recursive)
&& ! is_dir($destination)) {
throw new RuntimeException(sprintf('Directory "%s" was not created', $destination));
}
} catch (Throwable $e) {
$this->logger->error('Failed to create directory: ' . $destination);
Expand Down
84 changes: 78 additions & 6 deletions src/Events/Metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@

use DateTimeImmutable;
use PackageVersions\Versions;
use Scoutapm\Config;
use Scoutapm\Config\ConfigKey;
use Scoutapm\Connector\Command;
use Scoutapm\Helper\Timer;
use const PHP_VERSION;
use function array_key_exists;
use function array_keys;
use function array_map;
use function dirname;
use function explode;
use function file_exists;
use function getenv;
use function gethostname;
use function is_readable;
use function is_string;
use function realpath;

/**
* Also called AppServerLoad in other agents
Expand All @@ -24,11 +33,15 @@ final class Metadata implements Command
/** @var Timer */
private $timer;

public function __construct(DateTimeImmutable $now)
/** @var Config */
private $config;

public function __construct(DateTimeImmutable $now, Config $config)
{
// Construct and stop the timer to use its timestamp logic. This event
// is a single point in time, not a range.
$this->timer = new Timer((float) $now->format('U.u'));
$this->timer = new Timer((float) $now->format('U.u'));
$this->config = $config;
}

/**
Expand All @@ -44,20 +57,79 @@ private function data() : array
'framework_version' => '',
'environment' => '',
'app_server' => '',
'hostname' => gethostname(),
'hostname' => $this->config->get(ConfigKey::HOSTNAME) ?? gethostname(),
'database_engine' => '',
'database_adapter' => '',
'application_name' => '',
'application_name' => $this->config->get(ConfigKey::APPLICATION_NAME) ?? '',
'libraries' => $this->getLibraries(),
'paas' => '',
'application_root' => '',
'scm_subdirectory' => '',
'application_root' => $this->applicationRoot(),
'scm_subdirectory' => $this->scmSubdirectory(),
'git_sha' => $this->rootPackageGitSha(),
];
}

/**
* Try to locate a file or folder in any parent directory (upwards of this library itself)
*/
private function locateFileOrFolder(string $fileOrFolder) : ?string
{
// Starting 3 levels up will avoid finding scout-apm-php's own contents
$dir = dirname(__DIR__, 3);
$rootOrHome = '/';

while (dirname($dir) !== $dir && $dir !== $rootOrHome) {
$fileOrFolderAttempted = $dir . '/' . $fileOrFolder;
if (file_exists($fileOrFolderAttempted) && is_readable($fileOrFolderAttempted)) {
return realpath($dir);
}
$dir = dirname($dir);
}

return null;
}

private function applicationRoot() : string
{
$applicationRootConfiguration = $this->config->get(ConfigKey::APPLICATION_ROOT);
if (is_string($applicationRootConfiguration) && $applicationRootConfiguration !== '') {
return $applicationRootConfiguration;
}

$composerJsonLocation = $this->locateFileOrFolder('composer.json');
if ($composerJsonLocation !== null) {
return $composerJsonLocation;
}

if (! array_key_exists('DOCUMENT_ROOT', $_SERVER)) {
return '';
}

return $_SERVER['DOCUMENT_ROOT'];
}

private function scmSubdirectory() : string
{
$scmSubdirectoryConfiguration = $this->config->get(ConfigKey::SCM_SUBDIRECTORY);
if (is_string($scmSubdirectoryConfiguration) && $scmSubdirectoryConfiguration !== '') {
return $scmSubdirectoryConfiguration;
}

return $this->locateFileOrFolder('.git') ?? $this->applicationRoot();
}

private function rootPackageGitSha() : string
{
$revisionShaConfiguration = $this->config->get(ConfigKey::REVISION_SHA);
if (is_string($revisionShaConfiguration) && $revisionShaConfiguration !== '') {
return $revisionShaConfiguration;
}

$herokuSlugCommit = getenv('HEROKU_SLUG_COMMIT');
if (is_string($herokuSlugCommit) && $herokuSlugCommit !== '') {
return $herokuSlugCommit;
}

return explode('@', Versions::getVersion(Versions::ROOT_PACKAGE_NAME))[1];
}

Expand Down
Loading