rails_log_parser 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ecf14cd343a96269393660a0f6daa54bfac43cf45c694840400b295c106b145
4
- data.tar.gz: 8690da0687f22e6ee16ecc1cee247c943fb928e28a973b72e45ff141a3792ff8
3
+ metadata.gz: fe25438fe1aa9e1fe506de50b0d337e26c387648f01aefa8fc558db5e1914bab
4
+ data.tar.gz: 9ec5eb524baba92586f71dff16fdd31243b5b4a7e68d767a11fb01ec2f40bd3f
5
5
  SHA512:
6
- metadata.gz: 0ea42a77488c2151e04ef87a0c8d1c8788a3cb1367e8193a67df696622f3319ebdd9d42aa1213d7c4fac7fb00e0a6906401794e36c3e96644e080a80743c3c89
7
- data.tar.gz: a43c0c02ce22d35386217c36b7cab9af0cfacde931e6f85931289ab0f890623a6d49b3836695735d7ed4c587efc46708c170052685ffdb1d1a232559c436fe12
6
+ metadata.gz: 6cb369807f23be8de3ad39d3a616e000d374cd5bb7591b48394c12b38f61b5685fe464bb5712e35d20523089bbd0e69f8e090df7581985df1f30124ab5ba685b
7
+ data.tar.gz: 5386f801caf9f7a259c5dbc6061793300c6d0347528511f4e0974fe1f64450fd90b92d900730d136f2ce92b8285f9e9c20cdefd9885884e355eae8c0b2ebfcce
data/Gemfile.lock CHANGED
@@ -1,27 +1,108 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails_log_parser (0.0.13)
4
+ rails_log_parser (0.0.16)
5
5
  enumerize (~> 2.4)
6
6
  json (>= 2.0)
7
+ rails (>= 2.1)
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
11
- activesupport (6.1.7.3)
12
+ actioncable (5.2.5)
13
+ actionpack (= 5.2.5)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ actionmailer (5.2.5)
17
+ actionpack (= 5.2.5)
18
+ actionview (= 5.2.5)
19
+ activejob (= 5.2.5)
20
+ mail (~> 2.5, >= 2.5.4)
21
+ rails-dom-testing (~> 2.0)
22
+ actionpack (5.2.5)
23
+ actionview (= 5.2.5)
24
+ activesupport (= 5.2.5)
25
+ rack (~> 2.0, >= 2.0.8)
26
+ rack-test (>= 0.6.3)
27
+ rails-dom-testing (~> 2.0)
28
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
+ actionview (5.2.5)
30
+ activesupport (= 5.2.5)
31
+ builder (~> 3.1)
32
+ erubi (~> 1.4)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
35
+ activejob (5.2.5)
36
+ activesupport (= 5.2.5)
37
+ globalid (>= 0.3.6)
38
+ activemodel (5.2.5)
39
+ activesupport (= 5.2.5)
40
+ activerecord (5.2.5)
41
+ activemodel (= 5.2.5)
42
+ activesupport (= 5.2.5)
43
+ arel (>= 9.0)
44
+ activestorage (5.2.5)
45
+ actionpack (= 5.2.5)
46
+ activerecord (= 5.2.5)
47
+ marcel (~> 1.0.0)
48
+ activesupport (5.2.5)
12
49
  concurrent-ruby (~> 1.0, >= 1.0.2)
13
- i18n (>= 1.6, < 2)
14
- minitest (>= 5.1)
15
- tzinfo (~> 2.0)
16
- zeitwerk (~> 2.3)
50
+ i18n (>= 0.7, < 2)
51
+ minitest (~> 5.1)
52
+ tzinfo (~> 1.1)
53
+ arel (9.0.0)
54
+ builder (3.2.4)
17
55
  concurrent-ruby (1.2.2)
56
+ crass (1.0.6)
18
57
  diff-lcs (1.4.4)
19
58
  enumerize (2.5.0)
20
59
  activesupport (>= 3.2)
60
+ erubi (1.10.0)
61
+ globalid (0.4.2)
62
+ activesupport (>= 4.2.0)
21
63
  i18n (1.12.0)
22
64
  concurrent-ruby (~> 1.0)
23
65
  json (2.6.3)
66
+ loofah (2.9.1)
67
+ crass (~> 1.0.2)
68
+ nokogiri (>= 1.5.9)
69
+ mail (2.7.1)
70
+ mini_mime (>= 0.1.1)
71
+ marcel (1.0.1)
72
+ method_source (1.0.0)
73
+ mini_mime (1.1.0)
74
+ mini_portile2 (2.4.0)
24
75
  minitest (5.18.0)
