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
@@ -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