log2mail 0.0.1.pre3 → 0.0.1.pre4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3636434bfe17fe625cb2e6077f816d1a623b85bc
4
- data.tar.gz: de33d7d97f5f51641d373f3b3197f838b6a61b93
3
+ metadata.gz: bdf651d2af53d14c8f36ea64233f8db4116c055d
4
+ data.tar.gz: 1c35a2f8dc2df19d6635b66d24c279029bcff909
5
5
  SHA512:
6
- metadata.gz: b30c40a090d017da981ccfef1ce53edadcb0d4f89e4a42e20fc59596febf2988517694fc98d394459be169d759027e4f585709b253cdfb4ac6c7e5969f465c24
7
- data.tar.gz: d2a55f7581c4ce4ab8da660cf147c064238a9bfa5c6c1bbf8f03da16dd59b2907a66973b92d84acca22cf09c2a4ff2e70968dad285c70026e93685f3fead2016
6
+ metadata.gz: cba00ca0b65e5f87f434450f44bbef5e17e94fdfb8c144e4f73ce002648425e96ba39f5a2d63ba9c7eac0d82df28ba3e15be46576629a4053818ccfd12d60a13
7
+ data.tar.gz: 5e640a298954ebf3fae7745f86ea478f9f4ff8f56e0d71bd8284f5ddd8feef13c1e530d2735ef8b8395e9f2a21f99f2c6e417f143adcce5152dd132d396f2889
@@ -4,6 +4,7 @@ log2mail.rb Installation
4
4
  Requirements
5
5
  ------------
6
6
 
7
+ * Linux
7
8
  * Ruby 1.9.3
8
9
 
9
10
  Debian 7 (and higher)
@@ -25,6 +26,7 @@ Create a configuration file, e.g. at `/usr/local/etc/log2mail.conf`:
25
26
 
26
27
  cat > /usr/local/etc/log2mail.conf <<EOF
27
28
  defaults
29
+ sendmail = /usr/sbin/sendmail
28
30
  mailto = your@mail.address
29
31
  file = /tmp/test.log
30
32
  pattern = test
data/README.md CHANGED
@@ -9,7 +9,7 @@ log2mail.rb(1) -- monitors (log) files for patterns and reports hits by mail
9
9
 
10
10
  `log2mail.rb` helps having an eye on your systems' log files. It efficiently monitors multiple files and reports as soon as specified (regular expression) patterns match.
11
11
 
12
- On startup, `log2mail.rb` opens all files on the 'watch list' and seeks to EOF. All new data are parsed about once a minute (see `--sleeptime`). <!-- If necessary, i.e. when multiline patterns are set for the file, every new data is put into a fixed-size buffer. The buffer is rolled over when it gets full. --> Matched patterns are reported to the configured mail address(es) (see `mailto` configuration option).
12
+ On startup, `log2mail.rb` opens all files on the 'watch list' and seeks to EOF. All new data are parsed about once a minute (see `--sleeptime`). Matched patterns are reported to the configured mail address(es) (see `mailto` configuration option).
13
13
 
14
14
  Log files are reopened automatically when rotated.
15
15
 
@@ -1,7 +1,7 @@
1
1
  # old-style syntax
2
2
  defaults
3
- mailto = recipient@test.itstrauss.eu
4
3
  fromaddr = log2mail
4
+ mailto = recipient@test.itstrauss.eu
5
5
  file = test.log
6
6
  pattern = string pattern
7
7
  pattern = /regexp pattern/
data/lib/ext/string.rb CHANGED
@@ -20,4 +20,10 @@ class String
20
20
  rxp
21
21
  end
22
22
  end
23
+
24
+ def pluralize
25
+ return self if self.to_s[-1] == 's'
26
+ (self.to_s + 's')
27
+ end
28
+
23
29
  end
data/lib/ext/symbol.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Symbol
2
+
3
+ def pluralize
4
+ self.to_s.pluralize.to_sym
5
+ end
6
+
7
+ end
data/lib/log2mail.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  %w{ main mail terminal-table }.each {|r| require r}
2
- %w{ kernel string main }.each {|r| require_relative "ext/#{r}"}
2
+ %w{ kernel string symbol main }.each {|r| require_relative "ext/#{r}"}
3
3
  begin
