diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37ec790d8..28e4ea75e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Added
- [#405](https://github.com/hyperf-cloud/hyperf/pull/405) Added Context::override() method.
+- [#415](https://github.com/hyperf-cloud/hyperf/pull/415) Added handlers configuration for logger, now you could config multiple handlers to logger.
## Fixed
- [#414](https://github.com/hyperf-cloud/hyperf/pull/414) Fixed WebSocketExceptionHandler typo
diff --git a/src/grpc-client/tests/GPBMetadata/Grpc.php b/src/grpc-client/tests/GPBMetadata/Grpc.php
index 1c74297e0..7b5f24f93 100644
--- a/src/grpc-client/tests/GPBMetadata/Grpc.php
+++ b/src/grpc-client/tests/GPBMetadata/Grpc.php
@@ -1,5 +1,15 @@
internalAddGeneratedFile(hex2bin(
- "0aae010a0a677270632e70726f746f12046772706322200a04496e666f12" .
- "0a0a026964180120012805120c0a046e616d6518022001280922360a0955" .
- "7365725265706c79120f0a076d65737361676518012001280912180a0469" .
- "6e666f18022001280b320a2e677270632e496e666f32380a0c757365725f" .
- "7365727669636512280a0767657455736572120a2e677270632e496e666f" .
- "1a0f2e677270632e557365725265706c792200620670726f746f33"
+ '0aae010a0a677270632e70726f746f12046772706322200a04496e666f12' .
+ '0a0a026964180120012805120c0a046e616d6518022001280922360a0955' .
+ '7365725265706c79120f0a076d65737361676518012001280912180a0469' .
+ '6e666f18022001280b320a2e677270632e496e666f32380a0c757365725f' .
+ '7365727669636512280a0767657455736572120a2e677270632e496e666f' .
+ '1a0f2e677270632e557365725265706c792200620670726f746f33'
));
static::$is_initialized = true;
}
}
-
diff --git a/src/grpc-client/tests/Grpc/Info.php b/src/grpc-client/tests/Grpc/Info.php
index 3031ea6fb..075c46e75 100644
--- a/src/grpc-client/tests/Grpc/Info.php
+++ b/src/grpc-client/tests/Grpc/Info.php
@@ -1,24 +1,33 @@
grpc.Info
+ * Generated from protobuf message grpc.Info
.
*/
class Info extends \Google\Protobuf\Internal\Message
{
/**
- * Generated from protobuf field int32 id = 1;
+ * Generated from protobuf field int32 id = 1;
.
*/
private $id = 0;
+
/**
- * Generated from protobuf field string name = 2;
+ * Generated from protobuf field string name = 2;
.
*/
private $name = '';
@@ -26,19 +35,20 @@ class Info extends \Google\Protobuf\Internal\Message
* Constructor.
*
* @param array $data {
- * Optional. Data for populating the Message object.
+ * Optional. Data for populating the Message object.
*
- * @type int $id
- * @type string $name
+ * @var int $id
+ * @var string $name
* }
*/
- public function __construct($data = NULL) {
+ public function __construct($data = null)
+ {
\GPBMetadata\Grpc::initOnce();
parent::__construct($data);
}
/**
- * Generated from protobuf field int32 id = 1;
+ * Generated from protobuf field int32 id = 1;
.
* @return int
*/
public function getId()
@@ -47,7 +57,7 @@ class Info extends \Google\Protobuf\Internal\Message
}
/**
- * Generated from protobuf field int32 id = 1;
+ * Generated from protobuf field int32 id = 1;
.
* @param int $var
* @return $this
*/
@@ -60,7 +70,7 @@ class Info extends \Google\Protobuf\Internal\Message
}
/**
- * Generated from protobuf field string name = 2;
+ * Generated from protobuf field string name = 2;
.
* @return string
*/
public function getName()
@@ -69,17 +79,15 @@ class Info extends \Google\Protobuf\Internal\Message
}
/**
- * Generated from protobuf field string name = 2;
+ * Generated from protobuf field string name = 2;
.
* @param string $var
* @return $this
*/
public function setName($var)
{
- GPBUtil::checkString($var, True);
+ GPBUtil::checkString($var, true);
$this->name = $var;
return $this;
}
-
}
-
diff --git a/src/grpc-client/tests/Grpc/UserReply.php b/src/grpc-client/tests/Grpc/UserReply.php
index 534a5bbd1..7684a7cc6 100644
--- a/src/grpc-client/tests/Grpc/UserReply.php
+++ b/src/grpc-client/tests/Grpc/UserReply.php
@@ -1,44 +1,54 @@
grpc.UserReply
+ * Generated from protobuf message grpc.UserReply
.
*/
class UserReply extends \Google\Protobuf\Internal\Message
{
/**
- * Generated from protobuf field string message = 1;
+ * Generated from protobuf field string message = 1;
.
*/
private $message = '';
+
/**
- * Generated from protobuf field .grpc.Info info = 2;
+ * Generated from protobuf field .grpc.Info info = 2;
.
*/
- private $info = null;
+ private $info;
/**
* Constructor.
*
* @param array $data {
- * Optional. Data for populating the Message object.
+ * Optional. Data for populating the Message object.
*
- * @type string $message
- * @type \Grpc\Info $info
+ * @var string $message
+ * @var \Grpc\Info $info
* }
*/
- public function __construct($data = NULL) {
+ public function __construct($data = null)
+ {
\GPBMetadata\Grpc::initOnce();
parent::__construct($data);
}
/**
- * Generated from protobuf field string message = 1;
+ * Generated from protobuf field string message = 1;
.
* @return string
*/
public function getMessage()
@@ -47,20 +57,20 @@ class UserReply extends \Google\Protobuf\Internal\Message
}
/**
- * Generated from protobuf field string message = 1;
+ * Generated from protobuf field string message = 1;
.
* @param string $var
* @return $this
*/
public function setMessage($var)
{
- GPBUtil::checkString($var, True);
+ GPBUtil::checkString($var, true);
$this->message = $var;
return $this;
}
/**
- * Generated from protobuf field .grpc.Info info = 2;
+ * Generated from protobuf field .grpc.Info info = 2;
.
* @return \Grpc\Info
*/
public function getInfo()
@@ -69,7 +79,7 @@ class UserReply extends \Google\Protobuf\Internal\Message
}
/**
- * Generated from protobuf field .grpc.Info info = 2;
+ * Generated from protobuf field .grpc.Info info = 2;
.
* @param \Grpc\Info $var
* @return $this
*/
@@ -80,6 +90,4 @@ class UserReply extends \Google\Protobuf\Internal\Message
return $this;
}
-
}
-
diff --git a/src/grpc-client/tests/RequestTest.php b/src/grpc-client/tests/RequestTest.php
index e16953ad4..56a70bb52 100644
--- a/src/grpc-client/tests/RequestTest.php
+++ b/src/grpc-client/tests/RequestTest.php
@@ -26,10 +26,9 @@ class RequestTest extends TestCase
public function testRequest()
{
$request = new Request($path = 'grpc.service/path', $info = new Info());
- $this->assertSame([
- 'content-type' => 'application/grpc+proto',
- 'user-agent' => 'grpc-php-hyperf/1.0 (hyperf-grpc-client/dev-master)',
- ], $request->headers);
+ $this->assertSame(2, count($request->headers));
+ $this->assertSame('application/grpc+proto', $request->headers['content-type']);
+ $this->assertRegExp('/^grpc-php-hyperf\/1.0 \(hyperf-grpc-client\/.*\)$/', $request->headers['user-agent']);
$this->assertSame($path, $request->path);
$this->assertSame(Parser::serializeMessage($info), $request->data);
}
@@ -37,10 +36,9 @@ class RequestTest extends TestCase
public function testGetDefaultHeaders()
{
$request = new Request($path = 'grpc.service/path', $info = new Info());
- $this->assertSame([
- 'content-type' => 'application/grpc+proto',
- 'user-agent' => 'grpc-php-hyperf/1.0 (hyperf-grpc-client/dev-master)',
- ], $request->getDefaultHeaders());
+ $this->assertSame(2, count($request->getDefaultHeaders()));
+ $this->assertSame('application/grpc+proto', $request->getDefaultHeaders()['content-type']);
+ $this->assertRegExp('/^grpc-php-hyperf\/1.0 \(hyperf-grpc-client\/.*\)$/', $request->getDefaultHeaders()['user-agent']);
}
public function testUserDefinedHeaders()
@@ -49,10 +47,10 @@ class RequestTest extends TestCase
'content-type' => 'application/grpc',
'foo' => 'bar',
]);
- $this->assertSame([
- 'content-type' => 'application/grpc',
- 'user-agent' => 'grpc-php-hyperf/1.0 (hyperf-grpc-client/dev-master)',
- 'foo' => 'bar',
- ], $request->headers);
+
+ $this->assertSame(3, count($request->headers));
+ $this->assertSame('application/grpc', $request->headers['content-type']);
+ $this->assertRegExp('/^grpc-php-hyperf\/1.0 \(hyperf-grpc-client\/.*\)$/', $request->headers['user-agent']);
+ $this->assertSame('bar', $request->headers['foo']);
}
}
diff --git a/src/logger/publish/logger.php b/src/logger/publish/logger.php
index 900417b2f..616fed008 100644
--- a/src/logger/publish/logger.php
+++ b/src/logger/publish/logger.php
@@ -12,16 +12,18 @@ declare(strict_types=1);
return [
'default' => [
- 'handler' => [
- 'class' => \Monolog\Handler\StreamHandler::class,
- 'constructor' => [
- 'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
- 'level' => \Monolog\Logger::DEBUG,
+ 'handlers' => [
+ [
+ 'class' => Monolog\Handler\StreamHandler::class,
+ 'constructor' => [
+ 'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
+ 'level' => Monolog\Logger::DEBUG,
+ ],
+ 'formatter' => [
+ 'class' => Monolog\Formatter\LineFormatter::class,
+ 'constructor' => [],
+ ],
],
],
- 'formatter' => [
- 'class' => \Monolog\Formatter\LineFormatter::class,
- 'constructor' => [],
- ],
],
];
diff --git a/src/logger/src/LoggerFactory.php b/src/logger/src/LoggerFactory.php
index a42efa2f4..dde6b4473 100644
--- a/src/logger/src/LoggerFactory.php
+++ b/src/logger/src/LoggerFactory.php
@@ -14,8 +14,11 @@ namespace Hyperf\Logger;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Logger\Exception\InvalidConfigException;
+use Hyperf\Utils\Arr;
use Monolog\Formatter\FormatterInterface;
+use Monolog\Formatter\LineFormatter;
use Monolog\Handler\HandlerInterface;
+use Monolog\Handler\StreamHandler;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
@@ -50,11 +53,11 @@ class LoggerFactory
}
$config = $config[$group];
- $handler = $this->handler($config);
+ $handlers = $this->handlers($config);
return make(Logger::class, [
'name' => $name,
- 'handlers' => [$handler],
+ 'handlers' => $handlers,
]);
}
@@ -67,16 +70,55 @@ class LoggerFactory
return $this->loggers[$name] = $this->make($name, $group);
}
- protected function handler(array $config): HandlerInterface
+ protected function getDefaultFormatterConfig($config)
{
- $handlerClass = $config['handler']['class'];
- $handlerConstructor = $config['handler']['constructor'];
+ $formatterClass = Arr::get($config, 'formatter.class', LineFormatter::class);
+ $formatterConstructor = Arr::get($config, 'formatter.constructor', []);
+ return [
+ 'class' => $formatterClass,
+ 'constructor' => $formatterConstructor,
+ ];
+ }
+
+ protected function getDefaultHandlerConfig($config)
+ {
+ $handlerClass = Arr::get($config, 'handler.class', StreamHandler::class);
+ $handlerConstructor = Arr::get($config, 'handler.constructor', [
+ 'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
+ 'level' => Logger::DEBUG,
+ ]);
+
+ return [
+ 'class' => $handlerClass,
+ 'constructor' => $handlerConstructor,
+ ];
+ }
+
+ protected function handlers(array $config): array
+ {
+ $handlerConfigs = $config['handlers'] ?? [[]];
+ $handlers = [];
+ $defaultHandlerConfig = $this->getDefaultHandlerConfig($config);
+ $defaultFormatterConfig = $this->getDefaultFormatterConfig($config);
+ foreach ($handlerConfigs as $value) {
+ $class = $value['class'] ?? $defaultHandlerConfig['class'];
+ $constructor = $value['constructor'] ?? $defaultHandlerConfig['constructor'];
+ $formatterConfig = $value['formatter'] ?? $defaultFormatterConfig;
+
+ $handlers[] = $this->handler($class, $constructor, $formatterConfig);
+ }
+
+ return $handlers;
+ }
+
+ protected function handler($class, $constructor, $formatterConfig): HandlerInterface
+ {
/** @var HandlerInterface $handler */
- $handler = make($handlerClass, $handlerConstructor);
+ $handler = make($class, $constructor);
- $formatterClass = $config['formatter']['class'];
- $formatterConstructor = $config['formatter']['constructor'];
+ $formatterClass = $formatterConfig['class'];
+ $formatterConstructor = $formatterConfig['constructor'];
/** @var FormatterInterface $formatter */
$formatter = make($formatterClass, $formatterConstructor);
diff --git a/src/logger/tests/LoggerFactoryTest.php b/src/logger/tests/LoggerFactoryTest.php
index aac705755..c67f3ad4d 100644
--- a/src/logger/tests/LoggerFactoryTest.php
+++ b/src/logger/tests/LoggerFactoryTest.php
@@ -18,10 +18,13 @@ use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\ApplicationContext;
use Mockery;
+use Monolog\Handler\StreamHandler;
+use Monolog\Handler\TestHandler;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
+use ReflectionClass;
/**
* @internal
@@ -47,17 +50,43 @@ class LoggerFactoryTest extends TestCase
ApplicationContext::setContainer($container);
$factory = $container->get(LoggerFactory::class);
$logger = $factory->get('hyperf');
- $this->assertInstanceOf(StdoutLoggerInterface::class, $logger);
- $this->assertInstanceOf(LoggerInterface::class, $logger);
- $this->assertInstanceOf(Logger::class, $logger);
$this->assertInstanceOf(\Hyperf\Logger\Logger::class, $logger);
}
- private function mockContainer()
+ public function testHandlerConfig()
+ {
+ $container = $this->mockContainer();
+ $factory = $container->get(LoggerFactory::class);
+ $logger = $factory->get('hyperf', 'default');
+ $this->assertInstanceOf(\Hyperf\Logger\Logger::class, $logger);
+ $reflectionClass = new ReflectionClass($logger);
+ $handlersProperty = $reflectionClass->getProperty('handlers');
+ $handlersProperty->setAccessible(true);
+ $handlers = $handlersProperty->getValue($logger);
+ $this->assertCount(1, $handlers);
+ $this->assertInstanceOf(StreamHandler::class, current($handlers));
+ }
+
+ public function testHandlersConfig()
+ {
+ $container = $this->mockContainer();
+ $factory = $container->get(LoggerFactory::class);
+ $logger = $factory->get('hyperf', 'default-handlers');
+ $this->assertInstanceOf(\Hyperf\Logger\Logger::class, $logger);
+ $reflectionClass = new ReflectionClass($logger);
+ $handlersProperty = $reflectionClass->getProperty('handlers');
+ $handlersProperty->setAccessible(true);
+ $handlers = $handlersProperty->getValue($logger);
+ $this->assertCount(2, $handlers);
+ $this->assertInstanceOf(StreamHandler::class, $handlers[0]);
+ $this->assertInstanceOf(TestHandler::class, $handlers[1]);
+ }
+
+ private function mockContainer(): ContainerInterface
{
$container = Mockery::mock(ContainerInterface::class);
- $container->shouldReceive('get')->once()->with(ConfigInterface::class)->andReturn(new Config([
+ $container->shouldReceive('get')->with(ConfigInterface::class)->andReturn(new Config([
'logger' => [
'default' => [
'handler' => [
@@ -72,11 +101,31 @@ class LoggerFactoryTest extends TestCase
'constructor' => [],
],
],
+ 'default-handlers' => [
+ 'handlers' => [
+ [
+ 'class' => \Monolog\Handler\StreamHandler::class,
+ 'constructor' => [
+ 'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
+ 'level' => \Monolog\Logger::DEBUG,
+ ],
+ ],
+ [
+ 'class' => \Monolog\Handler\TestHandler::class,
+ 'constructor' => [
+ 'level' => \Monolog\Logger::DEBUG,
+ ],
+ ]
+ ],
+ 'formatter' => [
+ 'class' => \Monolog\Formatter\LineFormatter::class,
+ 'constructor' => [],
+ ],
+ ],
],
]));
$container->shouldReceive('get')
- ->once()
->with(LoggerFactory::class)
->andReturn(new LoggerFactory($container));