logster 2.4.2 → 2.5.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +2 -0
  4. data/Guardfile +2 -0
  5. data/README.md +1 -1
  6. data/Rakefile +2 -0
  7. data/assets/javascript/client-app.js +69 -53
  8. data/assets/javascript/vendor.js +580 -559
  9. data/assets/stylesheets/client-app.css +1 -1
  10. data/client-app/README.md +2 -2
  11. data/client-app/app/components/env-tab.js +16 -36
  12. data/client-app/app/components/message-row.js +1 -1
  13. data/client-app/app/components/page-nav.js +30 -0
  14. data/client-app/app/components/patterns-list.js +2 -1
  15. data/client-app/app/controllers/index.js +68 -92
  16. data/client-app/app/controllers/show.js +6 -0
  17. data/client-app/app/models/group.js +20 -0
  18. data/client-app/app/models/message-collection.js +159 -57
  19. data/client-app/app/routes/index.js +0 -2
  20. data/client-app/app/routes/settings.js +3 -1
  21. data/client-app/app/styles/app.css +17 -2
  22. data/client-app/app/templates/components/env-tab.hbs +5 -7
  23. data/client-app/app/templates/components/message-info.hbs +13 -8
  24. data/client-app/app/templates/components/page-nav.hbs +13 -0
  25. data/client-app/app/templates/components/patterns-list.hbs +6 -4
  26. data/client-app/app/templates/index.hbs +45 -11
  27. data/client-app/app/templates/settings.hbs +10 -1
  28. data/client-app/app/templates/show.hbs +2 -0
  29. data/client-app/package-lock.json +2817 -1215
  30. data/client-app/package.json +12 -12
  31. data/client-app/tests/integration/components/env-tab-test.js +29 -8
  32. data/client-app/tests/integration/components/message-info-test.js +10 -2
  33. data/lib/examples/sidekiq_logster_reporter.rb +2 -0
  34. data/lib/logster.rb +2 -2
  35. data/lib/logster/base_store.rb +40 -4
  36. data/lib/logster/cache.rb +9 -8
  37. data/lib/logster/defer_logger.rb +2 -0
  38. data/lib/logster/group.rb +124 -0
  39. data/lib/logster/grouping_pattern.rb +29 -0
  40. data/lib/logster/ignore_pattern.rb +2 -0
  41. data/lib/logster/logger.rb +2 -0
  42. data/lib/logster/message.rb +3 -1
  43. data/lib/logster/middleware/reporter.rb +2 -2
  44. data/lib/logster/middleware/viewer.rb +12 -1
  45. data/lib/logster/pattern.rb +13 -0
  46. data/lib/logster/redis_store.rb +99 -10
  47. data/lib/logster/scheduler.rb +2 -0
  48. data/lib/logster/suppression_pattern.rb +5 -2
  49. data/lib/logster/version.rb +1 -1
  50. data/lib/logster/web.rb +2 -0
  51. data/logster.gemspec +4 -1
  52. data/test/examples/test_sidekiq_reporter_example.rb +2 -0
  53. data/test/fake_data/Gemfile +2 -0
  54. data/test/fake_data/generate.rb +2 -0
  55. data/test/logster/middleware/test_viewer.rb +3 -1
  56. data/test/logster/test_base_store.rb +2 -0
  57. data/test/logster/test_cache.rb +19 -12
  58. data/test/logster/test_defer_logger.rb +2 -0
  59. data/test/logster/test_group.rb +92 -0
  60. data/test/logster/test_ignore_pattern.rb +2 -0
  61. data/test/logster/test_logger.rb +3 -1
  62. data/test/logster/test_message.rb +2 -0
  63. data/test/logster/test_pattern.rb +2 -2
  64. data/test/logster/test_redis_rate_limiter.rb +2 -0
  65. data/test/logster/test_redis_store.rb +253 -0
  66. data/test/test_helper.rb +6 -0
  67. metadata +26 -6
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/defer_logger'
3
5
  require 'logster/logger'
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../test_helper'
4
+ require 'logster/group'
5
+ require 'logster/message'
6
+
7
+ class TestGroup < MiniTest::Test
8
+ def test_changed_is_true_for_new_instances
9
+ assert Logster::Group.new("/somekey/").changed?
10
+ end
11
+
12
+ def test_from_json_works_correctly
13
+ time = (Time.new.to_f * 1000).to_i - 5000
14
+ json = JSON.generate(
15
+ key: '/somekey/',
16
+ messages_keys: [111, 222, 333].map(&:to_s),
17
+ timestamp: time
18
+ )
19
+ group = Logster::Group.from_json(json)
20
+ refute group.changed?
21
+ assert_equal 3, group.count
22
+ assert_equal time, group.timestamp
23
+ end
24
+
25
+ def test_doesnt_add_duplicate_messages
26
+ group = get_group
27
+ msg1 = get_message
28
+ assert_equal 0, group.count
29
+ group.add_message(msg1)
30
+ assert_equal 1, group.count
31
+ assert_equal msg1.timestamp, group.timestamp
32
+ group.add_message(msg1)
33
+ assert_equal 1, group.count
34
+
35
+ msg2 = get_message
36
+ msg2.timestamp -= 10000
37
+ group.add_message(msg2)
38
+ assert_equal 2, group.count
39
+ assert_equal msg1.timestamp, group.timestamp
40
+ end
41
+
42
+ def test_adding_multiple_messages_works_correctly
43
+ group = get_group
44
+ messages = [
45
+ get_message(10),
46
+ get_message(5),
47
+ get_message(74),
48
+ get_message(26)
49
+ ]
50
+ messages << messages[0]
51
+ group.messages = messages
52
+ assert_equal 4, group.count
53
+ assert_equal 74, group.timestamp
54
+ expected = messages.uniq(&:key).sort_by(&:timestamp).map(&:key).reverse
55
+ assert_equal expected, group.messages_keys
56
+ end
57
+
58
+ def test_doesnt_exceed_max_size
59
+ Logster::Group.instance_variable_set(:@max_size, 5)
60
+ group = get_group
61
+ messages = [
62
+ get_message(10),
63
+ get_message(5),
64
+ get_message(74),
65
+ get_message(26),
66
+ get_message(44),
67
+ get_message(390)
68
+ ]
69
+ messages.each { |m| group.add_message(m) }
70
+ assert_equal 5, group.count
71
+ assert_equal 390, group.timestamp
72
+ refute_includes group.messages_keys, messages.find { |m| m.timestamp == 10 }.key
73
+
74
+ group = get_group
75
+ group.messages = messages
76
+ assert_equal 5, group.count
77
+ assert_equal 390, group.timestamp
78
+ refute_includes group.messages.map(&:timestamp), 5
79
+ ensure
80
+ Logster::Group.remove_instance_variable(:@max_size)
81
+ end
82
+
83
+ private
84
+
85
+ def get_group
86
+ Logster::Group.new("/groupkey/")
87
+ end
88
+
89
+ def get_message(timestamp = nil)
90
+ Logster::Message.new(0, '', 'testmessage', timestamp)
91
+ end
92
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/ignore_pattern'
3
5
 
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/logger'
3
5
  require 'logger'
