Skip to content

Commit

Permalink
[Patch] Log Record Updates for Laravel 10 (#36)
Browse files Browse the repository at this point in the history
* Introduces LogRecordFactory class to which allows a Log Record to be built differently depending on mongolog version that host laravel application is running. 
* Introduces ContentProcessingStrategy as a Singleton to manage diverse content handlers
* Implements StringContentHandler, ArrayContentHandler, and LogRecordContentHandler and register them with ContentProcessingStrategy singleton
* Enhance test coverage for ContentProcessingStrategy & ScrubMessageTest
* Updates Composer to upgrade to v3 mongolog for Illuminate\Contracts v10
* Updates to Composer to sustain support for to v2 mongolog for Illuminate\Contracts v9
* Exposes the regex repository through scrubber service to scrubber class.
* Bug fixes
* Remove support for illuminate\contracts 8
* Updates README credit adding @majchrosoft && @lucaxue 🖖🏼 🖖🏼 🙏🏼 👏🏼 

---------

Co-authored-by: yordadev <yordadev@users.noreply.github.com>
  • Loading branch information
yordadev and yordadev authored Jun 21, 2023
1 parent b23104b commit f101d84
Show file tree
Hide file tree
Showing 20 changed files with 634 additions and 165 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ jobs:
fail-fast: false
matrix:
php: [8.1, 8.2]
laravel: [8.*, 9.*, 10.*]
laravel: [9.*, 10.*]
include:
- laravel: 10.*
testbench: 8
- laravel: 9.*
testbench: 7.*
- laravel: 8.*
testbench: 6.*

name: PHP${{ matrix.php }} - Laravel ${{ matrix.laravel }}

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,5 +252,7 @@ composer test

- [Yorda](https://github.com/yordadev)
- [Whizboy-Arnold](https://github.com/Whizboy-Arnold)
- [majchrosoft](https://github.com/majchrosoft)
- [Lucaxue](https://github.com/lucaxue)
- [All Contributors](../../contributors)

5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
"minimum-stability": "dev",
"require": {
"php": "^8.1",
"illuminate/contracts": "^8.0|^9.0|^10.0",
"illuminate/contracts": "^9.0|^10.0",
"monolog/monolog": "^2.0|^3",
"guzzlehttp/guzzle": "^7.4.5"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"laravel/pint": "^1.0",
"orchestra/testbench": "^7.0",
"orchestra/testbench": "^7.0|^8.4",
"phpunit/phpunit": "^9.5"
},
"autoload": {
Expand Down
238 changes: 117 additions & 121 deletions composer.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/Repositories/RegexRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public static function checkAndSanitize(string $regex, string $content, int &$hi
return preg_replace("~$regex~i", config('scrubber.redaction'), $content, -1, $hits);
}

public static function check(string $regex, string $content): int
{
return preg_match_all("~$regex~i", $content);
}

public function getRegexCollection(): Collection
{
return $this->regexCollection;
Expand Down
45 changes: 7 additions & 38 deletions src/Scrubber.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,20 @@

namespace YorCreative\Scrubber;

use Monolog\LogRecord;
use YorCreative\Scrubber\Repositories\RegexRepository;
use YorCreative\Scrubber\Services\ScrubberService;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ContentProcessingStrategy;

class Scrubber
{
public static function processMessage($content): array|string
public static function processMessage($content): array|string|LogRecord
{
return is_array($content)
? self::processArray($content)
: self::processString($content);
return app()->get(ContentProcessingStrategy::class)->processContent($content);
}

private static function processArray(array $content): array
public static function getRegexRepository(): RegexRepository
{
$jsonContent = ScrubberService::encodeRecord($content);
if ('' === $jsonContent) {
// failed to convert array to JSON, so process array recursively
return self::processArrayRecursively($content);
}

ScrubberService::autoSanitize($jsonContent);

return ScrubberService::decodeRecord($jsonContent);
}

private static function processArrayRecursively(array $content): array
{
foreach ($content as $key => $value) {
if (null !== $value) {
if (is_array($value)) {
$content[$key] = self::processArray($value);
} elseif (is_object($value) && ! method_exists($value, '__toString')) {
$content[$key] = self::processArray((array) $value);
} else {
$content[$key] = self::processString((string) $value);
}
}
}

return $content;
}

private static function processString($content): string
{
ScrubberService::autoSanitize($content);

return $content;
return ScrubberService::getRegexRepository();
}
}
15 changes: 15 additions & 0 deletions src/ScrubberServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
use Illuminate\Support\ServiceProvider;
use YorCreative\Scrubber\Clients\GitLabClient;
use YorCreative\Scrubber\Repositories\RegexRepository;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ContentProcessingStrategy;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Handlers\ArrayContentHandler;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Handlers\LogRecordContentHandler;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Handlers\StringContentHandler;
use YorCreative\Scrubber\Strategies\RegexLoader\Loaders\DefaultCore;
use YorCreative\Scrubber\Strategies\RegexLoader\Loaders\SecretLoader;
use YorCreative\Scrubber\Strategies\RegexLoader\Loaders\SpecificCore;
Expand Down Expand Up @@ -50,6 +54,7 @@ public function boot()
]));
});
}

$this->app->singleton(RegexLoaderStrategy::class, function () {
$regexLoaderStrategy = new RegexLoaderStrategy();
$regexLoaderStrategy->setLoader(new DefaultCore());
Expand All @@ -76,5 +81,15 @@ public function boot()
$this->app->scoped(RegexRepository::class, function ($app) {
return new RegexRepository($app->make(RegexLoaderStrategy::class)->load());
});

$this->app->singleton(ContentProcessingStrategy::class, function () {
$contentProcessingStrategy = new ContentProcessingStrategy();

$contentProcessingStrategy->setHandler(new StringContentHandler());
$contentProcessingStrategy->setHandler(new ArrayContentHandler());
$contentProcessingStrategy->setHandler(new LogRecordContentHandler());

return $contentProcessingStrategy;
});
}
}
14 changes: 13 additions & 1 deletion src/Services/ScrubberService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace YorCreative\Scrubber\Services;

use Carbon\Carbon;
use Monolog\LogRecord;
use YorCreative\Scrubber\Interfaces\RegexCollectionInterface;
use YorCreative\Scrubber\Repositories\RegexRepository;
use YorCreative\Scrubber\SecretManager\Secret;
Expand All @@ -26,7 +27,13 @@ public static function decodeRecord($scrubbedContent): mixed

// set datetime back to DateTimeInterface for papertrail specifically.
if (isset($scrubbedContent['datetime'])) {
$scrubbedContent['datetime'] = Carbon::parse($scrubbedContent['datetime']);
$datetime = match (true) {
is_array($scrubbedContent['datetime']) => $scrubbedContent['datetime']['date'],
$scrubbedContent instanceof LogRecord => Carbon::instance($scrubbedContent['datetime']),
default => Carbon::parse($scrubbedContent['datetime'])
};

$scrubbedContent['datetime'] = $datetime;
}

return $scrubbedContent;
Expand Down Expand Up @@ -54,4 +61,9 @@ protected static function patternChecker(string $regexPattern, string &$jsonCont
*
**/
}

public static function getRegexRepository(): RegexRepository
{
return app()->get(RegexRepository::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace YorCreative\Scrubber\Strategies\ContentProcessingStrategy;

use Illuminate\Support\Collection;
use Monolog\LogRecord;
use RuntimeException;

class ContentProcessingStrategy
{
protected Collection $handlers;

public function __construct()
{
$this->handlers = new Collection();
}

public function setHandler(ProcessHandlerContract $handler): void
{
$this->handlers->push($handler);
}

public function processContent(mixed $content): string|array|LogRecord
{
$index = $this->detectHandlerIndex($content);

return is_null($index)
? throw new RuntimeException('Cannot process content: '.json_encode($content))
: $this->getHandlers()->get($index)->processContent($content);
}

private function detectHandlerIndex(mixed $content): ?int
{
return $this->getHandlers()->search(function (ProcessHandlerContract $handler) use ($content) {
return $handler->canProcess($content);
});
}

public function getHandlers(): Collection
{
return $this->handlers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Handlers;

use Monolog\LogRecord;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ProcessHandlerContract;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Traits\ProcessArrayTrait;

class ArrayContentHandler implements ProcessHandlerContract
{
use ProcessArrayTrait;

public function canProcess(mixed $content): bool
{
return is_array($content);
}

public function processContent(mixed $content): string|array|LogRecord
{
return $this->processArray($content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Handlers;

use Monolog\LogRecord;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ContentProcessingStrategy;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ProcessHandlerContract;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Traits\ProcessArrayTrait;
use YorCreative\Scrubber\Support\LogRecordFactory;

class LogRecordContentHandler implements ProcessHandlerContract
{
use ProcessArrayTrait;

public function canProcess(mixed $content): bool
{
return $content instanceof LogRecord;
}

public function processContent(mixed $content): string|array|LogRecord
{
$logRecordArr = $content->toArray();

$logRecordArr['message'] = empty($message = $logRecordArr['message'])
? $message
: app(ContentProcessingStrategy::class)->processContent($logRecordArr['message']);

$logRecordArr['context'] = empty($context = $logRecordArr['context'])
? []
: app(ContentProcessingStrategy::class)->processContent($context);

return LogRecordFactory::buildRecord(
$logRecordArr['datetime'],
$logRecordArr['channel'],
$logRecordArr['level'],
$logRecordArr['message'],
$logRecordArr['context'],
$logRecordArr['extra']
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Handlers;

use Monolog\LogRecord;
use YorCreative\Scrubber\Services\ScrubberService;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ProcessHandlerContract;

class StringContentHandler implements ProcessHandlerContract
{
public function canProcess(mixed $content): bool
{
return is_string($content);
}

public function processContent(mixed $content): string|array|LogRecord
{
ScrubberService::autoSanitize($content);

return $content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace YorCreative\Scrubber\Strategies\ContentProcessingStrategy;

use Monolog\LogRecord;

interface ProcessHandlerContract
{
public function canProcess(mixed $content): bool;

public function processContent(mixed $content): string|array|LogRecord;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace YorCreative\Scrubber\Strategies\ContentProcessingStrategy\Traits;

use YorCreative\Scrubber\Services\ScrubberService;

trait ProcessArrayTrait
{
public function processArrayRecursively(array $content): array
{
foreach ($content as $key => $value) {
if (null !== $value) {
if (is_array($value)) {
$content[$key] = $this->processArray($value);
} elseif (is_object($value) && ! method_exists($value, '__toString')) {
$content[$key] = $this->processArray((array) $value);
} else {
$value = (string) $value;

ScrubberService::autoSanitize($value);

$content[$key] = $value;
}
}
}

return $content;
}

public function processArray(array $content): array
{
$jsonContent = ScrubberService::encodeRecord($content);
if ('' === $jsonContent) {
// failed to convert array to JSON, so process array recursively
return $this->processArrayRecursively($content);
}

ScrubberService::autoSanitize($jsonContent);

return ScrubberService::decodeRecord($jsonContent);
}
}
Loading

0 comments on commit f101d84

Please sign in to comment.