logster 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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