logster 1.4.0.pre → 2.0.0.pre

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.
@@ -20,7 +20,6 @@ module Logster::Rails
20
20
  Logster.logger = ::Rails.logger = config.logger = logger
21
21
  end
22
22
 
23
-
24
23
  def self.initialize!(app)
25
24
  return unless Logster.config.environments.include?(Rails.env.to_sym)
26
25
 
@@ -8,7 +8,7 @@ module Logster
8
8
 
9
9
  attr_reader :duration, :callback
10
10
 
11
- def self.clear_all(redis, redis_prefix=nil)
11
+ def self.clear_all(redis, redis_prefix = nil)
12
12
  prefix = key_prefix(redis_prefix)
13
13
 
14
14
  redis.eval "
@@ -123,7 +123,7 @@ module Logster
123
123
  end
124
124
 
125
125
  def save(message)
126
- if keys=message.solved_keys
126
+ if keys = message.solved_keys
127
127
  keys.each do |solved|
128
128
  return true if @redis.hget(solved_key, solved)
129
129
  end
@@ -144,18 +144,20 @@ module Logster
144
144
  def delete(msg)
145
145
  @redis.multi do
146
146
  @redis.hdel(hash_key, msg.key)
147
+ @redis.hdel(env_key, msg.key)
147
148
  @redis.hdel(grouping_key, msg.grouping_key)
148
149
  @redis.lrem(list_key, -1, msg.key)
149
150
  end
150
151
  end
151
152
 
152
- def replace_and_bump(message)
153
+ def replace_and_bump(message, save_env: true)
153
154
  # TODO make it atomic
154
155
  exists = @redis.hexists(hash_key, message.key)
155
156
  return false unless exists
156
157
 
157
158
  @redis.multi do
158
- @redis.hset(hash_key, message.key, message.to_json)
159
+ @redis.hset(hash_key, message.key, message.to_json(exclude_env: true))
160
+ @redis.hset(env_key, message.key, (message.env || {}).to_json) if save_env
159
161
  @redis.lrem(list_key, -1, message.key)
160
162
  @redis.rpush(list_key, message.key)
161
163
  end
@@ -183,7 +185,7 @@ module Logster
183
185
  clear_solved
184
186
  end
185
187
 
186
- def latest(opts={})
188
+ def latest(opts = {})
187
189
  limit = opts[:limit] || 50
188
190
  severity = opts[:severity]
189
191
  before = opts[:before]
@@ -200,13 +202,12 @@ module Logster
200
202
 
201
203
  begin
202
204
  keys = @redis.lrange(list_key, start, finish) || []
203
- break unless keys and keys.count > 0
204
- rows = @redis.hmget(hash_key, keys)
205
+ break unless keys && (keys.count > 0)
206
+ rows = bulk_get(keys)
205
207
 
206
208
  temp = []
207
209
 
208
- rows.each do |s|
209
- row = Message.from_json(s)
210
+ rows.each do |row|
210
211
  break if before && before == row.key
211
212
  row = nil if severity && !severity.include?(row.severity)
212
213
 
@@ -236,10 +237,14 @@ module Logster
236
237
  keys = @redis.smembers(protected_key) || []
237
238
  if keys.empty?
238
239
  @redis.del(hash_key)
240
+ @redis.del(env_key)
239
241
  else
240
242
  protected = @redis.mapped_hmget(hash_key, *keys)
243
+ protected_env = @redis.mapped_hmget(env_key, *keys)
241
244
  @redis.del(hash_key)
245
+ @redis.del(env_key)
242
246
  @redis.mapped_hmset(hash_key, protected)
247
+ @redis.mapped_hmset(env_key, protected_env)
243
248
 
244
249
  sorted = protected
245
250
  .values
@@ -264,15 +269,39 @@ module Logster
264
269
  @redis.del(list_key)
265
270
  @redis.del(protected_key)
266
271
  @redis.del(hash_key)
272
+ @redis.del(env_key)
267
273
  @redis.del(grouping_key)
268
274
  @redis.del(solved_key)
269
275
  end
270
276
 
271
- def get(message_key)
277
+ def get(message_key, load_env: true)
272
278
  json = @redis.hget(hash_key, message_key)
273
279
  return nil unless json
274
280
 
