これまでで機能的には充実したバックアップツールができましたが、本番運用するにはまだ足りないものがあります。それは「信頼性」です。
途中でコピーに失敗したら?
何が起きたか後から追跡できるか?
今回は、ログ出力と例外処理を追加し、ツールを堅牢化(Robustness)します。
前回: 圧縮機能の追加 | 目次 | 次回: 振り返りと発展
ログ機能の追加
print 文での出力は手軽ですが、本番では不十分です。Log::Dispatch などを使うのが一般的ですが、今回はツール自体にログ機能を組み込みます。
Backup::Engine にロガーを持たせましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| package Backup::Logger;
use Moo;
use Time::Piece;
has log_file => (is => 'ro');
sub info {
my ($self, $msg) = @_;
$self->_write("INFO", $msg);
}
sub error {
my ($self, $msg) = @_;
$self->_write("ERROR", $msg);
}
sub _write {
my ($self, $level, $msg) = @_;
my $t = localtime->strftime('%Y-%m-%d %H:%M:%S');
my $line = "[$t] [$level] $msg\n";
print $line; # 標準出力にも出す
if ($self->log_file) {
open my $fh, '>>', $self->log_file or return;
print $fh $line;
close $fh;
}
}
1;
|
Engineへの組み込みと例外処理
Engine側では Try::Tiny を使ってエラーを捕捉し、確実にログに残すようにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| package Backup::Engine;
use Moo;
use Try::Tiny;
# ... (以前のuse) ...
use Backup::Logger;
has logger => (is => 'lazy', builder => sub { Backup::Logger->new });
# ...
sub run {
my $self = shift;
$self->logger->info("Backup started using " . ref($self->strategy));
try {
$self->prepare;
my $files = $self->scan;
$self->strategy->execute($self, $files);
$self->logger->info("Backup completed successfully.");
}
catch {
my $e = $_;
$self->logger->error("Backup FAILED: $e");
};
}
|
Strategy側でのエラーハンドリング
Strategy側でも、個々のファイルのコピー失敗で全体を止めないように配慮します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| sub execute {
my ($self, $engine, $files) = @_;
my $errors = 0;
foreach my $file (@$files) {
try {
# ... コピー処理 ...
copy($file, $dest) or die "Copy failed: $!";
}
catch {
$engine->logger->error("Failed to process $file: $_");
$errors++;
};
}
if ($errors > 0) {
$engine->logger->error("Completed with $errors errors.");
}
}
|
リトライ機能(発展)
ネットワークドライブへのバックアップなどでは、一過性のエラーが発生しがちです。Decoratorパターンを使って「リトライ機能付きStrategy」を作ることもできますが、今回はシンプルにサブルーチン再試行のイディオムを紹介します。
1
2
3
4
5
| use Sub::Retry;
retry 3, 1, sub {
copy($file, $dest) or die;
};
|
このような堅牢化コードを入れることで、夜間バッチで無人で動かしても安心なツールになります。
次回は最終回。これまでの設計を振り返り、さらなる発展の可能性を探ります。