4
6
 
5
- class TestStore
7
+ class TestStore < Logster::BaseStore
6
8
  attr_accessor :calls
7
9
 
8
10
  def report(*args)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/message'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/redis_store'
3
5
  require 'logster/pattern'
@@ -9,8 +11,6 @@ class TestPattern < Minitest::Test
9
11
  end
10
12
  end
11
13
 
12
- Logster::PATTERNS << FakePattern
13
-
14
14
  class TestRedisStore < Logster::BaseStore
15
15
  def get_patterns(set_name)
16
16
  ["/differentstore/"]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/redis_store'
3
5
  require 'rack'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require 'logster/redis_store'
3
5
  require 'rack'
@@ -23,6 +25,55 @@ class TestRedisStore < Minitest::Test
23
25
  assert_nil(@store.get_env(msg.key))
24
26
  end
25
27
 
28
+ def test_delete_with_custom_grouping_patterns
29
+ Logster.config.enable_custom_patterns_via_ui = true
30
+ Logster::GroupingPattern.new(/delete/, store: @store).save
31
+ msg1 = @store.report(Logger::WARN, '', 'this will be deleted')
32
+ msg2 = @store.report(Logger::WARN, '', 'delete this plz')
33
+
34
+ groups = @store.find_pattern_groups
35
+ assert_equal 1, groups.size
36
+ assert_equal [msg2.key, msg1.key], groups[0].messages_keys
37
+
38
+ @store.delete(msg1)
39
+ groups = @store.find_pattern_groups
40
+ assert_equal 1, groups.size
41
+ assert_equal [msg2.key], groups[0].messages_keys
42
+
43
+ @store.delete(msg2)
44
+ groups = @store.find_pattern_groups
45
+ assert_equal 0, groups.size
46
+ ensure
47
+ Logster.config.enable_custom_patterns_via_ui = false
48
+ end
49
+
50
+ def test_bulk_delete_with_custom_grouping_patterns
51
+ Logster.config.enable_custom_patterns_via_ui = true
52
+ Logster::GroupingPattern.new(/delete/, store: @store).save
53
+ keys = []
54
+ gkeys = []
55
+ 6.times do |n|
56
+ m = @store.report(Logger::WARN, '', "#{n} delete")
57
+ keys << m.key
58
+ gkeys << m.grouping_key
59
+ end
60
+
61
+ groups = @store.find_pattern_groups
62
+ assert_equal 1, groups.size
63
+ assert_equal keys.reverse, groups[0].messages_keys
64
+
65
+ @store.bulk_delete(keys[0..2], gkeys[0..2])
66
+ groups = @store.find_pattern_groups
67
+ assert_equal 1, groups.size
68
+ assert_equal keys[3..5].reverse, groups[0].messages_keys
69
+
70
+ @store.bulk_delete(keys, gkeys)
71
+ groups = @store.find_pattern_groups
72
+ assert_equal 0, groups.size
73
+ ensure
74
+ Logster.config.enable_custom_patterns_via_ui = false
75
+ end
76
+
26
77
  def test_latest
