backup 3.1.3 → 3.2.0
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 +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
|