275
- Message.from_json(json)
281
+ message = Message.from_json(json)
282
+ if load_env
283
+ message.env = get_env(message_key) || {}
284
+ end
285
+ message
286
+ end
287
+
288
+ def bulk_get(message_keys)
289
+ envs = @redis.hmget(env_key, message_keys)
290
+ @redis.hmget(hash_key, message_keys).map!.with_index do |json, ind|
291
+ message = Message.from_json(json)
292
+ env = envs[ind]
293
+ if !message.env || message.env.size == 0
294
+ env = env && env.size > 0 ? ::JSON.parse(env) : {}
295
+ message.env = env
296
+ end
297
+ message
298
+ end
299
+ end
300
+
301
+ def get_env(message_key)
302
+ json = @redis.hget(env_key, message_key)
303
+ return if !json || json.size == 0
304
+ JSON.parse(json)
276
305
  end
277
306
 
278
307
  def protect(message_key)
@@ -321,10 +350,9 @@ module Logster
321
350
 
322
351
  if ignores.length > 0
323
352
  start = count ? 0 - count : 0
324
- message_keys = @redis.lrange(list_key, start, -1 ) || []
353
+ message_keys = @redis.lrange(list_key, start, -1) || []
325
354
 
326
- @redis.hmget(hash_key, message_keys).each do |json|
327
- message = Message.from_json(json)
355
+ bulk_get(message_keys).each do |message|
328
356
  unless (ignores & (message.solved_keys || [])).empty?
329
357
  delete message
330
358
  end
@@ -339,6 +367,7 @@ module Logster
339
367
  unless @redis.sismember(protected_key, removed_key)
340
368
  rmsg = get removed_key
341
369
  @redis.hdel(hash_key, rmsg.key)
370
+ @redis.hdel(env_key, rmsg.key)
342
371
  @redis.hdel(grouping_key, rmsg.grouping_key)
343
372
  break
344
373
  else
@@ -352,7 +381,8 @@ module Logster
352
381
  end
353
382
 
354
383
  def update_message(message)
355
- @redis.hset(hash_key, message.key, message.to_json)
384
+ @redis.hset(hash_key, message.key, message.to_json(exclude_env: true))
385
+ @redis.hset(env_key, message.key, (message.env || {}).to_json)
356
386
  if message.protected
357
387
  @redis.sadd(protected_key, message.key)
358
388
  else
@@ -385,7 +415,7 @@ module Logster
385
415
  start = -limit
386
416
  finish = -1
387
417
 
388
- return [start,finish] unless before || after
418
+ return [start, finish] unless before || after
389
419
 
390
420
  found = nil
391
421
  find = before || after
@@ -463,12 +493,12 @@ module Logster
463
493
  env_matches?(value, search)
464
494
  else
465
495
  case search
466
- when Regexp
467
- value.to_s =~ search
468
- when String
469
- value.to_s =~ Regexp.new(search, Regexp::IGNORECASE)
496
+ when Regexp
497
+ value.to_s =~ search
498
+ when String
499
+ value.to_s =~ Regexp.new(search, Regexp::IGNORECASE)
470
500
  else
471
- false
501
+ false
472
502
  end
473
503
  end
474
504
  end
@@ -506,6 +536,10 @@ module Logster
506
536
  @hash_key ||= "__LOGSTER__MAP"
507
537
  end
508
538
 
539
+ def env_key
540
+ @env_key ||= "__LOGSTER__ENV_MAP"
541
+ end
542
+
509
543
  def protected_key
510
544
  @saved_key ||= "__LOGSTER__SAVED"
511
545
  end
@@ -1,3 +1,3 @@
1
1
  module Logster
2
- VERSION = "1.4.0.pre"
2
+ VERSION = "2.0.0.pre"
3
3
  end
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "guard-minitest"
32
32
  spec.add_development_dependency "timecop"
33
33
  spec.add_development_dependency "byebug"
34
+ spec.add_development_dependency "rubocop", "~> 0.61.1"
34
35
  end
@@ -6,5 +6,5 @@ Logster.config.application_version = "ABC123"
6
6
  Logster.store = Logster::RedisStore.new
7
7
 
8
8
  10.times do
