Added logging.

This commit is contained in:
Jens True 2023-05-31 14:34:35 +00:00
parent 7a6e71dc6c
commit 4057581b94
10 changed files with 95 additions and 22 deletions

2
.gitignore vendored

@ -1,3 +1,5 @@
/vendor/ /vendor/
/output/
/temp/
config.yml config.yml
composer.phar composer.phar

@ -9,3 +9,6 @@ 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

@ -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

@ -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

@ -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();
$io->info("Rclone version: ". $rclone->getVersion());
$ntfy = new Ntfy\Ntfy($config['notification']['domain']);
$loader = new ArrayLoader($config['templates']); $rclone = new Rclone\Rclone();
$rclone->setLogger($app->getLogger()->withName('rclone'));
$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];
@ -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();
} }
} }