Added logging.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
/vendor/
|
/vendor/
|
||||||
|
/output/
|
||||||
|
/temp/
|
||||||
config.yml
|
config.yml
|
||||||
composer.phar
|
composer.phar
|
5
Makefile
5
Makefile
@ -8,4 +8,7 @@ analyze-all:
|
|||||||
install:
|
install:
|
||||||
php composer.phar install --no-dev
|
php composer.phar install --no-dev
|
||||||
install-dev:
|
install-dev:
|
||||||
php composer.phar install
|
php composer.phar install
|
||||||
|
test:
|
||||||
|
./vendor/bin/phpunit tests --testdox --coverage-filter src --coverage-html output/coverage
|
||||||
|
|
4
backup
4
backup
@ -21,9 +21,11 @@ if (! $autoload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require $autoload;
|
require $autoload;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Composer\InstalledVersions;
|
||||||
|
|
||||||
|
|
||||||
$package = \Composer\InstalledVersions::getRootPackage();
|
$package = \Composer\InstalledVersions::getRootPackage();
|
||||||
use Symfony\Component\Console\Application;
|
|
||||||
|
|
||||||
$application = new Application('backup', $package['version']);
|
$application = new Application('backup', $package['version']);
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
notification:
|
notification:
|
||||||
domain: ntfy.jcktrue.dk
|
domain: ntfy.jcktrue.dk
|
||||||
topic: backup
|
topic: backup
|
||||||
|
logging:
|
||||||
|
stream: output.log
|
||||||
rclone:
|
rclone:
|
||||||
bwlimit: 6M
|
bwlimit: 6M
|
||||||
backup:
|
backup:
|
||||||
- title: Example
|
- title: Example
|
||||||
source: test/src
|
source: temp/source
|
||||||
destination: test/dest
|
destination: temp/destination
|
||||||
templates:
|
templates:
|
||||||
notify: |
|
notify: |
|
||||||
{{ config.title }}
|
{{ config.title }}
|
||||||
|
11
output.log
Normal file
11
output.log
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[2023-05-31T14:30:00.890085+00:00] app.INFO: Initialization complete [] []
|
||||||
|
[2023-05-31T14:30:00.946882+00:00] rclone.INFO: Execute command ["'rclone' 'size' '--json' 'temp/source'"] []
|
||||||
|
[2023-05-31T14:30:00.989664+00:00] rclone.INFO: Return code [0] []
|
||||||
|
[2023-05-31T14:30:00.989960+00:00] rclone.INFO: Execute command ["'rclone' 'size' '--json' 'temp/destination'"] []
|
||||||
|
[2023-05-31T14:30:01.047504+00:00] rclone.INFO: Return code [0] []
|
||||||
|
[2023-05-31T14:30:01.047810+00:00] rclone.INFO: Execute command ["'rclone' 'copy' 'temp/source' 'temp/destination' '--bwlimit' '6M'"] []
|
||||||
|
[2023-05-31T14:30:01.097485+00:00] rclone.INFO: Return code [0] []
|
||||||
|
[2023-05-31T14:30:01.097797+00:00] rclone.INFO: Execute command ["'rclone' 'size' '--json' 'temp/destination'"] []
|
||||||
|
[2023-05-31T14:30:01.142119+00:00] rclone.INFO: Return code [0] []
|
||||||
|
[2023-05-31T14:30:01.174134+00:00] notification.DEBUG: Sending ntfy notification {"topic":"backup","title":"Example","message":"Example\nFrom temp/source to temp/destination\nBackup started: May 31, 2023 14:30\nSource size: 8.00B\nDestination before: 8.00B\nDestination after: 8.00B\nDestination change : 0.00B\nBackup completed: May 31, 2023 14:30\n"} []
|
||||||
|
[2023-05-31T14:30:01.363282+00:00] notification.DEBUG: Result of ntfy notification ["{\"id\":\"iHfqUGi4dUsC\",\"time\":1685543401,\"expires\":1685586601,\"event\":\"message\",\"topic\":\"backup\",\"title\":\"Example\",\"message\":\"Example\\nFrom temp/source to temp/destination\\nBackup started: May 31, 2023 14:30\\nSource size: 8.00B\\nDestination before: 8.00B\\nDestination after: 8.00B\\nDestination change : 0.00B\\nBackup completed: May 31, 2023 14:30\"}\n"] []
|
36
src/App.php
Normal file
36
src/App.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Symfony\Component\Yaml\Exception\ParseException;
|
||||||
|
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
|
||||||
|
|
||||||
|
class App {
|
||||||
|
protected Logger $logger;
|
||||||
|
protected array $config;
|
||||||
|
|
||||||
|
function __construct(string $configFile)
|
||||||
|
{
|
||||||
|
$this->config = Yaml::parseFile($configFile);
|
||||||
|
|
||||||
|
$logger = new Logger('app');
|
||||||
|
$logger->pushHandler(new StreamHandler($this->getConfig()['logging']['stream']));
|
||||||
|
$logger->info("Initialization complete");
|
||||||
|
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getConfig() : array
|
||||||
|
{
|
||||||
|
return $this->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogger() : Logger
|
||||||
|
{
|
||||||
|
return $this->logger;
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,7 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Symfony\Component\Yaml\Exception\ParseException;
|
|
||||||
|
|
||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
use Twig\Loader\ArrayLoader;
|
use Twig\Loader\ArrayLoader;
|
||||||
@ -30,22 +29,27 @@ class CommandBackup extends Command
|
|||||||
$io->title('Start backup process');
|
$io->title('Start backup process');
|
||||||
|
|
||||||
$io->info('Opening: '.$input->getArgument('config'));
|
$io->info('Opening: '.$input->getArgument('config'));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$config = Yaml::parseFile($input->getArgument('config'));
|
$app = new App($input->getArgument('config'));
|
||||||
} catch (ParseException $e) {
|
}
|
||||||
|
catch (\Throwable $e) {
|
||||||
$io->error('Unable to parse the YAML string: '. $e->getMessage());
|
$io->error('Unable to parse the YAML string: '. $e->getMessage());
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$rclone = new Rclone\Rclone();
|
$rclone = new Rclone\Rclone();
|
||||||
$io->info("Rclone version: ". $rclone->getVersion());
|
$rclone->setLogger($app->getLogger()->withName('rclone'));
|
||||||
$ntfy = new Ntfy\Ntfy($config['notification']['domain']);
|
|
||||||
|
|
||||||
$loader = new ArrayLoader($config['templates']);
|
$ntfy = new Ntfy\Ntfy($app->getConfig()['notification']['domain']);
|
||||||
|
$ntfy->setLogger( $app->getLogger()->withName('notification'));
|
||||||
|
|
||||||
|
$loader = new ArrayLoader($app->getConfig()['templates']);
|
||||||
$twig = new Environment($loader);
|
$twig = new Environment($loader);
|
||||||
$twig->addExtension(new Twig\AppExtension());
|
$twig->addExtension(new Twig\AppExtension());
|
||||||
|
|
||||||
foreach($io->progressIterate( $config['backup']) as $conf) {
|
foreach ($io->progressIterate($app->getConfig()['backup']) as $conf) {
|
||||||
try {
|
try {
|
||||||
$template = array();
|
$template = array();
|
||||||
$template['config'] = $conf;
|
$template['config'] = $conf;
|
||||||
@ -63,7 +67,7 @@ class CommandBackup extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
}
|
}
|
||||||
$ntfy->send($config['notification']['topic'], $conf['title'], $message);
|
$ntfy->send($app->getConfig()['notification']['topic'], $conf['title'], $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->success("Complete");
|
$io->success("Complete");
|
||||||
|
@ -25,15 +25,15 @@ class CommandShow extends Command
|
|||||||
$io = new SymfonyStyle($input, $output);
|
$io = new SymfonyStyle($input, $output);
|
||||||
$io->title('List backup entities');
|
$io->title('List backup entities');
|
||||||
|
|
||||||
$io->note('Reading from: '.$input->getArgument('config'));
|
|
||||||
try {
|
try {
|
||||||
$config = Yaml::parseFile($input->getArgument('config'));
|
$app = new App($input->getArgument('config'));
|
||||||
} catch (ParseException $e) {
|
}
|
||||||
|
catch (\Throwable $e) {
|
||||||
$io->error('Unable to parse the YAML string: '. $e->getMessage());
|
$io->error('Unable to parse the YAML string: '. $e->getMessage());
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->table(['Description', 'Source', 'Destination'], $config['backup']);
|
$io->table(['Description', 'Source', 'Destination'], $app->getConfig()['backup']);
|
||||||
|
|
||||||
$io->success("Done");
|
$io->success("Done");
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace App\Ntfy;
|
namespace App\Ntfy;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerAwareTrait;
|
||||||
|
|
||||||
class Ntfy
|
class Ntfy
|
||||||
{
|
{
|
||||||
|
use LoggerAwareTrait;
|
||||||
protected string $domain;
|
protected string $domain;
|
||||||
function __construct(string $domain)
|
function __construct(string $domain)
|
||||||
{
|
{
|
||||||
@ -11,7 +14,8 @@ class Ntfy
|
|||||||
|
|
||||||
function send(string $topic, string $title, string $message): void
|
function send(string $topic, string $title, string $message): void
|
||||||
{
|
{
|
||||||
file_get_contents(
|
$this->logger->debug("Sending ntfy notification", ["topic"=>$topic, "title"=>$title, "message"=>$message]);
|
||||||
|
$result = file_get_contents(
|
||||||
'https://'.$this->domain.'/'.$topic, false, stream_context_create(
|
'https://'.$this->domain.'/'.$topic, false, stream_context_create(
|
||||||
['http' => [
|
['http' => [
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
@ -22,6 +26,7 @@ class Ntfy
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
$this->logger->debug("Result of ntfy notification", [$result]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,13 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace App\Rclone;
|
namespace App\Rclone;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerAwareTrait;
|
||||||
|
use Psr\Log\NullLogger;
|
||||||
|
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||||
|
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class Rclone
|
class Rclone
|
||||||
{
|
{
|
||||||
|
use LoggerAwareTrait;
|
||||||
|
|
||||||
protected string $rclonePath;
|
protected string $rclonePath;
|
||||||
/**
|
/**
|
||||||
* Global options
|
* Global options
|
||||||
@ -21,6 +27,7 @@ class Rclone
|
|||||||
function __construct(string $rclonePath = "rclone")
|
function __construct(string $rclonePath = "rclone")
|
||||||
{
|
{
|
||||||
$this->rclonePath = $rclonePath;
|
$this->rclonePath = $rclonePath;
|
||||||
|
$this->setLogger(new NullLogger);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$version = $this->exec('--version');
|
$version = $this->exec('--version');
|
||||||
@ -28,7 +35,6 @@ class Rclone
|
|||||||
catch(ProcessFailedException $e)
|
catch(ProcessFailedException $e)
|
||||||
{
|
{
|
||||||
throw new Exception("Check installation of rclone");
|
throw new Exception("Check installation of rclone");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->version = explode("\n", $version)[0];
|
$this->version = explode("\n", $version)[0];
|
||||||
@ -68,7 +74,7 @@ class Rclone
|
|||||||
* @param array<String> $options
|
* @param array<String> $options
|
||||||
*/
|
*/
|
||||||
protected function exec(string $command, array $options = array()) : string
|
protected function exec(string $command, array $options = array()) : string
|
||||||
{
|
{
|
||||||
$process = new Process(
|
$process = new Process(
|
||||||
array_merge(
|
array_merge(
|
||||||
[$this->rclonePath],
|
[$this->rclonePath],
|
||||||
@ -77,14 +83,16 @@ class Rclone
|
|||||||
$options
|
$options
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
$this->logger->info("Execute command", [$process->getCommandLine()]);
|
||||||
$process->setTimeout(4*3600);
|
$process->setTimeout(4*3600);
|
||||||
$process->run();
|
$process->run();
|
||||||
|
|
||||||
// executes after the command finishes
|
// executes after the command finishes
|
||||||
if (!$process->isSuccessful()) {
|
if (!$process->isSuccessful()) {
|
||||||
|
$this->logger->error("Failed execution");
|
||||||
throw new ProcessFailedException($process);
|
throw new ProcessFailedException($process);
|
||||||
}
|
}
|
||||||
|
$this->logger->info("Return code", [$process->getExitCode()]);
|
||||||
return $process->getOutput();
|
return $process->getOutput();
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user