logster 1.2.11 → 1.3.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -17
  3. data/.travis.yml +15 -16
  4. data/CHANGELOG.md +130 -130
  5. data/Gemfile +4 -4
  6. data/Guardfile +8 -8
  7. data/LICENSE.txt +22 -22
  8. data/README.md +99 -96
  9. data/Rakefile +24 -23
  10. data/assets/fonts/FontAwesome.otf +0 -0
  11. data/assets/fonts/fontawesome-webfont.eot +0 -0
  12. data/assets/fonts/fontawesome-webfont.svg +639 -639
  13. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  14. data/assets/fonts/fontawesome-webfont.woff +0 -0
  15. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  16. data/assets/images/Icon-144_rounded.png +0 -0
  17. data/assets/images/Icon-144_square.png +0 -0
  18. data/assets/images/icon_144x144.png +0 -0
  19. data/assets/images/icon_64x64.png +0 -0
  20. data/assets/javascript/client-app.js +81 -0
  21. data/assets/javascript/vendor.js +5302 -0
  22. data/assets/stylesheets/client-app.css +1 -0
  23. data/assets/stylesheets/vendor.css +4 -0
  24. data/build_client_app.sh +12 -0
  25. data/client-app/.editorconfig +20 -0
  26. data/client-app/.ember-cli +9 -0
  27. data/client-app/.eslintignore +19 -0
  28. data/client-app/.eslintrc.js +46 -0
  29. data/client-app/.gitignore +23 -0
  30. data/client-app/.travis.yml +27 -0
  31. data/client-app/.watchmanconfig +3 -0
  32. data/client-app/README.md +57 -0
  33. data/client-app/app/app.js +14 -0
  34. data/client-app/app/components/message-info.js +18 -0
  35. data/client-app/app/components/message-row.js +45 -0
  36. data/client-app/app/components/panel-resizer.js +75 -0
  37. data/client-app/app/components/tab-contents.js +27 -0
  38. data/client-app/app/components/tab-link.js +5 -0
  39. data/client-app/app/components/tabbed-section.js +32 -0
  40. data/client-app/app/components/time-formatter.js +25 -0
  41. data/client-app/app/components/update-time.js +21 -0
  42. data/client-app/app/controllers/index.js +83 -0
  43. data/client-app/app/controllers/show.js +13 -0
  44. data/client-app/app/index.html +29 -0
  45. data/client-app/app/initializers/app-init.js +55 -0
  46. data/client-app/app/lib/preload.js +14 -0
  47. data/client-app/app/lib/utilities.js +140 -0
  48. data/client-app/app/models/message-collection.js +158 -0
  49. data/client-app/app/models/message.js +99 -0
  50. data/client-app/app/resolver.js +3 -0
  51. data/client-app/app/router.js +14 -0
  52. data/client-app/app/routes/index.js +53 -0
  53. data/client-app/app/routes/show.js +14 -0
  54. data/{assets/stylesheets → client-app/app/styles}/app.css +387 -390
  55. data/{assets/javascript → client-app/app}/templates/application.hbs +2 -2
  56. data/client-app/app/templates/components/message-info.hbs +44 -0
  57. data/{assets/javascript → client-app/app/templates}/components/message-row.hbs +17 -17
  58. data/client-app/app/templates/components/tabbed-section.hbs +10 -0
  59. data/client-app/app/templates/components/time-formatter.hbs +1 -0
  60. data/{assets/javascript → client-app/app}/templates/index.hbs +57 -57
  61. data/{assets/javascript → client-app/app}/templates/show.hbs +4 -4
  62. data/client-app/config/environment.js +51 -0
  63. data/client-app/config/optional-features.json +3 -0
  64. data/client-app/config/targets.js +18 -0
  65. data/client-app/ember-cli-build.js +29 -0
  66. data/client-app/package-lock.json +11365 -0
  67. data/client-app/package.json +56 -0
  68. data/client-app/testem.js +25 -0
  69. data/client-app/tests/index.html +34 -0
  70. data/client-app/tests/integration/components/message-info-test.js +26 -0
  71. data/client-app/tests/integration/components/message-row-test.js +26 -0
  72. data/client-app/tests/integration/components/panel-resizer-test.js +26 -0
  73. data/client-app/tests/integration/components/tab-contents-test.js +26 -0
  74. data/client-app/tests/integration/components/tab-link-test.js +26 -0
  75. data/client-app/tests/integration/components/tabbed-section-test.js +26 -0
  76. data/client-app/tests/integration/components/time-formatter-test.js +26 -0
  77. data/client-app/tests/integration/components/update-time-test.js +26 -0
  78. data/client-app/tests/test-helper.js +8 -0
  79. data/client-app/tests/unit/controllers/index-test.js +12 -0
  80. data/client-app/tests/unit/controllers/show-test.js +12 -0
  81. data/client-app/tests/unit/initializers/app-init-test.js +31 -0
  82. data/client-app/tests/unit/routes/index-test.js +11 -0
  83. data/client-app/tests/unit/routes/show-test.js +11 -0
  84. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  85. data/lib/logster.rb +54 -54
  86. data/lib/logster/base_store.rb +130 -130
  87. data/lib/logster/configuration.rb +25 -25
  88. data/lib/logster/ignore_pattern.rb +65 -65
  89. data/lib/logster/logger.rb +102 -101
  90. data/lib/logster/message.rb +227 -226
  91. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  92. data/lib/logster/middleware/reporter.rb +56 -54
  93. data/lib/logster/middleware/viewer.rb +220 -251
  94. data/lib/logster/rails/railtie.rb +58 -58
  95. data/lib/logster/redis_store.rb +481 -477
  96. data/lib/logster/version.rb +3 -3
  97. data/lib/logster/web.rb +14 -14
  98. data/logster.gemspec +34 -33
  99. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  100. data/test/fake_data/Gemfile +4 -4
  101. data/test/fake_data/generate.rb +10 -10
  102. data/test/logster/middleware/test_reporter.rb +21 -21
  103. data/test/logster/middleware/test_viewer.rb +96 -70
  104. data/test/logster/test_base_store.rb +147 -147
  105. data/test/logster/test_ignore_pattern.rb +41 -41
  106. data/test/logster/test_logger.rb +74 -74
  107. data/test/logster/test_message.rb +34 -34
  108. data/test/logster/test_redis_rate_limiter.rb +230 -230
  109. data/test/logster/test_redis_store.rb +427 -414
  110. data/test/test_helper.rb +38 -37
  111. data/vendor/assets/javascripts/logster.js.erb +39 -39
  112. metadata +83 -24
  113. data/assets/javascript/app.js +0 -817
  114. data/assets/javascript/components/message-info.hbs +0 -47
  115. data/assets/javascript/components/panel-resizer.hbs +0 -0
  116. data/assets/javascript/components/tab-contents.hbs +0 -1
  117. data/assets/javascript/components/tab-link.hbs +0 -1
  118. data/assets/javascript/components/tabbed-section.hbs +0 -6
  119. data/assets/javascript/external/ember-template-compiler.js +0 -22346
  120. data/assets/javascript/external/ember.js +0 -58500
  121. data/assets/javascript/external/ember.min.js +0 -17
  122. data/assets/javascript/external/jquery.min.js +0 -5
  123. data/assets/javascript/external/lodash.min.js +0 -87
  124. data/assets/javascript/external/moment.min.js +0 -6
  125. data/assets/stylesheets/font-awesome.min.css +0 -4
  126. data/bower.json +0 -25
