Replace logger with CLIMate output
This commit is contained in:
22
bin/acme
22
bin/acme
@@ -2,12 +2,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Auryn\Injector;
|
use Auryn\Injector;
|
||||||
use Bramus\Monolog\Formatter\ColoredLineFormatter;
|
|
||||||
use Kelunik\AcmeClient\LoggerColorScheme;
|
|
||||||
use League\CLImate\CLImate;
|
use League\CLImate\CLImate;
|
||||||
use Monolog\Handler\StreamHandler;
|
|
||||||
use Monolog\Logger;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
if (!file_exists(__DIR__ . "/../vendor/autoload.php")) {
|
if (!file_exists(__DIR__ . "/../vendor/autoload.php")) {
|
||||||
echo <<<HELP
|
echo <<<HELP
|
||||||
@@ -35,7 +30,7 @@ $commands = [
|
|||||||
"revoke",
|
"revoke",
|
||||||
];
|
];
|
||||||
|
|
||||||
$help = implode("\n ", array_map(function($command) {
|
$help = implode("\n ", array_map(function ($command) {
|
||||||
return "bin/acme {$command}";
|
return "bin/acme {$command}";
|
||||||
}, $commands));
|
}, $commands));
|
||||||
|
|
||||||
@@ -110,19 +105,12 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$handler = new StreamHandler("php://stdout", Logger::DEBUG);
|
$injector->share($climate);
|
||||||
$handler->setFormatter(new ColoredLineFormatter(new LoggerColorScheme, null, null, true, true));
|
|
||||||
|
|
||||||
$logger = new Logger("ACME");
|
|
||||||
$logger->pushHandler($handler);
|
|
||||||
|
|
||||||
$injector->alias(LoggerInterface::class, Logger::class);
|
|
||||||
$injector->share($logger);
|
|
||||||
|
|
||||||
$command = $injector->make($class);
|
$command = $injector->make($class);
|
||||||
|
|
||||||
Amp\run(function () use ($command, $climate, $logger) {
|
Amp\run(function () use ($command, $climate) {
|
||||||
$handler = function($e) use ($logger) {
|
$handler = function ($e) use ($climate) {
|
||||||
$error = (string) $e;
|
$error = (string) $e;
|
||||||
$lines = explode("\n", $error);
|
$lines = explode("\n", $error);
|
||||||
$lines = array_filter($lines, function ($line) {
|
$lines = array_filter($lines, function ($line) {
|
||||||
@@ -130,7 +118,7 @@ Amp\run(function () use ($command, $climate, $logger) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$logger->error($line);
|
$climate->error($line)->br();
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -11,18 +11,20 @@
|
|||||||
"tls"
|
"tls"
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"amphp/process": "^0.1.1",
|
"php": "^5.5|^7",
|
||||||
"bramus/monolog-colored-line-formatter": "^2",
|
|
||||||
"ext-openssl": "*",
|
"ext-openssl": "*",
|
||||||
|
"amphp/process": "^0.1.1",
|
||||||
"kelunik/acme": "^0.3",
|
"kelunik/acme": "^0.3",
|
||||||
"kelunik/certificate": "^1",
|
"kelunik/certificate": "^1",
|
||||||
"league/climate": "^3",
|
"league/climate": "^3",
|
||||||
"monolog/monolog": "^1.17",
|
|
||||||
"php": "^5.5|^7",
|
|
||||||
"psr/log": "^1",
|
|
||||||
"rdlowrey/auryn": "^1",
|
"rdlowrey/auryn": "^1",
|
||||||
"webmozart/assert": "^1"
|
"webmozart/assert": "^1"
|
||||||
},
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^5",
|
||||||
|
"fabpot/php-cs-fixer": "^1.9",
|
||||||
|
"macfja/phar-builder": "^0.2.3"
|
||||||
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
@@ -40,11 +42,6 @@
|
|||||||
"src/functions.php"
|
"src/functions.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"require-dev": {
|
|
||||||
"phpunit/phpunit": "^5",
|
|
||||||
"fabpot/php-cs-fixer": "^1.9",
|
|
||||||
"macfja/phar-builder": "^0.2.3"
|
|
||||||
},
|
|
||||||
"extra": {
|
"extra": {
|
||||||
"phar-builder": {
|
"phar-builder": {
|
||||||
"compression": "GZip",
|
"compression": "GZip",
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ namespace Kelunik\AcmeClient\Commands;
|
|||||||
use Kelunik\AcmeClient\Stores\CertificateStore;
|
use Kelunik\AcmeClient\Stores\CertificateStore;
|
||||||
use Kelunik\Certificate\Certificate;
|
use Kelunik\Certificate\Certificate;
|
||||||
use League\CLImate\Argument\Manager;
|
use League\CLImate\Argument\Manager;
|
||||||
use Psr\Log\LoggerInterface;
|
use League\CLImate\CLImate;
|
||||||
|
|
||||||
class Check implements Command {
|
class Check implements Command {
|
||||||
private $logger;
|
private $climate;
|
||||||
|
|
||||||
public function __construct(LoggerInterface $logger) {
|
public function __construct(CLImate $climate) {
|
||||||
$this->logger = $logger;
|
$this->climate = $climate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(Manager $args) {
|
public function execute(Manager $args) {
|
||||||
@@ -32,13 +32,13 @@ class Check implements Command {
|
|||||||
$pem = (yield $certificateStore->get($args->get("name")));
|
$pem = (yield $certificateStore->get($args->get("name")));
|
||||||
$cert = new Certificate($pem);
|
$cert = new Certificate($pem);
|
||||||
|
|
||||||
$this->logger->info("Certificate is valid until " . date("d.m.Y", $cert->getValidTo()));
|
$this->climate->info("Certificate is valid until " . date("d.m.Y", $cert->getValidTo()));
|
||||||
|
|
||||||
if ($cert->getValidTo() > time() + $args->get("ttl") * 24 * 60 * 60) {
|
if ($cert->getValidTo() > time() + $args->get("ttl") * 24 * 60 * 60) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->warning("Certificate is going to expire within the specified " . $args->get("ttl") . " days.");
|
$this->climate->comment("Certificate is going to expire within the specified " . $args->get("ttl") . " days.");
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Kelunik\AcmeClient\Commands;
|
namespace Kelunik\AcmeClient\Commands;
|
||||||
|
|
||||||
use Amp\CombinatorException;
|
|
||||||
use Amp\Dns\Record;
|
use Amp\Dns\Record;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Kelunik\Acme\AcmeClient;
|
use Kelunik\Acme\AcmeClient;
|
||||||
@@ -15,15 +14,15 @@ use Kelunik\AcmeClient\Stores\ChallengeStore;
|
|||||||
use Kelunik\AcmeClient\Stores\KeyStore;
|
use Kelunik\AcmeClient\Stores\KeyStore;
|
||||||
use Kelunik\AcmeClient\Stores\KeyStoreException;
|
use Kelunik\AcmeClient\Stores\KeyStoreException;
|
||||||
use League\CLImate\Argument\Manager;
|
use League\CLImate\Argument\Manager;
|
||||||
use Psr\Log\LoggerInterface;
|
use League\CLImate\CLImate;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class Issue implements Command {
|
class Issue implements Command {
|
||||||
private $logger;
|
private $climate;
|
||||||
|
|
||||||
public function __construct(LoggerInterface $logger) {
|
public function __construct(CLImate $climate) {
|
||||||
$this->logger = $logger;
|
$this->climate = $climate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(Manager $args) {
|
public function execute(Manager $args) {
|
||||||
@@ -49,7 +48,7 @@ class Issue implements Command {
|
|||||||
yield \Amp\resolve($this->checkDnsRecords($domains));
|
yield \Amp\resolve($this->checkDnsRecords($domains));
|
||||||
|
|
||||||
$docRoots = explode(":", str_replace("\\", "/", $args->get("path")));
|
$docRoots = explode(":", str_replace("\\", "/", $args->get("path")));
|
||||||
$docRoots = array_map(function($root) {
|
$docRoots = array_map(function ($root) {
|
||||||
return rtrim($root, "/");
|
return rtrim($root, "/");
|
||||||
}, $docRoots);
|
}, $docRoots);
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ class Issue implements Command {
|
|||||||
try {
|
try {
|
||||||
$keyPair = (yield $keyStore->get("accounts/{$keyFile}.pem"));
|
$keyPair = (yield $keyStore->get("accounts/{$keyFile}.pem"));
|
||||||
} catch (KeyStoreException $e) {
|
} catch (KeyStoreException $e) {
|
||||||
$this->logger->error("Account key not found, did you run 'bin/acme setup'?");
|
$this->climate->error("Account key not found, did you run 'bin/acme setup'?");
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ class Issue implements Command {
|
|||||||
|
|
||||||
if (!empty($errors)) {
|
if (!empty($errors)) {
|
||||||
foreach ($errors as $error) {
|
foreach ($errors as $error) {
|
||||||
$this->logger->error($error->getMessage());
|
$this->climate->error($error->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AcmeException("Issuance failed, not all challenges could be solved.");
|
throw new AcmeException("Issuance failed, not all challenges could be solved.");
|
||||||
@@ -105,7 +104,7 @@ class Issue implements Command {
|
|||||||
$keyPair = (yield $keyStore->put($path, $keyPair));
|
$keyPair = (yield $keyStore->put($path, $keyPair));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->info("Requesting certificate ...");
|
$this->climate->info("Requesting certificate ...");
|
||||||
|
|
||||||
$location = (yield $acme->requestCertificate($keyPair, $domains));
|
$location = (yield $acme->requestCertificate($keyPair, $domains));
|
||||||
$certificates = (yield $acme->pollForCertificate($location));
|
$certificates = (yield $acme->pollForCertificate($location));
|
||||||
@@ -114,7 +113,7 @@ class Issue implements Command {
|
|||||||
$certificateStore = new CertificateStore($path);
|
$certificateStore = new CertificateStore($path);
|
||||||
yield $certificateStore->put($certificates);
|
yield $certificateStore->put($certificates);
|
||||||
|
|
||||||
$this->logger->info("Successfully issued certificate, see {$path}/" . reset($domains));
|
$this->climate->info("Successfully issued certificate, see {$path}/" . reset($domains));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function solveChallenge(AcmeService $acme, KeyPair $keyPair, $domain, $path) {
|
private function solveChallenge(AcmeService $acme, KeyPair $keyPair, $domain, $path) {
|
||||||
@@ -132,11 +131,9 @@ class Issue implements Command {
|
|||||||
throw new AcmeException("Protocol violation: Invalid Token!");
|
throw new AcmeException("Protocol violation: Invalid Token!");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->debug("Generating payload...");
|
|
||||||
$payload = $acme->generateHttp01Payload($keyPair, $token);
|
$payload = $acme->generateHttp01Payload($keyPair, $token);
|
||||||
|
|
||||||
$this->logger->info("Providing payload at http://{$domain}/.well-known/acme-challenge/{$token}");
|
$this->climate->whisper("Providing payload at http://{$domain}/.well-known/acme-challenge/{$token}");
|
||||||
|
|
||||||
|
|
||||||
$challengeStore = new ChallengeStore($path);
|
$challengeStore = new ChallengeStore($path);
|
||||||
|
|
||||||
@@ -144,13 +141,10 @@ class Issue implements Command {
|
|||||||
$challengeStore->put($token, $payload, isset($user) ? $user : null);
|
$challengeStore->put($token, $payload, isset($user) ? $user : null);
|
||||||
|
|
||||||
yield $acme->verifyHttp01Challenge($domain, $token, $payload);
|
yield $acme->verifyHttp01Challenge($domain, $token, $payload);
|
||||||
$this->logger->info("Successfully self-verified challenge.");
|
|
||||||
|
|
||||||
yield $acme->answerChallenge($challenge->uri, $payload);
|
yield $acme->answerChallenge($challenge->uri, $payload);
|
||||||
$this->logger->info("Answered challenge... waiting");
|
|
||||||
|
|
||||||
yield $acme->pollForChallenge($location);
|
yield $acme->pollForChallenge($location);
|
||||||
$this->logger->info("Challenge successful. {$domain} is now authorized.");
|
|
||||||
|
$this->climate->info("{$domain} is now authorized.");
|
||||||
|
|
||||||
yield $challengeStore->delete($token);
|
yield $challengeStore->delete($token);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -179,8 +173,6 @@ class Issue implements Command {
|
|||||||
if (!empty($errors)) {
|
if (!empty($errors)) {
|
||||||
throw new AcmeException("Couldn't resolve the following domains to an IPv4 record: " . implode(array_keys($errors)));
|
throw new AcmeException("Couldn't resolve the following domains to an IPv4 record: " . implode(array_keys($errors)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->info("Checked DNS records, all fine.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findSuitableCombination(stdClass $response) {
|
private function findSuitableCombination(stdClass $response) {
|
||||||
|
|||||||
@@ -3,22 +3,19 @@
|
|||||||
namespace Kelunik\AcmeClient\Commands;
|
namespace Kelunik\AcmeClient\Commands;
|
||||||
|
|
||||||
use Amp\File\FilesystemException;
|
use Amp\File\FilesystemException;
|
||||||
use Amp\Promise;
|
|
||||||
use Generator;
|
|
||||||
use Kelunik\Acme\AcmeClient;
|
use Kelunik\Acme\AcmeClient;
|
||||||
use Kelunik\Acme\AcmeException;
|
|
||||||
use Kelunik\Acme\AcmeService;
|
use Kelunik\Acme\AcmeService;
|
||||||
use Kelunik\AcmeClient\Stores\CertificateStore;
|
use Kelunik\AcmeClient\Stores\CertificateStore;
|
||||||
use Kelunik\AcmeClient\Stores\KeyStore;
|
use Kelunik\AcmeClient\Stores\KeyStore;
|
||||||
use Kelunik\Certificate\Certificate;
|
use Kelunik\Certificate\Certificate;
|
||||||
use League\CLImate\Argument\Manager;
|
use League\CLImate\Argument\Manager;
|
||||||
use Psr\Log\LoggerInterface;
|
use League\CLImate\CLImate;
|
||||||
|
|
||||||
class Revoke implements Command {
|
class Revoke implements Command {
|
||||||
private $logger;
|
private $climate;
|
||||||
|
|
||||||
public function __construct(LoggerInterface $logger) {
|
public function __construct(CLImate $climate) {
|
||||||
$this->logger = $logger;
|
$this->climate = $climate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(Manager $args) {
|
public function execute(Manager $args) {
|
||||||
@@ -34,7 +31,7 @@ class Revoke implements Command {
|
|||||||
$keyPair = (yield $keyStore->get("accounts/{$keyFile}.pem"));
|
$keyPair = (yield $keyStore->get("accounts/{$keyFile}.pem"));
|
||||||
$acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair);
|
$acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair);
|
||||||
|
|
||||||
$this->logger->info("Revoking certificate ...");
|
$this->climate->info("Revoking certificate ...");
|
||||||
|
|
||||||
$path = dirname(dirname(__DIR__)) . "/data/certs/" . $keyFile . "/" . $args->get("name") . "/cert.pem";
|
$path = dirname(dirname(__DIR__)) . "/data/certs/" . $keyFile . "/" . $args->get("name") . "/cert.pem";
|
||||||
|
|
||||||
@@ -46,12 +43,12 @@ class Revoke implements Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($cert->getValidTo() < time()) {
|
if ($cert->getValidTo() < time()) {
|
||||||
$this->logger->warning("Certificate did already expire, no need to revoke it.");
|
$this->climate->info("Certificate did already expire, no need to revoke it.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->info("Certificate was valid for: " . implode(", ", $cert->getNames()));
|
$this->climate->info("Certificate was valid for: " . implode(", ", $cert->getNames()));
|
||||||
yield $acme->revokeCertificate($pem);
|
yield $acme->revokeCertificate($pem);
|
||||||
$this->logger->info("Certificate has been revoked.");
|
$this->climate->info("Certificate has been revoked.");
|
||||||
|
|
||||||
yield (new CertificateStore(dirname(dirname(__DIR__)) . "/data/certs/" . $keyFile))->delete($args->get("name"));
|
yield (new CertificateStore(dirname(dirname(__DIR__)) . "/data/certs/" . $keyFile))->delete($args->get("name"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace Kelunik\AcmeClient\Commands;
|
|||||||
|
|
||||||
use Amp\Dns\Record;
|
use Amp\Dns\Record;
|
||||||
use Amp\Dns\ResolutionException;
|
use Amp\Dns\ResolutionException;
|
||||||
use Amp\Promise;
|
|
||||||
use Generator;
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Kelunik\Acme\AcmeClient;
|
use Kelunik\Acme\AcmeClient;
|
||||||
use Kelunik\Acme\AcmeException;
|
use Kelunik\Acme\AcmeException;
|
||||||
@@ -15,14 +13,13 @@ use Kelunik\Acme\Registration;
|
|||||||
use Kelunik\AcmeClient\Stores\KeyStore;
|
use Kelunik\AcmeClient\Stores\KeyStore;
|
||||||
use Kelunik\AcmeClient\Stores\KeyStoreException;
|
use Kelunik\AcmeClient\Stores\KeyStoreException;
|
||||||
use League\CLImate\Argument\Manager;
|
use League\CLImate\Argument\Manager;
|
||||||
use Psr\Log\LoggerInterface;
|
use League\CLImate\CLImate;
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
class Setup implements Command {
|
class Setup implements Command {
|
||||||
private $logger;
|
private $climate;
|
||||||
|
|
||||||
public function __construct(LoggerInterface $logger) {
|
public function __construct(CLImate $climate) {
|
||||||
$this->logger = $logger;
|
$this->climate = $climate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(Manager $args) {
|
public function execute(Manager $args) {
|
||||||
@@ -42,26 +39,24 @@ class Setup implements Command {
|
|||||||
$keyStore = new KeyStore(dirname(dirname(__DIR__)) . "/data");
|
$keyStore = new KeyStore(dirname(dirname(__DIR__)) . "/data");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->logger->info("Loading private key ...");
|
|
||||||
$keyPair = (yield $keyStore->get($path));
|
$keyPair = (yield $keyStore->get($path));
|
||||||
$this->logger->info("Existing private key successfully loaded.");
|
$this->climate->info("Existing private key successfully loaded.");
|
||||||
} catch (KeyStoreException $e) {
|
} catch (KeyStoreException $e) {
|
||||||
$this->logger->info("No existing private key found, generating new one ...");
|
$this->climate->info("No private key found, generating new one ...");
|
||||||
$keyPair = (new OpenSSLKeyGenerator)->generate($bits);
|
|
||||||
$this->logger->info("Generated new private key with {$bits} bits.");
|
|
||||||
|
|
||||||
$this->logger->info("Saving new private key ...");
|
$keyPair = (new OpenSSLKeyGenerator)->generate($bits);
|
||||||
$keyPair = (yield $keyStore->put($path, $keyPair));
|
$keyPair = (yield $keyStore->put($path, $keyPair));
|
||||||
$this->logger->info("New private key successfully saved.");
|
|
||||||
|
$this->climate->info("Generated new private key with {$bits} bits.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair);
|
$acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair);
|
||||||
|
|
||||||
$this->logger->info("Registering with ACME server " . substr($server, 8) . " ...");
|
$this->climate->info("Registering with ACME server " . substr($server, 8) . " ...");
|
||||||
|
|
||||||
/** @var Registration $registration */
|
/** @var Registration $registration */
|
||||||
$registration = (yield $acme->register($email));
|
$registration = (yield $acme->register($email));
|
||||||
$this->logger->notice("Registration successful with the following contact information: " . implode(", ", $registration->getContact()));
|
$this->climate->whisper("Registration successful with the following contact information: " . implode(", ", $registration->getContact()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkEmail($email) {
|
private function checkEmail($email) {
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Kelunik\AcmeClient;
|
|
||||||
|
|
||||||
use Bramus\Ansi\ControlSequences\EscapeSequences\Enums\SGR;
|
|
||||||
use Bramus\Monolog\Formatter\ColorSchemes\ColorSchemeInterface;
|
|
||||||
use Bramus\Monolog\Formatter\ColorSchemes\ColorSchemeTrait;
|
|
||||||
use Monolog\Logger;
|
|
||||||
|
|
||||||
class LoggerColorScheme implements ColorSchemeInterface {
|
|
||||||
use ColorSchemeTrait {
|
|
||||||
ColorSchemeTrait::__construct as private __constructTrait;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->__constructTrait();
|
|
||||||
|
|
||||||
$this->setColorizeArray([
|
|
||||||
Logger::DEBUG => $this->ansi->color(SGR::COLOR_FG_WHITE)->get(),
|
|
||||||
Logger::INFO => $this->ansi->color(SGR::COLOR_FG_WHITE_BRIGHT)->get(),
|
|
||||||
Logger::NOTICE => $this->ansi->color(SGR::COLOR_FG_GREEN)->get(),
|
|
||||||
Logger::WARNING => $this->ansi->color(SGR::COLOR_FG_YELLOW)->get(),
|
|
||||||
Logger::ERROR => $this->ansi->color(SGR::COLOR_FG_RED)->get(),
|
|
||||||
Logger::CRITICAL => $this->ansi->color(SGR::COLOR_FG_RED)->get(),
|
|
||||||
Logger::ALERT => $this->ansi->color(SGR::COLOR_FG_RED)->get(),
|
|
||||||
Logger::EMERGENCY => $this->ansi->color(SGR::COLOR_FG_RED)->get(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user