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
@@ -18,20 +18,19 @@
18
18
  "test": "ember test"
19
19
  },
20
20
  "devDependencies": {
21
- "jquery": "3.4.1",
22
21
  "@ember/optional-features": "^0.6.3",
23
22
  "broccoli-asset-rev": "^2.7.0",
24
23
  "ember-ajax": "^5.0.0",
25
- "ember-cli": "~3.8.2",
24
+ "ember-cli": "^3.8.3",
26
25
  "ember-cli-app-version": "^3.2.0",
27
- "ember-cli-babel": "^7.1.2",
28
- "ember-cli-dependency-checker": "^3.1.0",
26
+ "ember-cli-babel": "^7.12.0",
27
+ "ember-cli-dependency-checker": "^3.2.0",
29
28
  "ember-cli-eslint": "^4.2.3",
30
- "ember-cli-htmlbars": "^3.0.0",
29
+ "ember-cli-htmlbars": "^3.1.0",
31
30
  "ember-cli-htmlbars-inline-precompile": "^1.0.3",
32
31
  "ember-cli-inject-live-reload": "^1.8.2",
33
32
  "ember-cli-sri": "^2.1.1",
34
- "ember-cli-template-lint": "^1.0.0-beta.1",
33
+ "ember-cli-template-lint": "^1.0.0-beta.3",
35
34
  "ember-cli-uglify": "^2.1.0",
36
35
  "ember-data": "~3.8.0",
37
36
  "ember-export-application-global": "^2.0.0",
@@ -39,19 +38,20 @@
39
38
  "ember-load-initializers": "^1.1.0",
40
39
  "ember-maybe-import-regenerator": "^0.1.6",
41
40
  "ember-qunit": "^3.4.1",
42
- "ember-resolver": "^5.0.1",
43
- "ember-source": "~3.8.0",
41
+ "ember-resolver": "^5.3.0",
42
+ "ember-source": "^3.8.3",
44
43
  "ember-welcome-page": "^3.2.0",
45
- "eslint-plugin-ember": "^5.2.0",
44
+ "eslint-plugin-ember": "^5.4.0",
45
+ "jquery": "3.4.1",
46
46
  "loader.js": "^4.7.0",
47
- "prettier": "^1.16.4",
48
- "qunit-dom": "^0.8.0"
47
+ "prettier": "^1.19.1",
48
+ "qunit-dom": "^0.8.5"
49
49
  },
50
50
  "engines": {
51
51
  "node": "6.* || 8.* || >= 10.*"
52
52
  },
53
53
  "dependencies": {
54
- "lodash": "~4.17.13",
54
+ "lodash": "^4.17.15",
55
55
  "moment": "~2.22.2"
56
56
  }
57
57
  }