76
+ nio4r (2.5.7)
77
+ nokogiri (1.10.10)
78
+ mini_portile2 (~> 2.4.0)
79
+ rack (2.2.3)
80
+ rack-test (1.1.0)
81
+ rack (>= 1.0, < 3)
82
+ rails (5.2.5)
83
+ actioncable (= 5.2.5)
84
+ actionmailer (= 5.2.5)
85
+ actionpack (= 5.2.5)
86
+ actionview (= 5.2.5)
87
+ activejob (= 5.2.5)
88
+ activemodel (= 5.2.5)
89
+ activerecord (= 5.2.5)
90
+ activestorage (= 5.2.5)
91
+ activesupport (= 5.2.5)
92
+ bundler (>= 1.3.0)
93
+ railties (= 5.2.5)
94
+ sprockets-rails (>= 2.0.0)
95
+ rails-dom-testing (2.0.3)
96
+ activesupport (>= 4.2.0)
97
+ nokogiri (>= 1.6)
98
+ rails-html-sanitizer (1.3.0)
99
+ loofah (~> 2.3)
100
+ railties (5.2.5)
101
+ actionpack (= 5.2.5)
102
+ activesupport (= 5.2.5)
103
+ method_source
104
+ rake (>= 0.8.7)
105
+ thor (>= 0.19.0, < 2.0)
25
106
  rake (12.3.3)
26
107
  rspec (3.10.0)
27
108
  rspec-core (~> 3.10.0)
@@ -36,9 +117,20 @@ GEM
36
117
  diff-lcs (>= 1.2.0, < 2.0)
37
118
  rspec-support (~> 3.10.0)
38
119
  rspec-support (3.10.3)
39
- tzinfo (2.0.6)
120
+ sprockets (4.0.2)
40
121
  concurrent-ruby (~> 1.0)
41
- zeitwerk (2.6.7)
122
+ rack (> 1, < 3)
123
+ sprockets-rails (3.2.2)
124
+ actionpack (>= 4.0)
125
+ activesupport (>= 4.0)
126
+ sprockets (>= 3.0.0)
127
+ thor (1.1.0)
128
+ thread_safe (0.3.6)
129
+ tzinfo (1.2.9)
130
+ thread_safe (~> 0.1)
131
+ websocket-driver (0.7.3)
132
+ websocket-extensions (>= 0.1.0)
133
+ websocket-extensions (0.1.5)
42
134
 
43
135
  PLATFORMS
44
136
  ruby
data/README.md CHANGED
@@ -31,7 +31,6 @@ Call the rake tasks in cronjobs:
31
31
  ```
32
32
  LOG_PATH=/srv/rails/log/production.log
33
33
  0,20,40 * * * * rake rails_log_parser:parse[22]' # summary of the last 22 minutes
34
- 59 23 * * * rake rails_log_parser:parse[22,true]' # summary of the last 22 minutes and save and analyse heuristic
35
34
  ```
36
35
 
37
36
  Or use it in your code:
@@ -43,12 +42,33 @@ puts parser.actions.select(&:fatal?).map(&:headline)
43
42
 
44
43
  ```ruby
45
44
  parser = RailsLogParser::Parser.from_file(log_path)
46
- parser.enable_heuristic(File.dirname(log_path)) # path to save heuristic stats
47
45
  print parser.summary(last_minutes: 22) # print summary for the last 22 minutes
48
46
  ```
49
47
 
48
+ You can configure filters:
49
+
50
+ ```ruby
51
+ # config/rails_log_parser.rb
52
+
53
+ RailsLogParser.configure do |parser|
54
+ parser.ignore_lines = [
55
+ /Error performing MailerConfigurationJob .+ Net::ReadTimeout/,
56
+ ]
57
+ end
58
+ ```
59
+
60
+
50
61
  ## Changelog
51
62
 
63
+ ### 0.0.16
64
+
65
+ * Remove heuristic
66
+ * Fix last action handling
67
+
68
+ ### 0.0.15
69
+
70
+ * Filter lines by config file
71
+
52
72
  ### 0.0.14
53
73
 
54
74
  * Better empty lines handling
@@ -4,10 +4,6 @@ require 'time'
4
4
  require 'securerandom'
5
5
 
6
6
  class RailsLogParser::Action
7
- class << self
8
- attr_accessor :last
9
- end
10
-
11
7
  SEVERITIES = %i[debug info warn error fatal].freeze