9
- Logster.store.report(Logger::WARN, "application", "test warning", backtrace: "method1\nmethod2", env: {something: ["hello world", "hello places"], another: {thing: "something else"}})
9
+ Logster.store.report(Logger::WARN, "application", "test warning", backtrace: "method1\nmethod2", env: { something: ["hello world", "hello places"], another: { thing: "something else" } })
10
10
  end
@@ -3,7 +3,6 @@ require 'rack'
3
3
  require 'logster/redis_store'
4
4
  require 'logster/middleware/reporter'
5
5
 
6
-
7
6
  class TestReporter < Minitest::Test
8
7
 
9
8
  def test_logs_errors
@@ -18,4 +17,3 @@ class TestReporter < Minitest::Test
18
17
  end
19
18
 
20
19
  end
21
-
@@ -33,9 +33,9 @@ class TestViewer < Minitest::Test
33
33
  assert_nil(viewer.send(:resolve_path, "/logs"))
34
34
  assert_nil(viewer.send(:resolve_path, "/admin/logsie"))
35
35
  assert_nil(viewer.send(:resolve_path, "/admin/logsie/bla"))
36
- assert_equal("/",viewer.send(:resolve_path, "/logsie"))
37
- assert_equal("/",viewer.send(:resolve_path, "/logsie/"))
38
- assert_equal("/hello/world",viewer.send(:resolve_path, "/logsie/hello/world"))
36
+ assert_equal("/", viewer.send(:resolve_path, "/logsie"))
37
+ assert_equal("/", viewer.send(:resolve_path, "/logsie/"))
38
+ assert_equal("/hello/world", viewer.send(:resolve_path, "/logsie/hello/world"))
39
39
  end
40
40
 
41
41
  def test_search_raceguard_s
@@ -53,7 +53,7 @@ class TestViewer < Minitest::Test
53
53
  def test_search_raceguard_f
54
54
  response = request.get("/logsie/messages.json?filter=0_1_2_3_4")
55
55
  result = JSON.parse(response.body)
56
- assert_equal([0,1,2,3,4], result['filter'])
56
+ assert_equal([0, 1, 2, 3, 4], result['filter'])
57
57
  end
58
58
 
59
59
  def test_regex_parse
@@ -53,14 +53,14 @@ class TestBaseStore < Minitest::Test
53
53
 
54
54
  def test_ignore_pattern_basic
55
55
  @store.ignore = [
56
- Logster::IgnorePattern.new(nil, {username: 'CausingErrors'})
56
+ Logster::IgnorePattern.new(nil, username: 'CausingErrors')
57
57
  ]
58
58
  @store.report(Logger::WARN, "test", "Foobar") #
59
- @store.report(Logger::WARN, "test", "Foobar", { env: { username: 'CausingErrors' }})
59
+ @store.report(Logger::WARN, "test", "Foobar", env: { username: 'CausingErrors' })
60
60
  @store.report(Logger::WARN, "test", "Foobar", env: nil)
61
- @store.report(Logger::WARN, "test", "Something Else", { env: { username: 'CausingErrors' }})
62
- @store.report(Logger::WARN, "test", "Something Else", { env: { 'username' => 'CausingErrors' }})
63
- @store.report(Logger::WARN, "test", "Something Else", { env: { username: 'GoodPerson' }}) #
61
+ @store.report(Logger::WARN, "test", "Something Else", env: { username: 'CausingErrors' })
62
+ @store.report(Logger::WARN, "test", "Something Else", env: { 'username' => 'CausingErrors' })
63
+ @store.report(Logger::WARN, "test", "Something Else", env: { username: 'GoodPerson' }) #
64
64
  @store.report(Logger::WARN, "test", "Can't verify CSRF token authenticity") #
65
65
 
66
66
  assert_equal(4, @store.count)
@@ -69,10 +69,10 @@ class TestBaseStore < Minitest::Test
69
69
  def test_ignore_pattern_real
