log2mail 0.0.1.pre3 → 0.0.1.pre4

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 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