12
8
  KNOWN_EXCEPTIONS = {
13
9
  "Can't verify CSRF token authenticity." => :warn,
@@ -28,7 +24,6 @@ class RailsLogParser::Action
28
24
  @id = id
29
25
  @messages = []
30
26
  @stacktrace = []
31
- self.class.last = self
32
27
  end
33
28
 
34
29
  def severity=(value)
@@ -45,6 +40,12 @@ class RailsLogParser::Action
45
40
  end
46
41
  end
47
42
 
43
+ def ignore?
44
+ @messages.any? do |message|
45
+ RailsLogParser.ignore_lines.any? {|ignore| message.match?(ignore) }
46
+ end
47
+ end
48
+
48
49
  def headline
49
50
  @headline.presence || @messages.first
50
51
  end
@@ -5,7 +5,7 @@ class RailsLogParser::Parser
5
5
  attr_writer :log_path
6
6
 
7
7
  def log_path
8
- @log_path || ENV['LOG_PATH']
8
+ @log_path || ENV['LOG_PATH'] || raise('no log_path given')
9
9
  end
10
10
 
11
11
  def from_file(path)
@@ -19,18 +19,16 @@ class RailsLogParser::Parser
19
19
  end
20
20
  end
21
21
 
22
- attr_reader :not_parseable_lines
22
+ attr_reader :not_parseable_lines, :last_action
23
23
 
24
24
  def initialize
25
+ config_file = File.join(Dir.pwd,'config/rails_log_parser.rb')
26
+ require config_file if File.file?(config_file)
27
+
25
28
  @actions = {}
26
29
  @not_parseable_lines = RailsLogParser::NotParseableLines.new
27
- @heuristic = nil
28
30
  end
29
31
 
30
- def enable_heuristic(path)
31
- @heuristic = path
32
- @heuristic_today = RailsLogParser::HeuristicStatFile.new(@heuristic, Date.today).tap { |p| p.write_stats(actions) }
33
- end
34
32
 
35
33
  def summary(last_minutes: nil)
36
34
  relevant = actions
@@ -47,7 +45,7 @@ class RailsLogParser::Parser
47
45
  end
48
46
 
49
47
  %i[warn error fatal].each do |severity|
50
- selected = relevant.select { |a| a.public_send("#{severity}?") }.reject(&:known_exception?)
48
+ selected = relevant.select { |a| a.public_send("#{severity}?") }.reject(&:known_exception?).reject(&:ignore?)
51
49
  next if selected.blank?
52
50
 
53
51
  summary_output.push("#{selected.count} lines with #{severity}:")
@@ -55,15 +53,6 @@ class RailsLogParser::Parser
55
53
  summary_output.push("\n\n")
56
54
  end
57
55
 
58
- unless @heuristic.nil?
59
- stats = RailsLogParser::HeuristicStatFile.build_heuristic(@heuristic, @heuristic_today)
60
- if stats.present?
61
- summary_output.push("Heuristic match! (threshold: #{RailsLogParser::HeuristicStatFile.heuristic_threshold})")
62
- stats.each { |k, v| summary_output.push("- #{k}: #{v.round(4)}") }
63
- summary_output.push("\n\n")
64
- end
65
- end
66
-
67
56
  summary_output.join("\n")
68
57
  end
69
58
 
@@ -80,6 +69,7 @@ class RailsLogParser::Parser
80
69
  @actions[params['id']].severity = params['severity_label']
81
70
  @actions[params['id']].datetime = params['datetime']
82
71
  @actions[params['id']].add_message(params['message']) unless params['message'].nil?
72
+ @last_action = @actions[params['id']]
83
73
  end
84
74
 
85
75
  def request(params)
@@ -110,8 +100,4 @@ class RailsLogParser::Parser
110
100
  @actions[params['id']] ||= RailsLogParser::Action.new(type, params['id'])
111
101
  @actions[params['id']].add_message(params['message'])
112
102
  end
113
-
114
- def last_action
115
- RailsLogParser::Action.last
116
- end
117
103
  end
@@ -2,9 +2,8 @@
2
2
 
3
3
  namespace :rails_log_parser do
4
4
  desc 'notify about found problems in production.log'
5
- task :parse, [:from_minutes, :heuristic] do |_t, args|
5
+ task :parse, [:from_minutes] do |_t, args|
6
6
  parser = RailsLogParser::Parser.from_file(RailsLogParser::Parser.log_path)
7
- parser.enable_heuristic(File.dirname(RailsLogParser::Parser.log_path)) if args[:heuristic] == 'true'
8
7
  print parser.summary(last_minutes: args[:from_minutes])
9
8
  end
10
9
  end
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'enumerize'
4
+ require 'active_support/core_ext/module/attribute_accessors'
4
5
 
5
6
  module RailsLogParser
6
- THRESHOLD_HEURISTIC = 0.02
7
- MIN_ACTIONS_HEURISTIC = 100000 # sum of last 10 days
7
+ mattr_accessor :ignore_lines, default: []
8
+
9
+ def self.configure
10
+ yield self
11
+ end
8
12
  end
9
13
 
10
14
  require_relative 'rails_log_parser/parser'
11
15
  require_relative 'rails_log_parser/action'
12
16
  require_relative 'rails_log_parser/line'
13
- require_relative 'rails_log_parser/heuristic_stat_file'
14
17
  require_relative 'rails_log_parser/not_parseable_lines'
15
18
 
16
19
  require 'rails_log_parser/railtie' if defined?(Rails::Railtie)
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'rails_log_parser'
5
- spec.version = '0.0.14'
5
+ spec.version = '0.0.16'
6
6
  spec.authors = ['Georg Limbach']
7
7
  spec.email = ['georg.limbach@lichtbit.com']
8
8
 
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency 'enumerize', '~> 2.4'
26
26
  spec.add_dependency 'json', '>= 2.0'
27
+ spec.add_dependency 'rails', '>= 2.1'
27
28
  spec.add_development_dependency 'rspec', '>= 3.0'
28
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_log_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Georg Limbach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-27 00:00:00.000000000 Z
11
+ date: 2023-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: enumerize
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '2.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -69,7 +83,6 @@ files:
69
83
  - Rakefile
70
84
  - lib/rails_log_parser.rb
71
85
  - lib/rails_log_parser/action.rb
72
- - lib/rails_log_parser/heuristic_stat_file.rb
73
86
  - lib/rails_log_parser/line.rb
74
87
  - lib/rails_log_parser/not_parseable_lines.rb
75
88
  - lib/rails_log_parser/parser.rb
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- RailsLogParser::HeuristicStatFile = Struct.new(:path, :date) do
6
- attr_reader :stats
7
-
8
- class << self
9
- def build_heuristic(path, today)
10
- sums = { actions: 0 }
11
- RailsLogParser::Action::KNOWN_EXCEPTIONS.each_key do |exception|
12
- sums[exception.to_sym] = 0
13
- end
14
- 10.times do |i|
15
- stats = RailsLogParser::HeuristicStatFile.new(path, today.date - (i + 1)).load_stats
16
- sums[:actions] += stats[:actions].to_i
17
- RailsLogParser::Action::KNOWN_EXCEPTIONS.each_key do |exception|
18
- sums[exception.to_sym] += stats.dig(:known_exceptions, exception.to_sym).to_i
19
- end
20
- end
21
- output = {}
22
- RailsLogParser::Action::KNOWN_EXCEPTIONS.each_key do |exception|
23
- next if sums[:actions] < heuristic_min_actions
24
-
25
- quota = sums[exception.to_sym].to_f / sums[:actions]
26
- next if quota == 0
27
- today_quota = today.rate(exception)
28
- next if today_quota == 0
29
-
30
- rate = ((today_quota - quota) / quota) / Math.sqrt(sums[:actions].to_f)
31
- output[exception] = rate if rate > heuristic_threshold
32
- end
33
- output
34
- end
35
-
36
- def heuristic_threshold
37
- @heuristic_threshold ||= ENV['RAILS_LOG_PARSER_THRESHOLD_HEURISTIC'] || RailsLogParser::THRESHOLD_HEURISTIC
38
- end
39
-
40
- def heuristic_min_actions
41
- @heuristic_min_actions ||= ENV['RAILS_LOG_PARSER_MIN_ACTIONS_HEURISTIC'] || RailsLogParser::MIN_ACTIONS_HEURISTIC
42
- end
43
- end
44
-
45
- def write_stats(actions)
46
- actions = actions.select { |action| action.datetime.to_date == date }.sort_by(&:datetime)
47
- @stats = {
48
- actions: actions.count,
49
- known_exceptions: {},
50
- starts_at: actions.first&.datetime,
51
- ends_at: actions.last&.datetime,
52
- }
53
-
54
- RailsLogParser::Action::KNOWN_EXCEPTIONS.each_key do |exception|
55
- @stats[:known_exceptions][exception.to_sym] = actions.count { |action| action.known_exception?(exception) }
56
- end
57
-
58
- delete_old_stats
59
- File.write(heuristic_file_path, @stats.to_json)
60
- end
61
-
62
- def delete_old_stats
63
- last_20_days = (0..19).map { |i| (Date.today - i) }.map { |date| File.join(path, "heuristic_stats_#{date}.json") }
64
- Dir[File.join(path, 'heuristic_stats_*.json')].reject { |file| last_20_days.include?(file) }.each do |file|
65
- File.unlink(file)
66
- end
67
- end
68
-
69
- def load_stats
70
- @stats = JSON.parse(File.read(heuristic_file_path), symbolize_names: true) if File.file?(heuristic_file_path)
71
- @stats ||= {}
72
- rescue JSON::ParserError
73
- @stats = {}
74
- end
75
-
76
- def heuristic_file_path
77
- @heuristic_file_path ||= File.join(path, "heuristic_stats_#{date}.json")
78
- end
79
-
80
- def rate(exception)
81
- return 0 if stats[:actions] == 0
82
-
83
- stats.dig(:known_exceptions, exception.to_sym).to_f / stats[:actions]
84
- end
85
- end