logster 2.5.1 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +9 -0
- data/README.md +15 -1
- data/Rakefile +1 -0
- data/assets/javascript/client-app.js +204 -168
- data/assets/javascript/vendor.js +5132 -5833
- data/assets/stylesheets/client-app.css +1 -1
- data/client-app/.eslintrc.js +17 -5
- data/client-app/.travis.yml +4 -3
- data/client-app/app/app.js +5 -7
- data/client-app/app/components/actions-menu.js +24 -17
- data/client-app/app/components/back-trace.js +148 -0
- data/client-app/app/components/env-tab.js +16 -12
- data/client-app/app/components/message-info.js +84 -7
- data/client-app/app/components/message-row.js +13 -15
- data/client-app/app/components/panel-resizer.js +63 -45
- data/client-app/app/components/patterns-list.js +6 -6
- data/client-app/app/components/update-time.js +13 -13
- data/client-app/app/controllers/index.js +4 -2
- data/client-app/app/index.html +1 -1
- data/client-app/app/initializers/app-init.js +1 -1
- data/client-app/app/lib/decorators.js +11 -0
- data/client-app/app/lib/preload.js +14 -3
- data/client-app/app/lib/utilities.js +63 -36
- data/client-app/app/models/group.js +6 -1
- data/client-app/app/models/message-collection.js +9 -7
- data/client-app/app/models/message.js +25 -20
- data/client-app/app/router.js +4 -6
- data/client-app/app/styles/app.css +18 -4
- data/client-app/app/templates/components/actions-menu.hbs +6 -2
- data/client-app/app/templates/components/back-trace.hbs +8 -0
- data/client-app/app/templates/components/message-info.hbs +7 -2
- data/client-app/app/templates/index.hbs +4 -1
- data/client-app/config/environment.js +1 -1
- data/client-app/config/optional-features.json +4 -1
- data/client-app/ember-cli-build.js +2 -3
- data/client-app/package-lock.json +9712 -2884
- data/client-app/package.json +25 -22
- data/client-app/preload-json-manager.rb +62 -0
- data/client-app/testem.js +0 -1
- data/client-app/tests/index.html +1 -1
- data/client-app/tests/integration/components/back-trace-test.js +109 -0
- data/client-app/tests/integration/components/message-info-test.js +4 -3
- data/client-app/tests/integration/components/patterns-list-test.js +7 -2
- data/lib/logster.rb +1 -0
- data/lib/logster/base_store.rb +16 -9
- data/lib/logster/configuration.rb +12 -2
- data/lib/logster/defer_logger.rb +1 -1
- data/lib/logster/logger.rb +12 -0
- data/lib/logster/message.rb +89 -30
- data/lib/logster/middleware/viewer.rb +44 -8
- data/lib/logster/redis_store.rb +69 -51
- data/lib/logster/suppression_pattern.rb +1 -1
- data/lib/logster/version.rb +1 -1
- data/logster.gemspec +1 -1
- data/test/logster/middleware/test_viewer.rb +100 -0
- data/test/logster/test_base_store.rb +16 -0
- data/test/logster/test_defer_logger.rb +1 -1
- data/test/logster/test_message.rb +142 -54
- data/test/logster/test_redis_store.rb +99 -39
- metadata +11 -6
@@ -38,7 +38,7 @@ module Logster
|
|
38
38
|
|
39
39
|
elsif resource =~ /\/message\/([0-9a-f]+)$/
|
40
40
|
if env[REQUEST_METHOD] != "DELETE"
|
41
|
-
return method_not_allowed("DELETE
|
41
|
+
return method_not_allowed("DELETE")
|
42
42
|
end
|
43
43
|
|
44
44
|
key = $1
|
@@ -87,7 +87,7 @@ module Logster
|
|
87
87
|
|
88
88
|
elsif resource =~ /\/clear$/
|
89
89
|
if env[REQUEST_METHOD] != "POST"
|
90
|
-
return method_not_allowed("POST
|
90
|
+
return method_not_allowed("POST")
|
91
91
|
end
|
92
92
|
Logster.store.clear
|
93
93
|
return [200, {}, ["Messages cleared"]]
|
@@ -139,12 +139,12 @@ module Logster
|
|
139
139
|
|
140
140
|
set_name = $1
|
141
141
|
req = Rack::Request.new(env)
|
142
|
-
return method_not_allowed if req.request_method == "GET"
|
142
|
+
return method_not_allowed(%w[POST PUT DELETE]) if req.request_method == "GET"
|
143
143
|
|
144
144
|
update_patterns(set_name, req)
|
145
145
|
elsif resource == "/reset-count.json"
|
146
146
|
req = Rack::Request.new(env)
|
147
|
-
return method_not_allowed("PUT
|
147
|
+
return method_not_allowed("PUT") if req.request_method != "PUT"
|
148
148
|
pattern = nil
|
149
149
|
if [true, "true"].include?(req.params["hard"])
|
150
150
|
pattern = Logster.store.ignore.find do |patt|
|
@@ -170,6 +170,16 @@ module Logster
|
|
170
170
|
else
|
171
171
|
not_found
|
172
172
|
end
|
173
|
+
elsif resource == '/solve-group'
|
174
|
+
return not_allowed unless Logster.config.enable_custom_patterns_via_ui
|
175
|
+
req = Rack::Request.new(env)
|
176
|
+
return method_not_allowed("POST") if req.request_method != "POST"
|
177
|
+
group = Logster.store.find_pattern_groups do |patt|
|
178
|
+
patt.inspect == req.params["regex"]
|
179
|
+
end.first
|
180
|
+
return not_found("No such pattern group exists") if !group
|
181
|
+
group.messages_keys.each { |k| Logster.store.solve(k) }
|
182
|
+
return [200, {}, []]
|
173
183
|
else
|
174
184
|
not_found
|
175
185
|
end
|
@@ -243,7 +253,7 @@ module Logster
|
|
243
253
|
when "DELETE"
|
244
254
|
record.destroy
|
245
255
|
else
|
246
|
-
return method_not_allowed(
|
256
|
+
return method_not_allowed(%w[POST PUT DELETE])
|
247
257
|
end
|
248
258
|
|
249
259
|
[200, { "Content-Type" => "application/json" }, [JSON.generate(pattern: record.to_s)]]
|
@@ -277,8 +287,11 @@ module Logster
|
|
277
287
|
[403, {}, [message]]
|
278
288
|
end
|
279
289
|
|
280
|
-
def method_not_allowed(
|
281
|
-
|
290
|
+
def method_not_allowed(allowed_methods)
|
291
|
+
if Array === allowed_methods
|
292
|
+
allowed_methods = allowed_methods.join(", ")
|
293
|
+
end
|
294
|
+
[405, { "Allow" => allowed_methods }, []]
|
282
295
|
end
|
283
296
|
|
284
297
|
def parse_regex(string)
|
@@ -317,13 +330,36 @@ module Logster
|
|
317
330
|
Rack::Utils.escape_html(JSON.fast_generate(payload))
|
318
331
|
end
|
319
332
|
|
333
|
+
def preload_backtrace_data
|
334
|
+
gems_data = []
|
335
|
+
Gem::Specification.find_all do |gem|
|
336
|
+
url = gem.metadata["source_code_uri"] || gem.homepage
|
337
|
+
if url && url.match(/^https?:\/\/github.com\//)
|
338
|
+
gems_data << { name: gem.name, url: url }
|
339
|
+
end
|
340
|
+
end
|
341
|
+
{
|
342
|
+
gems_data: gems_data,
|
343
|
+
directories: Logster.config.project_directories
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
320
347
|
def body(preload)
|
321
348
|
root_url = @logs_path
|
322
349
|
root_url += "/" if root_url[-1] != "/"
|
323
350
|
preload.merge!(
|
324
351
|
env_expandable_keys: Logster.config.env_expandable_keys,
|
325
|
-
patterns_enabled: Logster.config.enable_custom_patterns_via_ui
|
352
|
+
patterns_enabled: Logster.config.enable_custom_patterns_via_ui,
|
353
|
+
application_version: Logster.config.application_version
|
326
354
|
)
|
355
|
+
backtrace_links_enabled = Logster.config.enable_backtrace_links
|
356
|
+
gems_dir = Logster.config.gems_dir
|
357
|
+
gems_dir += "/" if gems_dir[-1] != "/"
|
358
|
+
preload.merge!(gems_dir: gems_dir, backtrace_links_enabled: backtrace_links_enabled)
|
359
|
+
|
360
|
+
if backtrace_links_enabled
|
361
|
+
preload.merge!(preload_backtrace_data)
|
362
|
+
end
|
327
363
|
<<~HTML
|
328
364
|
<!doctype html>
|
329
365
|
<html>
|
data/lib/logster/redis_store.rb
CHANGED
@@ -6,6 +6,7 @@ require 'logster/redis_rate_limiter'
|
|
6
6
|
|
7
7
|
module Logster
|
8
8
|
class RedisStore < BaseStore
|
9
|
+
ENV_PREFIX = "logster-env-"
|
9
10
|
|
10
11
|
attr_accessor :redis, :max_backlog, :redis_raw_connection
|
11
12
|
attr_writer :redis_prefix
|
@@ -21,15 +22,14 @@ module Logster
|
|
21
22
|
def save(message)
|
22
23
|
if keys = message.solved_keys
|
23
24
|
keys.each do |solved|
|
24
|
-
return
|
25
|
+
return false if @redis.hget(solved_key, solved)
|
25
26
|
end
|
26
27
|
end
|
27
|
-
apply_max_size_limit(message)
|
28
28
|
|
29
29
|
@redis.multi do
|
30
30
|
@redis.hset(grouping_key, message.grouping_key, message.key)
|
31
31
|
@redis.rpush(list_key, message.key)
|
32
|
-
update_message(message)
|
32
|
+
update_message(message, save_env: true)
|
33
33
|
end
|
34
34
|
|
35
35
|
trim
|
@@ -46,7 +46,7 @@ module Logster
|
|
46
46
|
save_pattern_group(group) if group.changed?
|
47
47
|
end
|
48
48
|
@redis.hdel(hash_key, msg.key)
|
49
|
-
|
49
|
+
delete_env(msg.key)
|
50
50
|
@redis.hdel(grouping_key, msg.grouping_key)
|
51
51
|
@redis.lrem(list_key, -1, msg.key)
|
52
52
|
end
|
@@ -60,26 +60,26 @@ module Logster
|
|
60
60
|
save_pattern_group(group) if group.changed?
|
61
61
|
end
|
62
62
|
@redis.hdel(hash_key, message_keys)
|
63
|
-
@redis.hdel(env_key, message_keys)
|
64
63
|
@redis.hdel(grouping_key, grouping_keys)
|
65
64
|
message_keys.each do |k|
|
66
65
|
@redis.lrem(list_key, -1, k)
|
66
|
+
delete_env(k)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
def replace_and_bump(message
|
71
|
+
def replace_and_bump(message)
|
72
72
|
# TODO make it atomic
|
73
73
|
exists = @redis.hexists(hash_key, message.key)
|
74
74
|
return false unless exists
|
75
75
|
|
76
76
|
@redis.multi do
|
77
77
|
@redis.hset(hash_key, message.key, message.to_json(exclude_env: true))
|
78
|
-
|
78
|
+
push_env(message.key, message.env_buffer) if message.has_env_buffer?
|
79
79
|
@redis.lrem(list_key, -1, message.key)
|
80
80
|
@redis.rpush(list_key, message.key)
|
81
81
|
end
|
82
|
-
|
82
|
+
message.env_buffer = [] if message.has_env_buffer?
|
83
83
|
check_rate_limits(message.severity)
|
84
84
|
|
85
85
|
true
|
@@ -164,22 +164,21 @@ module Logster
|
|
164
164
|
def clear
|
165
165
|
RedisRateLimiter.clear_all(@redis)
|
166
166
|
@redis.del(solved_key)
|
167
|
+
all_keys = @redis.lrange(list_key, 0, -1)
|
167
168
|
@redis.del(list_key)
|
168
|
-
|
169
|
-
if
|
169
|
+
protected_keys = @redis.smembers(protected_key) || []
|
170
|
+
if protected_keys.empty?
|
170
171
|
@redis.del(hash_key)
|
171
|
-
|
172
|
+
all_keys.each { |k| delete_env(k) }
|
172
173
|
@redis.del(pattern_groups_key)
|
173
174
|
@redis.del(grouping_key)
|
174
175
|
else
|
175
|
-
|
176
|
-
protected_env = @redis.mapped_hmget(env_key, *keys)
|
176
|
+
protected_messages = @redis.mapped_hmget(hash_key, *protected_keys)
|
177
177
|
@redis.del(hash_key)
|
178
|
-
@redis.
|
179
|
-
|
180
|
-
@redis.mapped_hmset(env_key, protected_env)
|
178
|
+
@redis.mapped_hmset(hash_key, protected_messages)
|
179
|
+
(all_keys - protected_keys).each { |k| delete_env(k) }
|
181
180
|
|
182
|
-
sorted =
|
181
|
+
sorted = protected_messages
|
183
182
|
.values
|
184
183
|
.map { |string|
|
185
184
|
Message.from_json(string) rescue nil
|
@@ -203,10 +202,10 @@ module Logster
|
|
203
202
|
# Delete everything, included protected messages
|
204
203
|
# (use in tests)
|
205
204
|
def clear_all
|
205
|
+
@redis.lrange(list_key, 0, -1).each { |k| delete_env(k) }
|
206
206
|
@redis.del(list_key)
|
207
207
|
@redis.del(protected_key)
|
208
208
|
@redis.del(hash_key)
|
209
|
-
@redis.del(env_key)
|
210
209
|
@redis.del(grouping_key)
|
211
210
|
@redis.del(solved_key)
|
212
211
|
@redis.del(ignored_logs_count_key)
|
@@ -235,17 +234,35 @@ module Logster
|
|
235
234
|
bulk_get(@redis.lrange(list_key, 0, -1), with_env: with_env)
|
236
235
|
end
|
237
236
|
|
237
|
+
BULK_ENV_GET_LUA = <<~LUA
|
238
|
+
local results = {};
|
239
|
+
for i = 1, table.getn(KEYS), 1 do
|
240
|
+
results[i] = { KEYS[i], redis.call('LRANGE', KEYS[i], 0, -1) };
|
241
|
+
end
|
242
|
+
return results;
|
243
|
+
LUA
|
244
|
+
|
238
245
|
def bulk_get(message_keys, with_env: true)
|
239
246
|
return [] if !message_keys || message_keys.size == 0
|
240
|
-
envs =
|
247
|
+
envs = nil
|
248
|
+
if with_env
|
249
|
+
envs = {}
|
250
|
+
@redis.eval(
|
251
|
+
BULK_ENV_GET_LUA,
|
252
|
+
keys: message_keys.map { |k| env_prefix(k) }
|
253
|
+
).to_h.each do |k, v|
|
254
|
+
next if v.size == 0
|
255
|
+
parsed = v.size == 1 ? JSON.parse(v[0]) : v.map { |e| JSON.parse(e) }
|
256
|
+
envs[env_unprefix(k)] = parsed
|
257
|
+
end
|
258
|
+
end
|
241
259
|
messages = @redis.hmget(hash_key, message_keys).map! do |json|
|
242
260
|
next if !json || json.size == 0
|
243
261
|
message = Message.from_json(json)
|
244
|
-
if with_env
|
262
|
+
if with_env && envs
|
245
263
|
env = envs[message.key]
|
246
264
|
if !message.env || message.env.size == 0
|
247
|
-
env = env
|
248
|
-
message.env = env
|
265
|
+
message.env = env || {}
|
249
266
|
end
|
250
267
|
end
|
251
268
|
message
|
@@ -255,20 +272,20 @@ module Logster
|
|
255
272
|
end
|
256
273
|
|
257
274
|
def get_env(message_key)
|
258
|
-
|
259
|
-
return if !
|
260
|
-
JSON.parse(
|
275
|
+
envs = @redis.lrange(env_prefix(message_key), 0, -1)
|
276
|
+
return if !envs || envs.size == 0
|
277
|
+
envs.size == 1 ? JSON.parse(envs[0]) : envs.map { |j| JSON.parse(j) }
|
261
278
|
end
|
262
279
|
|
263
280
|
def protect(message_key)
|
264
|
-
if message = get(message_key)
|
281
|
+
if message = get(message_key, load_env: false)
|
265
282
|
message.protected = true
|
266
283
|
update_message(message)
|
267
284
|
end
|
268
285
|
end
|
269
286
|
|
270
287
|
def unprotect(message_key)
|
271
|
-
if message = get(message_key)
|
288
|
+
if message = get(message_key, load_env: false)
|
272
289
|
message.protected = false
|
273
290
|
update_message(message)
|
274
291
|
else
|
@@ -376,13 +393,11 @@ module Logster
|
|
376
393
|
|
377
394
|
protected
|
378
395
|
|
379
|
-
def clear_solved
|
380
|
-
|
396
|
+
def clear_solved
|
381
397
|
ignores = Set.new(@redis.hkeys(solved_key) || [])
|
382
398
|
|
383
399
|
if ignores.length > 0
|
384
|
-
|
385
|
-
message_keys = @redis.lrange(list_key, start, -1) || []
|
400
|
+
message_keys = @redis.lrange(list_key, 0, -1) || []
|
386
401
|
|
387
402
|
bulk_get(message_keys).each do |message|
|
388
403
|
unless (ignores & (message.solved_keys || [])).empty?
|
@@ -397,7 +412,7 @@ module Logster
|
|
397
412
|
removed_keys = []
|
398
413
|
while removed_key = @redis.lpop(list_key)
|
399
414
|
unless @redis.sismember(protected_key, removed_key)
|
400
|
-
rmsg = get
|
415
|
+
rmsg = get(removed_key, load_env: false)
|
401
416
|
delete(rmsg)
|
402
417
|
break
|
403
418
|
else
|
@@ -410,9 +425,9 @@ module Logster
|
|
410
425
|
end
|
411
426
|
end
|
412
427
|
|
413
|
-
def update_message(message)
|
428
|
+
def update_message(message, save_env: false)
|
414
429
|
@redis.hset(hash_key, message.key, message.to_json(exclude_env: true))
|
415
|
-
|
430
|
+
push_env(message.key, message.env) if save_env
|
416
431
|
if message.protected
|
417
432
|
@redis.sadd(protected_key, message.key)
|
418
433
|
else
|
@@ -571,7 +586,7 @@ module Logster
|
|
571
586
|
end
|
572
587
|
|
573
588
|
def protected_key
|
574
|
-
@
|
589
|
+
@protected_key ||= "__LOGSTER__SAVED"
|
575
590
|
end
|
576
591
|
|
577
592
|
def grouping_key
|
@@ -608,22 +623,6 @@ module Logster
|
|
608
623
|
)
|
609
624
|
end
|
610
625
|
|
611
|
-
def apply_max_size_limit(message)
|
612
|
-
size = message.to_json(exclude_env: true).bytesize
|
613
|
-
env_size = message.env_json.bytesize
|
614
|
-
max_size = Logster.config.maximum_message_size_bytes
|
615
|
-
if size + env_size > max_size
|
616
|
-
# env is most likely the reason for the large size
|
617
|
-
# truncate it so the overall size is < the limit
|
618
|
-
if Array === message.env
|
619
|
-
# the - 1 at the end ensures the size goes a little bit below the limit
|
620
|
-
truncate_at = (message.env.size.to_f * max_size.to_f / (env_size + size)).to_i - 1
|
621
|
-
truncate_at = 1 if truncate_at < 1
|
622
|
-
message.env = message.env[0...truncate_at]
|
623
|
-
end
|
624
|
-
end
|
625
|
-
end
|
626
|
-
|
627
626
|
def register_rate_limit(severities, limit, duration, callback)
|
628
627
|
severities = [severities] unless severities.is_a?(Array)
|
629
628
|
redis = (@redis_raw_connection && @redis_prefix) ? @redis_raw_connection : @redis
|
@@ -636,5 +635,24 @@ module Logster
|
|
636
635
|
rate_limits[self.redis_prefix] << rate_limiter
|
637
636
|
rate_limiter
|
638
637
|
end
|
638
|
+
|
639
|
+
def push_env(message_key, env)
|
640
|
+
prefixed = env_prefix(message_key)
|
641
|
+
env = [env] unless Array === env
|
642
|
+
@redis.lpush(prefixed, env.map(&:to_json).reverse)
|
643
|
+
@redis.ltrim(prefixed, 0, Logster.config.max_env_count_per_message - 1)
|
644
|
+
end
|
645
|
+
|
646
|
+
def delete_env(message_key)
|
647
|
+
@redis.del(env_prefix(message_key))
|
648
|
+
end
|
649
|
+
|
650
|
+
def env_unprefix(key)
|
651
|
+
key.sub(ENV_PREFIX, "")
|
652
|
+
end
|
653
|
+
|
654
|
+
def env_prefix(key)
|
655
|
+
ENV_PREFIX + key
|
656
|
+
end
|
639
657
|
end
|
640
658
|
end
|
@@ -24,7 +24,7 @@ module Logster
|
|
24
24
|
def retro_delete_messages
|
25
25
|
keys = []
|
26
26
|
grouping_keys = []
|
27
|
-
@store.get_all_messages.each do |message|
|
27
|
+
@store.get_all_messages(with_env: false).each do |message|
|
28
28
|
if message =~ self.pattern
|
29
29
|
keys << message.key
|
30
30
|
grouping_keys << message.grouping_key
|
data/lib/logster/version.rb
CHANGED
data/logster.gemspec
CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency "guard"
|
33
33
|
spec.add_development_dependency "guard-minitest"
|
34
34
|
spec.add_development_dependency "timecop"
|
35
|
-
spec.add_development_dependency "byebug"
|
35
|
+
spec.add_development_dependency "byebug", "~> 11.0.0"
|
36
36
|
spec.add_development_dependency "rubocop", "~> 0.69.0"
|
37
37
|
spec.add_development_dependency "rubocop-discourse"
|
38
38
|
end
|
@@ -370,4 +370,104 @@ class TestViewer < Minitest::Test
|
|
370
370
|
response = request.get("/logsie/fetch-env/123456abc.json")
|
371
371
|
assert_equal(404, response.status)
|
372
372
|
end
|
373
|
+
|
374
|
+
def test_solve_group_api_requires_post_request
|
375
|
+
Logster.config.enable_custom_patterns_via_ui = true
|
376
|
+
Logster::GroupingPattern.new(/gotta be post/).save
|
377
|
+
msg = Logster.store.report(
|
378
|
+
Logger::WARN,
|
379
|
+
'',
|
380
|
+
'gotta be post 22',
|
381
|
+
env: { "application_version" => "abc" },
|
382
|
+
backtrace: "aa"
|
383
|
+
)
|
384
|
+
latest = Logster.store.latest
|
385
|
+
assert_equal(1, latest.size)
|
386
|
+
assert_equal(msg.key, latest.first["messages"].first.key)
|
387
|
+
%i[get head options].each do |m|
|
388
|
+
response = request.public_send(m, "/logsie/solve-group", params: { regex: "/gotta be post/" })
|
389
|
+
assert_equal(405, response.status)
|
390
|
+
assert_equal("POST", response.headers["Allow"])
|
391
|
+
end
|
392
|
+
latest = Logster.store.latest
|
393
|
+
assert_equal(1, latest.size)
|
394
|
+
assert_equal(msg.key, latest.first["messages"].first.key)
|
395
|
+
ensure
|
396
|
+
Logster.config.enable_custom_patterns_via_ui = false
|
397
|
+
end
|
398
|
+
|
399
|
+
def test_solve_group_returns_404_when_pattern_doesnt_exist
|
400
|
+
Logster.config.enable_custom_patterns_via_ui = true
|
401
|
+
Logster::GroupingPattern.new(/some pattern/).save
|
402
|
+
msg = Logster.store.report(
|
403
|
+
Logger::WARN,
|
404
|
+
'',
|
405
|
+
'some pattern 22',
|
406
|
+
env: { "application_version" => "abc" },
|
407
|
+
backtrace: "aa"
|
408
|
+
)
|
409
|
+
latest = Logster.store.latest
|
410
|
+
assert_equal(1, latest.size)
|
411
|
+
assert_equal(msg.key, latest.first["messages"].first.key)
|
412
|
+
response = request.post("/logsie/solve-group", params: { regex: "/i dont exist/" })
|
413
|
+
assert_equal(404, response.status)
|
414
|
+
latest = Logster.store.latest
|
415
|
+
assert_equal(1, latest.size)
|
416
|
+
assert_equal(msg.key, latest.first["messages"].first.key)
|
417
|
+
ensure
|
418
|
+
Logster.config.enable_custom_patterns_via_ui = false
|
419
|
+
end
|
420
|
+
|
421
|
+
def test_solving_grouped_messages
|
422
|
+
Logster.config.enable_custom_patterns_via_ui = true
|
423
|
+
backtrace = "a b c d"
|
424
|
+
Logster::GroupingPattern.new(/test pattern/).save
|
425
|
+
msg1 = Logster.store.report(Logger::WARN, '', 'test pattern 1', backtrace: backtrace)
|
426
|
+
msg2 = Logster.store.report(
|
427
|
+
Logger::WARN,
|
428
|
+
'',
|
429
|
+
'test pattern 2',
|
430
|
+
env: { "application_version" => "abc" },
|
431
|
+
backtrace: backtrace
|
432
|
+
)
|
433
|
+
msg3 = Logster.store.report(
|
434
|
+
Logger::WARN,
|
435
|
+
'',
|
436
|
+
'test pattern 3',
|
437
|
+
env: [{ "application_version" => "def" }, { "application_version" => "ghi" }],
|
438
|
+
backtrace: backtrace
|
439
|
+
)
|
440
|
+
group = Logster.store.find_pattern_groups { |p| p == /test pattern/ }.first
|
441
|
+
assert_equal([msg3, msg2, msg1].map(&:key), group.messages_keys)
|
442
|
+
|
443
|
+
latest = Logster.store.latest
|
444
|
+
assert_equal(1, latest.size)
|
445
|
+
assert_equal([msg1, msg2, msg3].map(&:key).sort, latest.first["messages"].map(&:key).sort)
|
446
|
+
|
447
|
+
response = request.post("/logsie/solve-group", params: { regex: "/test pattern/" })
|
448
|
+
group = Logster.store.find_pattern_groups { |p| p == /test pattern/ }.first
|
449
|
+
assert_equal([msg1.key], group.messages_keys)
|
450
|
+
assert_equal(200, response.status)
|
451
|
+
|
452
|
+
latest = Logster.store.latest
|
453
|
+
# msg1 remains cause it doesn't have application_version
|
454
|
+
assert_equal([msg1.key], latest.first["messages"].map(&:key))
|
455
|
+
assert_equal(1, latest.size)
|
456
|
+
|
457
|
+
msg4 = Logster.store.report(Logger::WARN, '', 'test pattern 4', backtrace: backtrace)
|
458
|
+
%w[abc def ghi].each do |version|
|
459
|
+
Logster.store.report(
|
460
|
+
Logger::WARN,
|
461
|
+
'',
|
462
|
+
'test pattern 5',
|
463
|
+
env: { "application_version" => version },
|
464
|
+
backtrace: backtrace
|
465
|
+
)
|
466
|
+
end
|
467
|
+
latest = Logster.store.latest
|
468
|
+
assert_equal([msg1.key, msg4.key].sort, latest.first["messages"].map(&:key).sort)
|
469
|
+
assert_equal(1, latest.size)
|
470
|
+
ensure
|
471
|
+
Logster.config.enable_custom_patterns_via_ui = false
|
472
|
+
end
|
373
473
|
end
|