@@ -1,414 +1,427 @@
1
- require_relative '../test_helper'
2
- require 'logster/redis_store'
3
- require 'rack'
4
-
5
- class TestRedisStore < Minitest::Test
6
-
7
- def setup
8
- @store = Logster::RedisStore.new(Redis.new)
9
- @store.clear_all
10
- end
11
-
12
- def teardown
13
- @store.clear_all
14
- end
15
-
16
- def test_delete
17
- msg = @store.report(Logger::WARN, "test", "testing")
18
- @store.delete(msg)
19
- latest = @store.latest
20
-
21
- assert_equal(0,latest.length)
22
- end
23
-
24
- def test_latest
25
- @store.report(Logger::WARN, "test", "IGNORE")
26
- @store.report(Logger::WARN, "test", "This is a warning")
27
- @store.report(Logger::WARN, "test", "This is another warning")
28
-
29
- latest = @store.latest(limit: 2)
30
-
31
- assert_equal(2, latest.length)
32
- assert_equal("This is a warning", latest[0].message)
33
- assert_equal("This is another warning", latest[1].message)
34
- assert_equal(Logger::WARN, latest[1].severity)
35
- assert_equal("test", latest[1].progname)
36
- assert(!latest[1].key.nil?)
37
- end
38
-
39
- def test_latest_after
40
- 10.times do |i|
41
- @store.report(Logger::WARN, "test", "A#{i}")
42
- end
43
-
44
- message = @store.latest[-1]
45
-
46
- 3.times do |i|
47
- @store.report(Logger::WARN, "test", i.to_s)
48
- end
49
-
50
- message = @store.latest(after: message.key, limit: 3)[0]
51
-
52
- assert_equal("0", message.message)
53
- end
54
-
55
- def test_latest_before
56
- 10.times do
57
- @store.report(Logger::WARN, "test", "A")
58
- end
59
- 10.times do
60
- @store.report(Logger::WARN, "test", "B")
61
- end
62
- 10.times do
63
- @store.report(Logger::WARN, "test", "C")
64
- end
65
-
66
- messages = @store.latest(limit: 10)
67
- assert_equal("C", messages[0].message)
68
- assert_equal(10, messages.length)
69
-
70
- messages = @store.latest(limit: 10, before: messages[0].key)
71
- assert_equal("B", messages[0].message)
72
- assert_equal(10, messages.length)
73
-
74
- messages = @store.latest(limit: 10, before: messages[0].key)
75
- assert_equal("A", messages[0].message)
76
- assert_equal(10, messages.length)
77
-
78
- end
79
-
80
- def test_get
81
- a_message = @store.report(Logger::WARN, "test", "A")
82
- b_message = @store.report(Logger::WARN, "test", "B")
83
- @store.report(Logger::WARN, "test", "C")
84
-
85
- assert_equal("A", @store.get(a_message.key).message)
86
- assert_equal("B", @store.get(b_message.key).message)
87
- end
88
-
89
- def test_backlog
90
- @store.max_backlog = 1
91
- @store.report(Logger::WARN, "test", "A")
92
- @store.report(Logger::WARN, "test", "A")
93
- @store.report(Logger::WARN, "test", "A")
94
- @store.report(Logger::WARN, "test", "B")
95
-
96
- latest = @store.latest
97
-
98
- assert_equal(1, latest.length)
99
- assert_equal("B", latest[0].message)
100
- end
101
-
102
- def test_save_unsave
103
- @store.max_backlog = 3
104
- @store.report(Logger::WARN, "test", "A")
105
- b_message = @store.report(Logger::WARN, "test", "B")
106
- @store.protect b_message.key
107
- c_message = @store.report(Logger::WARN, "test", "C")
108
- @store.protect c_message.key
109
- @store.report(Logger::WARN, "test", "D")
110
-
111
- latest = @store.latest
112
-
113
- assert_equal(3, latest.length)
114
- assert_equal("B", latest[0].message)
115
- assert_equal("C", latest[1].message)
116
- assert_equal(true, latest[1].protected)
117
- assert_equal("D", latest[2].message)
118
-
119
- # Saved messages still accessible by key
120
- assert_equal("B", @store.get(b_message.key).message)
121
- assert_equal(true, @store.get(b_message.key).protected)
122
-
123
- # Unsave does not delete message if still recent
124
- @store.unprotect c_message.key
125
- assert_equal("C", @store.get(c_message.key).message)
126
- assert_equal(false, @store.get(c_message.key).protected)
127
- end
128
-
129
- def test_clear
130
- @store.max_backlog = 25
131
- a_message = @store.report(Logger::WARN, "test", "A", timestamp: Time.now - (24*60*60))
132
- @store.protect a_message.key
133
- 20.times do
134
- @store.report(Logger::WARN, "test", "B")
135
- end
136
- c_message = @store.report(Logger::WARN, "test", "C", timestamp: Time.now + (24*60*60))
137
- @store.protect c_message.key
138
- d_message = @store.report(Logger::WARN, "test", "D")
139
- 10.times do
140
- @store.report(Logger::WARN, "test", "E")
141
- end
142
-
143
- latest = @store.latest
144
- assert_equal(25, latest.length)
145
-
146
- @store.clear
147
-
148
- # Protected messages are still accessible by their key
149
- assert_equal("C", @store.get(c_message.key).message)
150
- # Unprotected messages are gone
151
- assert_nil(@store.get(d_message.key))
152
-
153
- # The latest list is rebuilt with protected messages, earliest first
154
- # Including messages that previously fell off (A)
155
- latest = @store.latest
156
- assert_equal(2, latest.length)
157
- assert_equal("A", latest[0].message)
158
- assert_equal("C", latest[1].message)
159
- end
160
-
161
- def test_hash_cleanup
162
- @store.max_backlog = 2
163
- a_message = @store.report(Logger::WARN, "test", "A")
164
- @store.report(Logger::WARN, "test", "B")
165
- @store.report(Logger::WARN, "test", "C")
166
-
167
- assert_nil(@store.get(a_message.key))
168
- end
169
-
170
- def test_filter_latest
171
- @store.report(Logger::INFO, "test", "A")
172
- @store.report(Logger::WARN, "test", "B")
173
-
174
- messages = @store.latest
175
- assert_equal(2, messages.length)
176
-
177
- messages = @store.latest(after: messages.last.key)
178
- assert_equal(0, messages.length)
179
-
180
- 10.times do
181
- @store.report(Logger::INFO, "test", "A")
182
- end
183
- @store.report(Logger::ERROR, "test", "C")
184
- 10.times do
185
- @store.report(Logger::INFO, "test", "A")
186
- end
187
-
188
- latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2)
189
-
190
- assert_equal(2, latest.length)
191
- assert_equal("B", latest[0].message)
192
- assert_equal("C", latest[1].message)
193
-
194
- @store.report(Logger::ERROR, "test", "E")
195
- # respects after
196
- latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2, after: latest[1].key)
197
- assert_equal(1, latest.length);
198
- end
199
-
200
- def test_search
201
- @store.report(Logger::INFO, "test", "A")
202
- @store.report(Logger::INFO, "test", "B")
203
-
204
- messages = @store.latest
205
- assert_equal(2, messages.length)
206
-
207
- latest = @store.latest(search: "B")
208
-
209
- assert_equal(1, latest.length)
210
- end
211
-
212
- def test_regex_search
213
- @store.report(Logger::INFO, "test", "pattern_1")
214
- @store.report(Logger::INFO, "test", "pattern_2")
215
-
216
- messages = @store.latest
217
- assert_equal(2, messages.length)
218
-
219
- latest = @store.latest(search: /^pattern_[1]$/)
220
-
221
- assert_equal(1, latest.length)
222
- end
223
-
224
- def test_backtrace
225
- @store.report(Logger::INFO, "test", "pattern_1")
226
- message = @store.latest(limit: 1).first
227
- assert_match("test_backtrace", message.backtrace)
228
- end
229
-
230
- def test_ignore
231
- @store.ignore = [/^test/]
232
- @store.report(Logger::INFO, "test", "test it")
233
- @store.report(Logger::INFO, "test", " test it")
234
-
235
- assert_equal(1, @store.latest.count)
236
- end
237
-
238
- def test_solve
239
- Logster.config.application_version = "abc"
240
-
241
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
242
- m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1")
243
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace2")
244
-
245
- assert_equal(3, @store.latest.count)
246
-
247
- @store.solve(m.key)
248
-
249
- assert_equal(1, @store.latest.count)
250
-
251
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
252
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
253
-
254
- assert_equal(2, @store.latest.count)
255
-
256
- ensure
257
- Logster.config.application_version = nil
258
- end
259
-
260
- def test_solve_grouped
261
- Logster.config.allow_grouping = true
262
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
263
- m = @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg"})
264
-
265
- assert_equal(1, @store.latest.count)
266
-
267
- @store.solve(m.key)
268
-
269
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
270
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg"})
271
-
272
- assert_equal(0, @store.latest.count)
273
-
274
- ensure
275
- Logster.config.allow_grouping = false
276
- end
277
-
278
- def test_clears_solved
279
- m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1", env: {"application_version" => "abc"})
280
- @store.solve(m.key)
281
-
282
- assert_equal(1, @store.solved.length)
283
-
284
- @store.clear
285
- assert_equal(0, @store.solved.length)
286
- end
287
-
288
- def test_solving_with_some_missing_version
289
-
290
- m=@store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
291
- @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
292
-
293
- @store.solve(m.key)
294
-
295
- assert_equal(1, @store.latest.count)
296
- end
297
-
298
- def test_env
299
- env = Rack::MockRequest.env_for("/test").merge({
300
- "HTTP_HOST" => "www.site.com",
301
- "HTTP_USER_AGENT" => "SOME WHERE"
302
- })
303
- orig = env.dup
304
- orig["test"] = "tests"
305
- orig["test1"] = "tests1"
306
- Logster.add_to_env(env,"test","tests")
307
- Logster.add_to_env(env,"test1","tests1")
308
-
309
- orig.delete_if do |k,v|
310
- !%w{
311
- HTTP_HOST
312
- REQUEST_METHOD
313
- HTTP_USER_AGENT
314
- test
315
- test1
316
- }.include? k
317
- end
318
-
319
- @store.report(Logger::INFO, "test", "test", env: env)
320
-
321
- env = @store.latest.last.env
322
-
323
- env.delete "hostname"
324
- env.delete "process_id"
325
-
326
- assert_equal(orig, env)
327
- end
328
-
329
- def test_rate_limits
330
- %w{minute hour}.each do |duration|
331
- begin
332
- called = false
333
-
334
- assert_instance_of(
335
- Logster::RedisRateLimiter,
336
- @store.public_send("register_rate_limit_per_#{duration}", Logger::WARN, 0) do
337
- called = true
338
- end
339
- )
340
-
341
- @store.report(Logger::WARN, "test", "test")
342
- assert called
343
- ensure
344
- reset_redis
345
- end
346
- end
347
- end
348
-
349
- def test_rate_limits_only_checks_when_message_is_bumped_or_saved
350
- Logster.config.allow_grouping = true
351
- Logster.config.application_version = 'abc'
352
-
353
- @store.ignore = [/^ActiveRecord::RecordNotFound/]
354
- rate_limit = @store.register_rate_limit_per_minute(Logger::WARN, 0)
355
-
356
- message = @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
357
- assert_equal(1, rate_limit.retrieve_rate)
358
-
359
- @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
360
- assert_equal(2, rate_limit.retrieve_rate)
361
-
362
- @store.solve(message.key)
363
- @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
364
- assert_equal(2, rate_limit.retrieve_rate)
365
-
366
- @store.report(Logger::WARN, 'message 2', "Error!")
367
- assert_equal(3, rate_limit.retrieve_rate)
368
-
369
- @store.report(Logger::WARN, 'message 3', "ActiveRecord::RecordNotFound")
370
- assert_equal(3, rate_limit.retrieve_rate)
371
- ensure
372
- Logster.config.allow_grouping = false
373
- Logster.config.application_version = nil
374
- reset_redis
375
- end
376
-
377
- def test_rate_limits_with_prefix
378
- begin
379
- time = Time.now
380
- Timecop.freeze(time)
381
- current_namespace = 'first'
382
- @store.redis_prefix = Proc.new { current_namespace }
383
-
384
- called_first = 0
385
- called_second = 0
386
-
387
- @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_first += 1 }
388
- @store.report(Logger::WARN, "test", "test")
389
- assert_equal(1, called_first)
390
-
391
- current_namespace = 'second'
392
- @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_second += 1 }
393
- @store.report(Logger::WARN, "test", "test")
394
- assert_equal(1, called_first)
395
- assert_equal(1, called_second)
396
-
397
- Timecop.freeze(time + 10) do
398
- current_namespace = 'first'
399
- @store.report(Logger::WARN, "test", "test")
400
-
401
- assert_equal(2, called_first)
402
- assert_equal(1, called_second)
403
- end
404
- ensure
405
- reset_redis
406
- end
407
- end
408
-
409
- private
410
-
411
- def reset_redis
412
- @store.redis.flushall
413
- end
414
- end
1
+ require_relative '../test_helper'
2
+ require 'logster/redis_store'
3
+ require 'rack'
4
+
5
+ class TestRedisStore < Minitest::Test
6
+
7
+ def setup
8
+ @store = Logster::RedisStore.new(Redis.new)
9
+ @store.clear_all
10
+ end
11
+
12
+ def teardown
13
+ @store.clear_all
14
+ end
15
+
16
+ def test_delete
17
+ msg = @store.report(Logger::WARN, "test", "testing")
18
+ @store.delete(msg)
19
+ latest = @store.latest
20
+
21
+ assert_equal(0,latest.length)
22
+ end
23
+
24
+ def test_latest
25
+ @store.report(Logger::WARN, "test", "IGNORE")
26
+ @store.report(Logger::WARN, "test", "This is a warning")
27
+ @store.report(Logger::WARN, "test", "This is another warning")
28
+
29
+ latest = @store.latest(limit: 2)
30
+
31
+ assert_equal(2, latest.length)
32
+ assert_equal("This is a warning", latest[0].message)
33
+ assert_equal("This is another warning", latest[1].message)
34
+ assert_equal(Logger::WARN, latest[1].severity)
35
+ assert_equal("test", latest[1].progname)
36
+ assert(!latest[1].key.nil?)
37
+ end
38
+
39
+ def test_latest_after
40
+ 10.times do |i|
41
+ @store.report(Logger::WARN, "test", "A#{i}")
42
+ end
43
+
44
+ message = @store.latest[-1]
45
+
46
+ 3.times do |i|
47
+ @store.report(Logger::WARN, "test", i.to_s)
48
+ end
49
+
50
+ message = @store.latest(after: message.key, limit: 3)[0]
51
+
52
+ assert_equal("0", message.message)
53
+ end
54
+
55
+ def test_latest_before
56
+ 10.times do
57
+ @store.report(Logger::WARN, "test", "A")
58
+ end
59
+ 10.times do
60
+ @store.report(Logger::WARN, "test", "B")
61
+ end
62
+ 10.times do
63
+ @store.report(Logger::WARN, "test", "C")
64
+ end
65
+
66
+ messages = @store.latest(limit: 10)
67
+ assert_equal("C", messages[0].message)
68
+ assert_equal(10, messages.length)
69
+
70
+ messages = @store.latest(limit: 10, before: messages[0].key)
71
+ assert_equal("B", messages[0].message)
72
+ assert_equal(10, messages.length)
73
+
74
+ messages = @store.latest(limit: 10, before: messages[0].key)
75
+ assert_equal("A", messages[0].message)
76
+ assert_equal(10, messages.length)
77
+
78
+ end
79
+
80
+ def test_get
81
+ a_message = @store.report(Logger::WARN, "test", "A")
82
+ b_message = @store.report(Logger::WARN, "test", "B")
83
+ @store.report(Logger::WARN, "test", "C")
84
+
85
+ assert_equal("A", @store.get(a_message.key).message)
86
+ assert_equal("B", @store.get(b_message.key).message)
87
+ end
88
+
89
+ def test_backlog
90
+ @store.max_backlog = 1
91
+ @store.report(Logger::WARN, "test", "A")
92
+ @store.report(Logger::WARN, "test", "A")
93
+ @store.report(Logger::WARN, "test", "A")
94
+ @store.report(Logger::WARN, "test", "B")
95
+
96
+ latest = @store.latest
97
+
98
+ assert_equal(1, latest.length)
99
+ assert_equal("B", latest[0].message)
100
+ end
101
+
102
+ def test_save_unsave
103
+ @store.max_backlog = 3
104
+ @store.report(Logger::WARN, "test", "A")
105
+ b_message = @store.report(Logger::WARN, "test", "B")
106
+ @store.protect b_message.key
107
+ c_message = @store.report(Logger::WARN, "test", "C")
108
+ @store.protect c_message.key
109
+ @store.report(Logger::WARN, "test", "D")
110
+
111
+ latest = @store.latest
112
+
113
+ assert_equal(3, latest.length)
114
+ assert_equal("B", latest[0].message)
115
+ assert_equal("C", latest[1].message)
116
+ assert_equal(true, latest[1].protected)
117
+ assert_equal("D", latest[2].message)
118
+
119
+ # Saved messages still accessible by key
120
+ assert_equal("B", @store.get(b_message.key).message)
121
+ assert_equal(true, @store.get(b_message.key).protected)
122
+
123
+ # Unsave does not delete message if still recent
124
+ @store.unprotect c_message.key
125
+ assert_equal("C", @store.get(c_message.key).message)
126
+ assert_equal(false, @store.get(c_message.key).protected)
127
+ end
128
+
129
+ def test_clear
130
+ @store.max_backlog = 25
131
+ a_message = @store.report(Logger::WARN, "test", "A", timestamp: Time.now - (24*60*60))
132
+ @store.protect a_message.key
133
+ 20.times do
134
+ @store.report(Logger::WARN, "test", "B")
135
+ end
136
+ c_message = @store.report(Logger::WARN, "test", "C", timestamp: Time.now + (24*60*60))
137
+ @store.protect c_message.key
138
+ d_message = @store.report(Logger::WARN, "test", "D")
139
+ 10.times do
140
+ @store.report(Logger::WARN, "test", "E")
141
+ end
142
+
143
+ latest = @store.latest
144
+ assert_equal(25, latest.length)
145
+
146
+ @store.clear
147
+
148
+ # Protected messages are still accessible by their key
149
+ assert_equal("C", @store.get(c_message.key).message)
150
+ # Unprotected messages are gone
151
+ assert_nil(@store.get(d_message.key))
152
+
153
+ # The latest list is rebuilt with protected messages, earliest first
154
+ # Including messages that previously fell off (A)
155
+ latest = @store.latest
156
+ assert_equal(2, latest.length)
157
+ assert_equal("A", latest[0].message)
158
+ assert_equal("C", latest[1].message)
159
+ end
160
+
161
+ def test_hash_cleanup
162
+ @store.max_backlog = 2
163
+ a_message = @store.report(Logger::WARN, "test", "A")
164
+ @store.report(Logger::WARN, "test", "B")
165
+ @store.report(Logger::WARN, "test", "C")
166
+
167
+ assert_nil(@store.get(a_message.key))
168
+ end
169
+
170
+ def test_filter_latest
171
+ @store.report(Logger::INFO, "test", "A")
172
+ @store.report(Logger::WARN, "test", "B")
173
+
174
+ messages = @store.latest
175
+ assert_equal(2, messages.length)
176
+
177
+ messages = @store.latest(after: messages.last.key)
178
+ assert_equal(0, messages.length)
179
+
180
+ 10.times do
181
+ @store.report(Logger::INFO, "test", "A")
182
+ end
183
+ @store.report(Logger::ERROR, "test", "C")
184
+ 10.times do
185
+ @store.report(Logger::INFO, "test", "A")
186
+ end
187
+
188
+ latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2)
189
+
190
+ assert_equal(2, latest.length)
191
+ assert_equal("B", latest[0].message)
192
+ assert_equal("C", latest[1].message)
193
+
194
+ @store.report(Logger::ERROR, "test", "E")
195
+ # respects after
196
+ latest = @store.latest(severity: [Logger::ERROR, Logger::WARN], limit: 2, after: latest[1].key)
197
+ assert_equal(1, latest.length);
198
+ end
199
+
200
+ def test_search
201
+ @store.report(Logger::INFO, "test", "A")
202
+ @store.report(Logger::INFO, "test", "B")
203
+
204
+ messages = @store.latest
205
+ assert_equal(2, messages.length)
206
+
207
+ latest = @store.latest(search: "B")
208
+
209
+ assert_equal(1, latest.length)
210
+ end
211
+
212
+ def test_search_exclude_results
213
+ @store.report(Logger::INFO, "test", "A")
214
+ @store.report(Logger::INFO, "test", "B")
215
+
216
+ messages = @store.latest
217
+ assert_equal(2, messages.length)
218
+
219
+ latest = @store.latest(search: "-A")
220
+
221
+ assert_equal(1, latest.length)
222
+ assert_equal("B", latest[0].message)
223
+ end
224
+
225
+ def test_regex_search
226
+ @store.report(Logger::INFO, "test", "pattern_1")
227
+ @store.report(Logger::INFO, "test", "pattern_2")
228
+
229
+ messages = @store.latest
230
+ assert_equal(2, messages.length)
231
+
232
+ latest = @store.latest(search: /^pattern_[1]$/)
233
+
234
+ assert_equal(1, latest.length)
235
+ end
236
+
237
+ def test_backtrace
238
+ @store.report(Logger::INFO, "test", "pattern_1")
239
+ message = @store.latest(limit: 1).first
240
+ assert_match("test_backtrace", message.backtrace)
241
+ end
242
+
243
+ def test_ignore
244
+ @store.ignore = [/^test/]
245
+ @store.report(Logger::INFO, "test", "test it")
246
+ @store.report(Logger::INFO, "test", " test it")
247
+
248
+ assert_equal(1, @store.latest.count)
249
+ end
250
+
251
+ def test_solve
252
+ Logster.config.application_version = "abc"
253
+
254
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
255
+ m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1")
256
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace2")
257
+
258
+ assert_equal(3, @store.latest.count)
259
+
260
+ @store.solve(m.key)
261
+
262
+ assert_equal(1, @store.latest.count)
263
+
264
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
265
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
266
+
267
+ assert_equal(2, @store.latest.count)
268
+
269
+ ensure
270
+ Logster.config.application_version = nil
271
+ end
272
+
273
+ def test_solve_grouped
274
+ Logster.config.allow_grouping = true
275
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
276
+ m = @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg"})
277
+
278
+ assert_equal(1, @store.latest.count)
279
+
280
+ @store.solve(m.key)
281
+
282
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
283
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "efg"})
284
+
285
+ assert_equal(0, @store.latest.count)
286
+
287
+ ensure
288
+ Logster.config.allow_grouping = false
289
+ end
290
+
291
+ def test_clears_solved
292
+ m = @store.report(Logger::WARN, "application", "test error2", backtrace: "backtrace1", env: {"application_version" => "abc"})
293
+ @store.solve(m.key)
294
+
295
+ assert_equal(1, @store.solved.length)
296
+
297
+ @store.clear
298
+ assert_equal(0, @store.solved.length)
299
+ end
300
+
301
+ def test_solving_with_some_missing_version
302
+
303
+ m=@store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1", env: { "application_version" => "xyz"})
304
+ @store.report(Logger::WARN, "application", "test error1", backtrace: "backtrace1")
305
+
306
+ @store.solve(m.key)
307
+
308
+ assert_equal(1, @store.latest.count)
309
+ end
310
+
311
+ def test_env
312
+ env = Rack::MockRequest.env_for("/test").merge({
313
+ "HTTP_HOST" => "www.site.com",
314
+ "HTTP_USER_AGENT" => "SOME WHERE"
315
+ })
316
+ orig = env.dup
317
+ orig["test"] = "tests"
318
+ orig["test1"] = "tests1"
319
+ Logster.add_to_env(env,"test","tests")
320
+ Logster.add_to_env(env,"test1","tests1")
321
+
322
+ orig.delete_if do |k,v|
323
+ !%w{
324
+ HTTP_HOST
325
+ REQUEST_METHOD
326
+ HTTP_USER_AGENT
327
+ test
328
+ test1
329
+ }.include? k
330
+ end
331
+
332
+ @store.report(Logger::INFO, "test", "test", env: env)
333
+
334
+ env = @store.latest.last.env
335
+
336
+ env.delete "hostname"
337
+ env.delete "process_id"
338
+
339
+ assert_equal(orig, env)
340
+ end
341
+
342
+ def test_rate_limits
343
+ %w{minute hour}.each do |duration|
344
+ begin
345
+ called = false
346
+
347
+ assert_instance_of(
348
+ Logster::RedisRateLimiter,
349
+ @store.public_send("register_rate_limit_per_#{duration}", Logger::WARN, 0) do
350
+ called = true
351
+ end
352
+ )
353
+
354
+ @store.report(Logger::WARN, "test", "test")
355
+ assert called
356
+ ensure
357
+ reset_redis
358
+ end
359
+ end
360
+ end
361
+
362
+ def test_rate_limits_only_checks_when_message_is_bumped_or_saved
363
+ Logster.config.allow_grouping = true
364
+ Logster.config.application_version = 'abc'
365
+
366
+ @store.ignore = [/^ActiveRecord::RecordNotFound/]
367
+ rate_limit = @store.register_rate_limit_per_minute(Logger::WARN, 0)
368
+
369
+ message = @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
370
+ assert_equal(1, rate_limit.retrieve_rate)
371
+
372
+ @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
373
+ assert_equal(2, rate_limit.retrieve_rate)
374
+
375
+ @store.solve(message.key)
376
+ @store.report(Logger::WARN, 'message 1', "Error!", backtrace: 'here')
377
+ assert_equal(2, rate_limit.retrieve_rate)
378
+
379
+ @store.report(Logger::WARN, 'message 2', "Error!")
380
+ assert_equal(3, rate_limit.retrieve_rate)
381
+
382
+ @store.report(Logger::WARN, 'message 3', "ActiveRecord::RecordNotFound")
383
+ assert_equal(3, rate_limit.retrieve_rate)
384
+ ensure
385
+ Logster.config.allow_grouping = false
386
+ Logster.config.application_version = nil
387
+ reset_redis
388
+ end
389
+
390
+ def test_rate_limits_with_prefix
391
+ begin
392
+ time = Time.now
393
+ Timecop.freeze(time)
394
+ current_namespace = 'first'
395
+ @store.redis_prefix = Proc.new { current_namespace }
396
+
397
+ called_first = 0
398
+ called_second = 0
399
+
400
+ @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_first += 1 }
401
+ @store.report(Logger::WARN, "test", "test")
402
+ assert_equal(1, called_first)
403
+
404
+ current_namespace = 'second'
405
+ @store.register_rate_limit_per_minute(Logger::WARN, 0) { called_second += 1 }
406
+ @store.report(Logger::WARN, "test", "test")
407
+ assert_equal(1, called_first)
408
+ assert_equal(1, called_second)
409
+
410
+ Timecop.freeze(time + 10) do
411
+ current_namespace = 'first'
412
+ @store.report(Logger::WARN, "test", "test")
413
+
414
+ assert_equal(2, called_first)
415
+ assert_equal(1, called_second)
416
+ end
417
+ ensure
418
+ reset_redis
419
+ end
420
+ end
421
+
422
+ private
423
+
424
+ def reset_redis
425
+ @store.redis.flushall
426
+ end
427
+ end