diff --git a/examples/custom_logger.php b/examples/custom_logger.php new file mode 100644 index 00000000..3ae61aba --- /dev/null +++ b/examples/custom_logger.php @@ -0,0 +1,73 @@ +'; + + // Create your custom logger instance + $customLogger = new Logger('my-custom-logger'); + $customLogger->pushHandler(new StreamHandler('/tmp/typesense-custom.log', Logger::DEBUG)); + + // You can also use any other PSR-3 compatible logger + // For example: Symfony's Logger, Laravel's Logger, etc. + + echo "--------Example 1: Client with Custom Logger-------\n"; + // Initialize Typesense client with custom logger + $client = new Client( + [ + 'api_key' => 'xyz', + 'nodes' => [ + [ + 'host' => 'localhost', + 'port' => '8108', + 'protocol' => 'http', + ], + ], + 'client' => new HttplugClient(), + 'logger' => $customLogger, // Inject your custom logger here + ] + ); + + // Use the client - all logs will now use your custom logger + $health = $client->health->retrieve(); + print_r($health); + echo "✓ Using custom logger - logs written to /tmp/typesense-custom.log\n"; + + echo "\n--------Example 2: Client with Default Logger-------\n"; + // Example without custom logger (uses default): + $clientWithDefaultLogger = new Client( + [ + 'api_key' => 'xyz', + 'nodes' => [ + [ + 'host' => 'localhost', + 'port' => '8108', + 'protocol' => 'http', + ], + ], + 'client' => new HttplugClient(), + 'log_level' => Logger::INFO, // You can customize the log level + ] + ); + + $health2 = $clientWithDefaultLogger->health->retrieve(); + print_r($health2); + echo "✓ Using default logger - logs written to stdout\n"; + +} catch (Exception $e) { + echo $e->getMessage(); +} + diff --git a/src/Lib/Configuration.php b/src/Lib/Configuration.php index e0767ca2..b2c788c9 100644 --- a/src/Lib/Configuration.php +++ b/src/Lib/Configuration.php @@ -110,9 +110,22 @@ public function __construct(array $config) $this->numRetries = (float)($config['num_retries'] ?? 3); $this->retryIntervalSeconds = (float)($config['retry_interval_seconds'] ?? 1.0); - $this->logLevel = $config['log_level'] ?? Logger::WARNING; - $this->logger = new Logger('typesense'); - $this->logger->pushHandler(new StreamHandler('php://stdout', $this->logLevel)); + // Allow custom logger injection + if (isset($config['logger'])) { + if (!$config['logger'] instanceof LoggerInterface) { + throw new ConfigError('Logger must implement Psr\Log\LoggerInterface'); + } + + if (isset($config['log_level'])) { + throw new \InvalidArgumentException('Setting log_level is not allowed when a custom logger is provided.'); + } + + $this->logger = $config['logger']; + } else { + $this->logLevel = $config['log_level'] ?? Logger::WARNING; + $this->logger = new Logger('typesense'); + $this->logger->pushHandler(new StreamHandler('php://stdout', $this->logLevel)); + } if (isset($config['client'])) { if ($config['client'] instanceof HttpMethodsClient || $config['client'] instanceof ClientInterface) { diff --git a/tests/Feature/ConfigurationTest.php b/tests/Feature/ConfigurationTest.php new file mode 100644 index 00000000..4295afa3 --- /dev/null +++ b/tests/Feature/ConfigurationTest.php @@ -0,0 +1,161 @@ +baseConfig = [ + 'api_key' => 'test_api_key', + 'nodes' => [ + [ + 'host' => 'localhost', + 'port' => '8108', + 'protocol' => 'http', + ], + ], + ]; + } + + public function testConfigurationWithDefaultLogger(): void + { + $config = new Configuration($this->baseConfig); + + $logger = $config->getLogger(); + + $this->assertInstanceOf(Logger::class, $logger); + } + + public function testConfigurationWithCustomLogger(): void + { + // Create a custom logger + $customLogger = new Logger('custom-test-logger'); + $customLogger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG)); + + // Add custom logger to config + $configWithCustomLogger = array_merge($this->baseConfig, [ + 'logger' => $customLogger, + ]); + + $config = new Configuration($configWithCustomLogger); + + $logger = $config->getLogger(); + + // Assert that the logger is the same instance we passed + $this->assertSame($customLogger, $logger); + } + + public function testConfigurationWithCustomLogLevel(): void + { + // Add custom log level to config + $configWithLogLevel = array_merge($this->baseConfig, [ + 'log_level' => Logger::DEBUG, + ]); + + $config = new Configuration($configWithLogLevel); + + $logger = $config->getLogger(); + $this->assertInstanceOf(Logger::class, $logger); + } + + public function testConfigurationWithCustomLoggerThrowsExceptionWhenLogLevelIsAlsoProvided(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Setting log_level is not allowed when a custom logger is provided.'); + + $customLogger = new Logger('custom-logger-with-level'); + $customLogger->pushHandler(new StreamHandler('php://stdout', Logger::ERROR)); + + $configWithBoth = array_merge($this->baseConfig, [ + 'logger' => $customLogger, + 'log_level' => Logger::DEBUG, + ]); + + new Configuration($configWithBoth); + } + + public function testConfigurationWithInvalidLoggerThrowsException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Logger must implement Psr\Log\LoggerInterface'); + + // Try to pass a non-logger object (should throw exception) + $configWithInvalidLogger = array_merge($this->baseConfig, [ + 'logger' => 'not-a-logger-instance', + ]); + + new Configuration($configWithInvalidLogger); + } + + public function testConfigurationThrowsErrorWhenNodesAreMissing(): void + { + $this->expectException(ConfigError::class); + $this->expectExceptionMessage('`nodes` is not defined.'); + + new Configuration([ + 'api_key' => 'test_api_key', + ]); + } + + public function testConfigurationThrowsErrorWhenApiKeyIsMissing(): void + { + $this->expectException(ConfigError::class); + $this->expectExceptionMessage('`api_key` is not defined.'); + + new Configuration([ + 'nodes' => [ + [ + 'host' => 'localhost', + 'port' => '8108', + 'protocol' => 'http', + ], + ], + ]); + } + + public function testConfigurationWithAllOptions(): void + { + $customLogger = new Logger('full-config-logger'); + $customLogger->pushHandler(new StreamHandler('php://stdout', Logger::INFO)); + + $fullConfig = [ + 'api_key' => 'test_api_key', + 'nodes' => [ + [ + 'host' => 'localhost', + 'port' => '8108', + 'protocol' => 'http', + 'path' => '/api', + ], + ], + 'nearest_node' => [ + 'host' => 'nearest.example.com', + 'port' => '443', + 'protocol' => 'https', + ], + 'logger' => $customLogger, + 'num_retries' => 5, + 'retry_interval_seconds' => 2.0, + 'healthcheck_interval_seconds' => 30, + 'randomize_nodes' => false, + ]; + + $config = new Configuration($fullConfig); + + $this->assertSame($customLogger, $config->getLogger()); + $this->assertEquals(5, $config->getNumRetries()); + $this->assertEquals(2.0, $config->getRetryIntervalSeconds()); + $this->assertEquals(30, $config->getHealthCheckIntervalSeconds()); + $this->assertNotNull($config->getNearestNode()); + } +} +