4
4
  require 'syslog/logger'
5
5
  rescue LoadError
@@ -1,216 +1,15 @@
1
1
  module Log2mail
2
- class Config
3
-
4
- class <<self
5
- def parse_config(config_path)
6
- Log2mail::Config.new(config_path)
7
- # pp config.config
8
- # config.files.each do |f|
9
- # puts "File: #{f}"
10
- # config.patterns_for_file(f).each do |pattern|
11
- # puts " Pattern: #{pattern}; mailto: " + config.mailtos_for_pattern( f, pattern ).join(', ')
12
- # end
13
- # end
14
- end
15
- end
16
-
17
- # attr_reader :config
18
-
2
+ module Config
19
3
  INT_OPTIONS = [:sendtime, :resendtime, :maxlines]
20
4
  STR_OPTIONS = [:fromaddr, :sendmail]
21
5
  PATH_OPTIONS = [:template]
22
-
23
- def initialize(config_paths)
24
- $logger.debug "Reading configuration from #{config_paths}"
25
- @config_paths = Array(config_paths)
26
- expand_paths
27
- read_configuration
28
- validate_configuration
29
- end
30
-
31
- # returns all the paths of all files needed to be watched
32
- def files
33
- @config.keys - [:defaults]
34
- end
35
-
36
- def file_patterns
37
- h = {}
38
- files.each do |file|
39
- h[file] = patterns_for_file(file)
40
- end
41
- h
42
- end
43
-
44
- def patterns_for_file( file )
45
- Hash(@config[file][:patterns]).keys + \
46
- Hash(defaults[:patterns]).keys
47
- end
48
-
49
- def mailtos_for_pattern( file, pattern )
50
- m = []
51
- m.concat Hash( config_file_pattern(file, pattern)[:mailtos] ).keys
52
- m.concat Hash(Hash(Hash(defaults[:patterns])[pattern])[:mailtos]).keys
53
- m.concat Array(defaults[:mailtos]) if m.empty?
54
- m.uniq
55
- end
56
-
57
- def settings_for_mailto( file, pattern, mailto )
58
- h = defaults.reject {|k,v| k==:mailtos}
59
- h.merge config_file_mailto(file, pattern, mailto)
60
- end
61
-
62
- def defaults
63
- Hash(@config[:defaults])
64
- end
65
-
66
- def formatted( show_effective )
67
- Terminal::Table.new do |t|
68
- settings_header = show_effective ? 'Effective Settings' : 'Settings'
69
- t << ['File', 'Pattern', 'Recipient', settings_header]
70
- t << :separator
71
- files.each do |file|
72
- patterns_for_file(file).each do |pattern|
73
- mailtos_for_pattern(file, pattern).each do |mailto|
74
- settings = []
75
- if show_effective
76
- settings_for_mailto(file, pattern, mailto).each_pair \
77
- { |k,v| settings << '%s=%s' % [k,v] }
78
- else
79
- config_file_mailto(file, pattern, mailto).each_pair \
80
- { |k,v| settings << '%s=%s' % [k,v] }
81
- end
82
- t.add_row [file, pattern, mailto, settings.join($/)]
83
- end
84
- end
85
- end
86
- end
87
- end
88
-
89
- private
90
-
91
- def config_file(file)
92
- Hash(@config[file])
93
- end
94
-
95
- def config_file_pattern(file, pattern)
96
- Hash( Hash( config_file(file)[:patterns] )[pattern] )
97
- end
98
-
99
- def config_file_mailtos(file, pattern)
100
- Hash( config_file_pattern(file, pattern)[:mailtos] )
101
- end
102
-
103
- def config_file_mailto(file, pattern, mailto)
104
- Hash( config_file_mailtos(file, pattern)[mailto] )
105
- end
106
-
107
- def expand_paths
108
- expanded_paths = []
109
- @config_paths.each do |path|
110
- if ::File.directory?(path)
111
- expanded_paths.concat Dir.glob( ::File.join( path, '*[^~#]' ) )
112
- else
113
- expanded_paths << path
114
- end
115
- end
116
- @config_paths = expanded_paths
117
- end
118
-
119
- # tries to follow original code at https://github.com/lordlamer/log2mail/blob/master/config.cc#L192
120
- def read_configuration
121
- @config = {}
122
- @config_paths.each do |file|
123
- @section = nil; @pattern = nil; @mailto = nil
124
- # section, pattern, mailto are reset for every file (but not when included by 'include')
125
- parse_file( file )
126
- end
127
- end
128
-
129
- def parse_file( filename )
130
- IO.readlines(filename).each_with_index do |line, lineno|
131
- parse(filename, line, lineno + 1)
132
- end
133
- rescue Errno::ENOENT
134
- fail Error, "Configuration file or directory not found (or not readable): #{filename}"
135
- end
136
-
137
- def parse(file, line, lineno)
138
- line.strip!
139
- return if line =~ /^#/
140
- return if line =~ /^$/
141
- line =~ /^(\S+)\s*=?\s*"?(.*?)"?(\s*#.*)?$/ # drop double quotes on right hand side; drop comments
142
- key, value = $1.to_sym, $2.strip
143
- if key == :include # include shall work everywhere
144
- parse_file( ::File.join(Pathname(file).parent, value) )
145
- return
146
- end
147
- if key == :defaults and value.empty? # section: specifies top level; must be 'defaults' or 'file'
148
- @section = key
149
- @pattern = nil; @mailto = nil
150
- fail Error, "Invalid section. Section 'defaults' already specified." if @config[@section]
151
- @config[@section] = {}
152
- elsif key == :file
153
- @section = value
154
- @pattern = nil; @mailto = nil
155
- @config[@section] ||= {}
156
- elsif key == :pattern # must come inside 'file' (or 'defaults')
157
- # fail "Invalid section. All statements must appear after 'defaults' or 'file=...'" unless @section
158
- @pattern = value; @mailto = nil
159
- @config[@section][:patterns] ||= {}
160
- warning { "Redefining pattern section '#{value}' which has been defined already for '#{@section}'." } \
161
- if @config[@section][:patterns][value]
162
- @config[@section][:patterns][value] = {}
163
- elsif key == :mailto and @section != :defaults # must come inside 'pattern' (or 'defaults')
164
- fail Error, "'mailto' statements only allowed inside 'pattern' or 'defaults'." unless @pattern
165
- @mailto = value
166
- @config[@section][:patterns][@pattern][:mailtos] ||= {}
167
- warning { "Redefining mailto section '#{value}' which has been defined already for '#{@section}'." } \
168
- if @config[@section][:patterns][@pattern][:mailtos][value]
169
- @config[@section][:patterns][@pattern][:mailtos][value] = {}
170
- else # everything else must come inside 'defaults' or 'mailto'
171
- fail Error, "'#{key}' must be set within 'defaults' or 'mailto'." unless @section == :defaults or @mailto
172
- if INT_OPTIONS.include?(key)
173
- value = value.to_i
174
- elsif STR_OPTIONS.include?(key)
175
- elsif PATH_OPTIONS.include?(key)
176
- value = ::File.expand_path( value, Pathname(file).parent ) unless Pathname(value).absolute?
177
- elsif key == :mailto # special handling for 'mailto' in 'defaults' section
178
- @config[:defaults][:mailtos] ||= []
179
- @config[:defaults][:mailtos] << value
180
- return # skip the 'mailto' entry itself
181
- else
182
- fail Error, "'#{key}' is an unknown configuration statement."
183
- end
184
- if @section == :defaults and !@pattern and !@mailto
185
- warning { "Redefining value for '#{key}'." } if @config[@section][key]
186
- @config[@section][key] = value
187
- else
188
- warning { "Redefining value for '#{key}'." } \
189
- if @config[@section][:patterns][@pattern][:mailtos][@mailto][key]
190
- @config[@section][:patterns][@pattern][:mailtos][@mailto][key] = value
191
- end
192
- end
193
- rescue
194
- fail Error, "#{file} (line #{lineno}): #{$!.message}#$/[#{$!.class} at #{$!.backtrace.first}]"
195
- end
196
-
197
- def validate_configuration
198
- files.each do |file|
199
- patterns_for_file(file).each do |pattern|
200
- mailtos = mailtos_for_pattern(file, pattern)
201
- $logger.warn "Pattern #{file}:#{pattern} has no recipients." if mailtos.empty?
202
- end
203
- end
204
- # FIXME: empty configuration should cause FATAL error
205
- # TODO: illegal regexp pattern should cause ERROR
206
- end
207
-
208
- def warning(&block)
209
- file = block.binding.eval('file')
210
- lineno = block.binding.eval('lineno')
211
- message = block.call
212
- $logger.warn "#{file} (line #{lineno}): #{message}#$/[at #{caller.first}]"
213
- end
214
-
6
+ ATTRIBUTES = INT_OPTIONS + STR_OPTIONS + PATH_OPTIONS
215
7
  end
216
8
  end
9
+
10
+ require_relative 'config/config'
11
+ require_relative 'config/attribute'
12
+ require_relative 'config/section'
13
+ require_relative 'config/parser'
14
+ require_relative 'config/config_file_snippet'
15
+ require_relative 'config/config_file_handler'
@@ -0,0 +1,21 @@
1
+ module Log2mail
2
+ module Config
3
+ class Attribute
4
+ attr_reader :name, :value
5
+ def initialize( name, value )
6
+ @name = name.to_sym
7
+ @value = value
8
+ end
9
+ def ==(other)
10
+ self.name == other.name and self.value == other.value
11
+ end
12
+ def inspect
13
+ 'Attribute %s = %s' % [@name.inspect, @value.inspect]
14
+ end
15
+ def tree
16
+ {self.name => self.value}
17
+ # self.value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,45 @@
1
+ module Log2mail
2
+ module Config
3
+ class Config
4
+ attr_reader :sections
5
+ def initialize( sections = [] )
6
+ @sections = merge sections
7
+ end
8
+ def ==(other)
9
+ return false unless other.instance_of?(self.class)
10
+ self.sections == other.sections
11
+ end
12
+ def inspect
13
+ 'Config: %s' % [@sections.inspect]
14
+ end
15
+ def tree
16
+ h = {}
17
+ @sections.inject(h) do |h, a|
18
+ if a.name==:defaults
19
+ h[:defaults] = a.tree
20
+ else
21
+ a.tree.each_pair {|k,v| ( h[a.name.pluralize.to_sym] ||= {} )[k] = v }
22
+ end
23
+ h
24
+ end
25
+ h
26
+ end
27
+
28
+ private
29
+
30
+ # FIXME: needs specing
31
+ def merge( sections )
32
+ sections = sections.compact.find_all{ |s| s.respond_to?(:name) }
33
+ names = sections.map(&:name).uniq
34
+ names.map do |name|
35
+ secs_with_same_name = sections.find_all{ |sec| sec.name == name }
36
+ their_uniq_values = secs_with_same_name.map(&:value).uniq
37
+ their_uniq_values.map{ |val| secs_with_same_name.find_all{ |s| s.value == val }.reduce(:+) }
38
+ end.flatten(1)
39
+ rescue NoMethodError
40
+ fail "Invalid configuration: #{$!.inspect}"
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,142 @@
1
+ module Log2mail
2
+ module Config
3
+ class ConfigFileHandler
4
+
5
+ class <<self
6
+ def parse_config(config_path)
7
+ new(config_path)
8
+ end
9
+ end
10
+
11
+ def initialize(config_paths)
12
+ $logger.debug "Reading configuration from #{config_paths}"
13
+ @config_paths = Array(config_paths)
14
+ expand_paths
15
+ @config = Parser.new.parse_snippets( raw ).tree
16
+ validate_configuration
17
+ end
18
+
19
+ def expand_paths
20
+ expanded_paths = []
21
+ @config_paths.each do |path|
22
+ if ::File.directory?(path)
23
+ expanded_paths.concat Dir.glob( ::File.join( path, '*[^~#]' ) )
24
+ else
25
+ expanded_paths << path
26
+ end
27
+ end
28
+ @config_paths = expanded_paths
29
+ end
30
+
31
+ def raw
32
+ @config_paths.map do |file|
33
+ ConfigFileSnippet.new( IO.read(file), file )
34
+ end
35
+ end
36
+
37
+ # returns all the paths of all files needed to be watched
38
+ def files
39
+ Hash(@config[:files]).keys
40
+ end
41
+
42
+ # FIXME: specs
43
+ def file_patterns
44
+ h = {}
45
+ files.each do |file|
46
+ h[file] = patterns_for_file(file)
47
+ end
48
+ h
49
+ end
50
+
51
+ # returns the default settings
52
+ def defaults
53
+ Hash(@config[:defaults])
54
+ end
55
+
56
+ # returns all patterns for file
57
+ def patterns_for_file( file )
58
+ Hash(config_file(file)[:patterns]).keys + \
59
+ Hash(defaults[:patterns]).keys
60
+ end
61
+
62
+ def mailtos_for_pattern( file, pattern )
63
+ m = []
64
+ m.concat Hash( config_file_pattern(file, pattern)[:mailtos] ).keys
65
+ m.concat Hash(Hash(Hash(defaults[:patterns])[pattern])[:mailtos]).keys
66
+ m.concat Hash(defaults[:mailtos]).keys if m.empty?
67
+ m.uniq
68
+ end
69
+
70
+ def settings_for_mailto( file, pattern, mailto )
71
+ h = defaults.reject {|k,v| k==:mailtos}
72
+ h.merge config_file_mailto(file, pattern, mailto)
73
+ end
74
+
75
+
76
+
77
+ def formatted( show_effective = false )
78
+ Terminal::Table.new do |t|
79
+ settings_header = show_effective ? 'Effective Settings' : 'Settings'
80
+ t << ['File', 'Pattern', 'Recipient', settings_header]
81
+ t << :separator
82
+ files.each do |file|
83
+ patterns_for_file(file).each do |pattern|
84
+ mailtos_for_pattern(file, pattern).each do |mailto|
85
+ settings = []
86
+ if show_effective
87
+ settings_for_mailto(file, pattern, mailto).each_pair \
88
+ { |k,v| settings << '%s=%s' % [k,v] }
89
+ else
90
+ config_file_mailto(file, pattern, mailto).each_pair \
91
+ { |k,v| settings << '%s=%s' % [k,v] }
92
+ end
93
+ t.add_row [file, pattern, mailto, settings.join($/)]
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+
101
+
102
+ private
103
+
104
+ def config_file(file)
105
+ Hash(Hash(@config[:files])[file])
106
+ end
107
+
108
+ def config_file_pattern(file, pattern)
109
+ Hash( Hash( config_file(file)[:patterns] )[pattern] )
110
+ end
111
+
112
+ def config_file_mailtos(file, pattern)
113
+ config_file_pattern(file, pattern)[:mailtos] || {}
114
+ end
115
+
116
+ def config_file_mailto(file, pattern, mailto)
117
+ config_file_mailtos(file, pattern)[mailto] || {}
118
+ end
119
+
120
+
121
+ def validate_configuration
122
+ files.each do |file|
123
+ patterns_for_file(file).each do |pattern|
124
+ mailtos = mailtos_for_pattern(file, pattern)
125
+ $logger.warn "Pattern #{file}:#{pattern} has no recipients." if mailtos.empty?
126
+ end
127
+ end
128
+
129
+ $logger.warn "Attributes for a global default recipient specified. This may or may not be what you want. Consult `gem man log2mail` if unsure." \
130
+ if !defaults[:mailtos].nil? and defaults[:mailtos].any?{|k,v| !v.empty? }
131
+
132
+ $logger.warn "Attributes for a global default pattern specified. This may or may not be what you want. Consult `gem man log2mail` if unsure." \
133
+ if !defaults[:patterns].nil? and defaults[:patterns].any?{|k,v| !v.empty? }
134
+
135
+ # FIXME: empty configuration should cause FATAL error
136
+ # TODO: illegal regexp pattern should cause ERROR
137
+ end
138
+
139
+
140
+ end
141
+ end
142
+ end