logeater 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee9771f19fbb6a41d6a072d2f1a1fb8280cdcce7
4
- data.tar.gz: 2b82746a2c79e407ef544a1472ad31826a9f2587
3
+ metadata.gz: 678d8cc0af848644fd37cf1762d97a9d7779bd5a
4
+ data.tar.gz: 0ac115076cd9daedc7b74d4f01dd04e2d63e56a6
5
5
  SHA512:
6
- metadata.gz: da4a22896028c127870e05217d3c9125dc73ddc9e28b00afa4dea52d8dca980240ace54dba9f1aaf08d0a628aff861e423957f5080b5774a627a30db7653cdea
7
- data.tar.gz: d12c10c9df5a1a26fc1bcc36adfc8fb276046c43ef23bfc469057de9c72f8e7759305c53f576678667ecdaf3a452019289001f7cf9b78b53246122a2b8993f7c
6
+ metadata.gz: e3d70df2e56453afba0d382252555801e17e8d7c2ec7d83da83dc147e91bc61f6fd8dc4127bfaadf9c2d71bcdce561cd8c09403a5cc83a129443031ed3ff1545
7
+ data.tar.gz: 0085e7c733283ecb91ec507a5de05bb53bd2a2d1b4a63a23431cfb750505cd489e2425a63772da3f4f9cb92a274743551ac0a7c112cc0570d7f3c5813cc3272c
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
3
  /Gemfile.lock
4
+ /.ruby-version
4
5
  /_yardoc/
5
6
  /coverage/
6
7
  /doc/
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
- git clone git@github.com:concordia-publishing-house/logeater.git
18
- bundle
16
+ ###### Importing log files
19
17
 
20
- Create the development database
18
+ gem install logeater
19
+ DATABASE_URL=<production database url> logeater import <app name> <path to logs>/*.gz
21
20
 
22
- bundle exec rake db:create db:migrate
21
+ ###### Converting log files to JSON
23
22
 
24
- Install the gem
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
@@ -0,0 +1,7 @@
1
+ class AddIndexes < ActiveRecord::Migration
2
+ def change
3
+ add_index :requests, :completed_at
4
+ add_index :requests, :subdomain
5
+ add_index :requests, :params, using: :gin
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ class DropHttpResponse < ActiveRecord::Migration
2
+ def up
3
+ remove_column :requests, :http_response
4
+ end
5
+
6
+ def down
7
+ add_column :requests, :http_response, :string
8
+ end
9
+ end
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: 20150602022241) do
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", limit: 255, null: false
21
- t.string "logfile", limit: 255, null: false
22
- t.string "uuid", limit: 255, null: false
23
- t.string "subdomain", limit: 255
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", limit: 255
27
+ t.string "http_method"
28
28
  t.text "path"
29
29
  t.jsonb "params"
30
- t.string "controller", limit: 255
31
- t.string "action", limit: 255
32
- t.string "remote_ip", limit: 255
33
- t.string "format", limit: 255
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.string "http_response", limit: 255
36
- t.datetime "created_at", null: false
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
- finished_count - started_count,
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
@@ -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
@@ -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, :http_response, :duration)
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
@@ -1,3 +1,3 @@
1
1
  module Logeater
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
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.0"
27
+ spec.add_dependency "activerecord-import", "~> 0.10"
28
28
  spec.add_dependency "thor", "~> 0.19.1"
29
- spec.add_dependency "oj", "~> 2.12.14"
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
@@ -18,13 +18,13 @@ DatabaseCleaner.clean
18
18
  DatabaseCleaner.strategy = :transaction
19
19
 
20
20
  class ActiveSupport::TestCase
21
-
21
+
22
22
  setup do
23
23
  DatabaseCleaner.start
24
24
  end
25
-
25
+
26
26
  teardown do
27
27
  DatabaseCleaner.clean
28
28
  end
29
-
29
+
30
30
  end
@@ -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
@@ -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.2.4
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: 2015-10-03 00:00:00.000000000 Z
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.0
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.0
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.12.14
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.12.14
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.2.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