Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ $eppoClient = EppoClient::init(
$cache // optional, must be an instance of PSR-16 SimpleCache\CacheInterface. If not passed, FileSystem cache will be used
$httpClient // optional, must be an instance of PSR-18 ClientInterface. If not passed, Discovery will be used to find a suitable implementation
$requestFactory // optional, must be an instance of PSR-17 Factory. If not passed, Discovery will be used to find a suitable implementation
$logger // optional, must be an instance of PSR-3 LoggerInterface for SDK logs
);
```

Expand Down Expand Up @@ -131,6 +132,9 @@ The `init` function accepts the following optional configuration arguments.
| **`assignmentLogger`** | AssignmentLogger/IBanditLogger | Logs assignment events back to data warehoouse | `null` |
| **`httpClient`** | ClientInterface | For making HTTP requests. If not passed, Discovery will attempt to autoload an applicable pacakge | `null` |
| **`requestFactory`** | RequestFactoryInterface | Instance of PSR-17 Factory. If not passed, Discovery will be used to find a suitable implementation | null |
| **`logger`** | PSR-3 LoggerInterface | Logs SDK warnings/errors (replaces `syslog`/`error_log`) | `null` |

To preserve the previous syslog behavior, pass a PSR-3 logger configured for syslog, such as Monolog with a SyslogHandler.

## Assignment logger

Expand Down Expand Up @@ -216,6 +220,7 @@ $eppoClient = EppoClient::init(
$cache // optional, must be an instance of PSR-16 SimpleInterface. If not passed, FileSystem cache will be used
$httpClient // optional, must be an instance of PSR-18 ClientInterface. If not passed, Discovery will be used to find a suitable implementation
$requestFactory // optional, must be an instance of PSR-17 Factory. If not passed, Discovery will be used to find a suitable implementation
$logger // optional, must be an instance of PSR-3 LoggerInterface for SDK logs
);

$eppoClient->startPolling();
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"php": "^8.1",
"ext-json": "*",
"psr/simple-cache": "3.*",
"psr/log": "^2.0|^3.0",
"shrikeh/teapot": "^2.3",
"composer/semver": "^3.4",
"php-http/discovery": "^1.17",
Expand Down
16 changes: 8 additions & 8 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 13 additions & 3 deletions src/Config/ConfigurationLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@
use Eppo\DTO\FlagConfigResponse;
use Eppo\Exception\HttpRequestException;
use Eppo\Exception\InvalidApiKeyException;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;

class ConfigurationLoader
class ConfigurationLoader implements LoggerAwareInterface
{
use LoggerAwareTrait;

public function __construct(
private readonly APIRequestWrapper $apiRequestWrapper,
public readonly ConfigurationStore $configurationStore,
private readonly int $cacheAgeLimitMillis = 30 * 1000
) {
$this->setLogger(new NullLogger());
}

/**
Expand Down Expand Up @@ -46,7 +52,9 @@ public function fetchAndStoreConfiguration(?string $flagETag): void
$responseData = json_decode($response->body, true);

if ($responseData === null) {
syslog(LOG_WARNING, "[Eppo SDK] Empty or invalid response from the configuration server.");
$this->logger->warning(
'[Eppo SDK] Empty or invalid response from the configuration server.'
);
return;
}
$fcr = FlagConfigResponse::fromArray($responseData);
Expand Down Expand Up @@ -79,7 +87,9 @@ public function fetchAndStoreConfiguration(?string $flagETag): void
// Configuration object.
$banditResource = $this->apiRequestWrapper->getBandits();
if (!$banditResource?->body) {
syslog(E_ERROR, "[Eppo SDK] Empty or invalid bandit response from the configuration server.");
$this->logger->error(
'[Eppo SDK] Empty or invalid bandit response from the configuration server.'
);
} else {
$banditResponse = new ConfigResponse($banditResource->body, date('c'), $banditResource->eTag);
}
Expand Down
18 changes: 15 additions & 3 deletions src/Config/ConfigurationStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
namespace Eppo\Config;

use Eppo\DTO\ConfigurationWire\ConfigurationWire;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
use Psr\SimpleCache\CacheInterface;
use Throwable;

class ConfigurationStore
class ConfigurationStore implements LoggerAwareInterface
{
use LoggerAwareTrait;

private const CONFIG_KEY = "EPPO_configuration_v1";
private ?Configuration $configuration = null;

public function __construct(private readonly CacheInterface $cache)
{
$this->setLogger(new NullLogger());
}

public function getConfiguration(): Configuration
Expand All @@ -37,7 +43,10 @@ public function getConfiguration(): Configuration
return $this->configuration;
} catch (Throwable $e) {
// Safe to ignore as the const `CONFIG_KEY` contains no invalid characters
syslog(LOG_ERR, "[Eppo SDK] Error loading config from cache " . $e->getMessage());
$this->logger->error(
'[Eppo SDK] Error loading config from cache ' . $e->getMessage(),
['exception' => $e]
);
return Configuration::emptyConfig();
}
}
Expand All @@ -49,7 +58,10 @@ public function setConfiguration(Configuration $configuration): void
$this->cache->set(self::CONFIG_KEY, json_encode($configuration->toConfigurationWire()->toArray()));
} catch (Throwable $e) {
// Safe to ignore as the const `CONFIG_KEY` contains no invalid characters
syslog(LOG_ERR, "[Eppo SDK] Error loading config from cache " . $e->getMessage());
$this->logger->error(
'[Eppo SDK] Error saving config to cache ' . $e->getMessage(),
['exception' => $e]
);
}
}
}
7 changes: 0 additions & 7 deletions src/DTO/Bandit/AttributeSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ public function __construct(
foreach ($numericAttributes as $key => $value) {
if (self::isNumberType($value)) {
$numeric[$key] = $value;
} else {
syslog(
LOG_WARNING,
"[Eppo SDK] non-numeric attribute passed in `\$numericAttributes` for key $key"
);
}
}
$this->numericAttributes = $numeric;
Expand All @@ -51,8 +46,6 @@ public static function fromArray(array $attributes): self
$numericAttributes[$key] = $value;
} elseif (self::isCategoricalType($value)) {
$categoricalAttributes[$key] = $value;
} else {
syslog(LOG_WARNING, "[Eppo SDK] Unsupported attribute type: " . gettype($value));
}
}
return new self($numericAttributes, $categoricalAttributes);
Expand Down
4 changes: 0 additions & 4 deletions src/DTO/Bandit/Bandit.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ public static function fromArray(array $arr): Bandit
$updatedAt = new DateTime($arr['updatedAt']);
}
} catch (\Exception $e) {
syslog(
LOG_WARNING,
"[Eppo SDK] invalid timestamp for bandit model {$arr['updatedAt']}: " . $e->getMessage()
);
$updatedAt = new DateTime();
} finally {
return new Bandit(
Expand Down
Loading