70
70
  @store.ignore = [
71
71
  /^ActionController::RoutingError \(No route matches/,
72
- Logster::IgnorePattern.new("Can't verify CSRF token authenticity", { REQUEST_URI: /\/trackback\/$/ })
72
+ Logster::IgnorePattern.new("Can't verify CSRF token authenticity", REQUEST_URI: /\/trackback\/$/)
73
73
  ]
74
74
  # blocked
75
- @store.report(Logger::WARN, "whatever", "Can't verify CSRF token authenticity", {
75
+ @store.report(Logger::WARN, "whatever", "Can't verify CSRF token authenticity",
76
76
  env: {
77
77
  HTTP_HOST: 'meta.discourse.org',
78
78
  REQUEST_URI: '/t/use-more-standard-smiley-codes-instead-of-smile/1822/trackback/',
@@ -85,9 +85,9 @@ class TestBaseStore < Minitest::Test
85
85
  blog_name: 'get free spam for cheap'
86
86
  }
87
87
  }
88
- })
88
+ )
89
89
  # logged
90
- @store.report(Logger::WARN, "whatever", "Can't verify CSRF token authenticity", {
90
+ @store.report(Logger::WARN, "whatever", "Can't verify CSRF token authenticity",
91
91
  env: {
92
92
  HTTP_HOST: 'meta.discourse.org',
93
93
  REQUEST_URI: '/session',
@@ -99,12 +99,12 @@ class TestBaseStore < Minitest::Test
99
99
  form_authenticity_token: 'incorrect'
100
100
  }
101
101
  }
102
- })
102
+ )
103
103
  assert_equal(1, @store.count)
104
104
  end
105
105
 
106
106
  def test_timestamp
107
- time = Time.now - 24*60*60
107
+ time = Time.now - 24 * 60 * 60
108
108
  message = @store.report(Logger::WARN, "test", "B", timestamp: time)
109
109
 
110
110
  assert_equal(time, message.timestamp)
@@ -122,20 +122,20 @@ class TestBaseStore < Minitest::Test
122
122
  # Backtrace can be passed via backtrace param or env
123
123
  message = @store.report(Logger::WARN, "test", "A", backtrace: error.backtrace)
124
124
  assert_equal(error.backtrace.join("\n"), message.backtrace)
125
- message = @store.report(Logger::WARN, "test", "B", env: {backtrace: error.backtrace})
125
+ message = @store.report(Logger::WARN, "test", "B", env: { backtrace: error.backtrace })
126
126
  assert_equal(error.backtrace.join("\n"), message.backtrace)
127
127
 
128
128
  # Via env takes priority
129
- message = @store.report(Logger::WARN, "test", "C", backtrace: "Garbage", env: {backtrace: error.backtrace})
129
+ message = @store.report(Logger::WARN, "test", "C", backtrace: "Garbage", env: { backtrace: error.backtrace })
130
130
  assert_equal(error.backtrace.join("\n"), message.backtrace)
131
131
 
132
132
  # Backtrace is always a string
133
133
  # Cannot do an equal assert here, because it uses `caller` when not provided
134
134
  message = @store.report(Logger::WARN, "test", "D", backtrace: nil)
135
135
  assert_kind_of(String, message.backtrace)
136
- message = @store.report(Logger::WARN, "test", "E", env: {backtrace: nil})
136
+ message = @store.report(Logger::WARN, "test", "E", env: { backtrace: nil })
137
137
  assert_kind_of(String, message.backtrace)
138
- message = @store.report(Logger::WARN, "test", "F", backtrace: nil, env: {backtrace: nil})
138
+ message = @store.report(Logger::WARN, "test", "F", backtrace: nil, env: { backtrace: nil })
139
139
  assert_kind_of(String, message.backtrace)
140
140
  message = @store.report(Logger::WARN, "test", "G")
141
141
  assert_kind_of(String, message.backtrace)
@@ -17,25 +17,25 @@ class TestIgnorePattern < Minitest::Test
17
17
 
18
18
  def test_env_pattern
19
19
  msg = Logster::Message.new(Logger::WARN, "test", "my error")
20
- msg.env = {"frogs" => "are big"}
20
+ msg.env = { "frogs" => "are big" }
21
21
 
22
- pattern = Logster::IgnorePattern.new(nil, {frogs: "big"})
22
+ pattern = Logster::IgnorePattern.new(nil, frogs: "big")
23
23
 
24
24
  assert pattern.matches? msg
25
25
 
26
- msg.env = {legs: nil}
26
+ msg.env = { legs: nil }
27
27
  assert !(pattern.matches? msg)
28
28
 
