snoopit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +39 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/dictionaries/rbirch.xml +9 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/snoopit.iml +233 -0
- data/.idea/vcs.xml +7 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +411 -0
- data/Rakefile +1 -0
- data/bin/snoopit +173 -0
- data/lib/snoopit.rb +22 -0
- data/lib/snoopit/detected.rb +50 -0
- data/lib/snoopit/file_info.rb +104 -0
- data/lib/snoopit/file_tracker.rb +83 -0
- data/lib/snoopit/logger.rb +30 -0
- data/lib/snoopit/notification_manager.rb +123 -0
- data/lib/snoopit/notifier.rb +25 -0
- data/lib/snoopit/notifiers/email.rb +61 -0
- data/lib/snoopit/notifiers/http.rb +85 -0
- data/lib/snoopit/notifiers/https.rb +21 -0
- data/lib/snoopit/notifiers/stomp.rb +59 -0
- data/lib/snoopit/register.rb +69 -0
- data/lib/snoopit/sniffer.rb +51 -0
- data/lib/snoopit/snooper.rb +149 -0
- data/lib/snoopit/snoopy.rb +67 -0
- data/lib/snoopit/version.rb +3 -0
- data/snoopit.gemspec +27 -0
- data/spec/bin/snoopit_spec.rb +258 -0
- data/spec/file_info_spec.rb +131 -0
- data/spec/file_tracker_spec.rb +172 -0
- data/spec/notification_manager_spec.rb +103 -0
- data/spec/notifiers/email_spec.rb +36 -0
- data/spec/notifiers/http_spec.rb +37 -0
- data/spec/notifiers/https_spec.rb +38 -0
- data/spec/notifiers/stomp_spec.rb +34 -0
- data/spec/register_spec.rb +105 -0
- data/spec/snooper_spec.rb +538 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/log/snoop_log.test +593 -0
- data/spec/support/log/snoop_log_2.test +593 -0
- data/spec/support/multiple_snoopies.json +82 -0
- data/spec/support/regexp_tester.rb +10 -0
- data/spec/support/snoopies.json +93 -0
- data/spec/support/snoopies_notifiers.json +66 -0
- data/spec/support/test_notifier.rb +18 -0
- data/spec/support/test_notifier_load.rb +18 -0
- data/support/snoopies.json +110 -0
- metadata +190 -0
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/snoopit
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'logger'
|
4
|
+
require 'snoopit'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
options[:snoopies_file]='snoopies.json'
|
8
|
+
options[:snoopies] = []
|
9
|
+
options[:verbose] = false
|
10
|
+
options[:line_numbers] = false
|
11
|
+
options[:new_line] = true
|
12
|
+
options[:json] = false
|
13
|
+
options[:pretty_json] = false
|
14
|
+
options[:notifications] = false
|
15
|
+
options[:tracking] = false
|
16
|
+
options[:tracking_file] = './snoopit_db.json'
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
|
20
|
+
opts.banner = 'Usage: snoopit [options]'
|
21
|
+
|
22
|
+
opts.on('-s', '--snoopers snoopies.json', 'File contains one or more regular expressions to locate a line of interest in a file' ) do |snoopies_file|
|
23
|
+
options[:snoopies_file] = snoopies_file
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-S', '--snooper snooper_name', 'Only use the named snooper. This option can be used more than once to use several snoopers.' ) do |snooper|
|
27
|
+
options[:snoopies] << snooper
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-t', '--template', 'Generate a template snoopies.json file to stdout' ) do
|
31
|
+
template = File.expand_path '../../support/snoopies.json', __FILE__
|
32
|
+
File.readlines(template).each do |line|
|
33
|
+
puts line
|
34
|
+
end
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-T', '--tracking', 'Enable log file tracking using file ./snoopit_db.json') do
|
39
|
+
options[:tracking] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-f', '--tracking-file file_name', 'Specify a different tracking file name and location instead of the default ./snoopit_db.json') do |tracking_file|
|
43
|
+
options[:tracking] = true
|
44
|
+
options[:tracking_file] = tracking_file
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-j', '--json', 'Generate output in json' ) do
|
48
|
+
options[:json] = true
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-J', '--pretty-json', 'Generate output in pretty json' ) do
|
52
|
+
options[:json] = true
|
53
|
+
options[:pretty_json] = true
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on('-N', '--no-newline', 'Do not output new line between found items' ) do |l|
|
57
|
+
options[:new_line] = false
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on('-n', '--enable-notifications', 'Enable notifications' ) do |n|
|
61
|
+
options[:notifications] = true
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('-l', '--line-numbers', 'show line numbers' ) do |l|
|
65
|
+
options[:line_numbers] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on_tail('-v', '--verbose', 'prints out file name, matched line number') do
|
69
|
+
options[:verbose] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on_tail('-h', '--help') do
|
73
|
+
puts opts
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
end.parse!
|
78
|
+
|
79
|
+
def verbose(snoopy_name, sniffed)
|
80
|
+
puts ''
|
81
|
+
puts "File: #{sniffed.file}"
|
82
|
+
puts "Snooper: #{snoopy_name}"
|
83
|
+
puts "Match at line number: #{sniffed.line_no}"
|
84
|
+
puts "** Sniffed Out ** #{sniffed.match}"
|
85
|
+
puts ''
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_start_line_number(sniffed)
|
89
|
+
start = sniffed.line_no - sniffed.after.size
|
90
|
+
start = 1 if start <= 0
|
91
|
+
start
|
92
|
+
end
|
93
|
+
|
94
|
+
def line_before(line_no, sniffed)
|
95
|
+
sniffed.before.register.each do |b|
|
96
|
+
unless b.nil?
|
97
|
+
puts "#{line_no}: #{b}"
|
98
|
+
line_no += 1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
line_no
|
102
|
+
end
|
103
|
+
|
104
|
+
def line_after(line_no, sniffed)
|
105
|
+
sniffed.after.register.each do |a|
|
106
|
+
unless a.nil?
|
107
|
+
puts "#{line_no}: #{a}"
|
108
|
+
line_no += 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
line_no
|
112
|
+
end
|
113
|
+
|
114
|
+
def line_numbers(sniffed)
|
115
|
+
line_no = get_start_line_number sniffed
|
116
|
+
line_no = line_before line_no, sniffed
|
117
|
+
puts "#{line_no}: #{sniffed.match}"
|
118
|
+
line_no += 1
|
119
|
+
line_after line_no, sniffed
|
120
|
+
end
|
121
|
+
|
122
|
+
def no_line_numbers(sniffed)
|
123
|
+
sniffed.before.register.each {|b| puts b unless b.nil? }
|
124
|
+
puts sniffed.match
|
125
|
+
sniffed.after.register.each {|a| puts a unless a.nil? }
|
126
|
+
end
|
127
|
+
|
128
|
+
def dump_lines(snoopies, newline, line_no, is_verbose)
|
129
|
+
snoopies.each do |snoopie|
|
130
|
+
snoopie.sniffers.each do |sniffer|
|
131
|
+
sniffer.sniffed.each do |sniffed|
|
132
|
+
verbose snoopie.name, sniffed if is_verbose
|
133
|
+
line_no ? line_numbers(sniffed) : no_line_numbers(sniffed)
|
134
|
+
puts '' if newline
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def dump_json(snoopies, pretty=false)
|
141
|
+
if pretty
|
142
|
+
puts JSON.pretty_generate snoopies
|
143
|
+
else
|
144
|
+
puts snoopies.to_json
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if options[:tracking]
|
149
|
+
puts "File tracking database: #{options[:tracking_file]}"
|
150
|
+
else
|
151
|
+
options[:tracking_file] = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
unless File.exist? options[:snoopies_file]
|
155
|
+
puts "Snooper json file does not exist: #{options[:snoopies_file]}"
|
156
|
+
exit 1
|
157
|
+
end
|
158
|
+
|
159
|
+
#debug_file = File.open(File.expand_path('../../tmp/debug.log', __FILE__), 'w+')
|
160
|
+
#snooper = Snoopit::Snooper.new options[:notifications], options[:tracking_file], debug_file, ::Logger::DEBUG
|
161
|
+
#snooper = Snoopit::Snooper.new options[:notifications], options[:tracking_file], STDOUT, ::Logger::DEBUG
|
162
|
+
snooper = Snoopit::Snooper.new options[:notifications], options[:tracking_file]
|
163
|
+
snooper.load_file options[:snoopies_file]
|
164
|
+
snoopies = snooper.snoop options[:snoopies]
|
165
|
+
if options[:json]
|
166
|
+
dump_json snoopies, options[:pretty_json]
|
167
|
+
else
|
168
|
+
dump_lines snoopies, options[:new_line], options[:line_numbers], options[:verbose]
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
|
data/lib/snoopit.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'snoopit/version'
|
2
|
+
require 'snoopit/logger'
|
3
|
+
require 'snoopit/register'
|
4
|
+
require 'snoopit/snooper'
|
5
|
+
require 'snoopit/snoopy'
|
6
|
+
require 'snoopit/sniffer'
|
7
|
+
require 'snoopit/detected'
|
8
|
+
require 'snoopit/file_info'
|
9
|
+
require 'snoopit/file_tracker'
|
10
|
+
require 'snoopit/notification_manager'
|
11
|
+
require 'snoopit/notifier'
|
12
|
+
require 'snoopit/notifiers/email'
|
13
|
+
require 'snoopit/notifiers/http'
|
14
|
+
require 'snoopit/notifiers/https'
|
15
|
+
|
16
|
+
module Snoopit
|
17
|
+
|
18
|
+
def self.logger
|
19
|
+
Snoopit::Logging.logger
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Snoopit
|
2
|
+
class Detected
|
3
|
+
|
4
|
+
attr :comment, :before, :after, :after_count, :regexp, :match, :finished, :file, :line_no
|
5
|
+
|
6
|
+
def initialize(comment, pre_before, after, match, file, line_no)
|
7
|
+
setup_before pre_before
|
8
|
+
@comment = comment
|
9
|
+
@after_count = 0
|
10
|
+
@after = Register.new(after)
|
11
|
+
@match = match
|
12
|
+
@file = file
|
13
|
+
@line_no = line_no
|
14
|
+
@finished = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_before(pre_before)
|
18
|
+
@before = Register.new pre_before.size, pre_before.register
|
19
|
+
end
|
20
|
+
|
21
|
+
def track(line)
|
22
|
+
return if line == @match
|
23
|
+
if @after_count < @after.size
|
24
|
+
@after.push_front line
|
25
|
+
@after_count += 1
|
26
|
+
else
|
27
|
+
@finished = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def finished?
|
32
|
+
@finished
|
33
|
+
end
|
34
|
+
|
35
|
+
def as_json(options=nil)
|
36
|
+
{
|
37
|
+
after: @after,
|
38
|
+
match: @match,
|
39
|
+
before: @before,
|
40
|
+
file: @file,
|
41
|
+
line_no: @line_no
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_json(*a)
|
46
|
+
as_json.to_json(*a)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Snoopit
|
4
|
+
class FileInfo
|
5
|
+
|
6
|
+
attr_accessor :file, :line_no, :offset, :size, :mtime, :last_line, :init_stat
|
7
|
+
|
8
|
+
def initialize(file=nil)
|
9
|
+
@file = file
|
10
|
+
@line_no = 0
|
11
|
+
@offset = 0
|
12
|
+
@size = 0
|
13
|
+
@mtime = nil
|
14
|
+
@last_line = nil
|
15
|
+
@init_stat = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Update file Info if the file has changed use the file handle to move the file pointer
|
19
|
+
# to the character where reading will start
|
20
|
+
#
|
21
|
+
# @return [boolean] true if updated false not updated
|
22
|
+
def updated?(file_handle)
|
23
|
+
c_stat = File.stat @file
|
24
|
+
if (c_stat.size == @size) && (c_stat.mtime.to_i == @mtime.to_i) && (! @init_stat)
|
25
|
+
Snoopit.logger.debug 'FileTracker.updated? file has not changed: ' + @file
|
26
|
+
updated = false
|
27
|
+
elsif c_stat.size < @size
|
28
|
+
Snoopit.logger.debug 'FileTracker.updated? file size is smaller it is a new new file: ' + @file
|
29
|
+
updated = new_file? file_handle, c_stat
|
30
|
+
elsif (c_stat.size == @size) && (! @mtime.nil?) && (c_stat.mtime.to_i > @mtime.to_i)
|
31
|
+
Snoopit.logger.debug 'FileTracker.updated? file size is same but file time is newer it is a new file: ' + @file
|
32
|
+
updated = new_file? file_handle, c_stat
|
33
|
+
else
|
34
|
+
Snoopit.logger.debug 'FileTracker.updated? reading from last read location: ' + @file
|
35
|
+
updated = read_from_last? file_handle, c_stat
|
36
|
+
end
|
37
|
+
@init_stat = false
|
38
|
+
updated
|
39
|
+
end
|
40
|
+
|
41
|
+
def new_file?(file_handle, stat)
|
42
|
+
# seek to 0
|
43
|
+
Snoopit.logger.debug 'FileTracker.updated? file new read from start of file: ' + @file
|
44
|
+
@offset = 0
|
45
|
+
@size = stat.size
|
46
|
+
@mtime = stat.mtime
|
47
|
+
@last_line = nil
|
48
|
+
file_handle.seek 0, IO::SEEK_SET
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_from_last?(file_handle, stat)
|
53
|
+
# seek to last position + 1
|
54
|
+
old_size = @size
|
55
|
+
@size = stat.size
|
56
|
+
@mtime = stat.mtime
|
57
|
+
Snoopit.logger.debug "File pointer at byte: #{file_handle.tell}"
|
58
|
+
file_handle.seek old_size, IO::SEEK_SET
|
59
|
+
Snoopit.logger.debug "Starting read from byte: #{file_handle.tell} destination byte #{old_size} new size #{@size}"
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_last_line(file_handle)
|
64
|
+
line = nil
|
65
|
+
unless @last_line.nil?
|
66
|
+
Snoopit.logger.debug "File point at byte: #{file_handle.tell}"
|
67
|
+
file_handle.seek (-@last_line.bytesize), IO::SEEK_END
|
68
|
+
Snoopit.logger.debug "Seeked to byte: #{file_handle.tell}"
|
69
|
+
line = file_handle.readline
|
70
|
+
end
|
71
|
+
line
|
72
|
+
end
|
73
|
+
|
74
|
+
def as_json(*)
|
75
|
+
{
|
76
|
+
file: @file,
|
77
|
+
line_no: @line_no,
|
78
|
+
offset: @offset,
|
79
|
+
size: @size,
|
80
|
+
mtime: @mtime.iso8601,
|
81
|
+
last_line: @last_line
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_json(*args)
|
86
|
+
as_json.to_json(*args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def from_json(json_str)
|
90
|
+
from_hash JSON.parse(json_str)
|
91
|
+
end
|
92
|
+
|
93
|
+
def from_hash(hash)
|
94
|
+
@file = hash['file']
|
95
|
+
@line_no = hash['line_no']
|
96
|
+
@offset = hash['offset']
|
97
|
+
@size = hash['size']
|
98
|
+
@mtime = Time.parse hash['mtime']
|
99
|
+
@last_line = hash['last_line']
|
100
|
+
@init_stat = false
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Snoopit
|
2
|
+
class FileTracker
|
3
|
+
|
4
|
+
attr_accessor :files, :db_file
|
5
|
+
|
6
|
+
def initialize(db_file='./snooper_db.json', remove=false)
|
7
|
+
@files = { }
|
8
|
+
@db_file = db_file
|
9
|
+
if remove
|
10
|
+
remove_db @db_file
|
11
|
+
else
|
12
|
+
load_db @db_file
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def foreach(file, &block)
|
17
|
+
file_info = get_file(file)
|
18
|
+
unless file_info.nil?
|
19
|
+
read_lines(file_info, block)
|
20
|
+
save_db @db_file
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_file(file)
|
25
|
+
return nil unless File.exist? file
|
26
|
+
@files[file] = FileInfo.new(file) if @files[file].nil?
|
27
|
+
@files[file]
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_lines(file_info, block)
|
31
|
+
begin
|
32
|
+
fh = File.new(file_info.file)
|
33
|
+
if file_info.updated? fh
|
34
|
+
fh.each_line do |line|
|
35
|
+
file_info.offset += line.bytesize
|
36
|
+
file_info.line_no += 1
|
37
|
+
file_info.last_line = line
|
38
|
+
block.call line, file_info.line_no
|
39
|
+
end
|
40
|
+
end
|
41
|
+
ensure
|
42
|
+
fh.close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_db(db_file)
|
47
|
+
if (! db_file.nil?) && (File.exist? db_file)
|
48
|
+
Snoopit.logger.debug 'Loading from db file: ' + db_file
|
49
|
+
hash_db = JSON.parse(IO.read db_file)
|
50
|
+
hash_db.each do |key, file_info|
|
51
|
+
unless file_info.nil?
|
52
|
+
fi = FileInfo.new
|
53
|
+
fi.from_hash(file_info)
|
54
|
+
@files[key] = fi
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def save_db(db_file)
|
61
|
+
unless db_file.nil?
|
62
|
+
File.open db_file, 'w' do |f|
|
63
|
+
f.write @files.to_json
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_db(db_file)
|
69
|
+
if (! db_file.nil?) && (File.exist? db_file)
|
70
|
+
File.delete db_file
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def as_json(*)
|
75
|
+
{ files: @files }
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_json(*args)
|
79
|
+
as_json.to_json(*args)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'awesome_print'
|
3
|
+
|
4
|
+
module Snoopit
|
5
|
+
module Logging
|
6
|
+
|
7
|
+
LEVEL_MAP = {
|
8
|
+
info: ::Logger::INFO,
|
9
|
+
warn: ::Logger::WARN,
|
10
|
+
error: ::Logger::ERROR,
|
11
|
+
fatal: ::Logger::FATAL,
|
12
|
+
debug: ::Logger::DEBUG
|
13
|
+
}
|
14
|
+
|
15
|
+
def self.create_logger(out=STDOUT, level=::Logger::INFO)
|
16
|
+
@logger = ::Logger.new(out)
|
17
|
+
@logger.level = level
|
18
|
+
@logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.logger
|
22
|
+
@logger || create_logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def logger
|
26
|
+
Snoopit::Logging.logger
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|