Compare commits

...

8 Commits
1.1.0 ... 1.2.0

8 changed files with 138 additions and 70 deletions

View File

@ -4,7 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://git.d3data.de/D3Public/guzzleFactory/compare/1.1.0...rel_1.x) ## [Unreleased](https://git.d3data.de/D3Public/guzzleFactory/compare/1.2.0...rel_1.x)
## [1.2.0](https://git.d3data.de/D3Public/guzzleFactory/compare/1.1.0...1.2.0) - 2025-02-10
### Added
- special log handlers
### Changed
- use [LoggerFactory](https://packagist.org/packages/d3/logger-factory) instead of internal methods
## [1.1.0](https://git.d3data.de/D3Public/guzzleFactory/compare/1.0.0...1.1.0) - 2025-01-27 ## [1.1.0](https://git.d3data.de/D3Public/guzzleFactory/compare/1.0.0...1.1.0) - 2025-01-27
### Added ### Added
@ -15,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.0.0](https://git.d3data.de/D3Public/guzzleFactory/releases/tag/1.0.0) - 2025-01-01 ## [1.0.0](https://git.d3data.de/D3Public/guzzleFactory/releases/tag/1.0.0) - 2025-01-01
### Added ### Added
- initial implementation - initial implementation
- can create am cutom Guzzle instance - can create a custom Guzzle instance
- "accept" option - "accept" option
- "contentType" option - "contentType" option
- "userAgent" option - "userAgent" option

View File

@ -17,7 +17,13 @@ composer require d3/guzzle-factory
``` ```
$guzzleFactory = GuzzleFactory::create(); $guzzleFactory = GuzzleFactory::create();
$guzzleFactory->setUserAgent('myApi-php-client/1.0.0')); $guzzleFactory->setUserAgent('myApi-php-client/1.0.0'));
$guzzleFactory->addFileLogger('myPluginLogger', 'plugin_requests.log', Logger::DEBUG, 5); $guzzleFactory->addFileLogger(
'myPluginLogger',
'plugin_requests.log',
Logger::DEBUG,
5,
[\D3\LoggerFactory\LoggerFactory::SPECIAL_HANDLERS_BUFFERING] // optional, see LoggerFactory for details
);
$guzzleFactory->setMessageFormatter( $guzzleFactory->setMessageFormatter(
'{method} {uri} HTTP/{version} {req_body}'.PHP_EOL.'RESPONSE: {code} - {res_body}', '{method} {uri} HTTP/{version} {req_body}'.PHP_EOL.'RESPONSE: {code} - {res_body}',
['myUsername', 'myPassword'] ['myUsername', 'myPassword']

View File

@ -19,8 +19,8 @@
], ],
"require": { "require": {
"guzzlehttp/guzzle": "^7.0", "guzzlehttp/guzzle": "^7.0",
"monolog/monolog": "^1.20", "d3/sensitive-message-formatter": "^1.0",
"d3/sensitive-message-formatter": "^1.0" "d3/logger-factory": "^1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^10.5", "phpunit/phpunit": "^10.5",

View File

@ -17,9 +17,9 @@ declare(strict_types=1);
namespace D3\GuzzleFactory\Apps; namespace D3\GuzzleFactory\Apps;
use D3\LoggerFactory\LoggerFactory;
use Exception; use Exception;
use InvalidArgumentException; use InvalidArgumentException;
use Monolog\Handler\StreamHandler;
use Monolog\Logger; use Monolog\Logger;
use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Registry;
use RuntimeException; use RuntimeException;
@ -36,35 +36,42 @@ trait OxidLoggerTrait
} }
/** /**
* @throws Exception * @param string $loggerName
* @param string $filePath
* @param int $logLevel
* @param int|null $maxFiles
* @param array<int|string, string|array<string, string|int>> $specialHandlers
*
* @return void
* @throws InvalidArgumentException * @throws InvalidArgumentException
* @throws Exception
*/ */
public function addCombinedOxidAndFileLogger( public function addCombinedOxidAndFileLogger(
string $loggerName, string $loggerName,
string $filePath, string $filePath,
int $logLevel = Logger::INFO, int $logLevel = Logger::INFO,
?int $maxFiles = null ?int $maxFiles = null,
): void array $specialHandlers = []
{ ): void {
if (!class_exists(Registry::class)) {
throw new RuntimeException(__METHOD__.' can executed in OXID eShop installations only');
}
$logger = new Logger($loggerName);
$stream_handler = $this->getFileLoggerStreamHandler($filePath, $logLevel, $maxFiles);
$logger->pushHandler($stream_handler);
$oxidLogFilePath = $this->getOxidLogPath('oxideshop.log');
$oxidStreamHandler = new StreamHandler($oxidLogFilePath, Logger::ERROR);
$logger->pushHandler($oxidStreamHandler);
if (isset($this->loggers['oxid'])) { if (isset($this->loggers['oxid'])) {
unset($this->loggers['oxid']); unset($this->loggers['oxid']);
} }
$this->loggers[$loggerName] = $logger; $this->loggers[$loggerName] = $this->getLoggerFactory()->getCombinedOxidAndFileLogger(
$loggerName,
$filePath,
$logLevel,
$maxFiles,
/** @phpstan-ignore argument.type */
$specialHandlers
);
} }
/**
* @deprecated use LoggerFactory::getOxidLogPath
* @param string $fileName
* @return string
*/
public function getOxidLogPath(string $fileName): string public function getOxidLogPath(string $fileName): string
{ {
if (!class_exists(Registry::class)) { if (!class_exists(Registry::class)) {

View File

@ -52,6 +52,7 @@ class GuzzleFactory
foreach ($this->getLoggers() as $logger) { foreach ($this->getLoggers() as $logger) {
/** @var 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning' $logLevelName */ /** @var 'alert'|'critical'|'debug'|'emergency'|'error'|'info'|'notice'|'warning' $logLevelName */
/** @phpstan-ignore argument.type */
$logLevelName = Logger::getLevelName($this->getMessageLevel()); $logLevelName = Logger::getLevelName($this->getMessageLevel());
$stack->push( $stack->push(
Middleware::log( Middleware::log(

View File

@ -18,11 +18,10 @@ declare(strict_types=1);
namespace D3\GuzzleFactory; namespace D3\GuzzleFactory;
use D3\GuzzleFactory\Apps\OxidLoggerTrait; use D3\GuzzleFactory\Apps\OxidLoggerTrait;
use D3\LoggerFactory\LoggerFactory;
use Exception; use Exception;
use InvalidArgumentException; use InvalidArgumentException;
use Monolog\Handler\AbstractProcessingHandler; use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger; use Monolog\Logger;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -34,25 +33,36 @@ trait LoggerTrait
protected array $loggers = []; protected array $loggers = [];
protected ?int $messageLevel = null; protected ?int $messageLevel = null;
protected function getLoggerFactory(): LoggerFactory
{
return LoggerFactory::create();
}
/** /**
* @throws Exception * @param string $loggerName
* @param string $filePath
* @param int $logLevel
* @param int|null $maxFiles
* @param array<int|string, string|array<string, string|int>> $specialHandlers
*
* @return void
* @throws InvalidArgumentException * @throws InvalidArgumentException
* @throws Exception
*/ */
public function addFileLogger( public function addFileLogger(
string $loggerName, string $loggerName,
string $filePath, string $filePath,
int $logLevel = Logger::INFO, int $logLevel = Logger::INFO,
?int $maxFiles = null ?int $maxFiles = null,
): void array $specialHandlers = [] // see LoggerFactory constants
{ ): void {
$logger = new Logger($loggerName); $this->loggers[$loggerName] = $this->getLoggerFactory()
$stream_handler = $this->getFileLoggerStreamHandler($filePath, $logLevel, $maxFiles); /** @phpstan-ignore argument.type */
$logger->pushHandler($stream_handler); ->getFileLogger($loggerName, $filePath, $logLevel, $maxFiles, $specialHandlers);
$this->loggers[$loggerName] = $logger;
} }
/** /**
* @deprecated use LoggerFactory::getFileLoggerStreamHandler
* @param string $filePath * @param string $filePath
* @param int $logLevel * @param int $logLevel
* @param int|null $maxFiles * @param int|null $maxFiles
@ -63,11 +73,8 @@ trait LoggerTrait
string $filePath, string $filePath,
int $logLevel = Logger::INFO, int $logLevel = Logger::INFO,
?int $maxFiles = null ?int $maxFiles = null
): AbstractProcessingHandler ): AbstractProcessingHandler {
{ return $this->getLoggerFactory()->getFileLoggerStreamHandler($filePath, $logLevel, $maxFiles);
return is_null($maxFiles) ?
new StreamHandler($filePath, $logLevel) :
new RotatingFileHandler($filePath, $maxFiles, $logLevel);
} }
public function addConfiguredLogger(LoggerInterface $logger): void public function addConfiguredLogger(LoggerInterface $logger): void

View File

@ -16,6 +16,7 @@
namespace D3\GuzzleFactory\tests\Apps; namespace D3\GuzzleFactory\tests\Apps;
use D3\GuzzleFactory\GuzzleFactory; use D3\GuzzleFactory\GuzzleFactory;
use D3\LoggerFactory\LoggerFactory;
use Monolog\Logger; use Monolog\Logger;
use ReflectionException; use ReflectionException;
use RuntimeException; use RuntimeException;
@ -40,25 +41,6 @@ trait OxidLoggerTestTrait
); );
} }
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\GuzzleFactory\GuzzleFactory::addCombinedOxidAndFileLogger
*/
public function testAddCombinedOxidAndFileLoggerWithoutOxid(): void
{
$sut = GuzzleFactory::create();
$this->expectException(RuntimeException::class);
$this->callMethod(
$sut,
'addCombinedOxidAndFileLogger',
['nameFixture', 'file/path.log', 1, 5]
);
}
/** /**
* @test * @test
* @return void * @return void
@ -109,11 +91,19 @@ trait OxidLoggerTestTrait
* @throws ReflectionException * @throws ReflectionException
* @covers \D3\GuzzleFactory\GuzzleFactory::addCombinedOxidAndFileLogger * @covers \D3\GuzzleFactory\GuzzleFactory::addCombinedOxidAndFileLogger
*/ */
public function testAddCombinedOxidAndFileLoggerInOxid(): void public function testAddCombinedOxidAndFileLogger(): void
{ {
require_once __DIR__.'/../Helpers/classAliases.php'; require_once __DIR__.'/../Helpers/classAliases.php';
$sut = GuzzleFactory::create(); $loggerFactory = $this->getMockBuilder(LoggerFactory::class)
->onlyMethods(['getCombinedOxidAndFileLogger'])
->getMock();
$loggerFactory->expects($this->once())->method('getCombinedOxidAndFileLogger');
$sut = $this->getMockBuilder(GuzzleFactory::class)
->onlyMethods(['getLoggerFactory'])
->getMock();
$sut->method('getLoggerFactory')->willReturn($loggerFactory);
$this->setValue($sut, 'loggers', ['oxid' => 1]); $this->setValue($sut, 'loggers', ['oxid' => 1]);

View File

@ -16,8 +16,8 @@
namespace D3\GuzzleFactory\tests; namespace D3\GuzzleFactory\tests;
use D3\GuzzleFactory\GuzzleFactory; use D3\GuzzleFactory\GuzzleFactory;
use D3\LoggerFactory\LoggerFactory;
use Generator; use Generator;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler; use Monolog\Handler\StreamHandler;
use Monolog\Logger; use Monolog\Logger;
use ReflectionException; use ReflectionException;
@ -26,15 +26,45 @@ trait LoggerTestTrait
{ {
/** /**
* @test * @test
* @return void
* @throws ReflectionException * @throws ReflectionException
* @covers \D3\GuzzleFactory\GuzzleFactory::addFileLogger * @covers \D3\GuzzleFactory\GuzzleFactory::getLoggerFactory
* @covers \D3\GuzzleFactory\GuzzleFactory::getFileLoggerStreamHandler
* @dataProvider addFileLoggerDataProvider
*/ */
public function testAddFileLogger(int $logLevel, ?int $maxFiles, string $expectedHandlerClass): void public function testGetLoggerFactory(): void
{ {
$sut = GuzzleFactory::create(); $sut = GuzzleFactory::create();
$this->assertInstanceOf(
LoggerFactory::class,
$this->callMethod(
$sut,
'getLoggerFactory',
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\GuzzleFactory\GuzzleFactory::addFileLogger
* @dataProvider addFileLoggerDataProvider
*/
public function testAddFileLogger(int $logLevel, ?int $maxFiles): void
{
$logger = $this->getMockBuilder(Logger::class)
->disableOriginalConstructor()
->getMock();
$loggerFactory = $this->getMockBuilder(LoggerFactory::class)
->onlyMethods(['getFileLogger'])
->getMock();
$loggerFactory->expects($this->once())->method('getFileLogger')->willReturn($logger);
$sut = $this->getMockBuilder(GuzzleFactory::class)
->onlyMethods(['getLoggerFactory'])
->getMock();
$sut->method("getLoggerFactory")->willReturn($loggerFactory);
$this->callMethod( $this->callMethod(
$sut, $sut,
'addFileLogger', 'addFileLogger',
@ -44,15 +74,36 @@ trait LoggerTestTrait
$loggers = $this->getValue($sut, 'loggers'); $loggers = $this->getValue($sut, 'loggers');
$this->assertArrayHasKey('nameFixture', $loggers); $this->assertArrayHasKey('nameFixture', $loggers);
$this->assertInstanceOf(Logger::class, $loggers['nameFixture']); $this->assertInstanceOf(Logger::class, $loggers['nameFixture']);
$this->assertInstanceOf($expectedHandlerClass, $loggers['nameFixture']->getHandlers()[0]);
$this->assertSame($logLevel, $loggers['nameFixture']->popHandler('nameFixture')->getLevel());
} }
public static function addFileLoggerDataProvider(): Generator public static function addFileLoggerDataProvider(): Generator
{ {
yield 'no rotation' => [Logger::INFO, null, StreamHandler::class]; yield [Logger::INFO, null];
yield 'rotation 1' => [Logger::ERROR, 1, RotatingFileHandler::class]; }
yield 'rotation 20' => [Logger::DEBUG, 20, RotatingFileHandler::class];
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\GuzzleFactory\GuzzleFactory::getFileLoggerStreamHandler
*/
public function testGetFileLoggerStreamHandler(): void
{
$loggerFactory = $this->getMockBuilder(LoggerFactory::class)
->onlyMethods(['getFileLoggerStreamHandler'])
->getMock();
$loggerFactory->expects($this->once())->method('getFileLoggerStreamHandler');
$sut = $this->getMockBuilder(GuzzleFactory::class)
->onlyMethods(['getLoggerFactory'])
->getMock();
$sut->method("getLoggerFactory")->willReturn($loggerFactory);
$this->callMethod(
$sut,
'getFileLoggerStreamHandler',
['file/path.log', Logger::ERROR, 2]
);
} }
/** /**