logeater 0.2.4 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +7 -20
- data/db/migrate/20150110151439_create_requests.rb +3 -3
- data/db/migrate/20160218230227_add_indexes.rb +7 -0
- data/db/migrate/20160502131002_drop_http_response.rb +9 -0
- data/db/schema.rb +15 -13
- data/lib/logeater/cli.rb +2 -4
- data/lib/logeater/params_parser.rb +16 -16
- data/lib/logeater/parser.rb +30 -30
- data/lib/logeater/parser_errors.rb +5 -5
- data/lib/logeater/reader.rb +34 -30
- data/lib/logeater/version.rb +1 -1
- data/logeater.gemspec +2 -2
- data/test/integration/logeater_test.rb +16 -17
- data/test/test_helper.rb +3 -3
- data/test/unit/params_parser_test.rb +28 -28
- data/test/unit/parser_test.rb +68 -68
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 678d8cc0af848644fd37cf1762d97a9d7779bd5a
|
4
|
+
data.tar.gz: 0ac115076cd9daedc7b74d4f01dd04e2d63e56a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3d70df2e56453afba0d382252555801e17e8d7c2ec7d83da83dc147e91bc61f6fd8dc4127bfaadf9c2d71bcdce561cd8c09403a5cc83a129443031ed3ff1545
|
7
|
+
data.tar.gz: 0085e7c733283ecb91ec507a5de05bb53bd2a2d1b4a63a23431cfb750505cd489e2425a63772da3f4f9cb92a274743551ac0a7c112cc0570d7f3c5813cc3272c
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Logeater
|
2
2
|
|
3
|
-
Parses log files and imports them into a database
|
3
|
+
Parses log files and imports them into a database or converts them to json
|
4
4
|
|
5
5
|
### Features
|
6
6
|
|
@@ -12,30 +12,17 @@ Parses log files and imports them into a database
|
|
12
12
|
|
13
13
|
### Usage
|
14
14
|
|
15
|
-
Clone the gem
|
16
15
|
|
17
|
-
|
18
|
-
bundle
|
16
|
+
###### Importing log files
|
19
17
|
|
20
|
-
|
18
|
+
gem install logeater
|
19
|
+
DATABASE_URL=<production database url> logeater import <app name> <path to logs>/*.gz
|
21
20
|
|
22
|
-
|
21
|
+
###### Converting log files to JSON
|
23
22
|
|
24
|
-
|
23
|
+
gem install logeater
|
24
|
+
logeater parse <app name> <path to logs>/*.gz > parsed-logs.json
|
25
25
|
|
26
|
-
bundle exec rake install
|
27
|
-
|
28
|
-
Import log files
|
29
|
-
|
30
|
-
logeater my_app ~/Desktop/logs/*.gz
|
31
|
-
|
32
|
-
|
33
|
-
### To Do
|
34
|
-
|
35
|
-
- Set up databases without cloning the gem?
|
36
|
-
- Import to a [Heroku Postgres database](https://dashboard.heroku.com/apps/logs-production)?
|
37
|
-
- Parse other kinds of logs?
|
38
|
-
- Collect other data from Rails logs?
|
39
26
|
|
40
27
|
|
41
28
|
### Contributing
|
@@ -3,7 +3,7 @@ class CreateRequests < ActiveRecord::Migration
|
|
3
3
|
create_table :requests do |t|
|
4
4
|
t.string :app, null: false
|
5
5
|
t.string :logfile, null: false
|
6
|
-
|
6
|
+
|
7
7
|
t.string :uuid, null: false
|
8
8
|
t.string :subdomain
|
9
9
|
t.timestamp :started_at
|
@@ -18,10 +18,10 @@ class CreateRequests < ActiveRecord::Migration
|
|
18
18
|
t.string :format
|
19
19
|
t.integer :http_status
|
20
20
|
t.string :http_response
|
21
|
-
|
21
|
+
|
22
22
|
t.timestamps
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
add_index :requests, :app
|
26
26
|
add_index :requests, :logfile
|
27
27
|
add_index :requests, :uuid, unique: true
|
data/db/schema.rb
CHANGED
@@ -11,38 +11,40 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20160502131002) do
|
15
15
|
|
16
16
|
# These are extensions that must be enabled in order to support this database
|
17
17
|
enable_extension "plpgsql"
|
18
18
|
|
19
19
|
create_table "requests", force: :cascade do |t|
|
20
|
-
t.string "app",
|
21
|
-
t.string "logfile",
|
22
|
-
t.string "uuid",
|
23
|
-
t.string "subdomain"
|
20
|
+
t.string "app", null: false
|
21
|
+
t.string "logfile", null: false
|
22
|
+
t.string "uuid", null: false
|
23
|
+
t.string "subdomain"
|
24
24
|
t.datetime "started_at"
|
25
25
|
t.datetime "completed_at"
|
26
26
|
t.integer "duration"
|
27
|
-
t.string "http_method"
|
27
|
+
t.string "http_method"
|
28
28
|
t.text "path"
|
29
29
|
t.jsonb "params"
|
30
|
-
t.string "controller"
|
31
|
-
t.string "action"
|
32
|
-
t.string "remote_ip"
|
33
|
-
t.string "format"
|
30
|
+
t.string "controller"
|
31
|
+
t.string "action"
|
32
|
+
t.string "remote_ip"
|
33
|
+
t.string "format"
|
34
34
|
t.integer "http_status"
|
35
|
-
t.
|
36
|
-
t.datetime "
|
37
|
-
t.datetime "updated_at", null: false
|
35
|
+
t.datetime "created_at"
|
36
|
+
t.datetime "updated_at"
|
38
37
|
t.integer "user_id"
|
39
38
|
t.boolean "tester_bar"
|
40
39
|
end
|
41
40
|
|
42
41
|
add_index "requests", ["app"], name: "index_requests_on_app", using: :btree
|
42
|
+
add_index "requests", ["completed_at"], name: "index_requests_on_completed_at", using: :btree
|
43
43
|
add_index "requests", ["controller", "action"], name: "index_requests_on_controller_and_action", using: :btree
|
44
44
|
add_index "requests", ["http_status"], name: "index_requests_on_http_status", using: :btree
|
45
45
|
add_index "requests", ["logfile"], name: "index_requests_on_logfile", using: :btree
|
46
|
+
add_index "requests", ["params"], name: "index_requests_on_params", using: :gin
|
47
|
+
add_index "requests", ["subdomain"], name: "index_requests_on_subdomain", using: :btree
|
46
48
|
add_index "requests", ["uuid"], name: "index_requests_on_uuid", unique: true, using: :btree
|
47
49
|
|
48
50
|
end
|
data/lib/logeater/cli.rb
CHANGED
@@ -57,14 +57,12 @@ module Logeater
|
|
57
57
|
reader = Logeater::Reader.new(app, file, options.slice(:progress, :verbose))
|
58
58
|
reader.remove_existing_entries!
|
59
59
|
|
60
|
-
started_count = Logeater::Request.count
|
61
60
|
started_at = Time.now
|
62
|
-
reader.import
|
61
|
+
count = reader.import
|
63
62
|
finished_at = Time.now
|
64
|
-
finished_count = Logeater::Request.count
|
65
63
|
|
66
64
|
$stderr.puts " > \e[34mImported \e[1m%d\e[0;34m requests in \e[1m%.2f\e[0;34m seconds (%d of %d)\e[0m\n\n" % [
|
67
|
-
|
65
|
+
count,
|
68
66
|
finished_at - started_at,
|
69
67
|
i + 1,
|
70
68
|
files.length ]
|
@@ -3,15 +3,15 @@ require "ripper"
|
|
3
3
|
module Logeater
|
4
4
|
class ParamsParser
|
5
5
|
attr_reader :params
|
6
|
-
|
6
|
+
|
7
7
|
def initialize(params)
|
8
8
|
@params = params
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def parse!
|
12
12
|
identify tokenize_hash(clean(params))
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def clean(params)
|
16
16
|
loop do
|
17
17
|
result = params.gsub(/\#<((?:[\w]|::)+):[^<>]+>/) { "\"#{$1}\"" }
|
@@ -20,46 +20,46 @@ module Logeater
|
|
20
20
|
end
|
21
21
|
params
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
private
|
25
|
-
|
25
|
+
|
26
26
|
def tokenize_hash(ruby)
|
27
27
|
sexp = Ripper.sexp(ruby)
|
28
28
|
raise Parser::MalformedParameters.new(ruby) unless sexp
|
29
|
-
|
29
|
+
|
30
30
|
# [:program, [[:hash, ... ]]]
|
31
31
|
sexp[1][0]
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def identify(sexp)
|
35
35
|
case sexp[0]
|
36
|
-
|
36
|
+
|
37
37
|
# [:string_literal, [:string_content, [:@tstring_content, "utf8", [1, 2]]]]
|
38
38
|
# [:string_literal, [:string_content]]
|
39
39
|
when :string_literal then sexp[1][1] ? sexp[1][1][1] : ""
|
40
|
-
|
40
|
+
|
41
41
|
# [:@int, "10", [1, 14]]
|
42
42
|
when :@int then sexp[1].to_i
|
43
|
-
|
43
|
+
|
44
44
|
# [:@float, "10.56", [1, 14]]
|
45
45
|
when :@float then sexp[1].to_f
|
46
|
-
|
46
|
+
|
47
47
|
# [:unary, :-@, [:@float, \\\"173.41\\\", [1, 17285]]]
|
48
48
|
when :unary then
|
49
49
|
return -identify(sexp[2]) if sexp[1] == :-@
|
50
50
|
raise Parser::ParserNotImplemented, "Unknown unary operator: #{sexp[1].inspect}"
|
51
|
-
|
51
|
+
|
52
52
|
# [:var_ref, [:@kw, "true", [1, 12]]]
|
53
53
|
when :var_ref then
|
54
54
|
return true if sexp[1][1] == "true"
|
55
55
|
return false if sexp[1][1] == "false"
|
56
56
|
return nil if sexp[1][1] == "nil"
|
57
57
|
raise Parser::ParserNotImplemented, "Unknown variable: #{sexp[1].inspect}"
|
58
|
-
|
58
|
+
|
59
59
|
# [:array, [[:@int, "1", [1, 9]], [:@int, "4", [1, 12]]]]
|
60
60
|
# [:array, nil]
|
61
61
|
when :array then sexp[1] ? sexp[1].map { |sexp| identify(sexp) } : []
|
62
|
-
|
62
|
+
|
63
63
|
# [:hash,
|
64
64
|
# [:assoclist_from_args,
|
65
65
|
# [[:assoc_new,
|
@@ -67,7 +67,7 @@ module Logeater
|
|
67
67
|
# [:string_literal, [:string_content, [:@tstring_content, "✓", [1, 12]]]]]]]]]
|
68
68
|
# [:hash, nil]
|
69
69
|
when :hash then sexp[1] ? sexp[1][1].each_with_object({}) { |(_, key, value), hash| hash[identify(key)] = identify(value) } : {}
|
70
|
-
|
70
|
+
|
71
71
|
else
|
72
72
|
raise Parser::ParserNotImplemented, "I don't know how to identify #{sexp.inspect}"
|
73
73
|
nil
|
@@ -76,6 +76,6 @@ module Logeater
|
|
76
76
|
raise Parser::ParserNotImplemented, "An exception occurred when parsing #{sexp.inspect}\n#{$!.class.name}: #{$!.message}"
|
77
77
|
nil
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
end
|
81
81
|
end
|
data/lib/logeater/parser.rb
CHANGED
@@ -5,52 +5,52 @@ require "logeater/parser_errors"
|
|
5
5
|
|
6
6
|
module Logeater
|
7
7
|
class Parser
|
8
|
-
|
9
|
-
|
8
|
+
|
9
|
+
|
10
10
|
LINE_MATCHER = /^
|
11
11
|
[A-Z],\s
|
12
12
|
\[(?<timestamp>[^\s\]]+)(?:\s[^\]]*)?\]\s+
|
13
13
|
(?<log_level>[A-Z]+)\s+\-\-\s:\s+
|
14
14
|
(?<message>.*)
|
15
15
|
$/x.freeze
|
16
|
-
|
16
|
+
|
17
17
|
REQUEST_LINE_MATCHER = /^
|
18
18
|
\[(?<subdomain>[^\]]+)\]\s
|
19
19
|
\[(?<uuid>[\w\-]{36})\]\s+
|
20
20
|
(?:\[(?:guest|user\.(?<user_id>\d+)(?<tester_bar>:cph)?)\]\s+)?
|
21
21
|
(?<message>.*)
|
22
22
|
$/x.freeze
|
23
|
-
|
23
|
+
|
24
24
|
REQUEST_STARTED_MATCHER = /^
|
25
25
|
Started\s
|
26
26
|
(?<http_method>[A-Z]+)\s
|
27
27
|
"(?<path>[^"]+)"\sfor\s
|
28
28
|
(?<remote_ip>[\d\.]+)
|
29
29
|
/x.freeze
|
30
|
-
|
30
|
+
|
31
31
|
REQUEST_CONTROLLER_MATCHER = /^
|
32
32
|
Processing\sby\s
|
33
33
|
(?<controller>[A-Za-z0-9:]+)\#
|
34
34
|
(?<action>[a-z_0-9]+)\sas\s
|
35
35
|
(?<format>.*)
|
36
36
|
/x.freeze
|
37
|
-
|
37
|
+
|
38
38
|
REQUEST_PARAMETERS_MATCHER = /^
|
39
39
|
Parameters:\s
|
40
40
|
(?<params>\{.*\})
|
41
41
|
$/x.freeze
|
42
|
-
|
42
|
+
|
43
43
|
REQUEST_COMPLETED_MATCHER = /^
|
44
44
|
Completed\s
|
45
45
|
(?<http_status>\d\d\d)\s
|
46
46
|
(?:(?<http_response>.*)\s)?in\s
|
47
47
|
(?<duration>[\d\.]+)(?<units>ms)\b
|
48
48
|
/x.freeze # optional: (Views: 0.1ms | ActiveRecord: 50.0ms)
|
49
|
-
|
49
|
+
|
50
50
|
def parse!(line)
|
51
51
|
match = line.match LINE_MATCHER
|
52
52
|
raise UnmatchedLine.new(line) unless match
|
53
|
-
|
53
|
+
|
54
54
|
result = {
|
55
55
|
type: :generic,
|
56
56
|
timestamp: match["timestamp"],
|
@@ -59,13 +59,13 @@ module Logeater
|
|
59
59
|
}.merge(
|
60
60
|
parse_message(match["message"]))
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
def parse_message(message)
|
64
64
|
match = message.match REQUEST_LINE_MATCHER
|
65
65
|
return {} unless match
|
66
|
-
|
66
|
+
|
67
67
|
message = match["message"]
|
68
|
-
|
68
|
+
|
69
69
|
{ subdomain: match["subdomain"],
|
70
70
|
uuid: match["uuid"],
|
71
71
|
type: :request_line,
|
@@ -75,37 +75,37 @@ module Logeater
|
|
75
75
|
}.merge(
|
76
76
|
parse_message_extra(message))
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
def parse_message_extra(message)
|
80
80
|
match = message.match(REQUEST_STARTED_MATCHER)
|
81
81
|
return parse_request_started_message(match) if match
|
82
|
-
|
82
|
+
|
83
83
|
match = message.match(REQUEST_CONTROLLER_MATCHER)
|
84
84
|
return parse_request_controller_message(match) if match
|
85
|
-
|
85
|
+
|
86
86
|
match = message.match(REQUEST_PARAMETERS_MATCHER)
|
87
87
|
return parse_request_params_message(match) if match
|
88
|
-
|
88
|
+
|
89
89
|
match = message.match(REQUEST_COMPLETED_MATCHER)
|
90
90
|
return parse_request_completed_message(match) if match
|
91
|
-
|
91
|
+
|
92
92
|
{}
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
def parse_request_started_message(match)
|
96
96
|
{ type: :request_started,
|
97
97
|
http_method: match["http_method"],
|
98
98
|
path: parsed_uri[match["path"]],
|
99
99
|
remote_ip: match["remote_ip"] }
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
def parse_request_controller_message(match)
|
103
103
|
{ type: :request_controller,
|
104
104
|
controller: normalized_controller_name[match["controller"]],
|
105
105
|
action: match["action"],
|
106
106
|
format: match["format"] }
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
def parse_request_params_message(match)
|
110
110
|
{ type: :request_params,
|
111
111
|
params: ParamsParser.new(match["params"]).parse! }
|
@@ -113,35 +113,35 @@ module Logeater
|
|
113
113
|
log "Unable to parse parameters: #{match["params"].inspect}"
|
114
114
|
{ params: match["params"] }
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
def parse_request_completed_message(match)
|
118
118
|
{ type: :request_completed,
|
119
119
|
http_status: match["http_status"].to_i,
|
120
120
|
http_response: match["http_response"],
|
121
121
|
duration: match["duration"].to_i }
|
122
122
|
end
|
123
|
-
|
124
|
-
|
125
|
-
|
123
|
+
|
124
|
+
|
125
|
+
|
126
126
|
def log(statement)
|
127
127
|
$stderr.puts "\e[33m#{statement}\e[0m"
|
128
128
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
|
130
|
+
|
131
|
+
|
132
132
|
def initialize
|
133
133
|
@normalized_controller_name = Hash.new do |hash, controller_name|
|
134
134
|
hash[controller_name] = controller_name.underscore.gsub(/_controller$/, "")
|
135
135
|
end
|
136
|
-
|
136
|
+
|
137
137
|
@parsed_uri = Hash.new do |hash, uri|
|
138
138
|
hash[uri] = Addressable::URI.parse(uri).path
|
139
139
|
end
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
private
|
143
143
|
attr_reader :normalized_controller_name,
|
144
144
|
:parsed_uri
|
145
|
-
|
145
|
+
|
146
146
|
end
|
147
147
|
end
|
@@ -1,30 +1,30 @@
|
|
1
1
|
module Logeater
|
2
2
|
class Parser
|
3
|
-
|
3
|
+
|
4
4
|
class Error < ::ArgumentError
|
5
5
|
def initialize(message, input)
|
6
6
|
super "#{message}: #{input.inspect}"
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
class UnmatchedLine < Error
|
11
11
|
def initialize(input)
|
12
12
|
super "Unmatched line", input
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
class MalformedTimestamp < Error
|
17
17
|
def initialize(input)
|
18
18
|
super "Malformed timestamp", input
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
class MalformedParameters < Error
|
23
23
|
def initialize(input)
|
24
24
|
super "Malformed parameters", input
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
class ParserNotImplemented < Error
|
29
29
|
def initialize(input)
|
30
30
|
super "Unable to parse", input
|
data/lib/logeater/reader.rb
CHANGED
@@ -6,7 +6,7 @@ require "oj"
|
|
6
6
|
module Logeater
|
7
7
|
class Reader
|
8
8
|
attr_reader :app, :path, :filename, :batch_size
|
9
|
-
|
9
|
+
|
10
10
|
def initialize(app, path, options={})
|
11
11
|
@app = app
|
12
12
|
@path = path
|
@@ -15,25 +15,28 @@ module Logeater
|
|
15
15
|
@show_progress = options.fetch :progress, false
|
16
16
|
@batch_size = options.fetch :batch_size, 500
|
17
17
|
@verbose = options.fetch :verbose, false
|
18
|
+
@count = 0
|
18
19
|
@requests = {}
|
19
20
|
@completed_requests = []
|
20
21
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
|
23
|
+
|
24
|
+
|
24
25
|
def reimport
|
25
26
|
remove_existing_entries!
|
26
27
|
import
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
def import
|
31
|
+
@count = 0
|
30
32
|
each_request do |attributes|
|
31
33
|
completed_requests.push Logeater::Request.new(attributes)
|
32
34
|
save! if completed_requests.length >= batch_size
|
33
35
|
end
|
34
36
|
save!
|
37
|
+
@count
|
35
38
|
end
|
36
|
-
|
39
|
+
|
37
40
|
def parse(to: $stdout)
|
38
41
|
to << "["
|
39
42
|
first = true
|
@@ -49,19 +52,19 @@ module Logeater
|
|
49
52
|
ensure
|
50
53
|
to << "]"
|
51
54
|
end
|
52
|
-
|
55
|
+
|
53
56
|
def remove_existing_entries!
|
54
57
|
Logeater::Request.where(app: app, logfile: filename).delete_all
|
55
58
|
end
|
56
|
-
|
59
|
+
|
57
60
|
def show_progress?
|
58
61
|
@show_progress
|
59
62
|
end
|
60
|
-
|
63
|
+
|
61
64
|
def verbose?
|
62
65
|
@verbose
|
63
66
|
end
|
64
|
-
|
67
|
+
|
65
68
|
def each_line
|
66
69
|
File.open(path) do |file|
|
67
70
|
io = File.extname(path) == ".gz" ? Zlib::GzipReader.new(file) : file
|
@@ -74,7 +77,7 @@ module Logeater
|
|
74
77
|
end
|
75
78
|
end
|
76
79
|
alias :scan :each_line
|
77
|
-
|
80
|
+
|
78
81
|
def each_request
|
79
82
|
count = 0
|
80
83
|
each_line do |line|
|
@@ -85,63 +88,64 @@ module Logeater
|
|
85
88
|
end
|
86
89
|
count
|
87
90
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
+
|
92
|
+
|
93
|
+
|
91
94
|
private
|
92
95
|
attr_reader :parser, :requests, :completed_requests
|
93
|
-
|
96
|
+
|
94
97
|
def process_line!(line, &block)
|
95
98
|
attributes = parser.parse!(line)
|
96
|
-
|
99
|
+
|
97
100
|
return if [:generic, :request_line].member? attributes[:type]
|
98
|
-
|
101
|
+
|
99
102
|
if attributes[:type] == :request_started
|
100
103
|
requests[attributes[:uuid]] = attributes
|
101
104
|
.slice(:uuid, :subdomain, :http_method, :path, :remote_ip, :user_id, :tester_bar)
|
102
105
|
.merge(started_at: attributes[:timestamp], logfile: filename, app: app)
|
103
106
|
return
|
104
107
|
end
|
105
|
-
|
108
|
+
|
106
109
|
request_attributes = requests[attributes[:uuid]]
|
107
110
|
unless request_attributes
|
108
111
|
log "Attempting to record #{attributes[:type].inspect}; but there is no request started with UUID #{attributes[:uuid].inspect}" if verbose?
|
109
112
|
return
|
110
113
|
end
|
111
|
-
|
114
|
+
|
112
115
|
case attributes[:type]
|
113
116
|
when :request_controller
|
114
117
|
request_attributes.merge! attributes.slice(:controller, :action, :format)
|
115
|
-
|
118
|
+
|
116
119
|
when :request_params
|
117
120
|
request_attributes.merge! attributes.slice(:params)
|
118
|
-
|
121
|
+
|
119
122
|
when :request_completed
|
120
123
|
request_attributes.merge! attributes
|
121
|
-
.slice(:http_status, :
|
124
|
+
.slice(:http_status, :duration)
|
122
125
|
.merge(completed_at: attributes[:timestamp])
|
123
|
-
|
126
|
+
|
124
127
|
yield request_attributes
|
125
128
|
requests.delete attributes[:uuid]
|
126
129
|
end
|
127
|
-
|
130
|
+
|
128
131
|
rescue Logeater::Parser::UnmatchedLine
|
129
132
|
$stderr.puts "\e[90m#{$!.message}\e[0m" if verbose?
|
130
133
|
rescue Logeater::Parser::Error
|
131
134
|
log $!.message
|
132
135
|
end
|
133
|
-
|
136
|
+
|
134
137
|
def save!
|
135
138
|
return if completed_requests.empty?
|
136
|
-
Logeater::Request.import(completed_requests)
|
139
|
+
result = Logeater::Request.import(completed_requests)
|
140
|
+
@count += result.num_inserts
|
137
141
|
completed_requests.clear
|
138
142
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
143
|
+
|
144
|
+
|
145
|
+
|
142
146
|
def log(statement)
|
143
147
|
$stderr.puts "\e[33m#{statement}\e[0m"
|
144
148
|
end
|
145
|
-
|
149
|
+
|
146
150
|
end
|
147
151
|
end
|
data/lib/logeater/version.rb
CHANGED
data/logeater.gemspec
CHANGED
@@ -24,9 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency "standalone_migrations", "~> 4.0.0"
|
25
25
|
spec.add_dependency "addressable"
|
26
26
|
spec.add_dependency "ruby-progressbar"
|
27
|
-
spec.add_dependency "activerecord-import", "~> 0.10
|
27
|
+
spec.add_dependency "activerecord-import", "~> 0.10"
|
28
28
|
spec.add_dependency "thor", "~> 0.19.1"
|
29
|
-
spec.add_dependency "oj", "~> 2.
|
29
|
+
spec.add_dependency "oj", "~> 2.15"
|
30
30
|
|
31
31
|
spec.add_development_dependency "bundler", "~> 1.7"
|
32
32
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -2,27 +2,27 @@ require "test_helper"
|
|
2
2
|
|
3
3
|
class LogeaterTest < ActiveSupport::TestCase
|
4
4
|
attr_reader :logfile
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
|
7
7
|
context "Given the log of a single request, it" do
|
8
8
|
setup do
|
9
9
|
@logfile = File.expand_path("../../data/single_request.log", __FILE__)
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
should "identify the name of the logfile" do
|
13
13
|
assert_equal "single_request.log", reader.filename
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
should "create an entry in the database" do
|
17
17
|
assert_difference "Logeater::Request.count", +1 do
|
18
18
|
reader.import
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
should "set all the attributes" do
|
23
23
|
reader.import
|
24
24
|
request = Logeater::Request.first
|
25
|
-
|
25
|
+
|
26
26
|
params = {"refresh_page" => "true", "id" => "1035826228"}
|
27
27
|
assert_equal "test", request.app
|
28
28
|
assert_equal "single_request.log", request.logfile
|
@@ -39,42 +39,41 @@ class LogeaterTest < ActiveSupport::TestCase
|
|
39
39
|
assert_equal "71.218.222.249", request.remote_ip
|
40
40
|
assert_equal "JS", request.format
|
41
41
|
assert_equal 200, request.http_status
|
42
|
-
assert_equal "OK", request.http_response
|
43
42
|
end
|
44
|
-
|
43
|
+
|
45
44
|
should "erase any entries that had already been imported with that app and filename" do
|
46
45
|
Logeater::Request.create!(app: app, logfile: "single_request.log", uuid: "1")
|
47
46
|
Logeater::Request.create!(app: app, logfile: "single_request.log", uuid: "2")
|
48
47
|
Logeater::Request.create!(app: app, logfile: "single_request.log", uuid: "3")
|
49
|
-
|
48
|
+
|
50
49
|
assert_difference "Logeater::Request.count", -2 do
|
51
50
|
reader.reimport
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
55
|
-
|
56
|
-
|
54
|
+
|
55
|
+
|
57
56
|
context "Given a gzipped logfile, it" do
|
58
57
|
setup do
|
59
58
|
@logfile = File.expand_path("../../data/single_request.gz", __FILE__)
|
60
59
|
end
|
61
|
-
|
60
|
+
|
62
61
|
should "create an entry in the database" do
|
63
62
|
assert_difference "Logeater::Request.count", +1 do
|
64
63
|
reader.import
|
65
64
|
end
|
66
65
|
end
|
67
66
|
end
|
68
|
-
|
69
|
-
|
67
|
+
|
68
|
+
|
70
69
|
private
|
71
|
-
|
70
|
+
|
72
71
|
def app
|
73
72
|
"test"
|
74
73
|
end
|
75
|
-
|
74
|
+
|
76
75
|
def reader
|
77
76
|
Logeater::Reader.new(app, logfile)
|
78
77
|
end
|
79
|
-
|
78
|
+
|
80
79
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,83 +1,83 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
3
|
class ParamsParserTest < ActiveSupport::TestCase
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
|
6
6
|
context "Given a simple hash, it" do
|
7
7
|
should "parse it" do
|
8
8
|
assert_parses '{"utf8"=>"✓"}' => {"utf8"=>"✓"}
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
should "handle integers" do
|
12
12
|
assert_parses '{"person_id"=>10}' => {"person_id"=>10}
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
should "handle floats" do
|
16
16
|
assert_parses '{"person_id"=>10.56}' => {"person_id"=>10.56}
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
should "handle negatives" do
|
20
20
|
assert_parses '{"person_id"=>-10}' => {"person_id"=>-10}
|
21
21
|
assert_parses '{"person_id"=>-10.56}' => {"person_id"=>-10.56}
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
should "handle booleans" do
|
25
25
|
assert_parses '{"visible"=>true}' => {"visible"=>true}
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
should "handle nil" do
|
29
29
|
assert_parses '{"visible"=>nil}' => {"visible"=>nil}
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
should "handle arrays" do
|
33
33
|
assert_parses '{"ids"=>[1, 4]}' => {"ids"=>[1,4]}
|
34
34
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
|
36
|
+
|
37
|
+
|
38
38
|
should "handle empty strings" do
|
39
39
|
assert_parses '{"visible"=>""}' => {"visible"=>""}
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
should "handle empty arrays" do
|
43
43
|
assert_parses '{"array"=>[]}' => {"array"=>[]}
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
should "handle empty hashes" do
|
47
47
|
assert_parses '{"hash"=>{}}' => {"hash"=>{}}
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
51
|
-
|
50
|
+
|
51
|
+
|
52
52
|
context "Given a hash with more than one key, it" do
|
53
53
|
should "parse it" do
|
54
54
|
assert_parses '{"utf8"=>"✓", "authenticity_token"=>"kDM07..."}' => {"utf8"=>"✓", "authenticity_token"=>"kDM07..."}
|
55
55
|
end
|
56
56
|
end
|
57
|
-
|
58
|
-
|
57
|
+
|
58
|
+
|
59
59
|
context "Given a hash with a nested hash, it" do
|
60
60
|
should "handle nested hashes" do
|
61
61
|
assert_parses '{"person"=>{"name"=>"Tim"}}' => {"person"=>{"name"=>"Tim"}}
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
should "handle arrays of nested hashes" do
|
65
65
|
assert_parses '{"people"=>[{"id"=>1},{"id"=>2}]}' => {"people"=>[{"id"=>1},{"id"=>2}]}
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
69
|
-
|
68
|
+
|
69
|
+
|
70
70
|
context "Given a hash with a serialized Ruby object, it" do
|
71
71
|
should "parse it" do
|
72
72
|
assert_parses '{"tempfile"=>#<Tempfile:/tmp/RackMultipart20141213-1847-1c8fpzw>}' => {"tempfile"=>"Tempfile"}
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
should "ignore the object's ivars" do
|
76
76
|
assert_parses '{"photo"=>#<ActionDispatch::Http::UploadedFile:0x007f7c685318a8 @tempfile=#<Tempfile:/tmp/RackMultipart20141213-1847-1c8fpzw>, @original_filename="Martin-008.JPG", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"person[photo]\"; filename=\"Martin-008.JPG\"\\r\\nContent-Type: image/jpeg\\r\\n\">}' => {"photo"=>"ActionDispatch::Http::UploadedFile"}
|
77
77
|
end
|
78
78
|
end
|
79
|
-
|
80
|
-
|
79
|
+
|
80
|
+
|
81
81
|
context "Given an invalid input, it" do
|
82
82
|
should "raise MalformedParameters" do
|
83
83
|
assert_raises Logeater::Parser::MalformedParameters do
|
@@ -85,17 +85,17 @@ class ParamsParserTest < ActiveSupport::TestCase
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
89
|
-
|
88
|
+
|
89
|
+
|
90
90
|
private
|
91
|
-
|
91
|
+
|
92
92
|
def assert_parses(params)
|
93
93
|
value, result = params.to_a[0]
|
94
94
|
assert_equal result, parse!(value)
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
def parse!(value)
|
98
98
|
Logeater::ParamsParser.new(value).parse!
|
99
99
|
end
|
100
|
-
|
100
|
+
|
101
101
|
end
|
data/test/unit/parser_test.rb
CHANGED
@@ -2,247 +2,247 @@ require "test_helper"
|
|
2
2
|
|
3
3
|
class ParserTest < ActiveSupport::TestCase
|
4
4
|
attr_reader :line
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
|
6
|
+
|
7
|
+
|
8
8
|
context "given a line that doesn't start with the generic log prefix, it" do
|
9
9
|
setup do
|
10
10
|
@line = "\n"
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
should "raise UnmatchedLine" do
|
14
14
|
assert_raises Logeater::Parser::UnmatchedLine do
|
15
15
|
parse!
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
|
20
|
+
|
21
|
+
|
22
22
|
context "given any generic log line, it" do
|
23
23
|
setup do
|
24
24
|
@line = "I, [2015-01-10T15:18:05.850839 #18070] INFO -- : the message\n"
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
should "identify the line as :generic" do
|
28
28
|
assert_parses type: :generic
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
should "identify the log level" do
|
32
32
|
assert_parses log_level: "INFO"
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
should "identify the time, including milliseconds" do
|
36
36
|
assert_parses timestamp: "2015-01-10T15:18:05.850839"
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
should "identify the remainder of the log message" do
|
40
40
|
assert_parses message: "the message"
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
|
44
|
+
|
45
|
+
|
46
46
|
context "given a log line for a Rails request, it" do
|
47
47
|
setup do
|
48
48
|
@line = "I, [2015-01-10T15:18:05.850839 #18070] INFO -- : [livingsaviorco] [2d89d962-57c4-47c9-a9e9-6a16a5f22a12] [gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
should "identify the line as :generic" do
|
52
52
|
assert_parses type: :request_line
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
should "identify the subdomain" do
|
56
56
|
assert_parses subdomain: "livingsaviorco"
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
should "identify the request's ID" do
|
60
60
|
assert_parses uuid: "2d89d962-57c4-47c9-a9e9-6a16a5f22a12"
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
should "identify the remainder of the log message" do
|
64
64
|
assert_parses message: "[gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
|
68
|
+
|
69
|
+
|
70
70
|
context "given a log line for a Rails request" do
|
71
71
|
context "that indicates the current user (who was logged in with the tester bar), it" do
|
72
72
|
setup do
|
73
73
|
@line = "I, [2015-01-10T15:18:05.850839 #18070] INFO -- : [livingsaviorco] [2d89d962-57c4-47c9-a9e9-6a16a5f22a12] [user.902544074:cph] [gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
should "identify the user's id" do
|
77
77
|
assert_parses user_id: 902544074
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
should "notice that the user was logged-in with the tester bar" do
|
81
81
|
assert_parses tester_bar: true
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
should "identify the remainder of the log message" do
|
85
85
|
assert_parses message: "[gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
context "that indicates the current user (who was not logged in with the tester bar), it" do
|
90
90
|
setup do
|
91
91
|
@line = "I, [2015-01-10T15:18:05.850839 #18070] INFO -- : [livingsaviorco] [2d89d962-57c4-47c9-a9e9-6a16a5f22a12] [user.902544074] [gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
should "identify the user's id" do
|
95
95
|
assert_parses user_id: 902544074
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
should "notice that the user was not logged-in with the tester bar" do
|
99
99
|
assert_parses tester_bar: false
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
should "identify the remainder of the log message" do
|
103
103
|
assert_parses message: "[gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
104
104
|
end
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
context "that indicates that the user is logged out, it" do
|
108
108
|
setup do
|
109
109
|
@line = "I, [2015-01-10T15:18:05.850839 #18070] INFO -- : [livingsaviorco] [2d89d962-57c4-47c9-a9e9-6a16a5f22a12] [guest] [gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
should "identify the remainder of the log message" do
|
113
113
|
assert_parses message: "[gzip] Compress reponse by 42.2 KB (83.3%) (1.4ms)"
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
|
118
|
+
|
119
|
+
|
120
120
|
context "given the \"Started\" line, it" do
|
121
121
|
setup do
|
122
122
|
@line = "I, [2015-01-10T15:18:12.064392 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Started GET \"/people/1035826228?refresh_page=true\" for 71.218.222.249 at 2015-01-10 15:18:12 +0000"
|
123
123
|
end
|
124
|
-
|
124
|
+
|
125
125
|
should "identify the line as :request_started" do
|
126
126
|
assert_parses type: :request_started
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
should "identify the HTTP method" do
|
130
130
|
assert_parses http_method: "GET"
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
133
|
should "identify the path (without params)" do
|
134
134
|
assert_parses path: "/people/1035826228"
|
135
135
|
end
|
136
|
-
|
136
|
+
|
137
137
|
should "identify the remote client's IP address" do
|
138
138
|
assert_parses remote_ip: "71.218.222.249"
|
139
139
|
end
|
140
140
|
end
|
141
|
-
|
142
|
-
|
143
|
-
|
141
|
+
|
142
|
+
|
143
|
+
|
144
144
|
context "given the \"Processing by\" line, it" do
|
145
145
|
setup do
|
146
146
|
@line = "I, [2015-01-10T15:18:12.067034 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Processing by Api::V1::PeopleController#show as JS"
|
147
147
|
end
|
148
|
-
|
148
|
+
|
149
149
|
should "identify the line as :request_controller" do
|
150
150
|
assert_parses type: :request_controller
|
151
151
|
end
|
152
|
-
|
152
|
+
|
153
153
|
should "identify the controller and action" do
|
154
154
|
assert_parses controller: "api/v1/people", action: "show"
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
157
|
should "identify the format requested" do
|
158
158
|
assert_parses format: "JS"
|
159
159
|
end
|
160
160
|
end
|
161
|
-
|
162
|
-
|
163
|
-
|
161
|
+
|
162
|
+
|
163
|
+
|
164
164
|
context "given the \"Parameters\" line, it" do
|
165
165
|
setup do
|
166
166
|
@line = "I, [2015-01-10T15:18:12.067134 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Parameters: {\"refresh_page\"=>\"true\", \"id\"=>\"1035826228\"}"
|
167
167
|
end
|
168
|
-
|
168
|
+
|
169
169
|
should "identify the line as :request_params" do
|
170
170
|
assert_parses type: :request_params
|
171
171
|
end
|
172
|
-
|
172
|
+
|
173
173
|
should "identify the params" do
|
174
174
|
assert_parses params: {"refresh_page" => "true", "id" => "1035826228"}
|
175
175
|
end
|
176
176
|
end
|
177
|
-
|
177
|
+
|
178
178
|
context "when the \"Parameters\" line contains invalid syntax, it" do
|
179
179
|
setup do
|
180
180
|
@line = "I, [2015-01-10T15:18:12.067134 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Parameters: {\"refresh_page\"=>}"
|
181
181
|
end
|
182
|
-
|
182
|
+
|
183
183
|
should "return the params unparsed" do
|
184
184
|
assert_parses params: "{\"refresh_page\"=>}"
|
185
185
|
end
|
186
186
|
end
|
187
|
-
|
188
|
-
|
189
|
-
|
187
|
+
|
188
|
+
|
189
|
+
|
190
190
|
context "given the \"Completed\" line, it" do
|
191
191
|
setup do
|
192
192
|
@line = "I, [2015-01-10T15:18:12.262903 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Completed 401 Unauthorized in 2ms"
|
193
193
|
end
|
194
|
-
|
194
|
+
|
195
195
|
should "identify the line as :request_completed" do
|
196
196
|
assert_parses type: :request_completed
|
197
197
|
end
|
198
|
-
|
198
|
+
|
199
199
|
should "identify the HTTP response" do
|
200
200
|
assert_parses http_status: 401, http_response: "Unauthorized"
|
201
201
|
end
|
202
|
-
|
202
|
+
|
203
203
|
should "identify the duration of the request" do
|
204
204
|
assert_parses duration: 2
|
205
205
|
end
|
206
206
|
end
|
207
|
-
|
207
|
+
|
208
208
|
context "when the \"Completed\" line doesn't have a textual description of the status, it" do
|
209
209
|
setup do
|
210
210
|
@line = "I, [2015-01-10T15:18:12.262903 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Completed 307 in 0.8ms"
|
211
211
|
end
|
212
|
-
|
212
|
+
|
213
213
|
should "be cool with that" do
|
214
214
|
assert_parses http_status: 307, http_response: nil
|
215
215
|
end
|
216
216
|
end
|
217
|
-
|
217
|
+
|
218
218
|
context "when the \"Completed\" line contains a breakdown of times, it" do
|
219
219
|
setup do
|
220
220
|
@line = "I, [2015-01-10T15:18:12.262903 #2354] INFO -- : [livingsaviorco] [0fc5154a-c288-4bad-9c7a-de3d7e7d2496] Completed 200 OK in 196ms (Views: 0.1ms | ActiveRecord: 50.0ms)"
|
221
221
|
end
|
222
|
-
|
222
|
+
|
223
223
|
should "identify the duration of the request" do
|
224
224
|
assert_parses duration: 196
|
225
225
|
end
|
226
226
|
end
|
227
|
-
|
228
|
-
|
229
|
-
|
227
|
+
|
228
|
+
|
229
|
+
|
230
230
|
private
|
231
|
-
|
231
|
+
|
232
232
|
def assert_parses(expectations)
|
233
233
|
results = parse!
|
234
|
-
|
234
|
+
|
235
235
|
expectations.each do |key, value|
|
236
236
|
assert_equal value, results[key]
|
237
237
|
end
|
238
238
|
end
|
239
|
-
|
239
|
+
|
240
240
|
def parse!
|
241
241
|
parser.parse!(line)
|
242
242
|
end
|
243
|
-
|
243
|
+
|
244
244
|
def parser
|
245
245
|
Logeater::Parser.new
|
246
246
|
end
|
247
|
-
|
247
|
+
|
248
248
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logeater
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Lail
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.10
|
103
|
+
version: '0.10'
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.10
|
110
|
+
version: '0.10'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: thor
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 2.
|
131
|
+
version: '2.15'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 2.
|
138
|
+
version: '2.15'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: bundler
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,6 +241,8 @@ files:
|
|
241
241
|
- db/migrate/20150207183757_change_requests_params_to_jsonb.rb
|
242
242
|
- db/migrate/20150224021844_change_requests_path_to_text.rb
|
243
243
|
- db/migrate/20150602022241_add_user_id_and_tester_bar_to_requests.rb
|
244
|
+
- db/migrate/20160218230227_add_indexes.rb
|
245
|
+
- db/migrate/20160502131002_drop_http_response.rb
|
244
246
|
- db/schema.rb
|
245
247
|
- lib/logeater.rb
|
246
248
|
- lib/logeater/cli.rb
|
@@ -277,7 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
277
279
|
version: '0'
|
278
280
|
requirements: []
|
279
281
|
rubyforge_project:
|
280
|
-
rubygems_version: 2.
|
282
|
+
rubygems_version: 2.5.1
|
281
283
|
signing_key:
|
282
284
|
specification_version: 4
|
283
285
|
summary: Parses log files and imports them into a database
|