BackupMan 0.1.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.
data/README.rdoc ADDED
@@ -0,0 +1,138 @@
1
+ = BackupMan
2
+
3
+ * Homepage: http://github.com/oowoo/backupman
4
+
5
+ == DESCRIPTION:
6
+
7
+ A tool for system administrators to easily configure pull-over-SSH backups.
8
+ Install it on your backup server and pull your backups over SSH from there.
9
+
10
+ == BACKGROUND
11
+
12
+ When you have servers on the Internet or in a DMZ they are kind of "untrusted"
13
+ because they are vulnerable to become hacked/cracked. It is therefore a best
14
+ practice to <b>pull</b> backups from there instead of letting these servers
15
+ push their data to the backup servers. Advantage: When doing SSH-pull you do
16
+ not have to store any kind of authentication information on your "untrusted"
17
+ remote servers.
18
+
19
+ == FEATURES/PROBLEMS:
20
+
21
+ Currently these types of backups are configurable:
22
+
23
+ * tar - the tar becomes compressed on the source and is <b>streamed</b> over SSH to
24
+ the backup server; no tar files are created on the source side.
25
+
26
+ * rsync - changes on the source side become synchronized in a very efficient
27
+ way to a copy on the backup server; you get a 1:1 copy of your data on the
28
+ backup server
29
+
30
+ * mysqldump - this simply does a full dump of all your databases, compresses
31
+ it and streams it over to the backup server
32
+
33
+ == SYNOPSIS:
34
+
35
+ To run a fully configured backup configured in <tt>/etc/backup_man/somehost</tt> run
36
+
37
+ backup_man somehost
38
+
39
+ To run a backup with custom log destination (default is
40
+ <tt>/var/log/backup_man.log</tt>) and with a custom configuration file path
41
+ use
42
+
43
+ backup_man -l /full/path/to/logfile /full/path/to/configfile
44
+
45
+ Full synopsis:
46
+
47
+ Usage: backup_man [options] {configname | configpath}
48
+
49
+ Options are:
50
+ -l, --logpath=PATH Path to the log file.
51
+ This can NOT be configured in the config file.
52
+ Default: /var/log/backup_man.log
53
+ -d, --debug Debug mode.
54
+ Much output on screen and in the log file.
55
+ -h, --help Show this help message.
56
+
57
+
58
+ == REQUIREMENTS:
59
+
60
+ * gems: log4r
61
+
62
+ == INSTALL / CONFIGURATION ON THE BACKUP SERVER:
63
+
64
+ * <tt>sudo gem install backup_man</tt>
65
+ * remove the password from your ssh private key, e.g. do +ssh-keygen -p+
66
+ * create an configuration file in <tt>/etc/backup_man</tt>, copy this example and adapt to your needs:
67
+
68
+ ############################################################################
69
+
70
+ # = Global settings
71
+ # These settings are valid for all latter backup definitions
72
+
73
+ # the default destination directory; if you do not set this it defaults to
74
+ # /var/backups/backup_man
75
+ DESTDIR = "/Users/srm/Documents/Backups"
76
+
77
+ # where the lockfiles go (defaults to /var/lock/backup_man/_backupname_);
78
+ # backup_man ensures, that each backup task can only run once at a time and
79
+ # uses lockfiles for this
80
+ LOCKDIR = "#{DESTDIR}"
81
+
82
+ # ssh_app defines the call to ssh with command line parameters, e.g. if you
83
+ # want to use a special identity file; SSH_APP defaults to 'ssh'
84
+ SSH_APP='ssh -i /Users/srm/.ssh/id_rsa'
85
+
86
+ ############################################################################
87
+
88
+ # = Helper variables
89
+ # These are just used in this file to ease the backup definitions
90
+
91
+ dirs_to_backup = [ "/etc", "/home", "/root", "/srv", "/var/backups",
92
+ "/var/cache/git", "/var/log", "/var/mail", "/var/rails", "/var/svn",
93
+ "/var/www" ]
94
+
95
+ ############################################################################
96
+
97
+ # = Backup defintions
98
+ # Currently there are these types of backups:
99
+ # * Tar - as the name says; simple tar of a bunch of directories into a tgz
100
+ # * Rsync - rsync of a bunch of directories
101
+ # * Mysql - does a simple mysqldump of all databases
102
+ #
103
+ # == Common settings
104
+ # * backup: an array of directories to backup
105
+ # * to: the destination directory on the local machine (defaults to DESTDIR)
106
+ # * onlyif: specify a condition (as Ruby code) which is checked before the
107
+ # actual backup run; it must evaluate true to have the backup run - see the
108
+ # homepage for details on how to write conditions
109
+ #
110
+ Tar.new( 'akeso' ) do |b|
111
+ b.backup dirs_to_backup
112
+ # in this example, this job only runs on saturdays and only if the backup
113
+ # does not exist already
114
+ b.onlyif 'Date.today.cwday == 6 && !exists?'
115
+ end
116
+
117
+ Rsync.new( 'akeso' ) do |b|
118
+ b.backup dirs_to_backup
119
+ # rsync backups go to _DESTDIR_/rsync per default; to override use the "to"
120
+ # setting:
121
+ ## b.to '/var/backup/custom-rsync-destination'
122
+ end
123
+
124
+ Mysql.new( 'akeso' ) do |b|
125
+ # in this case, we simply do not run the backup if we already have one
126
+ b.onlyif '!exists?'
127
+ end
128
+
129
+
130
+ == CONFIGURATION TASKS ON THE REMOTES
131
+
132
+ * configure passwordless ssh login via certificates, e.g. do a +ssh-copy-id user@remotehostname+
133
+
134
+ == LICENSE:
135
+
136
+ Author:: Markus Strauss (mailto:markus@itstrauss.eu)
137
+ Copyright:: Copyright (c)2009 Markus Strauss
138
+ License:: GPLv3 (http://www.gnu.org/licenses/gpl-3.0.txt)
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "BackupMan"
8
+ gem.summary = %Q{A tool for system administrators to easily configure pull-over-SSH backups.}
9
+ gem.description = %Q{A tool for system administrators to easily configure pull-over-SSH backups. Install this gem on your backup server. Configure your backups definitions in /etc/backup_man and run backup_man from cron to securely pull your data over SSH.}
10
+ gem.email = "Markus@ITstrauss.eu"
11
+ gem.homepage = "http://github.com/oowoo/BackupMan"
12
+ gem.authors = ["Markus Strauss"]
13
+ gem.rubyforge_project = "backupman"
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ gem.add_development_dependency "yard", ">= 0"
16
+ gem.add_dependency "log4r", ">= 1.1.2"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::RubyforgeTasks.new do |rubyforge|
20
+ rubyforge.doc_task = "yardoc"
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
+ end
25
+
26
+ require 'spec/rake/spectask'
27
+ Spec::Rake::SpecTask.new(:spec) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.spec_files = FileList['spec/**/*_spec.rb']
30
+ end
31
+
32
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :spec => :check_dependencies
39
+
40
+ task :default => :spec
41
+
42
+ begin
43
+ require 'yard'
44
+ YARD::Rake::YardocTask.new
45
+ rescue LoadError
46
+ task :yardoc do
47
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
48
+ end
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/backup_man ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-11-15.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'rubygems'
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/backup_man")
8
+ require "backup_man/cli"
9
+
10
+ BackupMan::CLI.execute(STDOUT, ARGV)
@@ -0,0 +1,60 @@
1
+ ############################################################################
2
+
3
+ # = Global settings
4
+ # These settings are valid for all latter backup definitions
5
+
6
+ # the default destination directory; if you do not set this it defaults to
7
+ # /var/backups/backup_man
8
+ DESTDIR = "/Users/srm/Documents/Backups"
9
+
10
+ # where the lockfiles go (defaults to /var/lock/backup_man/_backupname_);
11
+ # backup_man ensures, that each backup task can only run once at a time and
12
+ # uses lockfiles for this
13
+ LOCKDIR = "#{DESTDIR}"
14
+
15
+ # ssh_app defines the call to ssh with command line parameters, e.g. if you
16
+ # want to use a special identity file; SSH_APP defaults to 'ssh'
17
+ SSH_APP='ssh -i /Users/srm/.ssh/id_rsa'
18
+
19
+ ############################################################################
20
+
21
+ # = Helper variables
22
+ # These are just used in this file to ease the backup definitions
23
+
24
+ dirs_to_backup = [ "/etc", "/home", "/root", "/srv", "/var/backups",
25
+ "/var/cache/git", "/var/log", "/var/mail", "/var/rails", "/var/svn",
26
+ "/var/www" ]
27
+
28
+ ############################################################################
29
+
30
+ # = Backup defintions
31
+ # Currently there are these types of backups:
32
+ # * Tar - as the name says; simple tar of a bunch of directories into a tgz
33
+ # * Rsync - rsync of a bunch of directories
34
+ # * Mysql - does a simple mysqldump of all databases
35
+ #
36
+ # == Common settings
37
+ # * backup: an array of directories to backup
38
+ # * to: the destination directory on the local machine (defaults to DESTDIR)
39
+ # * onlyif: specify a condition (as Ruby code) which is checked before the
40
+ # actual backup run; it must evaluate true to have the backup run - see the
41
+ # homepage for details on how to write conditions
42
+ #
43
+ Tar.new( 'akeso' ) do |b|
44
+ b.backup dirs_to_backup
45
+ # in this example, this job only runs on saturdays and only if the backup
46
+ # does not exist already
47
+ b.onlyif 'Date.today.cwday == 6 && !exists?'
48
+ end
49
+
50
+ Rsync.new( 'akeso' ) do |b|
51
+ b.backup dirs_to_backup
52
+ # rsync backups go to _DESTDIR_/rsync per default; to override use the "to"
53
+ # setting:
54
+ ## b.to '/var/backup/custom-rsync-destination'
55
+ end
56
+
57
+ Mysql.new( 'akeso' ) do |b|
58
+ # in this case, we simply do not run the backup if we already have one
59
+ b.onlyif '!exists?'
60
+ end
@@ -0,0 +1,110 @@
1
+ require 'backup_man/backup_man'
2
+ require 'backup_man/dsl'
3
+
4
+ module BackupMan
5
+
6
+ def self.log_end_of_operation
7
+ Log.instance.info( "Finished #{self}." )
8
+ end
9
+
10
+ # we wanna log when the program ends
11
+ at_exit { self.log_end_of_operation }
12
+
13
+
14
+ # References: _Design Patterns in Ruby_ by Russ Olsen
15
+ class Backup
16
+
17
+ # the name of our backup set; this is used for generating a default
18
+ # backup_directory; e.g. use the hostname of the machine to backup
19
+ attr_reader :name
20
+
21
+ # where shall our backup data go (directory path)
22
+ attr_reader :backup_directory
23
+
24
+ # user name of the remote machine (defaults to 'root')
25
+ attr_accessor :user
26
+
27
+ # hostname of the remote machine (defaults to job definition name)
28
+ attr_accessor :host
29
+
30
+ # DSL for conditional execution
31
+ include DSL
32
+
33
+ def_dsl :onlyif
34
+ def_dsl :backup, :data_sources
35
+ def_dsl :to, :backup_directory
36
+ def_dsl :user
37
+ def_dsl :host
38
+
39
+
40
+ # this method sets all the default values but does not overwrite existing
41
+ # settings; this method cannot be called in the initializer of Backup
42
+ # because the default values are not available at that time;
43
+ def set_defaults
44
+ @data_sources = [] unless @data_sources
45
+ @backup_directory = "#{BackupMan.instance.destdir}/#{@name}" unless @backup_directory
46
+ @onlyif = "true" if @onlyif.nil?
47
+ @user = 'root' unless @user
48
+ @host = @name unless @host
49
+ end
50
+
51
+ # yields the block which comes from the DSL configuration file; also
52
+ # registers the new backup configuration with {BackupMan}
53
+ def initialize( name )
54
+ @name = name
55
+ yield(self) if block_given?
56
+ BackupMan.instance.register_backup( self )
57
+ end
58
+
59
+ # calling this actually runs the backup; DO NOT override this; override
60
+ # _run instead
61
+ def run
62
+ log_begin_of_run
63
+ set_defaults
64
+ debug_log_dsl_info
65
+ onlyif = eval( @onlyif )
66
+ Log.instance.debug( "onlyif = { #{@onlyif} } evaluates #{onlyif}" )
67
+ if onlyif
68
+ unless @backup_directory
69
+ Log.instance.error( "#{self}: No backup directory. Don't know where to store all this stuff.")
70
+ else
71
+ FileUtils.mkdir_p @backup_directory
72
+ _run
73
+ end
74
+ else
75
+ Log.instance.info( "#{self}: Preconditions for backup run not fulfilled.")
76
+ end
77
+ log_end_of_run
78
+ end
79
+
80
+ # @return [String]
81
+ def to_s
82
+ "#{self.class} #{self.name}"
83
+ end
84
+
85
+
86
+
87
+ private
88
+
89
+ # @abstract override this to implement the actual backup commands
90
+ def _run
91
+ throw "Hey. Cannot run just 'Backup'."
92
+ end
93
+
94
+ # not used acutally
95
+ def log_begin_of_run
96
+ Log.instance.info( "Starting #{self.class} run for #{@name}." )
97
+ end
98
+
99
+ # simply logs that the program terminates
100
+ def log_end_of_run
101
+ Log.instance.info( "Finished #{self.class} run for #{@name}." )
102
+ end
103
+
104
+ # @return [String] the ssh command string including user@host
105
+ def ssh_connect_cmd
106
+ "#{BackupMan.instance.ssh_app} #{@user}@#{@host}"
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,45 @@
1
+ require 'singleton'
2
+
3
+ module BackupMan
4
+ # this is a singleton class managing the application; it holds some
5
+ # important global settings, like the global destination directory;
6
+ class BackupMan
7
+
8
+ include Singleton
9
+
10
+ attr_accessor :destdir, :ssh_app, :logfile, :lockdir
11
+
12
+ def initialize
13
+ @backups = []
14
+ end
15
+
16
+ def register_backup( backup )
17
+ @backups << backup
18
+ end
19
+
20
+ def run
21
+ @backups.each do |backup|
22
+ begin
23
+ backup.run
24
+ rescue Interrupt
25
+ Log.instance.warn( "Interrupt: Cancelling remaining operations.")
26
+ return
27
+ end
28
+ end
29
+ end
30
+
31
+ def build_lockfile_path( filename )
32
+ "#{@lockdir}/#{filename}"
33
+ end
34
+
35
+ def set_default( symbol, value )
36
+ const_s = symbol.to_s.upcase
37
+ if CLI.const_defined?( const_s )
38
+ self.instance_variable_set( "@#{symbol.to_s}", CLI.const_get( const_s ) )
39
+ else
40
+ self.instance_variable_set( "@#{symbol.to_s}", value )
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,101 @@
1
+ require 'optparse'
2
+ require 'date'
3
+ require 'pathname'
4
+
5
+ require_relative 'log'
6
+ require_relative 'tar'
7
+ require_relative 'rsync'
8
+ require_relative 'mysql'
9
+
10
+
11
+ module BackupMan
12
+ class CLI
13
+ def self.execute(stdout, arguments=[])
14
+
15
+ # NOTE: the option -p/--path= is given as an example, and should be replaced in your application.
16
+
17
+ options = {
18
+ :debug => false,
19
+ :logpath => '/var/log/backup_man.log'
20
+ }
21
+ mandatory_options = %w( )
22
+
23
+ parser = OptionParser.new do |opts|
24
+ opts.banner = <<-BANNER.gsub(/^ /,'')
25
+ BackupMan handles your SSH-pull-style backups. Call it via cron or any other scheduler.
26
+
27
+ Usage: #{File.basename($0)} [options] {configname | configpath}
28
+
29
+ Options are:
30
+ BANNER
31
+ opts.separator ""
32
+ opts.on("-l", "--logpath=PATH", String,
33
+ "Path to the log file. This can NOT be configured in the config file.",
34
+ "Default: /var/log/backup_man.log") { |arg| options[:logpath] = arg }
35
+ opts.on("-d", "--debug", "Debug mode.", "Much output on screen and in the log file." ) {
36
+ options[:debug] = true
37
+ }
38
+ opts.on("-h", "--help",
39
+ "Show this help message.") { stdout.puts opts; exit }
40
+ opts.parse!(arguments)
41
+
42
+ if mandatory_options && mandatory_options.find { |option| options[option.to_sym].nil? }
43
+ stdout.puts opts; exit
44
+ end
45
+ end
46
+
47
+ # doing our stuff here
48
+
49
+ # first we check if our logfile is writeable; if not, we give a warning
50
+ logfile = Pathname.new( options[:logpath] )
51
+ if (logfile.exist? && logfile.writable?) || logfile.parent.writable?
52
+ BackupMan.instance.logfile = logfile.to_s
53
+ else
54
+ Log.instance.warn( "Log file is not writeable: #{logfile}.")
55
+ end
56
+
57
+ # root-warning
58
+ Log.instance.warn( "Please do not run this program as root.") if `id -u`.strip == '0'
59
+
60
+ # reconfigure our Logger for debugging if necessary
61
+ if options[:debug]
62
+ Log.instance.enable_debugmode
63
+ Log.instance.debug( "Debugging mode enabled.")
64
+ end
65
+
66
+
67
+ unless ARGV[0]
68
+ Log.instance.fatal( "No config file given." )
69
+ stdout.puts parser
70
+ exit 1
71
+ else
72
+ config_filepath = Pathname.new( ARGV[0] )
73
+ config_filepath = Pathname.new "/etc/backup_man/#{config_filepath}" unless config_filepath.file?
74
+
75
+ unless config_filepath.file?
76
+ Log.instance.fatal( "Config file not found [#{config_filepath}].")
77
+ exit 2
78
+ end
79
+
80
+ # get and evaluate the config file; errors in the config file may be
81
+ # difficult to debug, so be careful
82
+ Log.instance.debug( "Reading file '#{config_filepath}'.")
83
+ eval( File.read( config_filepath ) )
84
+
85
+ # configure global defaults
86
+ BackupMan.instance.set_default( :destdir, '/var/backups/backup_man')
87
+ BackupMan.instance.set_default( :lockdir, '/var/lock/backup_man')
88
+ BackupMan.instance.set_default( :ssh_app, 'ssh')
89
+
90
+ Log.instance.debug( "Global settings:")
91
+ Log.instance.debug( " DESTDIR = #{BackupMan.instance.destdir}")
92
+ Log.instance.debug( " LOCKDIR = #{BackupMan.instance.lockdir}")
93
+ Log.instance.debug( " SSH_APP = #{BackupMan.instance.ssh_app}")
94
+
95
+ # run the whole thing
96
+ BackupMan.instance.run
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,74 @@
1
+
2
+ require 'digest/md5'
3
+
4
+ module BackupMan
5
+ # = Command class
6
+ # This class allows to run commands and makes sure the same command cannot
7
+ # run two times at the same time.
8
+ #
9
+ # This ensured by creating lockfiles in the working directory. To override
10
+ # define LOCKDIR.
11
+
12
+ class Command
13
+
14
+ attr_reader :cmd
15
+
16
+ def initialize( cmd )
17
+ @cmd = cmd
18
+ end
19
+
20
+ # returns true if the command is locked (= currently running)
21
+ def locked?
22
+ File.exists?( self.lockfile )
23
+ end
24
+
25
+ # returns the path to the lockfile for this command
26
+ def lockfile
27
+ hash = Digest::MD5.hexdigest(cmd.to_s)
28
+ BackupMan.instance.build_lockfile_path( "#{hash}.lock.txt" )
29
+ end
30
+
31
+ # returns a nice string representation of the command
32
+ def to_s
33
+ self.cmd.to_s
34
+ end
35
+
36
+ def run
37
+ unless locked?
38
+ lock
39
+ begin
40
+ Log.instance.info( "#{self}: Running.")
41
+ print `#{cmd}` unless TESTING
42
+ rescue Interrupt
43
+ Log.instance.warn( "#{self}: Operation interrupted.")
44
+ raise
45
+ ensure
46
+ unlock
47
+ end
48
+ else
49
+ Log.instance.warn( "Command already running: #{self}." )
50
+ end
51
+ end
52
+
53
+
54
+ private
55
+
56
+ def lock
57
+ Log.instance.debug( "Locking command " + self.cmd )
58
+ unless locked?
59
+ f = File.new( self.lockfile, "w" )
60
+ # FIXME: command output shall go into the correct logging lvl (eg ERROR)
61
+ f.write(self.cmd)
62
+ f.close
63
+ else
64
+ Log.instance.info( "Lockfile exists: " + lockfile )
65
+ end
66
+ end
67
+
68
+ def unlock
69
+ Log.instance.debug( "Unlocking command " + self.cmd )
70
+ FileUtils.remove_file( self.lockfile )
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,47 @@
1
+ module BackupMan
2
+
3
+ # use "include DSL" to use this module
4
+ module DSL
5
+
6
+ # extend host class with class methods when we're included
7
+ def self.included(host_class)
8
+ host_class.extend(ClassMethods)
9
+ end
10
+
11
+ def debug_log_dsl_info
12
+ Log.instance.debug( "Job settings:")
13
+ self.class.dsl_methods.each do |method, var|
14
+ Log.instance.debug( " #{method} = #{self.instance_variable_get("@#{var}")}" )
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ def def_dsl( name, var = name )
21
+ class_eval( %Q{
22
+ def #{name}( #{var} )
23
+ @#{var} = #{var}
24
+ end
25
+ })
26
+ register_dsl( name, var )
27
+ end
28
+
29
+ def register_dsl( name, var )
30
+ @dsl_methods = [] if @dsl_methods.nil?
31
+ @dsl_methods << [name, var]
32
+ puts "#{self} #{self.dsl_methods.join ","}"
33
+ end
34
+
35
+ # returns an array of all dsl methods of this and all superclasses
36
+ def dsl_methods
37
+ dsl_methods = []
38
+ if self.superclass != Object
39
+ dsl_methods = dsl_methods | self.superclass.dsl_methods
40
+ end
41
+ dsl_methods | @dsl_methods
42
+ end
43
+
44
+
45
+ end # ClassMethods
46
+ end #DSL
47
+ end #BackupMan
@@ -0,0 +1,48 @@
1
+ require 'singleton'
2
+ require 'delegate'
3
+ require 'rubygems'
4
+ require 'log4r'
5
+
6
+ module BackupMan
7
+
8
+ # = Singleton logger
9
+ #
10
+ # This one logs INFO and higher to a file and WARN and higher to STDERR. It
11
+ # does this by creating a new Log4r::Logger object and configuring it.
12
+ # SimpleDelegator wrapps this.
13
+ #
14
+ # By default, the logfile goes into workdir/backup_man.log. To override use
15
+ # LOGFILE in the backup case definition.
16
+ #
17
+ # == References
18
+ # * http://www.5dollarwhitebox.org/drupal/ruby_dual_output_logging
19
+ # * http://rubyforge.org/snippet/detail.php?type=snippet&id=146
20
+ #
21
+ class Log < SimpleDelegator
22
+ include Singleton
23
+ include Log4r
24
+
25
+ def initialize
26
+ @logger = Logger.new( "BackupMan" )
27
+ super( @logger )
28
+
29
+ console_format = PatternFormatter.new(:pattern => "%l:\t %m")
30
+ stderr_outputter = Log4r::StderrOutputter.new( 'console', :formatter => console_format )
31
+ stderr_outputter.level = WARN
32
+ @logger.add stderr_outputter
33
+
34
+ if filename = BackupMan.instance.logfile
35
+ file_format = PatternFormatter.new(:pattern => "[ %d ] %l\t %m")
36
+ file_outputter = FileOutputter.new('fileOutputter', :filename => filename, :trunc => false, :formatter => file_format )
37
+ file_outputter.level = DEBUG
38
+ @logger.add file_outputter
39
+ end
40
+ end
41
+
42
+ def enable_debugmode
43
+ Log4r::Outputter.each_outputter { |outputter| outputter.level = DEBUG }
44
+ end
45
+
46
+ end
47
+
48
+ end