@@ -35,11 +35,18 @@ module("Integration | Component | env-tab", function(hooks) {
35
35
  setupRenderingTest(hooks);
36
36
 
37
37
  test("it renders", async function(assert) {
38
- this.set("message", message);
39
- await render(hbs`{{env-tab message=message}}`);
38
+ const callback = newPosition => this.set("envPosition", newPosition);
39
+ this.setProperties({
40
+ message,
41
+ callback,
42
+ envPosition: 0
43
+ });
44
+ await render(
45
+ hbs`{{env-tab message=message envChangedAction=callback currentEnvPosition=envPosition}}`
46
+ );
40
47
 
41
48
  assert.equal(
42
- find(".env-number").textContent,
49
+ find(".current-number").textContent,
43
50
  "1/2",
44
51
  "shows the current over the total number of env objects"
45
52
  );
@@ -71,14 +78,21 @@ module("Integration | Component | env-tab", function(hooks) {
71
78
  });
72
79
 
73
80
  test("it works correctly", async function(assert) {
74
- this.set("message", message);
75
- await render(hbs`{{env-tab message=message}}`);
81
+ const callback = newPosition => this.set("envPosition", newPosition);
82
+ this.setProperties({
83
+ message,
84
+ callback,
85
+ envPosition: 0
86
+ });
87
+ await render(
88
+ hbs`{{env-tab message=message envChangedAction=callback currentEnvPosition=envPosition}}`
89
+ );
76
90
 
77
91
  const buttons = findAll("button.nav-btn");
78
92
  await click(buttons[2]);
79
93
 
80
94
  assert.equal(
81
- find(".env-number").textContent,
95
+ find(".current-number").textContent,
82
96
  "2/2",
83
97
  "shows the current over the total number of env objects"
84
98
  );
@@ -108,8 +122,15 @@ module("Integration | Component | env-tab", function(hooks) {
108
122
  env_expandable_keys: ["env_key_2", "default_expanded"]
109
123
  });
110
124
  init();
111
- this.set("message", message3);
112
- await render(hbs`{{env-tab message=message}}`);
125
+ const callback = newPosition => this.set("envPosition", newPosition);
126
+ this.setProperties({
127
+ message: message3,
128
+ callback,
129
+ envPosition: 0
130
+ });
131
+ await render(
132
+ hbs`{{env-tab message=message envChangedAction=callback currentEnvPosition=envPosition}}`
133
+ );
113
134
 
114
135
  const trs = findAll(".env-table tr");
115
136
  const expandable = trs[0];
@@ -17,14 +17,22 @@ module("Integration | Component | message-info", function(hooks) {
17
17
  setupRenderingTest(hooks);
18
18
 
19
19
  test("it renders", async function(assert) {
20
+ const callback = newPosition => this.set("currentEnvPosition", newPosition);
20
21
  this.setProperties({
21
22
  actionsInMenu: true,
22
23
  showTitle: false,
23
- message
24
+ envPosition: 0,
25
+ message,
26
+ callback
24
27
  });
25
28
 
26
29
  await render(
27
- hbs`{{message-info currentMessage=message showTitle=showTitle actionsInMenu=actionsInMenu}}`
30
+ hbs`{{message-info
31
+ currentMessage=message
32
+ showTitle=showTitle
33
+ currentEnvPosition=envPosition
34
+ envChangedAction=callback
35
+ actionsInMenu=actionsInMenu}}`
28
36
  );
29
37
  let activeTab = find(".message-info .content.active pre");
30
38
  assert.equal(
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class SidekiqLogsterReporter
2
4
  def call(ex, context = {})
3
5
  # Pass context to Logster
@@ -7,6 +7,8 @@ require 'logster/web'
7
7
  require 'logster/ignore_pattern'
8
8
  require 'logster/pattern'
9
9
  require 'logster/suppression_pattern'
10
+ require 'logster/grouping_pattern'
11
+ require 'logster/group'
10
12
  require 'logster/cache'
11
13
 
12
14
  if defined? Redis
@@ -17,8 +19,6 @@ else
17
19
  end
18
20
 
19
21
  module Logster
20
- PATTERNS = [SuppressionPattern]
21
-
22
22
  def self.logger=(logger)
23
23
  @logger = logger
24
24
  end
@@ -115,22 +115,39 @@ module Logster
115
115
 
116
116
  # increments the number of messages that have been suppressed by a pattern
117
117
  def increment_ignore_count(pattern)
118
+ not_implemented
118
119
  end
119
120
 
120
121
  # removes number of suppressed messages by a pattern
121
122
  def remove_ignore_count(pattern)
123
+ not_implemented
122
124
  end
123
125
 
124
126
  # returns a hash that maps patterns to the number of messages they
125
127
  # have suppressed
126
128
  def get_all_ignore_count
127
- {}
129
+ not_implemented
128
130
  end
129
131
 
130
132
  def rate_limited?(ip_address, perform: false, limit: 60)
131
133
  not_implemented
132
134
  end
133
135
 
136
+ # find all pattern groups; returns an array of Logster::Group
137
+ def find_pattern_groups(load_messages: true)
138
+ not_implemented
139
+ end
140
+
141
+ # saves an instance of Logster::Group
142
+ def save_pattern_group(group)
143
+ not_implemented
144
+ end
145
+
146
+ # removes the Logster::Group instance associated with the given pattern
147
+ def remove_pattern_group(pattern)
148
+ not_implemented
149
+ end
150
+
134
151
  def report(severity, progname, msg, opts = {})
135
152
  return if (!msg || (String === msg && msg.empty?)) && skip_empty
136
153
  return if level && severity < level
@@ -164,7 +181,7 @@ module Logster
164
181
  end
165
182
 
166
183
  if Logster.config.enable_custom_patterns_via_ui || allow_custom_patterns
167
- custom_ignore = @patterns_cache.fetch do
184
+ custom_ignore = @patterns_cache.fetch(Logster::SuppressionPattern::CACHE_KEY) do
168
185
  Logster::SuppressionPattern.find_all(store: self)
169
186
  end
170
187
  return if custom_ignore.any? do |pattern|
@@ -194,10 +211,29 @@ module Logster
194
211
  save message
195
212
  message
196
213
  end
214
+
215
+ message = similar || message
216
+
217
+ if Logster.config.enable_custom_patterns_via_ui || allow_custom_patterns
218
+ grouping_patterns = @patterns_cache.fetch(Logster::GroupingPattern::CACHE_KEY) do
219
+ Logster::GroupingPattern.find_all(store: self)
220
+ end
221
+
222
+ grouping_patterns.each do |pattern|
223
+ if message =~ pattern
224
+ group = find_pattern_groups() { |pat| pat == pattern }[0]
225
+ group ||= Logster::Group.new(pattern.inspect)
226
+ group.add_message(message)
227
+ save_pattern_group(group) if group.changed?
228
+ break
229
+ end
230
+ end
231
+ end
232
+ message
197
233
  end
198
234
 
199
- def clear_suppression_patterns_cache
200
- @patterns_cache.clear
235
+ def clear_patterns_cache(key)
236
+ @patterns_cache.clear(key)
201
237
  end
202
238
 
203
239
  private
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Logster
2
4
  class Cache
3
5
  def initialize(age = 2)
4
6
  @age = age
5
- @hash = { created_at: Process.clock_gettime(Process::CLOCK_MONOTONIC) }
7
+ @hash = {}
6
8
  end
7
9
 
8
- def fetch
9
- if !@hash.key?(:data) || @hash[:created_at] + @age < Process.clock_gettime(Process::CLOCK_MONOTONIC)
10
- @hash[:data] = yield
11
- @hash[:created_at] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
10
+ def fetch(key)
11
+ if !@hash.key?(key) || @hash[key][:created_at] + @age < Process.clock_gettime(Process::CLOCK_MONOTONIC)
12
+ @hash[key] = { data: yield, created_at: Process.clock_gettime(Process::CLOCK_MONOTONIC) }
12
13
  end
13
- @hash[:data]
14
+ @hash[key][:data]
14
15
  end
15
16
 
16
- def clear
17
- @hash.delete(:data)
17
+ def clear(key)
18
+ @hash.delete(key)
18
19
  end
19
20
  end
20
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logster/scheduler'
2
4
 
3
5
  module Logster
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logster
4
+ class Group
5
+ MAX_SIZE = 100
6
+
7
+ attr_reader :key, :messages_keys, :timestamp, :messages
8
+ attr_accessor :changed
9
+
10
+ def initialize(key, messages_keys = [], timestamp: 0)
11
+ @key = key
12
+ @messages_keys = messages_keys || []
13
+ @timestamp = timestamp
14
+ @changed = true
15
+ end
16
+
17
+ def self.from_json(json)
18
+ hash = JSON.parse(json)
19
+ group = new(
20
+ hash["key"],
21
+ hash["messages_keys"],
22
+ timestamp: hash["timestamp"] || 0
23
+ )
24
+ group.changed = false
25
+ group
26
+ end
27
+
28
+ def self.max_size
29
+ (defined?(@max_size) && @max_size) || MAX_SIZE
30
+ end
31
+
32
+ def to_h
33
+ {
34
+ key: @key,
35
+ messages_keys: @messages_keys,
36
+ timestamp: @timestamp
37
+ }
38
+ end
39
+
40
+ def to_h_web
41
+ {
42
+ regex: @key,
43
+ count: self.count,
44
+ timestamp: @timestamp,
45
+ messages: @messages,
46
+ severity: -1,
47
+ group: true
48
+ }
49
+ end
50
+
51
+ def to_json(opts = nil)
52
+ JSON.fast_generate(self.to_h, opts)
53
+ end
54
+
55
+ def add_message(message)
56
+ if !@messages_keys.include?(message.key)
57
+ @messages_keys.unshift(message.key)
58
+ @changed = true
59
+ end
60
+ if @timestamp < message.timestamp
61
+ @timestamp = message.timestamp
62
+ @messages_keys.unshift(@messages_keys.slice!(@messages_keys.index(message.key)))
63
+ @changed = true
64
+ end
65
+ if self.count > max_size
66
+ @messages_keys.slice!(max_size..-1)
67
+ @changed = true
68
+ end
69
+ end
70
+
71
+ def remove_message(message)
72
+ index = @messages_keys.index(message.key)
73
+ if index
74
+ @messages_keys.slice!(index)
75
+ @changed = true
76
+ end
77
+ end
78
+
79
+ def messages=(messages)
80
+ messages.compact!
81
+ messages.uniq!(&:key)
82
+ if messages.size > 0
83
+ messages.sort_by!(&:timestamp)
84
+ messages.reverse!
85
+ messages.slice!(max_size..-1) if messages.size > max_size
86
+ @messages = messages
87
+ before = @messages_keys.sort
88
+ @messages_keys = @messages.map(&:key)
89
+ @timestamp = @messages[0].timestamp
90
+ @changed = before != @messages_keys.sort
91
+ else
92
+ @messages_keys = []
93
+ @messages = []
94
+ @timestamp = 0
95
+ @changed = true
96
+ end
97
+ @messages
98
+ end
99
+
100
+ def changed?
101
+ @changed
102
+ end
103
+
104
+ def count
105
+ @messages_keys.size
106
+ end
107
+
108
+ private
109
+
110
+ def max_size
111
+ self.class.max_size
112
+ end
113
+
114
+ GroupWeb = Struct.new(*%i[regex count timestamp messages row_id]) do
115
+ def to_json(opts = nil)
116
+ JSON.fast_generate(self.to_h.merge(severity: -1, group: true), opts)
117
+ end
118
+
119
+ def key
120
+ self.regex # alias for testing convenience
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logster
4
+ class GroupingPattern < Pattern
5
+ CACHE_KEY = :grouping
6
+ def self.set_name
7
+ "__LOGSTER__grouping_patterns_set".freeze
8
+ end
9
+
10
+ def save(args = {})
11
+ super
12
+ existing_groups = @store.find_pattern_groups
13
+ group = Logster::Group.new(self.to_s)
14
+ messages = @store.get_all_messages(with_env: false)
15
+ messages.select! do |m|
16
+ m.message =~ self.pattern && existing_groups.none? { |g| g.messages_keys.include?(m.key) }
17
+ end
18
+ group.messages = messages
19
+ @store.save_pattern_group(group) if group.changed?
20
+ @store.clear_patterns_cache(CACHE_KEY)
21
+ end
22
+
23
+ def destroy(clear_cache: true) # arg used in tests
24
+ super()
25
+ @store.remove_pattern_group(self.pattern)
26
+ @store.clear_patterns_cache(CACHE_KEY) if clear_cache
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Logster
2
4
  class IgnorePattern
3
5