backupii 0.1.0.pre.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +37 -0
- data/bin/backupii +5 -0
- data/bin/docker_test +24 -0
- data/lib/backup/archive.rb +171 -0
- data/lib/backup/binder.rb +23 -0
- data/lib/backup/cleaner.rb +114 -0
- data/lib/backup/cli.rb +376 -0
- data/lib/backup/cloud_io/base.rb +40 -0
- data/lib/backup/cloud_io/cloud_files.rb +301 -0
- data/lib/backup/cloud_io/s3.rb +256 -0
- data/lib/backup/compressor/base.rb +34 -0
- data/lib/backup/compressor/bzip2.rb +37 -0
- data/lib/backup/compressor/custom.rb +51 -0
- data/lib/backup/compressor/gzip.rb +76 -0
- data/lib/backup/config/dsl.rb +103 -0
- data/lib/backup/config/helpers.rb +139 -0
- data/lib/backup/config.rb +122 -0
- data/lib/backup/database/base.rb +89 -0
- data/lib/backup/database/mongodb.rb +189 -0
- data/lib/backup/database/mysql.rb +194 -0
- data/lib/backup/database/openldap.rb +97 -0
- data/lib/backup/database/postgresql.rb +134 -0
- data/lib/backup/database/redis.rb +179 -0
- data/lib/backup/database/riak.rb +82 -0
- data/lib/backup/database/sqlite.rb +57 -0
- data/lib/backup/encryptor/base.rb +29 -0
- data/lib/backup/encryptor/gpg.rb +745 -0
- data/lib/backup/encryptor/open_ssl.rb +76 -0
- data/lib/backup/errors.rb +55 -0
- data/lib/backup/logger/console.rb +50 -0
- data/lib/backup/logger/fog_adapter.rb +27 -0
- data/lib/backup/logger/logfile.rb +134 -0
- data/lib/backup/logger/syslog.rb +116 -0
- data/lib/backup/logger.rb +199 -0
- data/lib/backup/model.rb +478 -0
- data/lib/backup/notifier/base.rb +128 -0
- data/lib/backup/notifier/campfire.rb +63 -0
- data/lib/backup/notifier/command.rb +101 -0
- data/lib/backup/notifier/datadog.rb +107 -0
- data/lib/backup/notifier/flowdock.rb +101 -0
- data/lib/backup/notifier/hipchat.rb +118 -0
- data/lib/backup/notifier/http_post.rb +116 -0
- data/lib/backup/notifier/mail.rb +235 -0
- data/lib/backup/notifier/nagios.rb +67 -0
- data/lib/backup/notifier/pagerduty.rb +82 -0
- data/lib/backup/notifier/prowl.rb +70 -0
- data/lib/backup/notifier/pushover.rb +73 -0
- data/lib/backup/notifier/ses.rb +126 -0
- data/lib/backup/notifier/slack.rb +149 -0
- data/lib/backup/notifier/twitter.rb +57 -0
- data/lib/backup/notifier/zabbix.rb +62 -0
- data/lib/backup/package.rb +53 -0
- data/lib/backup/packager.rb +108 -0
- data/lib/backup/pipeline.rb +122 -0
- data/lib/backup/splitter.rb +75 -0
- data/lib/backup/storage/base.rb +72 -0
- data/lib/backup/storage/cloud_files.rb +158 -0
- data/lib/backup/storage/cycler.rb +73 -0
- data/lib/backup/storage/dropbox.rb +208 -0
- data/lib/backup/storage/ftp.rb +118 -0
- data/lib/backup/storage/local.rb +63 -0
- data/lib/backup/storage/qiniu.rb +68 -0
- data/lib/backup/storage/rsync.rb +251 -0
- data/lib/backup/storage/s3.rb +157 -0
- data/lib/backup/storage/scp.rb +67 -0
- data/lib/backup/storage/sftp.rb +82 -0
- data/lib/backup/syncer/base.rb +70 -0
- data/lib/backup/syncer/cloud/base.rb +180 -0
- data/lib/backup/syncer/cloud/cloud_files.rb +83 -0
- data/lib/backup/syncer/cloud/local_file.rb +99 -0
- data/lib/backup/syncer/cloud/s3.rb +118 -0
- data/lib/backup/syncer/rsync/base.rb +55 -0
- data/lib/backup/syncer/rsync/local.rb +29 -0
- data/lib/backup/syncer/rsync/pull.rb +49 -0
- data/lib/backup/syncer/rsync/push.rb +206 -0
- data/lib/backup/template.rb +45 -0
- data/lib/backup/utilities.rb +235 -0
- data/lib/backup/version.rb +5 -0
- data/lib/backup.rb +141 -0
- data/templates/cli/archive +28 -0
- data/templates/cli/compressor/bzip2 +4 -0
- data/templates/cli/compressor/custom +7 -0
- data/templates/cli/compressor/gzip +4 -0
- data/templates/cli/config +123 -0
- data/templates/cli/databases/mongodb +15 -0
- data/templates/cli/databases/mysql +18 -0
- data/templates/cli/databases/openldap +24 -0
- data/templates/cli/databases/postgresql +16 -0
- data/templates/cli/databases/redis +16 -0
- data/templates/cli/databases/riak +17 -0
- data/templates/cli/databases/sqlite +11 -0
- data/templates/cli/encryptor/gpg +27 -0
- data/templates/cli/encryptor/openssl +9 -0
- data/templates/cli/model +26 -0
- data/templates/cli/notifier/zabbix +15 -0
- data/templates/cli/notifiers/campfire +12 -0
- data/templates/cli/notifiers/command +32 -0
- data/templates/cli/notifiers/datadog +57 -0
- data/templates/cli/notifiers/flowdock +16 -0
- data/templates/cli/notifiers/hipchat +16 -0
- data/templates/cli/notifiers/http_post +32 -0
- data/templates/cli/notifiers/mail +24 -0
- data/templates/cli/notifiers/nagios +13 -0
- data/templates/cli/notifiers/pagerduty +12 -0
- data/templates/cli/notifiers/prowl +11 -0
- data/templates/cli/notifiers/pushover +11 -0
- data/templates/cli/notifiers/ses +15 -0
- data/templates/cli/notifiers/slack +22 -0
- data/templates/cli/notifiers/twitter +13 -0
- data/templates/cli/splitter +7 -0
- data/templates/cli/storages/cloud_files +11 -0
- data/templates/cli/storages/dropbox +20 -0
- data/templates/cli/storages/ftp +13 -0
- data/templates/cli/storages/local +8 -0
- data/templates/cli/storages/qiniu +12 -0
- data/templates/cli/storages/rsync +17 -0
- data/templates/cli/storages/s3 +16 -0
- data/templates/cli/storages/scp +15 -0
- data/templates/cli/storages/sftp +15 -0
- data/templates/cli/syncers/cloud_files +22 -0
- data/templates/cli/syncers/rsync_local +20 -0
- data/templates/cli/syncers/rsync_pull +28 -0
- data/templates/cli/syncers/rsync_push +28 -0
- data/templates/cli/syncers/s3 +27 -0
- data/templates/general/links +3 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +16 -0
- data/templates/notifier/mail/success.erb +16 -0
- data/templates/notifier/mail/warning.erb +16 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- metadata +507 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Encryptor
|
5
|
+
class OpenSSL < Base
|
6
|
+
##
|
7
|
+
# The password that'll be used to encrypt the backup. This
|
8
|
+
# password will be required to decrypt the backup later on.
|
9
|
+
attr_accessor :password
|
10
|
+
|
11
|
+
##
|
12
|
+
# The password file to use to encrypt the backup.
|
13
|
+
attr_accessor :password_file
|
14
|
+
|
15
|
+
##
|
16
|
+
# Determines whether the 'base64' should be used or not
|
17
|
+
attr_accessor :base64
|
18
|
+
|
19
|
+
##
|
20
|
+
# Determines whether the 'salt' flag should be used
|
21
|
+
attr_accessor :salt
|
22
|
+
|
23
|
+
##
|
24
|
+
# Creates a new instance of Backup::Encryptor::OpenSSL and
|
25
|
+
# sets the password attribute to what was provided
|
26
|
+
def initialize(&block)
|
27
|
+
super
|
28
|
+
|
29
|
+
@base64 ||= false
|
30
|
+
@salt ||= true
|
31
|
+
@password_file ||= nil
|
32
|
+
|
33
|
+
instance_eval(&block) if block_given?
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# This is called as part of the procedure run by the Packager.
|
38
|
+
# It sets up the needed options to pass to the openssl command,
|
39
|
+
# then yields the command to use as part of the packaging procedure.
|
40
|
+
# Once the packaging procedure is complete, it will return
|
41
|
+
# so that any clean-up may be performed after the yield.
|
42
|
+
def encrypt_with
|
43
|
+
log!
|
44
|
+
yield "#{utility(:openssl)} #{options}", ".enc"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
##
|
50
|
+
# Uses the 256bit AES encryption cipher, which is what the
|
51
|
+
# US Government uses to encrypt information at the "Top Secret" level.
|
52
|
+
#
|
53
|
+
# The -base64 option will make the encrypted output base64 encoded,
|
54
|
+
# this makes the encrypted file readable using text editors
|
55
|
+
#
|
56
|
+
# The -salt option adds strength to the encryption
|
57
|
+
#
|
58
|
+
# Always sets a password option, if even no password is given,
|
59
|
+
# but will prefer the password_file option if both are given.
|
60
|
+
def options
|
61
|
+
opts = ["aes-256-cbc"]
|
62
|
+
opts << "-base64" if @base64
|
63
|
+
opts << "-salt" if @salt
|
64
|
+
|
65
|
+
opts <<
|
66
|
+
if @password_file.to_s.empty?
|
67
|
+
"-k #{Shellwords.escape(@password)}"
|
68
|
+
else
|
69
|
+
"-pass file:#{@password_file}"
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.join(" ")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
# Provides cascading errors with formatted messages.
|
5
|
+
# See the specs for details.
|
6
|
+
module NestedExceptions
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend(Module.new do
|
9
|
+
def wrap(wrapped_exception, msg = nil)
|
10
|
+
new(msg, wrapped_exception)
|
11
|
+
end
|
12
|
+
end)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(obj = nil, wrapped_exception = nil)
|
16
|
+
@wrapped_exception = wrapped_exception
|
17
|
+
msg = (obj.respond_to?(:to_str) ? obj.to_str : obj.to_s)
|
18
|
+
.gsub(%r{^ *}, " ").strip
|
19
|
+
msg = clean_name(self.class.name) + (msg.empty? ? "" : ": #{msg}")
|
20
|
+
|
21
|
+
if wrapped_exception
|
22
|
+
msg << "\n--- Wrapped Exception ---\n"
|
23
|
+
class_name = clean_name(wrapped_exception.class.name)
|
24
|
+
msg << class_name + ": " unless
|
25
|
+
wrapped_exception.message.start_with? class_name
|
26
|
+
msg << wrapped_exception.message
|
27
|
+
end
|
28
|
+
|
29
|
+
super(msg)
|
30
|
+
set_backtrace(wrapped_exception.backtrace) if wrapped_exception
|
31
|
+
end
|
32
|
+
|
33
|
+
def exception(obj = nil)
|
34
|
+
return self if obj.nil? || equal?(obj)
|
35
|
+
|
36
|
+
ex = self.class.new(obj, @wrapped_exception)
|
37
|
+
ex.set_backtrace(backtrace) unless ex.backtrace
|
38
|
+
ex
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def clean_name(name)
|
44
|
+
name.sub(%r{^Backup::}, "")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Error < StandardError
|
49
|
+
include NestedExceptions
|
50
|
+
end
|
51
|
+
|
52
|
+
class FatalError < Exception
|
53
|
+
include NestedExceptions
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
class Logger
|
5
|
+
class Console
|
6
|
+
class Options
|
7
|
+
##
|
8
|
+
# Disables all console output.
|
9
|
+
#
|
10
|
+
# This may also be set on the command line using +--quiet+.
|
11
|
+
#
|
12
|
+
# If +--no-quiet+ is used on the command line, console output
|
13
|
+
# will be enabled and any setting here will be ignored.
|
14
|
+
#
|
15
|
+
# @param [Boolean, nil]
|
16
|
+
# @return [Boolean, nil] Default: +false+
|
17
|
+
attr_reader :quiet
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@quiet = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def enabled?
|
24
|
+
!quiet
|
25
|
+
end
|
26
|
+
|
27
|
+
def quiet=(val)
|
28
|
+
@quiet = val unless quiet.nil?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
COLORS = {
|
33
|
+
info: "\e[32m%s\e[0m", # green
|
34
|
+
warn: "\e[33m%s\e[0m", # yellow
|
35
|
+
error: "\e[31m%s\e[0m" # red
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
def initialize(_options = nil)
|
39
|
+
$stdout.sync = $stderr.sync = true
|
40
|
+
end
|
41
|
+
|
42
|
+
def log(message)
|
43
|
+
io = message.level == :info ? $stdout : $stderr
|
44
|
+
lines = message.formatted_lines
|
45
|
+
lines.map! { |line| COLORS[message.level] % line } if io.tty?
|
46
|
+
io.puts lines
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require only the logger
|
4
|
+
require "formatador"
|
5
|
+
require "fog/core/logger"
|
6
|
+
|
7
|
+
module Backup
|
8
|
+
class Logger
|
9
|
+
module FogAdapter
|
10
|
+
class << self
|
11
|
+
# Logged as :info so these won't generate warnings.
|
12
|
+
# This is mostly to keep STDOUT clean and to provide
|
13
|
+
# supplemental messages for our own warnings.
|
14
|
+
# These will generally occur during retry attempts.
|
15
|
+
def write(message)
|
16
|
+
Logger.info message.chomp
|
17
|
+
end
|
18
|
+
|
19
|
+
def tty?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Fog::Logger[:warning] = Backup::Logger::FogAdapter
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
class Logger
|
5
|
+
class Logfile
|
6
|
+
class Error < Backup::Error; end
|
7
|
+
|
8
|
+
class Options
|
9
|
+
##
|
10
|
+
# Enable the use of Backup's log file.
|
11
|
+
#
|
12
|
+
# While not necessary, as this is +true+ by default,
|
13
|
+
# this may also be set on the command line using +--logfile+.
|
14
|
+
#
|
15
|
+
# The use of Backup's log file may be disabled using the
|
16
|
+
# command line option +--no-logfile+.
|
17
|
+
#
|
18
|
+
# If +--no--logfile+ is used on the command line, then the
|
19
|
+
# log file will be disabled and any setting here will be ignored.
|
20
|
+
#
|
21
|
+
# @param [Boolean, nil]
|
22
|
+
# @return [Boolean, nil] Default: +true+
|
23
|
+
attr_reader :enabled
|
24
|
+
|
25
|
+
##
|
26
|
+
# Path to directory where Backup's logfile will be written.
|
27
|
+
#
|
28
|
+
# This may be given as an absolute path, or a path relative
|
29
|
+
# to Backup's +--root-path+ (which defaults to +~/Backup+).
|
30
|
+
#
|
31
|
+
# This may also be set on the command line using +--log-path+.
|
32
|
+
# If set on the command line, any setting here will be ignored.
|
33
|
+
#
|
34
|
+
# @param [String]
|
35
|
+
# @return [String] Default: 'log'
|
36
|
+
attr_reader :log_path
|
37
|
+
|
38
|
+
##
|
39
|
+
# Backup's logfile in which backup logs can be written
|
40
|
+
#
|
41
|
+
# As there is already a log_path, this can simply be just a file name
|
42
|
+
# that will be created (If not exists) on log_path directory
|
43
|
+
#
|
44
|
+
# This may also be set on the command line using +--log-file+.
|
45
|
+
# If set on the command line, any setting here will be ignored.
|
46
|
+
#
|
47
|
+
# @param [String]
|
48
|
+
# @return [String] Default: 'backup.log'
|
49
|
+
attr_reader :log_file
|
50
|
+
|
51
|
+
##
|
52
|
+
# Size in bytes to truncate logfile to before backup jobs are run.
|
53
|
+
#
|
54
|
+
# This is done once before all +triggers+, so the maximum logfile size
|
55
|
+
# would be this value plus whatever the jobs produce.
|
56
|
+
#
|
57
|
+
# @param [Integer]
|
58
|
+
# @return [Integer] Default: +500_000+
|
59
|
+
attr_accessor :max_bytes
|
60
|
+
|
61
|
+
def initialize
|
62
|
+
@enabled = true
|
63
|
+
@log_path = ""
|
64
|
+
@max_bytes = 500_000
|
65
|
+
end
|
66
|
+
|
67
|
+
def enabled?
|
68
|
+
!!enabled
|
69
|
+
end
|
70
|
+
|
71
|
+
def enabled=(val)
|
72
|
+
@enabled = val unless enabled.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def log_path=(val)
|
76
|
+
@log_path = val.to_s.strip if log_path.empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def initialize(options)
|
81
|
+
@options = options
|
82
|
+
@logfile = setup_logfile
|
83
|
+
truncate!
|
84
|
+
end
|
85
|
+
|
86
|
+
def log(message)
|
87
|
+
File.open(@logfile, "a") { |f| f.puts message.formatted_lines }
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
##
|
93
|
+
# Returns the full path to the log file, based on the configured
|
94
|
+
# @options.log_path, and ensures the path to the log file exists.
|
95
|
+
def setup_logfile
|
96
|
+
# strip any trailing '/' in case the user supplied this as part of
|
97
|
+
# an absolute path, so we can match it against File.expand_path()
|
98
|
+
path = @options.log_path.chomp("/")
|
99
|
+
if path.empty?
|
100
|
+
path = File.join(Backup::Config.root_path, "log")
|
101
|
+
elsif path != File.expand_path(path)
|
102
|
+
path = File.join(Backup::Config.root_path, path)
|
103
|
+
end
|
104
|
+
FileUtils.mkdir_p(path)
|
105
|
+
log_file = @options.log_file || "backup.log"
|
106
|
+
path = File.join(path, log_file)
|
107
|
+
if File.exist?(path) && !File.writable?(path)
|
108
|
+
raise Error, "Log File at '#{path}' is not writable"
|
109
|
+
end
|
110
|
+
|
111
|
+
path
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Truncates the logfile to @options.max_bytes
|
116
|
+
def truncate!
|
117
|
+
return unless File.exist?(@logfile)
|
118
|
+
|
119
|
+
if File.stat(@logfile).size > @options.max_bytes
|
120
|
+
FileUtils.cp(@logfile, @logfile + "~")
|
121
|
+
File.open(@logfile + "~", "r") do |io_in|
|
122
|
+
File.open(@logfile, "w") do |io_out|
|
123
|
+
io_in.seek(-@options.max_bytes, IO::SEEK_END) && io_in.gets
|
124
|
+
while (line = io_in.gets)
|
125
|
+
io_out.puts line
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
FileUtils.rm_f(@logfile + "~")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
class Logger
|
5
|
+
class Syslog
|
6
|
+
class Options
|
7
|
+
##
|
8
|
+
# Enables logging to the system's Syslog compatible logger.
|
9
|
+
#
|
10
|
+
# This may also be enabled using +--syslog+ on the command line.
|
11
|
+
#
|
12
|
+
# If +--no-syslog+ is used on the command line, this will be
|
13
|
+
# disabled and any settings here will be ignored.
|
14
|
+
#
|
15
|
+
# @param [Boolean, nil]
|
16
|
+
# @return [Boolean, nil] Default: +false+
|
17
|
+
attr_reader :enabled
|
18
|
+
|
19
|
+
##
|
20
|
+
# Specify the identification string to be used with Syslog.
|
21
|
+
#
|
22
|
+
# @param [String]
|
23
|
+
# @return [String] Default: 'backup'
|
24
|
+
attr_accessor :ident
|
25
|
+
|
26
|
+
##
|
27
|
+
# Specify the options to be used with Syslog.
|
28
|
+
#
|
29
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more
|
30
|
+
# info. http://rdoc.info/stdlib/syslog/Syslog.open
|
31
|
+
#
|
32
|
+
# Note that setting this to +nil+ will cause this to default
|
33
|
+
# to a setting of +Syslog::LOG_PID | Syslog::LOG_CONS+
|
34
|
+
#
|
35
|
+
# @param [Integer]
|
36
|
+
# @return [Integer] Default: +Syslog::LOG_PID+
|
37
|
+
attr_accessor :options
|
38
|
+
|
39
|
+
##
|
40
|
+
# Specify the facility to be used with Syslog.
|
41
|
+
#
|
42
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more
|
43
|
+
# info. http://rdoc.info/stdlib/syslog/Syslog.open
|
44
|
+
#
|
45
|
+
# Note that setting this to +nil+ will cause this to default
|
46
|
+
# to a setting of +Syslog::LOG_USER+
|
47
|
+
#
|
48
|
+
# @param [Integer]
|
49
|
+
# @return [Integer] Default: +Syslog::LOG_LOCAL0+
|
50
|
+
attr_accessor :facility
|
51
|
+
|
52
|
+
##
|
53
|
+
# Specify the priority level to be used for +:info+ messages.
|
54
|
+
#
|
55
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more
|
56
|
+
# info. http://rdoc.info/stdlib/syslog/Syslog.log
|
57
|
+
#
|
58
|
+
# @param [Integer]
|
59
|
+
# @return [Integer] Default: +Syslog::LOG_INFO+
|
60
|
+
attr_accessor :info
|
61
|
+
|
62
|
+
##
|
63
|
+
# Specify the priority level to be used for +:warn+ messages.
|
64
|
+
#
|
65
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more
|
66
|
+
# info. http://rdoc.info/stdlib/syslog/Syslog.log
|
67
|
+
#
|
68
|
+
# @param [Integer]
|
69
|
+
# @return [Integer] Default: +Syslog::LOG_WARNING+
|
70
|
+
attr_accessor :warn
|
71
|
+
|
72
|
+
##
|
73
|
+
# Specify the priority level to be used for +:error+ messages.
|
74
|
+
#
|
75
|
+
# See the Ruby Standard Library documentation for +Syslog+ for more
|
76
|
+
# info. http://rdoc.info/stdlib/syslog/Syslog.log
|
77
|
+
#
|
78
|
+
# @param [Integer]
|
79
|
+
# @return [Integer] Default: +Syslog::LOG_ERR+
|
80
|
+
attr_accessor :error
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@enabled = false
|
84
|
+
@ident = "backup"
|
85
|
+
@options = ::Syslog::LOG_PID
|
86
|
+
@facility = ::Syslog::LOG_LOCAL0
|
87
|
+
@info = ::Syslog::LOG_INFO
|
88
|
+
@warn = ::Syslog::LOG_WARNING
|
89
|
+
@error = ::Syslog::LOG_ERR
|
90
|
+
end
|
91
|
+
|
92
|
+
def enabled?
|
93
|
+
!!enabled
|
94
|
+
end
|
95
|
+
|
96
|
+
def enabled=(val)
|
97
|
+
@enabled = val unless enabled.nil?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def initialize(options)
|
102
|
+
@options = options
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Message lines are sent without formatting (timestamp, level),
|
107
|
+
# since Syslog will provide it's own timestamp and priority.
|
108
|
+
def log(message)
|
109
|
+
level = @options.send(message.level)
|
110
|
+
::Syslog.open(@options.ident, @options.options, @options.facility) do |s|
|
111
|
+
message.lines.each { |line| s.log(level, "%s", line) }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "backup/logger/console"
|
4
|
+
require "backup/logger/logfile"
|
5
|
+
require "backup/logger/syslog"
|
6
|
+
require "backup/logger/fog_adapter"
|
7
|
+
|
8
|
+
module Backup
|
9
|
+
class Logger
|
10
|
+
class Config
|
11
|
+
Logger = Struct.new(:class, :options) do
|
12
|
+
def enabled?
|
13
|
+
options.enabled?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
DSL = Struct.new(:ignores, :console, :logfile, :syslog) do
|
18
|
+
def ignore_warning(str_or_regexp)
|
19
|
+
ignores << str_or_regexp
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :ignores, :loggers, :dsl
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@ignores = []
|
27
|
+
@loggers = [
|
28
|
+
Logger.new(Console, Console::Options.new),
|
29
|
+
Logger.new(Logfile, Logfile::Options.new),
|
30
|
+
Logger.new(Syslog, Syslog::Options.new)
|
31
|
+
]
|
32
|
+
@dsl = DSL.new(ignores, *loggers.map(&:options))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# All messages sent to the Logger are stored in Logger.messages
|
38
|
+
# and sent to all enabled logger's #log method as Message objects.
|
39
|
+
Message = Struct.new(:time, :level, :lines) do
|
40
|
+
##
|
41
|
+
# Returns an Array of the message lines in the following format:
|
42
|
+
#
|
43
|
+
# [YYYY/MM/DD HH:MM:SS][level] message line text
|
44
|
+
def formatted_lines
|
45
|
+
timestamp = time.strftime("%Y/%m/%d %H:%M:%S")
|
46
|
+
lines.map { |line| "[#{timestamp}][#{level}] #{line}" }
|
47
|
+
end
|
48
|
+
|
49
|
+
def matches?(ignores)
|
50
|
+
text = lines.join("\n")
|
51
|
+
ignores.any? do |obj|
|
52
|
+
obj.is_a?(Regexp) ? text.match(obj) : text.include?(obj)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
extend Forwardable
|
59
|
+
def_delegators :logger,
|
60
|
+
:start!, :abort!, :info, :warn, :error,
|
61
|
+
:messages, :has_warnings?, :has_errors?
|
62
|
+
|
63
|
+
##
|
64
|
+
# Allows the Logger to be configured.
|
65
|
+
#
|
66
|
+
# # shown with their default values
|
67
|
+
# Backup::Logger.configure do
|
68
|
+
# # Console options:
|
69
|
+
# console.quiet = false
|
70
|
+
#
|
71
|
+
# # Logfile options:
|
72
|
+
# logfile.enabled = true
|
73
|
+
# logfile.log_path = 'log'
|
74
|
+
# logfile.max_bytes = 500_000
|
75
|
+
#
|
76
|
+
# # Syslog options:
|
77
|
+
# syslog.enabled = false
|
78
|
+
# syslog.ident = 'backup'
|
79
|
+
# syslog.options = Syslog::LOG_PID
|
80
|
+
# syslog.facility = Syslog::LOG_LOCAL0
|
81
|
+
# syslog.info = Syslog::LOG_INFO
|
82
|
+
# syslog.warn = Syslog::LOG_WARNING
|
83
|
+
# syslog.error = Syslog::LOG_ERR
|
84
|
+
#
|
85
|
+
# # Ignore Warnings:
|
86
|
+
# # Converts :warn level messages to level :info
|
87
|
+
# ignore_warning 'that contains this string'
|
88
|
+
# ignore_warning /that matches this regexp/
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# See each Logger's Option class for details.
|
92
|
+
# @see Console::Options
|
93
|
+
# @see Logfile::Options
|
94
|
+
# @see Syslog::Options
|
95
|
+
def configure(&block)
|
96
|
+
config.dsl.instance_eval(&block)
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Called after each backup model/trigger has been performed.
|
101
|
+
def clear!
|
102
|
+
@logger = nil
|
103
|
+
logger.start!
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def config
|
109
|
+
@config ||= Config.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def logger
|
113
|
+
@logger ||= new(config)
|
114
|
+
end
|
115
|
+
|
116
|
+
def reset!
|
117
|
+
@config = @logger = nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
MUTEX = Mutex.new
|
122
|
+
|
123
|
+
##
|
124
|
+
# Returns an Array of Message objects for all logged messages received.
|
125
|
+
# These are used to attach log files to Mail notifications.
|
126
|
+
attr_reader :messages
|
127
|
+
|
128
|
+
def initialize(config)
|
129
|
+
@config = config
|
130
|
+
@messages = []
|
131
|
+
@loggers = []
|
132
|
+
@has_warnings = @has_errors = false
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Sends a message to the Logger using the specified log level.
|
137
|
+
# +obj+ may be any Object that responds to #to_s (i.e. an Exception)
|
138
|
+
[:info, :warn, :error].each do |level|
|
139
|
+
define_method level do |obj|
|
140
|
+
MUTEX.synchronize { log(obj, level) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Returns true if any +:warn+ level messages have been received.
|
146
|
+
def has_warnings?
|
147
|
+
@has_warnings
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Returns true if any +:error+ level messages have been received.
|
152
|
+
def has_errors?
|
153
|
+
@has_errors
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# The Logger is available as soon as Backup is loaded, and stores all
|
158
|
+
# messages it receives. Since the Logger may be configured via the
|
159
|
+
# command line and/or the user's +config.rb+, no messages are sent
|
160
|
+
# until configuration can be completed. (see CLI#perform)
|
161
|
+
#
|
162
|
+
# Once configuration is completed, this method is called to activate
|
163
|
+
# all enabled loggers and send them any messages that have been received
|
164
|
+
# up to this point. From this point onward, these loggers will be sent
|
165
|
+
# all messages as soon as they're received.
|
166
|
+
def start!
|
167
|
+
@config.loggers.each do |logger|
|
168
|
+
@loggers << logger.class.new(logger.options) if logger.enabled?
|
169
|
+
end
|
170
|
+
messages.each do |message|
|
171
|
+
@loggers.each { |logger| logger.log(message) }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# If errors are encountered by Backup::CLI while preparing to perform
|
177
|
+
# the backup jobs, this method is called to dump all messages to the
|
178
|
+
# console before Backup exits.
|
179
|
+
def abort!
|
180
|
+
console = Console.new
|
181
|
+
console.log(messages.shift) until messages.empty?
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def log(obj, level)
|
187
|
+
message = Message.new(Time.now.utc, level, obj.to_s.split("\n"))
|
188
|
+
|
189
|
+
if message.level == :warn && message.matches?(@config.ignores)
|
190
|
+
message.level = :info
|
191
|
+
end
|
192
|
+
@has_warnings ||= message.level == :warn
|
193
|
+
@has_errors ||= message.level == :error
|
194
|
+
|
195
|
+
messages << message
|
196
|
+
@loggers.each { |logger| logger.log(message) }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|