29
- msg.env = {legs: 3}
29
+ msg.env = { legs: 3 }
30
30
  assert !(pattern.matches? msg)
31
31
 
32
- msg.env = {frogs: "small"}
32
+ msg.env = { frogs: "small" }
33
33
  assert !pattern.matches?(msg)
34
34
 
35
35
  pattern = Logster::IgnorePattern.new(nil, "small")
36
36
  assert pattern.matches? msg
37
37
 
38
- msg.env = {frogs: "big"}
38
+ msg.env = { frogs: "big" }
39
39
  assert !(pattern.matches? msg)
40
40
  end
41
41
  end
@@ -26,19 +26,19 @@ class TestLogger < Minitest::Test
26
26
  def test_per_thread_override
27
27
  @logger.override_level = 2
28
28
 
29
- @logger.add(0, "test", "prog", backtrace: "backtrace", env: {a: "x"})
29
+ @logger.add(0, "test", "prog", backtrace: "backtrace", env: { a: "x" })
30
30
  Thread.new do
31
- @logger.add(0, "test", "prog", backtrace: "backtrace", env: {a: "x"})
31
+ @logger.add(0, "test", "prog", backtrace: "backtrace", env: { a: "x" })
32
32
  end.join
33
33
 
34
34
  @logger.override_level = nil
35
- @logger.add(0, "test", "prog", backtrace: "backtrace", env: {a: "x"})
35
+ @logger.add(0, "test", "prog", backtrace: "backtrace", env: { a: "x" })
36
36
 
37
37
  assert_equal 2, @store.calls.length
38
38
  end
39
39
 
40
40
  def test_backtrace
41
- @logger.add(0, "test", "prog", backtrace: "backtrace", env: {a: "x"})
41
+ @logger.add(0, "test", "prog", backtrace: "backtrace", env: { a: "x" })
42
42
  assert_equal "backtrace", @store.calls[0][3][:backtrace]
43
43
  end
44
44
 
@@ -54,20 +54,26 @@ class TestLogger < Minitest::Test
54
54
  @other_store = TestStore.new
55
55
  @logger.chain(Logster::Logger.new(@other_store))
56
56
 
57
- @logger.add(0, "test", "prog", backtrace: "backtrace", env: {a: "x"})
57
+ @logger.add(0, "test", "prog", backtrace: "backtrace", env: { a: "x" })
58
58
 
59
59
  [@store, @other_store].each do |store|
60
60
  assert_equal "backtrace", store.calls[0][3][:backtrace]
61
61
  end
62
62
  end
63
63
 
64
+ def test_progname_parameter
65
+ @logger.add(0, "test")
66
+ progname = @store.calls[0][1]
67
+ assert_nil progname
68
+ end
69
+
64
70
  class PlayLogger
65
71
  attr_accessor :skip_store
66
72
  def initialize(tester)
67
73
  @tester = tester
68
74
  end
69
75
 
70
- def add(s,m,p,&block)
76
+ def add(s, m, p, &block)
71
77
  @tester.assert(skip_store)
72
78
  end
73
79
  end
@@ -14,14 +14,12 @@ class TestMessage < MiniTest::Test
14
14
 
15
15
  msg1.merge_similar_message(msg2)
16
16
 
17
- msg1 = Logster::Message.from_json(msg1.to_json)
18
-
19
17
  assert_equal(20, msg1.timestamp)
20
18
  assert_equal(10, msg1.first_timestamp)
21
19
 
22
20
  assert Array === msg1.env
23
21
  assert_equal(msg1.env.size, 2)
24
- assert({ "a" => "1", "b" => "2" } <= msg1.env[0])
22
+ assert({ a: "1", b: "2" } <= msg1.env[0])
25
23
  assert({ "a" => "2", "c" => "3" } <= msg1.env[1])
26
24
  end
27
25
 
@@ -32,14 +30,13 @@ class TestMessage < MiniTest::Test
32
30
  msg2 = Logster::Message.new(0, '', 'test', 20)
33
31
  msg2.env = [{ e: "ee", f: "ff" }, { g: "gg", h: "hh" }]
34
32
 
35
- msg1.merge_similar_message(msg2)
36
- msg1 = Logster::Message.from_json(msg1.to_json)
33
+ msg1.merge_similar_message(msg2)
37
34
 