27
78
  @store.report(Logger::WARN, "test", "IGNORE")
28
79
  @store.report(Logger::WARN, "test", "This is a warning")
@@ -76,7 +127,58 @@ class TestRedisStore < Minitest::Test
76
127
  messages = @store.latest(limit: 10, before: messages[0].key)
77
128
  assert_equal("A", messages[0].message)
78
129
  assert_equal(10, messages.length)
130
+ end
131
+
132
+ def test_latest_with_custom_grouping
133
+ Logster.config.enable_custom_patterns_via_ui = true
134
+ Logster::GroupingPattern.new(/group 1/, store: @store).save
135
+ Logster::GroupingPattern.new(/group 2/, store: @store).save
136
+ msg1 = @store.report(Logger::WARN, '', 'first message')
137
+ group_1_keys = []
138
+ 3.times { |n| group_1_keys << @store.report(Logger::WARN, '', "group 1 #{n}").key }
139
+ msg2 = @store.report(Logger::WARN, '', 'second message')
140
+ group_1_keys << @store.report(Logger::WARN, '', "group 1 3").key
141
+ msg3 = @store.report(Logger::WARN, '', 'third message')
142
+ group_2_keys = []
143
+ 3.times { |n| group_2_keys << @store.report(Logger::WARN, '', "group 2 #{n}").key }
144
+ msg4 = @store.report(Logger::WARN, '', 'fourth message')
145
+
146
+ results = @store.latest
147
+ assert_equal [msg1.key, msg2.key, "/group 1/", msg3.key, "/group 2/", msg4.key], results.map(&:key)
148
+ groups = results.select { |r| r.class == Logster::Group::GroupWeb }
149
+ assert_equal(
150
+ [group_1_keys.last, group_2_keys.last],
151
+ groups.map(&:row_id)
152
+ )
153
+ assert_equal 4, groups[0].messages.size
154
+ assert_equal 3, groups[1].messages.size
79
155
 
156
+ results = @store.latest(before: groups[0].row_id, known_groups: groups.map(&:key))
157
+ assert_equal [msg1.key, msg2.key], results.map(&:key)
158
+
159
+ results = @store.latest(before: groups[1].row_id, known_groups: [groups[1].key])
160
+ assert_equal [msg1.key, msg2.key, "/group 1/", msg3.key], results.map(&:key)
161
+
162
+ results = @store.latest(before: msg2.key, known_groups: groups.map(&:key))
163
+ assert_equal [msg1.key], results.map(&:key)
164
+
165
+ results = @store.latest(after: groups[0].row_id)
166
+ assert_equal [msg3.key, "/group 2/", msg4.key], results.map(&:key)
167
+
168
+ results = @store.latest(after: msg2.key)
169
+ assert_equal ["/group 1/", msg3.key, "/group 2/", msg4.key], results.map(&:key)
170
+ assert_equal 4, results[0].messages.size
171
+
172
+ results = @store.latest(after: msg4.key)
173
+ assert_equal 0, results.size
174
+
175
+ group_2_keys << @store.report(Logger::WARN, '', "group 2 3").key
176
+ results = @store.latest(after: msg4.key)
177
+ assert_equal ["/group 2/"], results.map(&:key)
178
+ assert_equal group_2_keys.last, results[0].row_id
179
+ assert_equal 4, results[0].messages.size
180
+ ensure
181
+ Logster.config.enable_custom_patterns_via_ui = false
80
182
  end
