backup 3.1.3 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/lib/backup.rb +1 -0
- data/lib/backup/archive.rb +106 -79
- data/lib/backup/cli.rb +23 -4
- data/lib/backup/logger.rb +103 -59
- data/lib/backup/logger/console.rb +1 -1
- data/lib/backup/logger/logfile.rb +1 -1
- data/lib/backup/logger/syslog.rb +1 -1
- data/lib/backup/model.rb +3 -3
- data/lib/backup/notifier/mail.rb +84 -50
- data/lib/backup/storage/base.rb +10 -14
- data/lib/backup/storage/rsync.rb +232 -92
- data/lib/backup/syncer/base.rb +23 -8
- data/lib/backup/syncer/cloud/base.rb +4 -3
- data/lib/backup/syncer/cloud/cloud_files.rb +1 -1
- data/lib/backup/syncer/cloud/s3.rb +1 -1
- data/lib/backup/syncer/rsync/base.rb +13 -25
- data/lib/backup/syncer/rsync/local.rb +16 -30
- data/lib/backup/syncer/rsync/pull.rb +47 -11
- data/lib/backup/syncer/rsync/push.rb +167 -56
- data/lib/backup/utilities.rb +115 -104
- data/lib/backup/version.rb +1 -1
- data/templates/cli/archive +15 -7
- data/templates/cli/notifier/mail +1 -1
- data/templates/cli/storage/rsync +15 -7
- data/templates/cli/syncer/rsync_local +1 -1
- data/templates/cli/syncer/rsync_pull +10 -5
- data/templates/cli/syncer/rsync_push +10 -5
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
NzQwNGViMWM5ZjliYzFhNzVmNGI5NzhhZmFiNDMwYWM5ZTM0NmQ3MmE2NmJm
|
10
|
-
YTM5MTUwM2QyN2NlMTMxOWM5MWJhMzAwN2M4ZTExYjFkNTdlZjc4MDc2ODVl
|
11
|
-
NTI1ZGI4OTYzYTVkMTk3ZDFlYjhiNWVkMTZiMzJmZDU5ZTg5M2E=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ODUwM2U2MGFkYjQ5MTkwOTQ1Yzg0MWIzNWEwMzdmMDQwYzdhZGNkMmI3NWM5
|
14
|
-
YzY0MWJiMDA1ZmViZTQxMDVmZTU1ZDAwYmRhZjFhY2ZjZGEzZGFkYWU0ZmQ1
|
15
|
-
MDEwMWZkOGM3ZGJlY2Q3OWMwOTZhOTNmYzY1NTNjYWViZjU1MTA=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 97b183cb0d0c210153909788775e95a6a686a4cc
|
4
|
+
data.tar.gz: 4eb3b105dcdea08f667c8e94625ec9bdf174dc49
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5c1cd997c5e0823022b323b639c7977b7d3f573468c860f0fab496ed619b3b44baae8c56ab8dbe13e9ab0643844dac72be9ac6e79c19ef5859faf2fd05760c5
|
7
|
+
data.tar.gz: 07af01b103adf78549d1296145403dded6e8e6d4cea9dbd5b750839ca26f585c32e0c42a0359f46f9ac0bcc57a412e6b6fdb3e8ab5b8b0936f8496109af7ce09
|
data/lib/backup.rb
CHANGED
data/lib/backup/archive.rb
CHANGED
@@ -3,121 +3,148 @@
|
|
3
3
|
module Backup
|
4
4
|
class Archive
|
5
5
|
include Backup::Utilities::Helpers
|
6
|
+
attr_reader :name, :options
|
6
7
|
|
7
8
|
##
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
9
|
+
# Adds a new Archive to a Backup Model.
|
10
|
+
#
|
11
|
+
# Backup::Model.new(:my_backup, 'My Backup') do
|
12
|
+
# archive :my_archive do |archive|
|
13
|
+
# archive.add 'path/to/archive'
|
14
|
+
# archive.add '/another/path/to/archive'
|
15
|
+
# archive.exclude 'path/to/exclude'
|
16
|
+
# archive.exclude '/another/path/to/exclude'
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# All paths added using `add` or `exclude` will be expanded to their
|
21
|
+
# full paths from the root of the filesystem. Files will be added to
|
22
|
+
# the tar archive using these full paths, and their leading `/` will
|
23
|
+
# be preserved (using tar's `-P` option).
|
24
|
+
#
|
25
|
+
# /path/to/pwd/path/to/archive/...
|
26
|
+
# /another/path/to/archive/...
|
27
|
+
#
|
28
|
+
# When a `root` path is given, paths to add/exclude are taken as
|
29
|
+
# relative to the `root` path, unless given as absolute paths.
|
30
|
+
#
|
31
|
+
# Backup::Model.new(:my_backup, 'My Backup') do
|
32
|
+
# archive :my_archive do |archive|
|
33
|
+
# archive.root '~/my_data'
|
34
|
+
# archive.add 'path/to/archive'
|
35
|
+
# archive.add '/another/path/to/archive'
|
36
|
+
# archive.exclude 'path/to/exclude'
|
37
|
+
# archive.exclude '/another/path/to/exclude'
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# This directs `tar` to change directories to the `root` path to create
|
42
|
+
# the archive. Unless paths were given as absolute, the paths within the
|
43
|
+
# archive will be relative to the `root` path.
|
44
|
+
#
|
45
|
+
# path/to/archive/...
|
46
|
+
# /another/path/to/archive/...
|
47
|
+
#
|
48
|
+
# For absolute paths added to this archive, the leading `/` will be
|
49
|
+
# preserved. Take note that when archives are extracted, leading `/` are
|
50
|
+
# stripped by default, so care must be taken when extracting archives with
|
51
|
+
# mixed relative/absolute paths.
|
25
52
|
def initialize(model, name, &block)
|
26
|
-
@model
|
27
|
-
@name
|
28
|
-
@
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
##
|
36
|
-
# Adds new paths to the @paths instance variable array
|
37
|
-
def add(path)
|
38
|
-
@paths << File.expand_path(path)
|
39
|
-
end
|
40
|
-
|
41
|
-
##
|
42
|
-
# Adds new paths to the @excludes instance variable array
|
43
|
-
def exclude(path)
|
44
|
-
@excludes << File.expand_path(path)
|
53
|
+
@model = model
|
54
|
+
@name = name.to_s
|
55
|
+
@options = {
|
56
|
+
:root => false,
|
57
|
+
:paths => [],
|
58
|
+
:excludes => [],
|
59
|
+
:tar_options => ''
|
60
|
+
}
|
61
|
+
DSL.new(@options).instance_eval(&block)
|
45
62
|
end
|
46
63
|
|
47
|
-
##
|
48
|
-
# Adds the given String of +options+ to the `tar` command.
|
49
|
-
# e.g. '-h --xattrs'
|
50
|
-
def tar_options(options)
|
51
|
-
@tar_args = options
|
52
|
-
end
|
53
|
-
|
54
|
-
##
|
55
|
-
# Archives all the provided paths in to a single .tar file
|
56
|
-
# and places that .tar file in the folder which later will be packaged
|
57
|
-
# If the model is configured with a Compressor, the tar command output
|
58
|
-
# will be piped through the Compressor command and the file extension
|
59
|
-
# will be adjusted to indicate the type of compression used.
|
60
64
|
def perform!
|
61
|
-
Logger.info "#{
|
62
|
-
paths.map {|path| " #{path}" }.join("\n")
|
65
|
+
Logger.info "Creating Archive '#{ name }'..."
|
63
66
|
|
64
|
-
|
65
|
-
FileUtils.mkdir_p(
|
67
|
+
path = File.join(Config.tmp_path, @model.trigger, 'archives')
|
68
|
+
FileUtils.mkdir_p(path)
|
66
69
|
|
67
|
-
archive_ext = 'tar'
|
68
70
|
pipeline = Pipeline.new
|
69
|
-
|
70
71
|
pipeline.add(
|
71
|
-
"#{ utility(:tar) } #{
|
72
|
+
"#{ utility(:tar) } #{ tar_options } -cPf -#{ tar_root } " +
|
72
73
|
"#{ paths_to_exclude } #{ paths_to_package }",
|
73
74
|
tar_success_codes
|
74
75
|
)
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
77
|
+
extension = 'tar'
|
78
|
+
@model.compressor.compress_with do |command, ext|
|
79
|
+
pipeline << command
|
80
|
+
extension << ext
|
81
|
+
end if @model.compressor
|
82
82
|
|
83
83
|
pipeline << "#{ utility(:cat) } > " +
|
84
|
-
"'#{ File.join(
|
84
|
+
"'#{ File.join(path, "#{ name }.#{ extension }") }'"
|
85
85
|
pipeline.run
|
86
|
+
|
86
87
|
if pipeline.success?
|
87
|
-
Logger.info "#{
|
88
|
+
Logger.info "Archive '#{ name }' Complete!"
|
88
89
|
else
|
89
90
|
raise Errors::Archive::PipelineError,
|
90
|
-
"Failed to Create
|
91
|
+
"Failed to Create Archive '#{ name }'\n" +
|
91
92
|
pipeline.error_messages
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
95
96
|
private
|
96
97
|
|
97
|
-
|
98
|
-
|
98
|
+
def tar_root
|
99
|
+
options[:root] ? " -C '#{ File.expand_path(options[:root]) }'" : ''
|
100
|
+
end
|
101
|
+
|
99
102
|
def paths_to_package
|
100
|
-
paths.map {|path|
|
103
|
+
options[:paths].map {|path|
|
104
|
+
"'#{ prepare_path(path) }'"
|
105
|
+
}.join(' ')
|
101
106
|
end
|
102
107
|
|
103
|
-
##
|
104
|
-
# Returns a "tar-ready" string of all the specified excludes combined
|
105
108
|
def paths_to_exclude
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
+
options[:excludes].map {|path|
|
110
|
+
"--exclude='#{ prepare_path(path) }'"
|
111
|
+
}.join(' ')
|
109
112
|
end
|
110
113
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
114
|
+
def prepare_path(path)
|
115
|
+
options[:root] ? path : File.expand_path(path)
|
116
|
+
end
|
117
|
+
|
118
|
+
def tar_options
|
119
|
+
args = options[:tar_options]
|
120
|
+
gnu_tar? ? "--ignore-failed-read #{ args }".strip : args
|
115
121
|
end
|
116
122
|
|
117
|
-
##
|
118
|
-
# Returns successful GNU or BSD tar exit codes.
|
119
123
|
def tar_success_codes
|
120
124
|
gnu_tar? ? [0, 1] : [0]
|
121
125
|
end
|
126
|
+
|
127
|
+
class DSL
|
128
|
+
def initialize(options)
|
129
|
+
@options = options
|
130
|
+
end
|
131
|
+
|
132
|
+
def root(path)
|
133
|
+
@options[:root] = path
|
134
|
+
end
|
135
|
+
|
136
|
+
def add(path)
|
137
|
+
@options[:paths] << path
|
138
|
+
end
|
139
|
+
|
140
|
+
def exclude(path)
|
141
|
+
@options[:excludes] << path
|
142
|
+
end
|
143
|
+
|
144
|
+
def tar_options(opts)
|
145
|
+
@options[:tar_options] = opts
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
122
149
|
end
|
123
150
|
end
|
data/lib/backup/cli.rb
CHANGED
@@ -9,12 +9,27 @@ module Backup
|
|
9
9
|
##
|
10
10
|
# [Perform]
|
11
11
|
# Performs the backup process. The only required option is the --trigger [-t].
|
12
|
-
# If the other options (--config-file, --data-path, --cache-path, --tmp-path)
|
13
|
-
# they will fallback to the (good) defaults
|
12
|
+
# If the other options (--config-file, --data-path, --cache-path, --tmp-path)
|
13
|
+
# aren't specified they will fallback to the (good) defaults.
|
14
14
|
#
|
15
15
|
# If --root-path is given, it will be used as the base path for our defaults,
|
16
16
|
# as well as the base path for any option specified as a relative path.
|
17
17
|
# Any option given as an absolute path will be used "as-is".
|
18
|
+
#
|
19
|
+
# If the --check option is given, the config.rb and all model files will be
|
20
|
+
# loaded, but no triggers will be run. If the check fails, errors will be
|
21
|
+
# reported to the console. If the check passes, a success message will be
|
22
|
+
# reported to the console unless --quiet is set. Use --no-quiet to ensure
|
23
|
+
# these messages are output. The command will exit with status 0 if
|
24
|
+
# successful, or status 1 if there were problems.
|
25
|
+
#
|
26
|
+
# This command will exit with one of the following status codes:
|
27
|
+
#
|
28
|
+
# 0: All triggers were successful and no warnings were issued.
|
29
|
+
# 1: All triggers were successful, but some had warnings.
|
30
|
+
# 2: All triggers were processed, but some failed.
|
31
|
+
# 3: A fatal error caused Backup to exit.
|
32
|
+
# Some triggers may not have been processed.
|
18
33
|
desc 'perform', "Performs the backup for the specified trigger(s)."
|
19
34
|
long_desc "Performs the backup for the specified trigger(s).\n\n" +
|
20
35
|
"You may perform multiple backups by providing multiple triggers, separated by commas.\n\n" +
|
@@ -110,13 +125,13 @@ module Backup
|
|
110
125
|
# Finalize Logger configuration and begin real-time logging.
|
111
126
|
Logger.start!
|
112
127
|
|
113
|
-
rescue => err
|
128
|
+
rescue Exception => err
|
114
129
|
Logger.error Errors::CLIError.wrap(err)
|
115
130
|
Logger.error 'Configuration Check Failed.' if options[:check]
|
116
131
|
# Logger configuration will be ignored
|
117
132
|
# and messages will be output to the console only.
|
118
133
|
Logger.abort!
|
119
|
-
exit(1)
|
134
|
+
exit(options[:check] ? 1 : 3)
|
120
135
|
end
|
121
136
|
|
122
137
|
if options[:check]
|
@@ -128,10 +143,14 @@ module Backup
|
|
128
143
|
#
|
129
144
|
# Model#perform! handles all exceptions from this point,
|
130
145
|
# as each model may fail and return here to allow others to run.
|
146
|
+
warnings = errors = false
|
131
147
|
models.each do |model|
|
132
148
|
model.perform!
|
149
|
+
warnings ||= Logger.has_warnings?
|
150
|
+
errors ||= Logger.has_errors?
|
133
151
|
Logger.clear!
|
134
152
|
end
|
153
|
+
exit(errors ? 2 : 1) if errors || warnings
|
135
154
|
end
|
136
155
|
end
|
137
156
|
|
data/lib/backup/logger.rb
CHANGED
@@ -5,7 +5,7 @@ require 'backup/logger/logfile'
|
|
5
5
|
require 'backup/logger/syslog'
|
6
6
|
|
7
7
|
module Backup
|
8
|
-
|
8
|
+
class Logger
|
9
9
|
|
10
10
|
class Config
|
11
11
|
class Logger < Struct.new(:class, :options)
|
@@ -14,17 +14,22 @@ module Backup
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
DSL
|
17
|
+
class DSL < Struct.new(:ignores, :console, :logfile, :syslog)
|
18
|
+
def ignore_warning(str_or_regexp)
|
19
|
+
ignores << str_or_regexp
|
20
|
+
end
|
21
|
+
end
|
18
22
|
|
19
|
-
attr_reader :loggers, :dsl
|
23
|
+
attr_reader :ignores, :loggers, :dsl
|
20
24
|
|
21
25
|
def initialize
|
26
|
+
@ignores = []
|
22
27
|
@loggers = [
|
23
28
|
Logger.new(Console, Console::Options.new),
|
24
29
|
Logger.new(Logfile, Logfile::Options.new),
|
25
30
|
Logger.new(Syslog, Syslog::Options.new)
|
26
31
|
]
|
27
|
-
@dsl = DSL.new(
|
32
|
+
@dsl = DSL.new(ignores, *loggers.map(&:options))
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
@@ -40,13 +45,20 @@ module Backup
|
|
40
45
|
timestamp = time.strftime("%Y/%m/%d %H:%M:%S")
|
41
46
|
lines.map {|line| "[#{ timestamp }][#{ level }] #{ line }" }
|
42
47
|
end
|
48
|
+
|
49
|
+
def matches?(ignores)
|
50
|
+
text = lines.join("\n")
|
51
|
+
ignores.any? {|obj|
|
52
|
+
obj.is_a?(Regexp) ? text.match(obj) : text.include?(obj)
|
53
|
+
}
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
57
|
class << self
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
58
|
+
extend Forwardable
|
59
|
+
def_delegators :logger,
|
60
|
+
:start!, :abort!, :info, :warn, :error,
|
61
|
+
:messages, :has_warnings?, :has_errors?
|
50
62
|
|
51
63
|
##
|
52
64
|
# Allows the Logger to be configured.
|
@@ -69,6 +81,11 @@ module Backup
|
|
69
81
|
# syslog.info = Syslog::LOG_INFO
|
70
82
|
# syslog.warn = Syslog::LOG_WARNING
|
71
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/
|
72
89
|
# end
|
73
90
|
#
|
74
91
|
# See each Logger's Option class for details.
|
@@ -76,75 +93,102 @@ module Backup
|
|
76
93
|
# @see Logfile::Options
|
77
94
|
# @see Syslog::Options
|
78
95
|
def configure(&block)
|
79
|
-
|
96
|
+
config.dsl.instance_eval(&block)
|
80
97
|
end
|
81
98
|
|
82
99
|
##
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
100
|
+
# Called after each backup model/trigger has been performed.
|
101
|
+
def clear!
|
102
|
+
@logger = nil
|
103
|
+
logger.start!
|
87
104
|
end
|
88
105
|
|
89
|
-
|
90
|
-
# Returns true if any +:warn+ level messages have been received.
|
91
|
-
def has_warnings?
|
92
|
-
@has_warnings
|
93
|
-
end
|
106
|
+
private
|
94
107
|
|
95
|
-
|
96
|
-
|
97
|
-
# messages it receives. Since the Logger may be configured via the
|
98
|
-
# command line and/or the user's +config.rb+, no messages are sent
|
99
|
-
# until configuration can be completed. (see CLI#perform)
|
100
|
-
#
|
101
|
-
# Once configuration is completed, this method is called to activate
|
102
|
-
# all enabled loggers and send them any messages that have been received
|
103
|
-
# up to this point. From this point onward, these loggers will be sent
|
104
|
-
# all messages as soon as they're received.
|
105
|
-
def start!
|
106
|
-
@config.loggers.each do |logger|
|
107
|
-
@loggers << logger.class.new(logger.options) if logger.enabled?
|
108
|
-
end
|
109
|
-
@messages.each do |message|
|
110
|
-
@loggers.each {|logger| logger.log(message) }
|
111
|
-
end
|
108
|
+
def config
|
109
|
+
@config ||= Config.new
|
112
110
|
end
|
113
111
|
|
114
|
-
|
115
|
-
|
116
|
-
def clear!
|
117
|
-
messages.clear
|
118
|
-
@has_warnings = false
|
112
|
+
def logger
|
113
|
+
@logger ||= new(config)
|
119
114
|
end
|
120
115
|
|
121
|
-
|
122
|
-
|
123
|
-
# the backup jobs, this method is called to dump all messages to the
|
124
|
-
# console before Backup exits.
|
125
|
-
def abort!
|
126
|
-
console = Console.new
|
127
|
-
console.log(@messages.shift) until @messages.empty?
|
116
|
+
def reset!
|
117
|
+
@config = @logger = nil
|
128
118
|
end
|
119
|
+
end
|
129
120
|
|
130
|
-
|
121
|
+
##
|
122
|
+
# Returns an Array of Message objects for all logged messages received.
|
123
|
+
# These are used to attach log files to Mail notifications.
|
124
|
+
attr_reader :messages
|
125
|
+
|
126
|
+
def initialize(config)
|
127
|
+
@config = config
|
128
|
+
@messages = []
|
129
|
+
@loggers = []
|
130
|
+
@has_warnings = @has_errors = false
|
131
|
+
end
|
131
132
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
133
|
+
##
|
134
|
+
# Sends a message to the Logger using the specified log level.
|
135
|
+
# +obj+ may be any Object that responds to #to_s (i.e. an Exception)
|
136
|
+
[:info, :warn, :error].each do |level|
|
137
|
+
define_method level, lambda {|obj| log(obj, level) }
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Returns true if any +:warn+ level messages have been received.
|
142
|
+
def has_warnings?
|
143
|
+
@has_warnings
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Returns true if any +:error+ level messages have been received.
|
148
|
+
def has_errors?
|
149
|
+
@has_errors
|
150
|
+
end
|
138
151
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
152
|
+
##
|
153
|
+
# The Logger is available as soon as Backup is loaded, and stores all
|
154
|
+
# messages it receives. Since the Logger may be configured via the
|
155
|
+
# command line and/or the user's +config.rb+, no messages are sent
|
156
|
+
# until configuration can be completed. (see CLI#perform)
|
157
|
+
#
|
158
|
+
# Once configuration is completed, this method is called to activate
|
159
|
+
# all enabled loggers and send them any messages that have been received
|
160
|
+
# up to this point. From this point onward, these loggers will be sent
|
161
|
+
# all messages as soon as they're received.
|
162
|
+
def start!
|
163
|
+
@config.loggers.each do |logger|
|
164
|
+
@loggers << logger.class.new(logger.options) if logger.enabled?
|
165
|
+
end
|
166
|
+
messages.each do |message|
|
143
167
|
@loggers.each {|logger| logger.log(message) }
|
144
168
|
end
|
169
|
+
end
|
145
170
|
|
171
|
+
##
|
172
|
+
# If errors are encountered by Backup::CLI while preparing to perform
|
173
|
+
# the backup jobs, this method is called to dump all messages to the
|
174
|
+
# console before Backup exits.
|
175
|
+
def abort!
|
176
|
+
console = Console.new
|
177
|
+
console.log(messages.shift) until messages.empty?
|
146
178
|
end
|
147
179
|
|
148
|
-
|
180
|
+
private
|
181
|
+
|
182
|
+
def log(obj, level)
|
183
|
+
message = Message.new(Time.now, level, obj.to_s.split("\n"))
|
184
|
+
|
185
|
+
message.level = :info if message.level == :warn &&
|
186
|
+
message.matches?(@config.ignores)
|
187
|
+
@has_warnings ||= message.level == :warn
|
188
|
+
@has_errors ||= message.level == :error
|
189
|
+
|
190
|
+
messages << message
|
191
|
+
@loggers.each {|logger| logger.log(message) }
|
192
|
+
end
|
149
193
|
end
|
150
194
|
end
|