38
35
  # new env should be an array, but it should never have
39
36
  # another array of envs within itself (hence flatten(1))
40
37
  assert_equal(msg1.env.size, 4)
41
- assert_equal(msg1.env.map(&:keys).flatten(1), %w{a b c d e f g h})
42
- assert_equal(msg1.env.map(&:values).flatten(1), %w{aa bb cc dd ee ff gg hh})
38
+ assert_equal(msg1.env.map(&:keys).flatten(1).map(&:to_s), %w{a b c d e f g h})
39
+ assert_equal(msg1.env.map(&:values).flatten(1).map(&:to_s), %w{aa bb cc dd ee ff gg hh})
43
40
  end
44
41
 
45
42
  def test_merge_messages_one_with_array_envs
@@ -49,12 +46,11 @@ class TestMessage < MiniTest::Test
49
46
  msg2 = Logster::Message.new(0, '', 'test', 20)
50
47
  msg2.env = { e: "ee", f: "ff" }
51
48
 
52
- msg1.merge_similar_message(msg2)
53
- msg1 = Logster::Message.from_json(msg1.to_json)
49
+ msg1.merge_similar_message(msg2)
54
50
 
55
51
  assert_equal(msg1.env.size, 3)
56
- assert_equal(msg1.env.map(&:keys).flatten(1), %w{a b c d e f})
57
- assert_equal(msg1.env.map(&:values).flatten(1), %w{aa bb cc dd ee ff})
52
+ assert_equal(msg1.env.map(&:keys).flatten(1).map(&:to_s), %w{a b c d e f})
53
+ assert_equal(msg1.env.map(&:values).flatten(1).map(&:to_s), %w{aa bb cc dd ee ff})
58
54
  end
59
55
 
60
56
  def test_adds_application_version
@@ -75,8 +71,8 @@ class TestMessage < MiniTest::Test
75
71
 
76
72
  assert_equal(msg1.grouping_key, msg2.grouping_key)
77
73
 
78
- msg1.merge_similar_message(msg2)
79
- msg1 = Logster::Message.from_json(msg1.to_json)
74
+ save_env = msg1.merge_similar_message(msg2)
75
+ assert(save_env)
80
76
  assert_equal(msg1.count, 15 + 13)
81
77
  end
82
78
 
@@ -85,9 +81,39 @@ class TestMessage < MiniTest::Test
85
81
  hash = { "custom_key" => "key" }
86
82
  msg.populate_from_env([hash])
87
83
 
88
- msg = Logster::Message.from_json(msg.to_json)
89
84
  assert Array === msg.env
90
85
  assert_equal(msg.env.size, 1)
91
86
  assert hash <= msg.env[0]
92
87
  end
88
+
89
+ def test_ensure_env_samples_dont_exceed_50
90
+ msg1 = Logster::Message.new(0, '', 'test', 10, count: 50)
91
+ msg1.env = [{ a: 1 }]
92
+ msg2 = Logster::Message.new(0, '', 'test', 20, count: 13)
93
+ msg2.env = { b: 2 }
94
+
95
+ assert_equal(msg1.grouping_key, msg2.grouping_key)
96
+
97
+ save_env = msg1.merge_similar_message(msg2)
98
+
99
+ refute(save_env)
100
+ assert_equal(63, msg1.count) # update count
101
+ assert_equal([{ a: 1 }], msg1.env) # but don't merge msg2's env into msg1's
102
+ end
103
+
104
+ def test_message_to_h_respects_params
105
+ msg = Logster::Message.new(0, "", "test")
106
+ test_hash = { test_key: "this is a test" }
107
+ msg.env = test_hash
108
+ assert_equal(test_hash, msg.to_h[:env])
109
+ assert_nil(msg.to_h(exclude_env: true)[:env])
110
+ end
111
+
112
+ def test_message_to_json_respects_params
113
+ msg = Logster::Message.new(0, "", "test")
114
+ test_hash = { test_key: "this is a test" }
115
+ msg.env = test_hash
116
+ assert_includes(msg.to_json, test_hash.to_json)
117
+ refute_includes(msg.to_json(exclude_env: true), test_hash.to_json)
118
+ end
93
119
  end