hq-log-monitor-server 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "hq/systools/monitoring/log-monitor-server-script"
4
+
5
+ script = HQ::SysTools::Monitoring::LogMonitorServerScript.new
6
+ script.args = ARGV
7
+ script.main
8
+ exit script.status
@@ -0,0 +1,45 @@
1
+ @log-monitor-server
2
+ Feature: Log monitor server overview
3
+
4
+ Background:
5
+
6
+ Given the log monitor server config:
7
+ """
8
+ <log-monitor-server-config>
9
+ <server port="${port}"/>
10
+ <db host="${db-host}" port="${db-port}" name="${db-name}"/>
11
+ </log-monitor-server-config>
12
+ """
13
+
14
+ Scenario: No events
15
+
16
+ When I visit the overview page
17
+
18
+ Then I should see no summaries
19
+
20
+ Scenario: One summary
21
+
22
+ Given I submit the following events:
23
+ """
24
+ {
25
+ type: warning,
26
+ source: { class: class, host: host, service: service },
27
+ location: { file: logfile, line: 0 },
28
+ lines: { before: [], matching: WARNING blah, after: [] }
29
+ },
30
+ {
31
+ type: critical,
32
+ source: { class: class, host: host, service: service },
33
+ location: { file: logfile, line: 0 },
34
+ lines: { before: [], matching: CRITICAL blah, after: [] }
35
+ },
36
+ """
37
+
38
+ When I visit the overview page
39
+
40
+ Then I should see 1 summary
41
+ And the 1st summary should be:
42
+ | name | value |
43
+ | service | service |
44
+ | alerts | 2 |
45
+ | detail | 1 warning, 1 critical |
@@ -0,0 +1,41 @@
1
+ @log-monitor-server
2
+ Feature: Log monitor server submit event via HTTP
3
+
4
+ Background:
5
+
6
+ Given the log monitor server config:
7
+ """
8
+ <log-monitor-server-config>
9
+ <server port="${port}"/>
10
+ <db host="${db-host}" port="${db-port}" name="${db-name}"/>
11
+ </log-monitor-server-config>
12
+ """
13
+
14
+ Scenario: Submit event
15
+
16
+ When I submit the following event:
17
+ """
18
+ {
19
+ type: warning,
20
+ source: { class: class, host: host, service: service },
21
+ location: { file: logfile, line: 0 },
22
+ lines: {
23
+ before: [],
24
+ matching: WARNING blah,
25
+ after: [],
26
+ }
27
+ }
28
+ """
29
+
30
+ Then I should receive a 202 response
31
+ And the event should be in the database
32
+ And the summary should show:
33
+ """
34
+ {
35
+ _id: { class: class, host: host, service: service },
36
+ combined: { new: 1, total: 1 },
37
+ types: {
38
+ warning: { new: 1, total: 1 },
39
+ },
40
+ }
41
+ """
@@ -0,0 +1,59 @@
1
+ require "capybara/cucumber"
2
+ require "mongo"
3
+ require "net/http"
4
+
5
+ require "hq/systools/monitoring/log-monitor-server-script"
6
+
7
+ $token = (?a..?z).to_a.sample(10).join
8
+
9
+ # database stuff
10
+
11
+ $db_names = []
12
+
13
+ Before do
14
+ @db_names = []
15
+ end
16
+
17
+ def mongo_db_name name
18
+ if @db_names
19
+ @db_names << name unless @db_names.include? name
20
+ end
21
+ $db_names << name unless $db_names.include? name
22
+ "cuke_#{$token}_#{name}"
23
+ end
24
+
25
+ def mongo_conn
26
+ return $mongo if $mongo
27
+ $mongo = Mongo::MongoClient.new "localhost", 27017
28
+ return $mongo
29
+ end
30
+
31
+ def mongo_db name
32
+ mongo_conn[mongo_db_name name]
33
+ end
34
+
35
+ After do
36
+
37
+ @db_names.each do
38
+ |db_name|
39
+
40
+ mongo_db(db_name).collections.each do
41
+ |coll|
42
+ next if coll.name =~ /^system\./
43
+ coll.drop
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ at_exit do
51
+
52
+ $db_names.each do
53
+ |db_name|
54
+
55
+ mongo_conn.drop_database mongo_db_name(db_name)
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,139 @@
1
+ Given /^the log monitor server config:$/ do
2
+ |config_string|
3
+
4
+ # write config file
5
+
6
+ @log_monitor_server_port = 10000 + rand(55535)
7
+
8
+ @log_monitor_server_config =
9
+ Tempfile.new "cuke-log-monitor-server-"
10
+
11
+ config_string = config_string.clone
12
+
13
+ config_string.gsub! "${port}", @log_monitor_server_port.to_s
14
+ config_string.gsub! "${db-host}", "localhost"
15
+ config_string.gsub! "${db-port}", "27017"
16
+ config_string.gsub! "${db-name}", mongo_db_name("logMonitorServer")
17
+
18
+ @log_monitor_server_config.write config_string
19
+ @log_monitor_server_config.flush
20
+
21
+ @log_monitor_server_script =
22
+ HQ::SysTools::Monitoring::LogMonitorServerScript.new
23
+
24
+ @log_monitor_server_script.args = [
25
+ "--config",
26
+ @log_monitor_server_config.path,
27
+ "--quiet",
28
+ ]
29
+
30
+ @log_monitor_server_script.start
31
+
32
+ Capybara.app = @log_monitor_server_script
33
+
34
+ end
35
+
36
+ After do
37
+
38
+ @log_monitor_server_script.stop \
39
+ if @log_monitor_server_script
40
+
41
+ @log_monitor_server_config.unlink \
42
+ if @log_monitor_server_config
43
+
44
+ end
45
+
46
+ When /^I submit the following events?:$/ do
47
+ |event_string|
48
+
49
+ events_data = YAML.load "[#{event_string}]"
50
+
51
+ events_data.each do
52
+ |event_data|
53
+
54
+ event_json = MultiJson.dump event_data
55
+
56
+ Net::HTTP.start "localhost", @log_monitor_server_port do
57
+ |http|
58
+
59
+ request = Net::HTTP::Post.new "/submit-log-event"
60
+ request.body = event_json
61
+
62
+ @http_response = http.request request
63
+
64
+ end
65
+
66
+ end
67
+
68
+ @submitted_events = events_data
69
+
70
+ end
71
+
72
+ Then /^I should receive a (\d+) response$/ do
73
+ |response_code|
74
+ @http_response.code.should == response_code
75
+ end
76
+
77
+ Then /^the event should be in the database$/ do
78
+
79
+ db = mongo_db("logMonitorServer")
80
+
81
+ event = db["events"].find.first
82
+
83
+ event.should_not be_nil
84
+ event["timestamp"].should be_a Time
85
+
86
+ event.delete "_id"
87
+ event.delete "timestamp"
88
+
89
+ event.should == @submitted_events.first
90
+
91
+ end
92
+
93
+ Then /^the summary should show:$/ do
94
+ |expected_string|
95
+
96
+ expected_summary = YAML.load expected_string
97
+
98
+ db = mongo_db("logMonitorServer")
99
+
100
+ summary =
101
+ db["summaries"].find({
102
+ "_id" => expected_summary["_id"],
103
+ }).first
104
+
105
+ summary.should == expected_summary
106
+
107
+ end
108
+
109
+ # ui steps
110
+
111
+ When /^I visit the overview page$/ do
112
+ visit "/"
113
+ end
114
+
115
+ Then /^I should see no summaries$/ do
116
+ page.should have_content "No events have been logged"
117
+ end
118
+
119
+ Then /^I should see (\d+) summar(?:y|ies)$/ do
120
+ |count_str|
121
+ count = count_str.to_i
122
+ find("#summaries").should have_css(".summary", :count => count)
123
+ end
124
+
125
+ Then /^the (\d+(?:st|nd|rd|th)) summary should be:$/ do
126
+ |index_str, fields|
127
+
128
+ index = index_str.to_i
129
+
130
+ within "#summaries" do
131
+
132
+ fields.hashes.each do
133
+ |row|
134
+ find(".#{row["name"]}").text.should == row["value"]
135
+ end
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,396 @@
1
+ require "hq/tools/getopt"
2
+ require "net/http"
3
+ require "multi_json"
4
+ require "xml"
5
+
6
+ module HQ
7
+ module SysTools
8
+ module Monitoring
9
+ class LogMonitorClientScript
10
+
11
+ attr_accessor :args
12
+ attr_accessor :status
13
+
14
+ attr_accessor :stdout
15
+ attr_accessor :stderr
16
+
17
+ def main
18
+ process_args
19
+ read_config
20
+ read_cache
21
+ perform_checks
22
+ write_cache
23
+ end
24
+
25
+ def process_args
26
+
27
+ @opts, @args =
28
+ Tools::Getopt.process @args, [
29
+
30
+ { :name => :config,
31
+ :required => true },
32
+
33
+ ]
34
+
35
+ @args.empty? \
36
+ or raise "Extra args on command line"
37
+
38
+ end
39
+
40
+ def read_config
41
+
42
+ config_doc =
43
+ XML::Document.file @opts[:config]
44
+
45
+ @config_elem =
46
+ config_doc.root
47
+
48
+ @cache_elem =
49
+ @config_elem.find_first("cache")
50
+
51
+ @client_elem =
52
+ @config_elem.find_first("client")
53
+
54
+ @server_elem =
55
+ @config_elem.find_first("server")
56
+
57
+ @service_elems =
58
+ @config_elem.find("service").to_a
59
+
60
+ end
61
+
62
+ def read_cache
63
+
64
+ cache_path = @cache_elem["path"]
65
+
66
+ if File.exist? cache_path
67
+
68
+ @cache =
69
+ YAML.load File.read cache_path
70
+
71
+ else
72
+
73
+ @cache = {
74
+ files: {},
75
+ }
76
+
77
+ end
78
+
79
+ end
80
+
81
+ def write_cache
82
+
83
+ cache_path = @cache_elem["path"]
84
+ cache_temp_path = "#{cache_path}.new"
85
+
86
+ File.open cache_temp_path, "w" do
87
+ |cache_temp_io|
88
+
89
+ cache_temp_io.write YAML.dump @cache
90
+ cache_temp_io.fsync
91
+
92
+ end
93
+
94
+ File.rename cache_temp_path, cache_path
95
+
96
+ end
97
+
98
+ def perform_checks
99
+
100
+ @service_elems.each do
101
+ |service_elem|
102
+
103
+ fileset_elems = service_elem.find("fileset").to_a
104
+
105
+ fileset_elems.each do
106
+ |fileset_elem|
107
+
108
+ scan_elems = fileset_elem.find("scan").to_a
109
+ match_elems = fileset_elem.find("match").to_a
110
+
111
+ max_before =
112
+ match_elems.map {
113
+ |match_elem|
114
+ (match_elem["before"] || 0).to_i
115
+ }.max
116
+
117
+ max_after =
118
+ match_elems.map {
119
+ |match_elem|
120
+ (match_elem["after"] || 0).to_i
121
+ }.max
122
+
123
+ # find files
124
+
125
+ file_names =
126
+ scan_elems.map {
127
+ |scan_elem|
128
+ Dir[scan_elem["glob"]]
129
+ }.flatten
130
+
131
+ # scan files
132
+
133
+ file_names.each do
134
+ |file_name|
135
+
136
+ file_mtime = File.mtime file_name
137
+ file_size = File.size file_name
138
+
139
+ # fast check for modified files
140
+
141
+ cache_file = @cache[:files][file_name]
142
+
143
+ if cache_file &&
144
+ file_mtime == cache_file[:mtime] &&
145
+ file_size == cache_file[:size]
146
+ next
147
+ end
148
+
149
+ # scan the file for matching lines
150
+
151
+ mode = cache_file ? :scan : :report
152
+
153
+ File.open file_name, "r" do
154
+ |file_io|
155
+
156
+ file_reader =
157
+ ContextReader.new \
158
+ file_io,
159
+ max_before + max_after + 1
160
+
161
+ file_hash = 0
162
+
163
+ # check if the file has changed
164
+
165
+ if cache_file
166
+
167
+ if file_size < cache_file[:size]
168
+
169
+ changed = true
170
+
171
+ else
172
+
173
+ changed = false
174
+
175
+ cache_file[:lines].times do
176
+
177
+ line = file_reader.gets
178
+
179
+ unless line
180
+ changed = true
181
+ break
182
+ end
183
+
184
+ file_hash = [ file_hash, line.hash ].hash
185
+
186
+ end
187
+
188
+ if file_hash != cache_file[:hash]
189
+ changed = true
190
+ end
191
+
192
+ end
193
+
194
+ end
195
+
196
+ # go back to start if it changed
197
+
198
+ if changed
199
+ file_io.seek 0
200
+ file_reader.reset
201
+ file_hash = 0
202
+ end
203
+
204
+ # scan the new part of the file
205
+
206
+ while line = file_reader.gets
207
+
208
+ file_hash = [ file_hash, line.hash ].hash
209
+
210
+ # check for a match
211
+
212
+ match_elem =
213
+ match_elems.find {
214
+ |match_elem|
215
+ line =~ /#{match_elem["regex"]}/
216
+ }
217
+
218
+ # report the match
219
+
220
+ if match_elem
221
+
222
+ # get context
223
+
224
+ lines_before =
225
+ file_reader.lines_before \
226
+ (match_elem["before"] || 0).to_i + 1
227
+
228
+ lines_before.pop
229
+
230
+ lines_after =
231
+ file_reader.lines_after \
232
+ (match_elem["after"] || 0).to_i
233
+
234
+ # send event
235
+
236
+ submit_event({
237
+ type: match_elem["type"],
238
+ source: {
239
+ class: @client_elem["class"],
240
+ host: @client_elem["host"],
241
+ service: service_elem["name"],
242
+ },
243
+ location: {
244
+ file: file_name,
245
+ line: file_reader.last_line_number,
246
+ },
247
+ lines: {
248
+ before: lines_before,
249
+ matching: line,
250
+ after: lines_after,
251
+ },
252
+ })
253
+
254
+ end
255
+
256
+ end
257
+
258
+ # save the file's current info in the cache
259
+
260
+ @cache[:files][file_name] = {
261
+ mtime: file_mtime,
262
+ size: file_size,
263
+ lines: file_reader.next_line_number,
264
+ hash: file_hash,
265
+ }
266
+
267
+ end
268
+
269
+ end
270
+
271
+ end
272
+
273
+ end
274
+
275
+ end
276
+
277
+ def submit_event event
278
+
279
+ url =
280
+ URI.parse @server_elem["url"]
281
+
282
+ http =
283
+ Net::HTTP.new url.host, url.port
284
+
285
+ request =
286
+ Net::HTTP::Post.new url.path
287
+
288
+ request.body =
289
+ MultiJson.dump event
290
+
291
+ response =
292
+ http.request request
293
+
294
+ end
295
+
296
+ class ContextReader
297
+
298
+ def initialize source, buffer_size
299
+
300
+ @source = source
301
+ @buffer_size = buffer_size
302
+
303
+ @buffer = Array.new @buffer_size
304
+
305
+ reset
306
+
307
+ end
308
+
309
+ def lines_before_count
310
+ return @buffer_cursor - @buffer_start
311
+ end
312
+
313
+ def lines_after_count
314
+ return @buffer_end - @buffer_cursor
315
+ end
316
+
317
+ def lines_before count
318
+ count = [ count, lines_before_count ].min
319
+ return (0...count).map {
320
+ |i| @buffer[(@buffer_cursor - count + i) % @buffer_size]
321
+ }
322
+ end
323
+
324
+ def lines_after count
325
+ count = [ count, @buffer_size ].min
326
+ while lines_after_count < count
327
+ read_next_line or break
328
+ end
329
+ count = [ count, @buffer_end - @buffer_cursor].min
330
+ return (0...count).map {
331
+ |i| @buffer[(@buffer_cursor + i) % @buffer_size]
332
+ }
333
+ end
334
+
335
+ def read_next_line
336
+
337
+ # read a line
338
+
339
+ line = @source.gets
340
+ return false unless line
341
+
342
+ line.strip!
343
+ line.freeze
344
+
345
+ # shrink buffer if full
346
+
347
+ if @buffer_end - @buffer_start == @buffer_size
348
+ @buffer_start += 1
349
+ end
350
+
351
+ # add line to buffer
352
+
353
+ @buffer[@buffer_end % @buffer_size] = line
354
+ @buffer_end += 1
355
+
356
+ return true
357
+
358
+ end
359
+
360
+ def gets
361
+
362
+ # make sure the next line is in the buffer
363
+
364
+ if lines_after_count == 0
365
+ read_next_line or return nil
366
+ end
367
+
368
+ # return the line, advancing the cursor
369
+
370
+ ret = @buffer[@buffer_cursor % @buffer_size]
371
+ @buffer_cursor += 1
372
+ return ret
373
+
374
+ end
375
+
376
+ def last_line_number
377
+ raise "No last line" unless @buffer_cursor > 0
378
+ @buffer_cursor - 1
379
+ end
380
+
381
+ def next_line_number
382
+ @buffer_cursor
383
+ end
384
+
385
+ def reset
386
+ @buffer_start = 0
387
+ @buffer_cursor = 0
388
+ @buffer_end = 0
389
+ end
390
+
391
+ end
392
+
393
+ end
394
+ end
395
+ end
396
+ end
@@ -0,0 +1,299 @@
1
+ require "mongo"
2
+ require "multi_json"
3
+ require "rack"
4
+ require "webrick"
5
+ require "xml"
6
+
7
+ require "hq/tools/escape"
8
+ require "hq/tools/getopt"
9
+
10
+ module HQ
11
+ module SysTools
12
+ module Monitoring
13
+ class LogMonitorServerScript
14
+
15
+ include Tools::Escape
16
+
17
+ attr_accessor :args
18
+ attr_accessor :status
19
+
20
+ def initialize
21
+ @status = 0
22
+ end
23
+
24
+ def main
25
+ setup
26
+ trap "INT" do
27
+ @web_server.shutdown
28
+ end
29
+ run
30
+ end
31
+
32
+ def start
33
+ setup
34
+ Thread.new { run }
35
+ end
36
+
37
+ def stop
38
+ @web_server.shutdown
39
+ end
40
+
41
+ def setup
42
+ process_args
43
+ read_config
44
+ connect_db
45
+ init_server
46
+ end
47
+
48
+ def run
49
+ @web_server.start
50
+ end
51
+
52
+ def process_args
53
+
54
+ @opts, @args =
55
+ Tools::Getopt.process @args, [
56
+
57
+ { :name => :config,
58
+ :required => true },
59
+
60
+ { :name => :quiet,
61
+ :boolean => true },
62
+
63
+ ]
64
+
65
+ @args.empty? \
66
+ or raise "Extra args on command line"
67
+
68
+ end
69
+
70
+ def read_config
71
+
72
+ config_doc =
73
+ XML::Document.file @opts[:config]
74
+
75
+ @config_elem =
76
+ config_doc.root
77
+
78
+ @server_elem =
79
+ @config_elem.find_first("server")
80
+
81
+ @db_elem =
82
+ @config_elem.find_first("db")
83
+
84
+ end
85
+
86
+ def connect_db
87
+
88
+ @mongo =
89
+ Mongo::MongoClient.new \
90
+ @db_elem["host"],
91
+ @db_elem["port"].to_i
92
+
93
+ @db =
94
+ @mongo[@db_elem["name"]]
95
+
96
+ end
97
+
98
+ def init_server
99
+
100
+ @web_config = {
101
+ :Port => @server_elem["port"].to_i,
102
+ :AccessLog => [],
103
+ }
104
+
105
+ if @opts[:quiet]
106
+ @web_config.merge!({
107
+ :Logger => WEBrick::Log::new("/dev/null", 7),
108
+ :DoNotReverseLookup => true,
109
+ })
110
+ end
111
+
112
+ @web_server =
113
+ WEBrick::HTTPServer.new \
114
+ @web_config
115
+
116
+ @web_server.mount "/", Rack::Handler::WEBrick, self
117
+
118
+ end
119
+
120
+ def call env
121
+
122
+ case env["PATH_INFO"]
123
+
124
+ when "/submit-log-event"
125
+ submit_log_event env
126
+
127
+ when "/"
128
+ overview env
129
+
130
+ when "/favicon.ico"
131
+ [ 404, {}, [] ]
132
+
133
+ else
134
+ raise "Not found: #{env["PATH_INFO"]}"
135
+
136
+ end
137
+
138
+ end
139
+
140
+ def submit_log_event env
141
+
142
+ # decode it
143
+
144
+ event = MultiJson.load env["rack.input"].read
145
+
146
+ # add a timestamp
147
+
148
+ event["timestamp"] = Time.now
149
+
150
+ # insert it
151
+
152
+ @db["events"].insert event
153
+
154
+ # update summary
155
+
156
+ summary =
157
+ @db["summaries"].find({
158
+ "_id" => event["source"],
159
+ }).first
160
+
161
+ summary ||= {
162
+ "_id" => event["source"],
163
+ "combined" => { "new" => 0, "total" => 0 },
164
+ "types" => {},
165
+ }
166
+
167
+ summary["types"][event["type"]] ||=
168
+ { "new" => 0, "total" => 0 }
169
+
170
+ summary["types"][event["type"]]["new"] += 1
171
+ summary["types"][event["type"]]["total"] += 1
172
+
173
+ summary["combined"]["new"] += 1
174
+ summary["combined"]["total"] += 1
175
+
176
+ @db["summaries"].save summary
177
+
178
+ # respond successfully
179
+
180
+ return 202, {}, []
181
+
182
+ end
183
+
184
+ def overview env
185
+
186
+ summaries = @db["summaries"].find.to_a
187
+
188
+ headers = {}
189
+ html = []
190
+
191
+ headers["content-type"] = "text/html"
192
+
193
+ html << "<! DOCTYPE html>\n"
194
+ html << "<html>\n"
195
+ html << "<head>\n"
196
+
197
+ html << "<title>Overview - Log monitor</title>\n"
198
+
199
+ html << "</head>\n"
200
+ html << "<body>\n"
201
+
202
+ html << "<h1>Overview - Log monitor</h1>\n"
203
+
204
+ if summaries.empty?
205
+ html << "<p>No events have been logged</p>\n"
206
+ else
207
+
208
+ html << "<table id=\"summaries\">\n"
209
+ html << "<thead>\n"
210
+
211
+ html << "<tr>\n"
212
+ html << "<th>Service</th>\n"
213
+ html << "<th>Alerts</th>\n"
214
+ html << "<th>Details</th>\n"
215
+ html << "<th>More</th>\n"
216
+ html << "</tr>\n"
217
+
218
+ html << "</thead>\n"
219
+ html << "<tbody>\n"
220
+
221
+ summaries.each do
222
+ |summary|
223
+
224
+ html << "<tr class=\"summary\">\n"
225
+
226
+ html << "<td class=\"service\">%s</td>\n" % [
227
+ esc_ht(summary["_id"]["service"]),
228
+ ]
229
+
230
+ html << "<td class=\"alerts\">%s</td>\n" % [
231
+ esc_ht(summary["combined"]["new"].to_s),
232
+ ]
233
+
234
+ html << "<td class=\"detail\">%s</td>\n" % [
235
+ esc_ht(
236
+ summary["types"].map {
237
+ |type, counts|
238
+ "%s %s" % [ counts["new"], type ]
239
+ }.join ", "
240
+ ),
241
+ ]
242
+
243
+ html << "<td class=\"more\">%s</td>\n" % [
244
+ "<a href=\"%s\">more...</a>" % [
245
+ "/service/%s" % [
246
+ esc_ue(summary["_id"]["service"]),
247
+ ],
248
+ ],
249
+ ]
250
+
251
+ html << "</tr>\n"
252
+
253
+ end
254
+
255
+ html << "</tbody>\n"
256
+ html << "</table>\n"
257
+
258
+ end
259
+
260
+ html << "</body>\n"
261
+ html << "</html>\n"
262
+
263
+ return 200, headers, html
264
+
265
+ end
266
+
267
+ def sf format, *args
268
+
269
+ ret = []
270
+
271
+ format.scan(/%.|%%|[^%]+|%/).each do
272
+ |match|
273
+
274
+ case match
275
+
276
+ when ?%
277
+ raise "Error"
278
+
279
+ when "%%"
280
+ ret << ?%
281
+
282
+ when /^%(.)$/
283
+ ret << send("format_#{$1}", args.shift)
284
+
285
+ else
286
+ ret << match
287
+
288
+ end
289
+
290
+ end
291
+
292
+ return ret.join
293
+
294
+ end
295
+
296
+ end
297
+ end
298
+ end
299
+ end
metadata ADDED
@@ -0,0 +1,249 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hq-log-monitor-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James Pharaoh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bson_ext
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.8.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.8.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: hq-tools
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.2.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.2.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: libxml-ruby
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 2.6.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.6.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: mongo
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.8.2
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.8.2
78
+ - !ruby/object:Gem::Dependency
79
+ name: multi_json
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 1.6.1
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 1.6.1
94
+ - !ruby/object:Gem::Dependency
95
+ name: rack
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 1.5.1
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.5.1
110
+ - !ruby/object:Gem::Dependency
111
+ name: capybara
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: 2.0.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: 2.0.2
126
+ - !ruby/object:Gem::Dependency
127
+ name: cucumber
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: 1.2.1
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: 1.2.1
142
+ - !ruby/object:Gem::Dependency
143
+ name: rake
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: 10.0.3
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: 10.0.3
158
+ - !ruby/object:Gem::Dependency
159
+ name: rspec
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: 2.12.0
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: 2.12.0
174
+ - !ruby/object:Gem::Dependency
175
+ name: rspec_junit_formatter
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: simplecov
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ description: HQ log monitor system, central component
207
+ email:
208
+ - james@phsys.co.uk
209
+ executables:
210
+ - hq-log-monitor-server
211
+ extensions: []
212
+ extra_rdoc_files: []
213
+ files:
214
+ - lib/hq/systools/monitoring/log-monitor-client-script.rb
215
+ - lib/hq/systools/monitoring/log-monitor-server-script.rb
216
+ - features/submit-event.feature
217
+ - features/overview.feature
218
+ - features/support/steps.rb
219
+ - features/support/env.rb
220
+ - bin/hq-log-monitor-server
221
+ homepage: https://github.com/jamespharaoh/hq-log-monitor-server
222
+ licenses: []
223
+ post_install_message:
224
+ rdoc_options: []
225
+ require_paths:
226
+ - lib
227
+ required_ruby_version: !ruby/object:Gem::Requirement
228
+ none: false
229
+ requirements:
230
+ - - ! '>='
231
+ - !ruby/object:Gem::Version
232
+ version: '0'
233
+ required_rubygems_version: !ruby/object:Gem::Requirement
234
+ none: false
235
+ requirements:
236
+ - - ! '>='
237
+ - !ruby/object:Gem::Version
238
+ version: 1.3.6
239
+ requirements: []
240
+ rubyforge_project: hq-log-monitor-server
241
+ rubygems_version: 1.8.23
242
+ signing_key:
243
+ specification_version: 3
244
+ summary: HQ log monitor server
245
+ test_files:
246
+ - features/submit-event.feature
247
+ - features/overview.feature
248
+ - features/support/steps.rb
249
+ - features/support/env.rb