Skip to content

Commit

Permalink
Define third party loggers (#2)
Browse files Browse the repository at this point in the history
* Define third party loggers bundled in the core

* Run additional test
  • Loading branch information
convenient authored Jan 27, 2022
1 parent 0f9dfd7 commit 051b981
Show file tree
Hide file tree
Showing 8 changed files with 461 additions and 11 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ script:
- mysql -uroot -e 'SET @@global.sql_mode = NO_ENGINE_SUBSTITUTION; DROP DATABASE IF EXISTS magento_integration_tests; CREATE DATABASE magento_integration_tests;'
- cp dev/tests/integration/etc/install-config-mysql.travis-no-rabbitmq.php.dist dev/tests/integration/etc/install-config-mysql.php
- php $TRAVIS_BUILD_DIR/dev/prepare_phpunit_config.php $TRAVIS_BUILD_DIR/vendor/ampersand/travis-vanilla-magento/instances/ampmodule
- vendor/bin/phpunit -c $(pwd)/dev/tests/integration/phpunit.xml.dist --testsuite Integration --debug
- composer install -o
- IS_CI_PIPELINE=1 vendor/bin/phpunit -c $(pwd)/dev/tests/integration/phpunit.xml.dist --testsuite Integration --debug
# Output the custom loggers command filtering out modules we've accounted for
- if [[ $TEST_GROUP = magento_23 ]]; then php bin/magento ampersand:log-correlation-id:list-custom-loggers --filter "Klarna\Core\Logger\Logger" --filter "Dotdigitalgroup\Email\Logger\Logger" --filter "Amazon\Core\Logger\Logger" --filter "Amazon\Core\Logger\IpnLogger" ; fi
- if [[ $TEST_GROUP = magento_latest ]]; then php bin/magento ampersand:log-correlation-id:list-custom-loggers --filter "Yotpo\Yotpo\Model\Logger" --filter "Klarna\Core\Logger\Logger" --filter "Dotdigitalgroup\Email\Logger\Logger" --filter "Amazon\Core\Logger\Logger" --filter "Amazon\Core\Logger\IpnLogger"; fi
after_failure:
- test -d ./vendor/ampersand/travis-vanilla-magento/instances/ampmodule/var/report/ && for r in ./vendor/ampersand/travis-vanilla-magento/instances/ampmodule/var/report/*; do cat $r; done
- test -f ./vendor/ampersand/travis-vanilla-magento/instances/ampmodule/var/log/system.log && grep -v "Broken reference" ./vendor/ampersand/travis-vanilla-magento/instances/ampmodule/var/log/system.log
Expand Down
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ $ grep -ri 61e04d741bf78 ./var/log

If the request was long-running, or had an error it may also be flagged in new relic with the custom parameter `amp_correlation_id`

# Configuration and Customisation
## Configuration and Customisation

## Change the key name from `amp_correlation_id`
### Change the key name from `amp_correlation_id`

You can change the monolog/new relic key from `amp_correlation_id` using `app/etc/ampersand_magento2_log_correlation/di.xml`

Expand All @@ -92,7 +92,7 @@ You can change the monolog/new relic key from `amp_correlation_id` using `app/et
</type>
```

## Use existing correlation id from request header
### Use existing correlation id from request header

If you want to use an upstream correlation/trace ID you can define one `app/etc/ampersand_magento2_log_correlation/di.xml`

Expand All @@ -116,3 +116,37 @@ $ 2>&1 curl -H 'X-Your-Header-Here: abc123' https://your-magento-site.example.c
$ 2>&1 curl https://your-magento-site.example.com/ -vvv | grep "X-Log-Correlation-Id"
< X-Log-Correlation-Id: cid-61e4194d1bea5
```

### Custom Loggers

By default this module hooks into all vanilla magento loggers.

However third party modules may define additional loggers to write to custom files. If you want the correlation ID added to those logs as well you will need to create a module that depends on both `Ampersand_LogCorrelation_Id` and the module with the custom logger, you will then have to add the log handler in `di.xml` like so

```xml
<type name="This\Is\Some\Logger">
<arguments>
<argument name="processors" xsi:type="array">
<item name="correlationIdProcessor" xsi:type="array">
<item name="0" xsi:type="object">Ampersand\LogCorrelationId\Processor\MonologCorrelationId</item>
<item name="1" xsi:type="string">addCorrelationId</item>
</item>
</argument>
</arguments>
</type>
```

This module provides a command to try to help you keep track of the custom loggers in your system

```shell
$ php bin/magento ampersand:log-correlation-id:list-custom-loggers
Scanning for classes which extend Monolog\Logger
- You need to run 'composer dump-autoload --optimize' for this command to work
- Use this on your local environment to configure your di.xml
- See vendor/ampersand/magento2-log-correlation-id/README.md
--------------------------------------------------------------------------------
Dotdigitalgroup\Email\Logger\Logger
StripeIntegration\Payments\Logger\WebhooksLogger
Yotpo\Yotpo\Model\Logger
--------------------------------------------------------------------------------
DONE```
57 changes: 57 additions & 0 deletions src/Console/CommandList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Ampersand\LogCorrelationId\Console;

use Magento\Framework\Console\CommandListInterface;
use Magento\Framework\ObjectManagerInterface;

/**
* Provides list of commands to be available for uninstalled application
*
* @see \Magento\Backend\Console\CommandList
* @see \Magento\Deploy\Console\CommandList
*/
class CommandList implements CommandListInterface
{
/**
* @var ObjectManagerInterface
*/
private $objectManager;

/**
* @param ObjectManagerInterface $objectManager
*/
public function __construct(ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}

/**
* Gets list of command classes
*
* @return string[]
*/
private function getCommandsClasses(): array
{
return [
ListCustomLoggersCommand::class
];
}

/**
* @inheritdoc
*/
public function getCommands(): array
{
$commands = [];
foreach ($this->getCommandsClasses() as $class) {
if (class_exists($class)) {
$commands[] = $this->objectManager->get($class);
} else {
throw new \RuntimeException('Class ' . $class . ' does not exist');
}
}

return $commands;
}
}
193 changes: 193 additions & 0 deletions src/Console/ListCustomLoggersCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php
declare(strict_types=1);
namespace Ampersand\LogCorrelationId\Console;

use Magento\Framework\Console\Cli;
use Magento\Framework\Module\Dir as ModuleDir;
use Magento\Framework\Module\FullModuleList as ModuleList;
use Monolog\Logger as MonologLogger;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ListCustomLoggersCommand extends Command
{
/**
* Name of input option
*/
const INPUT_KEY_FILTER = 'filter';

/** @var ModuleDir $dir */
private $dir;
/** @var ModuleList $list */
private $list;

/**
* Constructor
*
* @param ModuleDir $dir
* @param ModuleList $list
*/
public function __construct(ModuleDir $dir, ModuleList $list)
{
$this->dir = $dir;
$this->list = $list;
parent::__construct();
}

/**
* Configure the command
*
* @return void
*/
protected function configure()
{
$options = [
new InputOption(
self::INPUT_KEY_FILTER,
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Filter a logger from the output results'
),
];

$this->setName('ampersand:log-correlation-id:list-custom-loggers');
$this->setDescription('List custom monolog loggers defined in magento modules');
$this->setDefinition($options);

parent::configure();
}

/**
* List all module classes that extend the monolog logger
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$returnCode = Cli::RETURN_SUCCESS;
try {
$output->writeln(str_pad('', 80, '-'));
$output->writeln("Scanning for classes which extend " . MonologLogger::class);
$output->writeln(str_pad('', 80, '-'));
$output->writeln("- You need to run 'composer dump-autoload --optimize' for this command to work");
$output->writeln("- Use this on your local environment to configure your di.xml");
$output->writeln("- See vendor/ampersand/magento2-log-correlation-id/README.md");
$output->writeln(str_pad('', 80, '-'));

$extendsMonologLogger = [];

$classMap = $this->getOptimisedAutoloadClassMap();

foreach ($this->list->getNames() as $moduleName) {
$moduleDirectory = $this->dir->getDir($moduleName) . DIRECTORY_SEPARATOR;

// Get all the files from the classmap pertaining to this module
$filesFromClassMap = array_filter(
$classMap,
function ($filepath) use ($moduleDirectory) {
return substr($filepath, 0, strlen($moduleDirectory)) === $moduleDirectory;
}
);

// From this list of files get the list of them that extend the Monolog Logger class
$moduleClassesThatExtendMonologLogger = array_filter(
array_keys($filesFromClassMap),
function ($class) use ($output) {
try {
$result = is_subclass_of($class, MonologLogger::class);
} catch (\Throwable $throwable) {
$output->writeln("<error>" . $throwable->getMessage() . "</error>");
$result = false;
}
return $result;
}
);

if (empty($moduleClassesThatExtendMonologLogger)) {
continue;
}
// phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge
$extendsMonologLogger = array_merge($extendsMonologLogger, $moduleClassesThatExtendMonologLogger);
}

$filters = $input->getOption(self::INPUT_KEY_FILTER);
$extendsMonologLogger = array_diff($extendsMonologLogger, $input->getOption(self::INPUT_KEY_FILTER));

if (!empty($filters) && !empty($extendsMonologLogger)) {
/*
* We have supplied filters but still have results output, so we have not filtered everything
* Likely a new logger has been added that needs accounted for in di.xml
*/
$returnCode = Cli::RETURN_FAILURE;
}

sort($extendsMonologLogger);
foreach ($extendsMonologLogger as $className) {
$output->writeln($className);
}

$output->writeln(str_pad('', 80, '-'));
$output->writeln('DONE');
} catch (\Throwable $throwable) {
$output->writeln("<error>" . $throwable->getMessage() . "</error>");
return Cli::RETURN_FAILURE;
}
return $returnCode;
}