81
183
 
82
184
  def test_get
@@ -261,6 +363,32 @@ class TestRedisStore < Minitest::Test
261
363
  assert(env <= latest[1].env)
262
364
  end
263
365
 
366
+ def test_clear_deletes_pattern_groups_if_not_protected
367
+ Logster.config.enable_custom_patterns_via_ui = true
368
+ Logster.config.allow_grouping = true
369
+ Logster::GroupingPattern.new(/discourse/, store: @store).save
370
+ Logster::GroupingPattern.new(/logster/, store: @store).save
371
+ msg = @store.report(Logger::WARN, '', 'discourse')
372
+ @store.protect(msg.key)
373
+ @store.report(Logger::WARN, '', 'logster')
374
+ groups = @store.find_pattern_groups
375
+ assert_equal 2, groups.size
376
+
377
+ @store.clear
378
+ groups = @store.find_pattern_groups
379
+ assert_equal 1, groups.size
380
+ assert_equal msg.key, groups[0].messages_keys[0]
381
+ assert_equal '/discourse/', groups[0].key
382
+
383
+ @store.unprotect(msg.key)
384
+ @store.clear
385
+ groups = @store.find_pattern_groups
386
+ assert_equal 0, groups.size
387
+ ensure
388
+ Logster.config.enable_custom_patterns_via_ui = false
389
+ Logster.config.allow_grouping = false
390
+ end
391
+
264
392
  def test_hash_cleanup
265
393
  @store.max_backlog = 2
266
394
  a_message = @store.report(Logger::WARN, "test", "A")
@@ -720,6 +848,131 @@ class TestRedisStore < Minitest::Test
720
848
  Logster.config.maximum_message_size_bytes = default
721
849
  end
722
850
 