/**
* This is the same autoloading mechanism as in vendor/magento/magento2-base/app/autoload.php
*
* However we cannot use the wrapper as we need direct access ot the composer autoloader data
*
* @phpcs:disable Magento2.Security.IncludeFile.FoundIncludeFile
* @phpcs:disable Magento2.Functions.DiscouragedFunction.DiscouragedWithAlternative
* @phpcs:disable Magento2.Exceptions.DirectThrow.FoundDirectThrow
*
* @return array<string, string>
* @throws \Exception
*/
protected function getOptimisedAutoloadClassMap(): array
{
// Get the vendor path
// @phpstan-ignore-next-line
$vendorDir = include VENDOR_PATH;
// @phpstan-ignore-next-line
$vendorAutoload = BP . "/{$vendorDir}/autoload.php";
if (!is_readable($vendorAutoload)) {
throw new \Exception("Could not find vendor/autoload.php at " . $vendorAutoload);
}

// Get the optimised autoload classmap
$classMap = (include $vendorAutoload)->getClassMap();

// Filter out files without a concrete class
foreach (array_filter($classMap) as $className => $filePath) {
try {
if (strpos($filePath, '/TestFramework/') !== false) {
unset($classMap[$className]); // Filter out any test framework results
continue;
}
if (strpos($className, '\\TestFramework\\') !== false) {
unset($classMap[$className]); // Filter out any test framework results
continue;
}
$realPath = realpath($filePath);
if (!$realPath) {
unset($classMap[$className]); // Could not work out realpath
continue;
}
} catch (\Throwable $throwable) {
unset($classMap[$className]); // Could not work out realpath
continue;
}
$classMap[$className] = $realPath;
}

return array_filter($classMap);
}
}
Loading

0 comments on commit 051b981

Please sign in to comment.