851
+ def test_custom_grouping_patterns
852
+ Logster.config.enable_custom_patterns_via_ui = true
853
+ Logster::GroupingPattern.new(/delete/, store: @store).save
854
+ msg1 = @store.report(Logger::WARN, '', 'delete this plz', timestamp: 1)
855
+ msg2 = @store.report(Logger::WARN, '', 'delete that plz', timestamp: 2)
856
+ group = @store.find_pattern_groups(load_messages: true)[0]
857
+ assert_equal 2, group.count
858
+ assert_equal "/delete/", group.key
859
+ assert_equal [msg2.key, msg1.key], group.messages_keys
860
+ assert_equal msg2.timestamp, group.timestamp
861
+ ensure
862
+ Logster.config.enable_custom_patterns_via_ui = false
863
+ end
864
+
865
+ def test_custom_grouping_patterns_with_similar_messages_grouping
866
+ Logster.config.enable_custom_patterns_via_ui = true
867
+ Logster.config.allow_grouping = true
868
+ Logster::GroupingPattern.new(/delete/, store: @store).save
869
+ backtrace = caller
870
+ @store.report(Logger::WARN, '', 'delete this plz', backtrace: backtrace, timestamp: 1)
871
+ msg2 = @store.report(Logger::WARN, '', 'delete that plz', backtrace: backtrace, timestamp: 2)
872
+ msg3 = @store.report(Logger::WARN, '', 'delete this plz', backtrace: backtrace, timestamp: 3)
873
+ group = @store.find_pattern_groups(load_messages: false)[0]
874
+ assert_equal 2, group.count
875
+ assert_equal [msg3.key, msg2.key], group.messages_keys
876
+ assert_equal msg3.timestamp, group.timestamp
877
+ ensure
878
+ Logster.config.enable_custom_patterns_via_ui = false
879
+ Logster.config.allow_grouping = false
880
+ end
881
+
882
+ def test_a_single_message_can_be_in_one_grouping_pattern
883
+ Logster.config.enable_custom_patterns_via_ui = true
884
+ Logster::GroupingPattern.new(/delete/, store: @store).save
885
+ Logster::GroupingPattern.new(/env/, store: @store).save
886
+ @store.report(Logger::WARN, '', 'delete and env')
887
+ groups = @store.find_pattern_groups
888
+ assert_equal 1, groups.size
889
+ assert_includes ["/delete/", "/env/"], groups[0].key
890
+ ensure
891
+ Logster.config.enable_custom_patterns_via_ui = false
892
+ end
893
+
894
+ def test_find_pattern_groups_works_correctly
895
+ Logster.config.enable_custom_patterns_via_ui = true
896
+ with_search = Logster::GroupingPattern.new(/with search/, store: @store)
897
+ with_search.save
898
+ Logster::GroupingPattern.new(/pattern group/, store: @store).save
899
+
900
+ groups = @store.find_pattern_groups
901
+ assert_equal 0, groups.size # because there are no messages yet
902
+
903
+ 2.times do |n|
904
+ @store.report(Logger::WARN, '', "with search #{n}")
905
+ @store.report(Logger::WARN, '', "pattern group #{n}")
906
+ end
907
+ groups = @store.find_pattern_groups
908
+ assert_equal 2, groups.size
909
+ groups.each do |g|
910
+ assert_equal 2, g.count
911
+ assert_nil g.messages
912
+ end
913
+
914
+ groups = @store.find_pattern_groups(load_messages: true)
915
+ assert_equal 2, groups.size
916
+ groups.each do |g|
917
+ assert_equal 2, g.count
918
+ assert_equal 2, g.messages.size
919
+ g.messages.each { |m| assert Logster::Message === m }
920
+ end
921
+
922
+ groups = @store.find_pattern_groups(load_messages: true) { |pat| pat == with_search.pattern }
923
+ assert_equal 1, groups.size
924
+ assert_equal 2, groups[0].count
925
+ assert groups[0].messages.all? { |m| m.message =~ /with search/ }
926
+ ensure
927
+ Logster.config.enable_custom_patterns_via_ui = false
928
+ end
929
+
930
+ def test_trimming_backlog_removes_messages_from_custom_grouping
931
+ prev_max_backlog = @store.max_backlog
932
+ @store.max_backlog = 4
933
+ Logster.config.enable_custom_patterns_via_ui = true
934
+ Logster::GroupingPattern.new(/trim/, store: @store).save
935
+ keys = []
936
+ 5.times do |n|
937
+ keys << @store.report(Logger::WARN, '', "trim backlog #{n}").key
938
+ end
939
+ groups = @store.find_pattern_groups
940
+ assert_equal 1, groups.size
941
+ assert_equal 4, groups[0].count
942
+ assert_equal keys[1..-1].reverse, groups[0].messages_keys
943
+ ensure
944
+ @store.max_backlog = prev_max_backlog
945
+ Logster.config.enable_custom_patterns_via_ui = false
946
+ end
947
+
948
+ def test_adding_grouping_pattern_works_retroactively
949
+ Logster.config.enable_custom_patterns_via_ui = true
950
+ @store.report(Logger::WARN, '', 'trim this plz')
951
+ @store.report(Logger::WARN, '', 'trim that plz')
952
+ Logster::GroupingPattern.new(/trim/, store: @store).save
953
+ results = @store.latest
954
+ assert_equal 1, results.size
955
+ assert_equal 2, results[0].messages.size
956
+
957
+ @store.report(Logger::WARN, '', 'trim this more plz')
958
+ results = @store.latest
959
+ assert_equal 1, results.size
960
+ assert_equal 3, results[0].messages.size
961
+ ensure
962
+ Logster.config.enable_custom_patterns_via_ui = false
963
+ end
964
+
965
+ def test_adding_grouping_pattern_doesnt_add_a_message_to_more_than_one_group
966
+ Logster.config.enable_custom_patterns_via_ui = true
967
+ @store.report(Logger::WARN, '', 'trim this plz')
968
+ Logster::GroupingPattern.new(/trim/, store: @store).save
969
+ Logster::GroupingPattern.new(/this/, store: @store).save
970
+ groups = @store.find_pattern_groups
971
+ assert_equal 1, groups.size
972
+ ensure
973
+ Logster.config.enable_custom_patterns_via_ui = false
974
+ end
975
+
723
976
  private
724
977
 
725
978
  def reset_redis
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'minitest'
2
4
  require 'minitest/unit'
3
5
  require 'minitest/autorun'
@@ -11,6 +13,7 @@ require 'byebug'
11
13
  class Logster::TestStore < Logster::BaseStore
12
14
  attr_accessor :reported
13
15
  def initialize
16
+ super
14
17
  @reported = []
15
18
  end
16
19
 
@@ -34,5 +37,8 @@ class Logster::TestStore < Logster::BaseStore
34
37
  # Do nothing
35
38
  end
36
39
 
40
+ def increment_ignore_count(pattern)
41
+ end
42
+
37
43
  # get, protect, unprotect: unimplemented
38
44
  end